Eu sempre precisei gerar organograma da empresa, mas cada vez mais o método que eu utilizava foi se tornando mais difícil (ou raro), pois dependia de uma licença funcional do Microsoft Visio — e, cada vez mais, o(s) computador(es) que ainda tinham alguma licença eram PC’s antigos, com uma versão bem desatualizada. Sendo assim, cada vez mais existir o risco de vulnerabilidades na rede (por ter o computador antigo) ou de não conseguir mais gerar o organograma.
Foi então que decidi criar um script em PowerShell, capaz de gerar o organograma diretamente a partir do Active Directory (AD). A ideia é simples:
- Você informa o nome do colaborador que “está no topo” do nível hierárquico que deseja visualizar;
- O script consulta o AD local, identifica os colaboradores subordinados (depois os subordinados de cada um deles, assim por diante), construindo a cadeia hierárquica completa em segundos.
- Em seguida, utiliza um “template” (um html, que eu já configurei o layout da forma que eu quero) e insere esse “array” de todos os membros e vinculos entre eles.
Enquanto o Visio usava o Outlook como base para essa estrutura, o meu script dispensa licenças pagas e trabalha diretamente com as informações do AD corporativo, garantindo dados sempre atualizados e consistentes. Eliminando a dependência de software legado.
Apesar de termos optado por utilizar o Google chart como padrão para o layout de organograma, vamos demonstrar aqui o uso do template mais agradável que testamos, com o BALKAN OrgChart JS (mas atenção: É necessário comprar uma licença para usar este componente, em nosso Github existem mais templates, com opções gratuítas para vocês escolher):
Abaixo o script PowerShell (altere as partes em cinza, conforme sua realidade):
# CUSTOM (CHANGE) THIS PART OF THE SCRIPT
#Default information (If is blank in some user, CHANGE)
$DefaultTelephone = "+55 (61) 98505-1070"
$DefaultTitle = "------------------"
$DefaultEmail = "equipe@nvlan.com.br"
# Avatar image (UTR with a Avatar image/icon)
$avatarURL = "https://openclipart.org/image/800px/307452"
$htmlFolder = "D:\Generate Organizational chart"
$templateFile = "template5.html" #(could be template1.html to template5.html)
#-------------------------------------------------------------------------------------------
$global:OrgArray = @()
$global:UserId = 1
# Get the avatar image
$webClient = New-Object System.Net.WebClient
$imageBytes = $webClient.DownloadData($avatarURL)
$contentType = $webClient.ResponseHeaders["Content-Type"]
if (-not $contentType) {
switch -regex ($url.ToLower()) {
'\.jpg$|\.jpeg$' { $contentType = "image/jpeg" }
'\.png$' { $contentType = "image/png" }
'\.gif$' { $contentType = "image/gif" }
'\.bmp$' { $contentType = "image/bmp" }
'\.webp$' { $contentType = "image/webp" }
default { $contentType = "application/octet-stream" }
}
}
# Convert image to Base64
$base64String = [System.Convert]::ToBase64String($imageBytes)
$DefaultIconBase64 = "data:$contentType;base64,$base64String"
function Order-OrgArray {
param(
[Parameter(Mandatory, ValueFromPipeline)]
[array]$OrgArray
)
process {
$OrgArray |
Select-Object *, @{Name="OrderTitle";Expression={
switch -regex ($_.Title.ToLower()) {
'^diretor' { 1; break }
'^secretar' { 2; break }
'^gerente' { 3; break }
'^coordenador' { 4; break }
'^estagi' { 6; break }
'jovem' { 7; break }
default { 5 }
}
}} |
Sort-Object PID, OrderTitle, Name
}
}
function Get-ADOrgTopology {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Manager,
[int]$Level,
[int]$ParentId
)
$UserData = Get-ADUser $Manager -Properties DisplayName, manager, mail, telephoneNumber, title, thumbnailPhoto
If(!$UserData) {
Write-Host "User $Manager not found!"
}
Else {
if (!$Level) { $Level = 1 }
$name = $UserData.DisplayName
$fullname = $UserData.Name
$title = $UserData.Title
#Set "tag" depending of the user title (change if you want)
switch -Wildcard ($title) {
"diretor*" { $tag = "ceo" }
"secret*" { $tag = "assistant" }
"gerente*" { $tag = "gerente" }
"coordenador*" { $tag = "coordenador" }
"estag*" { $tag = "estagiario" }
default { $tag = "0" }
}
#Setting default valeus, if the user value is blank
if (!$title) { $title = $DefaultTitle }
$mail = $UserData.mail
if (!$mail) { $mail = $DefaultEmail }
$telephoneNumber = $UserData.telephoneNumber
if (!$telephoneNumber) { $telephoneNumber = $DefaultTelephone }
$thumbnailPhoto = $UserData.thumbnailPhoto
If (!$thumbnailPhoto)
{
$thumbnailPhoto = "data:image/png;base64,$DefaultIconBase64"
}
Else
{
$thumbnailPhoto = [System.Convert]::ToBase64String($thumbnailPhoto)
$thumbnailPhoto = "data:image/png;base64,$thumbnailPhoto"
}
# Store the current ID
$CurrentId = $global:UserId
# Add to array with ParentId ($null if the first node)
$global:OrgArray += [PSCustomObject]@{
ID = $CurrentId
PID = $ParentId
Name = $name
FullName = $fullname
Title = $title
Tag = $tag
Email = $mail
Telephone = $telephoneNumber
thumbnailPhoto = $thumbnailPhoto
}
$global:UserId++
# Search the Team
$DirectReports = Get-ADUser -Filter { manager -eq $Manager }
$NewLevel = $Level + 1
$DirectReports | Where-Object { $_.Enabled -eq $true } | ForEach-Object {
# Set the current ID as ParentId for the Team
Get-ADOrgTopology -Manager $_.DistinguishedName -Level $NewLevel -ParentId $CurrentId
}
}
}
Clear-Host
$ManagerName = Read-Host "Insert the username in the top of you Organogram"
Get-ADOrgTopology -Manager $ManagerName
#Show (for test only)
#$OrgArray | Format-Table -AutoSize
If ($OrgArray) {
$OrgArray = Order-OrgArray -OrgArray $OrgArray
$templateFile = $htmlFolder +"\"+ $templateFile
If (Test-Path $templateFile) {
$Template_DefaultSite = Get-Content $templateFile
foreach ($line in $OrgArray) {
$string = " { id: `"" + $line.ID +"`","
If ($line.PID -ne 0) { $string += "pid: `""+$line.PID+"`","}
If ($line.Tag -ne "0") { $string += " tags: [`""+$line.Tag+"`"],"}
$string += " name: `""+ $line.Name +"`", Title: `""+ $line.Title +"`", Phone: `""+ $line.Telephone +"`", Email: `""+ $line.Email +"`", img: `""+ $line.thumbnailPhoto +"`"},"
$Template_DefaultSite = $Template_DefaultSite.replace("//NodeList",$string +"`n//NodeList")
}
$Template_DefaultSite | Out-File -Encoding utf8 -FilePath "$htmlFolder\index.html" -Force
Write-Host "Done"
}
Else {Write-Host "$templateFile not found!"}
}
Aqui, o arquivo de template (lembrando neste exemplo está o BALKAN OrgChart JS, que é necessário comprar uma licença para usar este componente, em nosso Github existem mais templates, com opções gratuítas para vocês escolher), para que você altere o script PowerShell para usar esse arquivo como base:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>OrgChart Exemplo</title>
<script src="https://balkan.app/js/OrgChart.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#tree {
width: 100%;
height: 100vh;
}
.node.ceo rect { fill: #339933; }
.node.assistant rect { fill: #4CAFAA; }
.node.gerente rect { fill: #D80073; }
.node.coordenador rect { fill: #FFBF00; }
.node.estagiario rect { fill: #A3D9F4; }
.node.QA rect { fill: #FFFFFF; }
.node.QA text { fill: #F57C00; }
</style>
</head>
<body>
<div id="tree"></div>
<script>
OrgChart.MIXED_LAYOUT_IF_NUMBER_OF_CHILDREN_IS_MORE_THEN = 1;
//REMOVE AS OPCOES DE OCULTAR E/OU EXPANDIR
OrgChart.templates.ana.plus = "";
OrgChart.templates.ana.minus = "";
// Alinhando todos os campos à direita
OrgChart.templates.ana.field_0 = '<text width="230" style="font-size: 10px;" fill="#ffffff" text-anchor="end" x="240" y="25">{val}</text>';
OrgChart.templates.ana.field_1 = '<text width="230" style="font-size: 10px;" fill="#ffffff" text-anchor="end" x="240" y="45">{val}</text>';
OrgChart.templates.ana.field_2 = '<text width="230" style="font-size: 11px;" fill="#ffffff" text-anchor="start" x="10" y="85">{val}</text>';
OrgChart.templates.ana.field_3 = '<text width="230" style="font-size: 11px;" fill="#ffffff" text-anchor="start" x="10" y="100">{val}</text>';
//Padrão do Template
OrgChart.templates.ana.node =
'<rect x="0" y="0" height="115" width="250" fill="#1BA1E2" stroke-width="1" stroke="#aeaeae"></rect>';
// Criando o gráfico
var chart = new OrgChart(document.getElementById("tree"), {
mouseScrool: OrgChart.action.none,
//AJUSTA A TELA
scaleInitial: OrgChart.match.boundary,
layout: OrgChart.treeRightOffset,
assistantSeparation: 50,
tags: {
department: { min: true },
"hidden": { template: "hiddenTemplate" }
},
searchDisplayField: 'name',
searchFieldsWeight: {
"name": 100, //percent
"Title": 20 //percent
},
nodeBinding: {
field_0: "Phone",
field_1: "Email",
field_2: "name",
field_3: "Title",
img_0: "img"
},
});
//RETIRA A OPCAO DE PODER CLICAR (E EXPANDIR O USUARIO)
chart.on('click', function (sender, args) {
if (args.node.min) {
sender.maximize(args.node.id);
}
else {
sender.minimize(args.node.id);
}
//return false;
});
// Carrega dados
chart.load([
//NodeList
]);
</script>
</body>
</html>
Fontes/Referências
https://balkan.app/OrgChartJS/Pricing
Mais Informações
Esperamos ter te ajudado e estaremos sempre a disposição para mais informações.
Se você tem interesse em algum assunto específico, tem alguma dúvida, precisa de ajuda, ou quer sugerir um post, entre em contato conosco pelo e-mail equipe@nvlan.com.br.
