Em projetos de desenvolvimento de Business Intelligence existe, normalmente, a necessidade de configurar segurança nos acessos aos dados por parte de utilizadores e/ou grupos. Um exemplo é a restrição de membros em dimensões nos cubos em Analysis Services. Nestes casos, uma solução possível passa por definir e carregar uma tabela para mapeamento entre utilizadores e os membros de dimensões a restringir.
Num dos nossos projetos, foi necessário carregar os grupos e respetivos utilizadores diretamente da Active Directory. Assim, os grupos deveriam estar localizados numa diretoria da AD previamente definida.
O objetivo era implementar um dtsx que extraísse esta informação da AD e que a carregasse numa tabela temporária para posteriormente ser tratada nos restantes processos do Datawarehouse. Na maioria dos casos, quando queremos transferir dados de uma base de dados relacional ou ficheiro, usamos o componente de Data Flow Task. Neste caso vamos dispensar esse componente e usar um Script Task.
Através deste componente é possível implementar código em C# com base no namespace AccountManagement da Framework .Net (3.5 ou superior). Este namespace permite pesquisar e manusear utilizadores e grupos na AD de forma mais simples do que o namespace DirectoryServices já existente em versões mais antigas da framework, onde era necessário um conhecimento mais extenso dos métodos e utilização bem como implicava escrever código mais longo.
No excerto do código C# que se segue, que está inserido na Script Task, num primeiro passo queremos identificar os grupos numa determinada organization Unit (OU) onde é suposto estarem inseridos os grupos, cujos utilizadores membros devem ser extraídos no processo. Nesta parte do código realizam-se os passos para fazer a pesquisa.
//Cria o contexto sobre o qual a pesquisa é feita. Os primeiros dois parâmetros são usados para definir o domínio e o terceiro é utilizado para definir a localização dentro da AD onde será feita a pesquisa.
PrincipalContext ouContext = new PrincipalContext(ContextType.Domain, “domain.com”, “OU=BIGroups,DC=domain,DC=com”);
//Cria um objeto GroupProncial que será usado como filtro na pesquisa na seguinte linha de código. Recebe o contexto de domínio a aplicar na pesquisa bem como o SamAccountName (nome único na AD) a pesquisar. Neste caso, utilizado o *, serão retornados todos os grupos existentes.
GroupPrincipal allGroupsInContext = new GroupPrincipal(ouContext, “*”);
//Instância do objeto que encapsula os métodos e padrões de pesquisa a executar no contexto especificado.
PrincipalSearcher ps = new PrincipalSearcher(allGroupsInContext);
//Executa a pesquisa. O resultado é uma coleção de objetos Principal (utilizadores ou grupos) que resultam da pesquisa.
PrincipalSearchResult<Principal> groupResults = ps.FindAll();
No segundo passo, o resultado da pesquisa é traduzido em utilizadores e respetivos grupos. É utilizado o objeto DataTable para preencher uma tabela com os dados enquanto o resultado da pesquisa é iterado. Finalmente, depois de todos os utilizadores estarem carregados na tabela em memória, os dados são inseridos na base de dados em Sql Server através da classe SqlBulkCopy.
//Cria o objeto DataTable para armazenar temporariamente os utilizadores e respetivo grupo da AD.
DataTable table = new DataTable();
table.Columns.Add(“User”, typeof(string));
table.Columns.Add(“Name”, typeof(string));
table.Columns.Add(“Email”, typeof(string));
table.Columns.Add(“Group”, typeof(string));
//Itera sobre os grupos na OU especificada na pesquisa
foreach (var group in groupResults)
{
GroupPrincipal gr = (GroupPrincipal)group;
//Obtém todos os membros de um determinado grupo
PrincipalSearchResult<Principal> userResults = gr.GetMembers();
//Itera sobre todos os membros existente dentro do grupo
foreach (Principal result in userResults)
{
//Estamos a pesquisar utilizadores apenas. O resto é ignorado (ex: grupos dentro do grupo)
if (result.StructuralObjectClass == “user”){
UserPrincipal user = (UserPrincipal)result;
//Guarda no datatable o utilizador
table.Rows.Add(user.SamAccountName, user.Name, user.EmailAddress, group.Name);
}
}
}
//Transfere os dados para uma tabela em Sql Server
using (SqlConnection conn = new SqlConnection(“Connection”))
{
conn.Open();
SqlBulkCopy bulk = new SqlBulkCopy(conn);
bulk.DestinationTableName = “StagingTable”;
bulk.WriteToServer(table);
}
Desta forma temos uma solução simples para extrair utilizadores da Active Directory sem ser necessário ter um conhecimento profundo da AD e interfaces de acesso. A utilização do namespace .Net e criação de algumas linhas em C# revelou ser uma forma bastante simples de pesquisar e obter a informação de utilizadores e grupos.
Este é um exemplo para um problema específico no nosso projeto, contudo, existem outras operações que se podem fazer no que diz respeito a aceder e manipular utilizadores e grupos através de classes do mesmo namespace.
Existem ainda outras formas de processar informação da Active Directory num pacote em SSIS (se quisermos evitar escrever código em .Net) e que permite usar outros componentes nativos do SSIS, como o Data Flow Task como explicado no seguinte artigo. Contudo, na minha opinião, não é tão simples, intuitivo e flexível como recorrendo ao ScriptTask e namespace .Net descritos neste exemplo.