├── Common ├── Domain.ps1 ├── Functions.ps1 ├── Jobs.ps1 ├── Office │ └── Excel │ │ └── ExcelObject.ps1 ├── Runspace.ps1 ├── Vars.ps1 └── getconfig.ps1 ├── Config └── Voyeur.config ├── Images ├── NCC-logoFront.png ├── NCC-logoFront_.png ├── NCCLogo.jpg ├── NCCLogo.png ├── User.png ├── ncc-logo.png └── ncc-logo_lab.png ├── LICENSE ├── Plugins ├── ACL.ps1 ├── Computers.ps1 ├── GppPasswords.ps1 ├── GroupMonitor.ps1 ├── GroupPolicy.ps1 ├── Groups.ps1 ├── PasswordPolicy.ps1 ├── Roles.ps1 └── users.ps1 ├── README.md ├── Utils ├── CsvReport.ps1 ├── ExcelReport.ps1 ├── JsonReport.ps1 └── XmlReport.ps1 └── voyeur.ps1 /Common/Domain.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------- 2 | # Return the Domain SID 3 | #--------------------------------------------------- 4 | 5 | function Get-DomainSID{ 6 | try{ 7 | $KerberosEncType = [System.DirectoryServices.AuthenticationTypes]::Sealing -bor [System.DirectoryServices.AuthenticationTypes]::Secure 8 | $SSLEncType = [System.DirectoryServices.AuthenticationTypes]::SecureSocketsLayer 9 | $GlobalCatalog = $Global:Forest.FindGlobalCatalog() 10 | if ($Global:AllDomainData -and $GlobalCatalog){ 11 | $newLDAPConnection = $Global:AllDomainData.LDAPBase 12 | if($UseSSL){ 13 | $DN = "GC://$($GlobalCatalog.Name):636/$($Global:AllDomainData.DistinguishedName)" 14 | $newLDAPConnection = ,$DN+$newLDAPConnection 15 | $ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $newLDAPConnection 16 | } 17 | elseif(-NOT $UseSSL){ 18 | $DN = "GC://$($GlobalCatalog.IPAddress)/$($Global:AllDomainData.DistinguishedName)" 19 | $newLDAPConnection = ,$DN+$newLDAPConnection 20 | $ADObject = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $newLDAPConnection 21 | $ADObject.AuthenticationType = $KerberosEncType 22 | } 23 | } 24 | if($ADObject){ 25 | #$ADObject | gm 26 | $DomainSid = New-Object System.Security.Principal.SecurityIdentifier($ADObject.objectSid[0], 0) 27 | return $DomainSid.ToString() 28 | } 29 | } 30 | catch 31 | { 32 | Write-Host "Function Get-DomainSID $($_.Exception.Message)" -ForegroundColor Red 33 | } 34 | 35 | } 36 | 37 | #--------------------------------------------------- 38 | # Return the Current Forest 39 | #--------------------------------------------------- 40 | 41 | function Get-CurrentForest{ 42 | try{ 43 | if($Global:AllDomainData){ 44 | $MyContext = $AllDomainData.DomainContext 45 | #Forest Level Connection 46 | if($UseCredentials){ 47 | $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest", $MyContext.forest,$($credential.UserName),$($credential.GetNetworkCredential().password)) 48 | } 49 | else{ 50 | $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest", $MyContext.forest) 51 | } 52 | 53 | $forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext) 54 | 55 | return $forest 56 | } 57 | } 58 | Catch{ 59 | Write-Host "Function Get-CurrentForest $($_.Exception.Message)" -ForegroundColor Red 60 | } 61 | } 62 | 63 | #--------------------------------------------------- 64 | # Try to return domain passed variable 65 | #--------------------------------------------------- 66 | function Get-DomainInfo{ 67 | <# 68 | try { 69 | $assemType = 'System.DirectoryServices.AccountManagement' 70 | $assem = [reflection.assembly]::LoadWithPartialName($assemType) 71 | } 72 | catch{ 73 | throw "Failed to load assembly System.DirectoryServices.AccountManagement" 74 | } 75 | #> 76 | try{ 77 | #Initialize array and create new object 78 | $isConnected = $false 79 | $LDAPRootDSE = $null 80 | $LDAP = $null 81 | $newLDAPConnection = @() 82 | $DomainObject = New-Object -TypeName PSCustomObject 83 | $KerberosEncType = [System.DirectoryServices.AuthenticationTypes]::Sealing -bor [System.DirectoryServices.AuthenticationTypes]::Secure 84 | $SSLEncType = [System.DirectoryServices.AuthenticationTypes]::SecureSocketsLayer 85 | if($Global:DomainName -and (-NOT $Global:UseSSL)){ 86 | $LDAP = "LDAP://{0}" -f $DomainName 87 | $LDAPRootDSE = "LDAP://{0}/RootDSE" -f $DomainName 88 | } 89 | elseif($Global:DomainName -and $Global:UseSSL){ 90 | $LDAPRootDSE = "LDAP://{0}:636/RootDSE" -f $DomainName 91 | $LDAP = "LDAP://{0}:636" -f $DomainName 92 | $UseSSL = $true 93 | } 94 | elseif(!$Global:DomainName -and $Global:UseSSL){ 95 | $CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 96 | $LDAPRootDSE = "LDAP://{0}:636/RootDSE" -f $CurrentDomain.name 97 | $LDAP = "LDAP://{0}:636" -f $CurrentDomain.name 98 | $UseSSL = $true 99 | } 100 | else{ 101 | $CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 102 | $LDAPRootDSE = "LDAP://{0}/RootDSE" -f $CurrentDomain.name 103 | $LDAP = "LDAP://{0}" -f $CurrentDomain.name 104 | 105 | } 106 | #Add LDAP connection and auth to array 107 | if($UseCredentials){ 108 | $newLDAPConnection+=$credential.UserName 109 | $newLDAPConnection+=$credential.GetNetworkCredential().password 110 | } 111 | $Conn = $newLDAPConnection.clone() 112 | $Conn = ,$LDAP+$Conn 113 | 114 | #Prepare new connection 115 | $DomainConnection = New-Object -TypeName System.DirectoryServices.DirectoryEntry($Conn) 116 | if(-NOT $UseSSL){$DomainConnection.AuthenticationType = $KerberosEncType} 117 | #elseif($UseSSL){$DomainConnection.AuthenticationType = $SSLEncType} 118 | 119 | 120 | #Prepare new RootDSE Connection 121 | $RootConn = $newLDAPConnection.clone() 122 | $RootConn = ,$LDAPRootDSE+$RootConn 123 | $RootDSEConnection = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $RootConn 124 | if(-NOT $UseSSL){$RootDSEConnection.AuthenticationType = $KerberosEncType} 125 | #elseif($UseSSL){$RootDSEConnection.AuthenticationType = $SSLEncType} 126 | 127 | $DomainObject | Add-Member -type NoteProperty -name LDAPBase -value $newLDAPConnection 128 | $DomainObject | Add-Member -type NoteProperty -name LDAPConnection -value $Conn 129 | $DomainObject | Add-Member -type NoteProperty -name RootDSELDAPConnection -value $RootConn 130 | 131 | #Check if connected 132 | if($DomainConnection.Name -and $RootDSEConnection.Name){ 133 | $isConnected = $true 134 | write-host "Successfully connect to $($DomainConnection.distinguishedname)" -ForegroundColor Green 135 | 136 | #Extract NetBios Name 137 | # Connect to the Configuration Naming Context 138 | $LDAPConfigurationNamingContext = "{0}/{1}" -f $($LDAP), $RootDSEConnection.Get("configurationNamingContext") 139 | $NewLDAP = $newLDAPConnection.clone() 140 | $NewLDAP = ,$LDAPConfigurationNamingContext+$NewLDAP 141 | 142 | $configSearchRoot = New-Object -TypeName System.DirectoryServices.DirectoryEntry ($NewLDAP) 143 | if(-NOT $UseSSL){$configSearchRoot.AuthenticationType = $KerberosEncType} 144 | #elseif($UseSSL){$configSearchRoot.AuthenticationType = $SSLEncType} 145 | 146 | Write-Host "Connected to $($configSearchRoot.distinguishedName)" -ForegroundColor Green 147 | 148 | # Configure the filter 149 | #$filter = "(NETBIOSName=*)" 150 | $filter = "(&(objectClass=crossRef)(systemFlags=3))" 151 | # Search for all partitions where the NetBIOSName is set 152 | $configSearch = New-Object DirectoryServices.DirectorySearcher($configSearchRoot, $filter) 153 | # Configure search to return dnsroot and ncname attributes 154 | $retVal = $configSearch.PropertiesToLoad.Add("dnsroot") 155 | $retVal = $configSearch.PropertiesToLoad.Add("ncname") 156 | $retVal = $configSearch.PropertiesToLoad.Add("name") 157 | 158 | #Search data 159 | $Data = $configSearch.FindAll() 160 | if($Data){ 161 | foreach ($info in $Data){ 162 | $DomainObject | Add-Member -type NoteProperty -name Name -value $info.Properties.Item("name")[0] 163 | $DomainObject | Add-Member -type NoteProperty -name DistinguishedName -value $info.Properties.Item("ncname")[0] 164 | $DomainObject | Add-Member -type NoteProperty -name DNSRoot -value $info.Properties.Item("dnsroot")[0] 165 | } 166 | } 167 | write-host $DomainObject | fl 168 | #Add values to object 169 | $DomainObject | Add-Member -type NoteProperty -name DomainConnection -value $DomainConnection 170 | $DomainObject | Add-Member -type NoteProperty -name RootDSEConnection -value $RootDSEConnection 171 | $DomainObject | Add-Member -type NoteProperty -name DNSHostName -value $RootDSEConnection.dnshostname.toString() 172 | 173 | #Get Domain context 174 | $NewConn = $newLDAPConnection.clone() 175 | $NewConn = ,$DomainObject.Name+$NewConn 176 | $NewConn = ,"Domain"+$NewConn 177 | $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext(` 178 | $NewConn) 179 | $CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) 180 | 181 | #Add values to object 182 | $DomainObject | Add-Member -type NoteProperty -name DomainContext -value $CurrentDomain 183 | $DomainObject | Add-Member -type NoteProperty -name DomainFunctionalLevel -value $CurrentDomain.DomainMode 184 | $DomainObject | Add-Member -type NoteProperty -name LDAP -value $LDAP 185 | $DomainObject | Add-Member -type NoteProperty -name LDAPRootDSE -value $LDAPRootDSE 186 | 187 | #Prepare Domain Connection 188 | $MyConn = $newLDAPConnection.clone() 189 | if($Global:SearchRoot){ 190 | $DN = "{0}/{1}" -f $LDAP,$Global:SearchRoot 191 | $MyConn = ,$DN+$MyConn 192 | } 193 | else{ 194 | $DN = "{0}" -f $LDAP 195 | $MyConn = ,$DN+$MyConn 196 | } 197 | $MyADConnection = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $MyConn 198 | if(-NOT $UseSSL){ 199 | $MyADConnection.AuthenticationType = $KerberosEncType 200 | } 201 | $DomainObject | Add-Member -type NoteProperty -name ADConnection -value $MyADConnection 202 | 203 | #> 204 | $DomainConnection.Close() 205 | $RootDSEConnection.Close() 206 | $configSearchRoot.Close() 207 | 208 | } 209 | return $DomainObject 210 | 211 | } 212 | catch{ 213 | Write-Host "Function Get-DomainInfo $($_.Exception)" -ForegroundColor Red 214 | break 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Common/Functions.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | The script scan Active Directory and retrieve huge data of many sources like: 4 | -User objects 5 | -Computer objects 6 | -Organizational Unit 7 | -Forest 8 | -Access control list 9 | -Etc. 10 | 11 | Ability to export data to CSV and Excel format. Default save format is CSV 12 | 13 | .NOTES 14 | Author : Juan Garrido (http://windowstips.wordpress.com) 15 | Twitter : @tr1ana 16 | Company : http://www.innotecsystem.com 17 | File Name : voyeur.ps1 18 | 19 | #> 20 | 21 | #--------------------------------------------------- 22 | # Construct detailed PSObject 23 | #--------------------------------------------------- 24 | 25 | function Set-PSObject ([Object] $Object, [String] $type) 26 | { 27 | $FinalObject = @() 28 | foreach ($Obj in $Object) 29 | { 30 | $NewObject = New-Object -TypeName PSObject -Property $Obj 31 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 32 | $FinalObject +=$NewObject 33 | } 34 | return $FinalObject 35 | } 36 | 37 | #--------------------------------------------------- 38 | # Get Color of ACL 39 | #--------------------------------------------------- 40 | 41 | Function Get-Color ([String] $Color) 42 | { 43 | try 44 | { 45 | return $ACLColors[$Color] 46 | } 47 | catch 48 | { 49 | Write-Host "ACL color not found" -ForegroundColor Red 50 | } 51 | } 52 | 53 | #--------------------------------------------------- 54 | # Volumetry Organizational Unit 55 | #--------------------------------------------------- 56 | function Get-OUVolumetry{ 57 | Param ( 58 | [parameter(Mandatory=$true, HelpMessage="ACL object")] 59 | [Object]$ACL, 60 | 61 | [parameter(Mandatory=$true, HelpMessage="Users object")] 62 | [Object]$Users 63 | 64 | ) 65 | Begin{ 66 | #Start computers plugin 67 | Write-Host ("{0}: {1}" -f "OU task", "Generating organizational unit volumetry from $($Global:AllDomainData.Name)")` 68 | -ForegroundColor Magenta 69 | } 70 | Process{ 71 | if($ACL){ 72 | #Connect to domain 73 | $Connection = $AllDomainData.ADConnection 74 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 75 | $Searcher.SearchScope = "subtree" 76 | $Searcher.SearchRoot = $Connection 77 | if($Searcher){ 78 | $Searcher.PageSize = 200 79 | } 80 | $ListOU = @() 81 | foreach ($result in $ACL){ 82 | $ou =[String]::Format($result.DN) 83 | $ouname = [String]::Format($result.Name) 84 | $Searcher.filter = "(&(objectclass=organizationalunit)(distinguishedname=$ou))" 85 | $tmpou = $Searcher.FindOne() 86 | $data = $tmpou.GetDirectoryEntry() 87 | $NumberOfUsers = 0 88 | if ($data){ 89 | $TotalUsers = $data.Children | Where-Object {$_.schemaClassName -eq "user"} | Select-Object sAMAccountName 90 | $InactiveUsers = 0 91 | $ActiveUsers = 0 92 | foreach ($u in $TotalUsers){ 93 | $match = $Users | Where-Object {$_.sAMAccountName -eq $u.sAMAccountNAme -and $_.isActive -eq $true} 94 | if ($match){ 95 | $ActiveUsers++ 96 | } 97 | else{ 98 | $InactiveUsers++ 99 | } 100 | } 101 | } 102 | try{ 103 | $Count = [String]::Format($TotalUsers.Count) 104 | $Active = [String]::Format($ActiveUsers) 105 | $Inactive = [String]::Format($InactiveUsers) 106 | $OUObject = New-Object -TypeName PSCustomObject 107 | $OUObject | Add-Member -type NoteProperty -name Name -value $ouname 108 | $OUObject | Add-Member -type NoteProperty -name DistinguishedName -value $ou 109 | $OUObject | Add-Member -type NoteProperty -name NumberOfUsers -value $Count 110 | $OUObject | Add-Member -type NoteProperty -name ActiveUsers -value $Active 111 | $OUObject | Add-Member -type NoteProperty -name InactiveUsers -value $Inactive 112 | #Add Metadata 113 | $OUObject.PSObject.typenames.insert(0,'Arsenal.AD.OuCount') 114 | $ListOU +=$OUObject 115 | #Need to resolv filters 116 | } 117 | catch{ 118 | Write-Host "Get-OUVolumetry problem...$($_.Exception.Message) in $($NumberofUsers)" -ForegroundColor Red 119 | } 120 | } 121 | } 122 | } 123 | End{ 124 | #Create custom object for store data 125 | $OUObject = New-Object -TypeName PSCustomObject 126 | $OUObject | Add-Member -type NoteProperty -name Data -value $ListOU 127 | #Formatting Excel Data 128 | $Excelformatting = New-Object -TypeName PSCustomObject 129 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $ListOU 130 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "OU Volumetry" 131 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "OU Volumetry" 132 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 133 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 134 | 135 | #Add Excel formatting into psobject 136 | $OUObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 137 | return $OUObject 138 | } 139 | } 140 | 141 | function New-Report([String] $Path, [String] $Domain) 142 | { 143 | $target = "$Path\Reports" 144 | if (!(Test-Path -Path $target)) 145 | { 146 | $tmpdir = New-Item -ItemType Directory -Path $target 147 | Write-Host "Folder Reports created in $target...." -ForegroundColor Yellow 148 | } 149 | $folder = "$target\" + ([System.Guid]::NewGuid()).ToString() + $Domain 150 | if (!(Test-Path -Path $folder)) 151 | { 152 | try 153 | { 154 | $tmpdir = New-Item -ItemType Directory -Path $folder 155 | return $folder 156 | } 157 | catch 158 | { 159 | Write-Verbose "Failed to create new directory. Trying to generate new guid...." 160 | New-Report -Path $Path -Domain $Domain 161 | } 162 | } 163 | 164 | } 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /Common/Jobs.ps1: -------------------------------------------------------------------------------- 1 | Function Run-Jobs{ 2 | [CmdletBinding()] 3 | Param 4 | ( 5 | [Parameter(HelpMessage="Computer or computers to gather information from", 6 | ValueFromPipeline=$true, 7 | ValueFromPipelineByPropertyName=$true, 8 | Position=0)] 9 | [ValidateNotNullOrEmpty()] 10 | [Alias('IpAddress','Server')] 11 | [Object] 12 | $ComputerName=$env:computername, 13 | 14 | [Parameter(HelpMessage="Plugin to retrieve data")] 15 | [Array] 16 | $Plugins = $false, 17 | 18 | [Parameter(HelpMessage="Object with AD data")] 19 | [Object] 20 | $ADObject = $false, 21 | 22 | [Parameter(HelpMessage="Maximum number of max jobs")] 23 | [ValidateRange(1,65535)] 24 | [int32] 25 | $MaxJobs = 10, 26 | 27 | [Parameter(HelpMessage="Set this if you want run Single Query")] 28 | [switch] 29 | $SingleQuery 30 | ) 31 | Begin{ 32 | Function Global:Get-DataFromAD{ 33 | $VerbosePreference = 'continue' 34 | If ($queue.count -gt 0) { 35 | Write-Verbose ("Queue Count: {0}" -f $queue.count) 36 | $TaskObject = $queue.Dequeue() 37 | #$TaskObject | fl 38 | $ScriptBlockPlugin = [ScriptBlock]::Create($(Get-Content $TaskObject.Plugin | Out-String)) 39 | $TaskName = $TaskObject.PluginName 40 | $Plugin = $TaskObject.Plugin 41 | #Create task 42 | $NewTask = Start-Job -Name $TaskName -FilePath $Plugin -ArgumentList $TaskObject 43 | 44 | #Register Event 45 | Register-ObjectEvent -InputObject $NewTask -EventName StateChanged -Action { 46 | #Set verbose to continue to see the output on the screen 47 | $VerbosePreference = 'continue' 48 | $TaskUpdate = $eventsubscriber.sourceobject.name 49 | $Global:Data += Receive-Job -Job $eventsubscriber.sourceobject 50 | Write-Verbose "Removing: $($eventsubscriber.sourceobject.Name)" 51 | Remove-Job -Job $eventsubscriber.sourceobject 52 | Write-Verbose "Unregistering: $($eventsubscriber.SourceIdentifier)" 53 | Unregister-Event $eventsubscriber.SourceIdentifier 54 | Write-Verbose "Removing: $($eventsubscriber.SourceIdentifier)" 55 | Remove-Job -Name $eventsubscriber.SourceIdentifier 56 | Remove-Variable results 57 | If ($queue.count -gt 0 -OR (Get-Job)){ 58 | Write-Verbose "Running Rjob" 59 | Get-DataFromAD 60 | }ElseIf (-NOT (Get-Job)) { 61 | $End = (Get-Date) 62 | $timeToCleanup = ($End - $Start).TotalMilliseconds 63 | Write-Host ('{0,-30} : {1,10:#,##0.00} ms' -f 'Time to exit background job', $timeToCleanup) -fore Green -Back Black 64 | Write-Host -ForegroundColor Green "Check the `$Data variable for report of online/offline systems" 65 | } 66 | } | Out-Null 67 | Write-Verbose "Created Event for $($NewTask.Name)" 68 | } 69 | } 70 | } 71 | Process{ 72 | #Capture date 73 | $Start = Get-Date 74 | if($Plugins -ne $false -and $ADObject){ 75 | #Queue the items up 76 | $Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) ) 77 | foreach ($plugin in $Plugins){ 78 | $PluginName = [io.path]::GetFileNameWithoutExtension($plugin) 79 | Write-Verbose "Adding $PluginName to queue..." 80 | $NewPlugin = $ADObject | Select-Object * 81 | $NewPlugin | Add-Member -type NoteProperty -name PluginName -value $PluginName -Force 82 | $NewPlugin | Add-Member -type NoteProperty -name Plugin -value $plugin -Force 83 | #Add PsObject to Queue 84 | $queue.Enqueue($NewPlugin) 85 | #$NewPlugin | fl 86 | } 87 | If ($queue.count -lt $MaxJobs) { 88 | $MaxJobs = $queue.count 89 | } 90 | # Start up to the max number of concurrent jobs 91 | # Each job will take care of running the rest 92 | for( $i = 0; $i -lt $MaxJobs; $i++ ) { 93 | Get-DataFromAD 94 | } 95 | } 96 | } 97 | End{ 98 | #Nothing to do here 99 | } 100 | 101 | 102 | 103 | } -------------------------------------------------------------------------------- /Common/Office/Excel/ExcelObject.ps1: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------- 2 | # Function to create OBJ Excel 3 | #--------------------------------------------------- 4 | 5 | function Create-Excel{ 6 | [cmdletbinding()] 7 | Param ( 8 | [parameter(Mandatory=$false, HelpMessage="Excel Debug")] 9 | [Alias('OpenExcel')] 10 | [Bool]$ExcelDebugging = $false 11 | ) 12 | Begin{ 13 | #Create Excel 14 | [Threading.Thread]::CurrentThread.CurrentCulture = 'en-US' 15 | $Excel = new-object -com Excel.Application 16 | $Excel.visible = $ExcelDebugging 17 | } 18 | Process{ 19 | $objWorkBook = $Excel.WorkBooks.Add() 20 | if($Excel.Version -le "14.0"){ 21 | #Delete sheets for Office 2010 22 | 1..2 | ForEach { 23 | $objWorkbook.WorkSheets.Item($_).Delete() 24 | } 25 | } 26 | } 27 | End{ 28 | #Create global vars for Excel formatting 29 | Set-Variable Excel -Value $Excel -Scope Global -Force 30 | Set-Variable WorkBook -Value $objWorkBook -Scope Global -Force 31 | } 32 | } 33 | 34 | #--------------------------------------------------- 35 | # Function to Get Excel Language settings 36 | #--------------------------------------------------- 37 | function Get-ExcelLanguage{ 38 | Begin{ 39 | #Define constant 40 | $msoLanguageIDUI = 2 41 | $EnglishLanguage = 1033 42 | } 43 | Process{ 44 | $LCID = $Excel.LanguageSettings.LanguageID($msoLanguageIDUI) 45 | if($LCID){ 46 | return $LCID 47 | } 48 | else{ 49 | Write-Host "No language detected... Try with English language...." -ForegroundColor Yellow 50 | return $EnglishLanguage 51 | } 52 | } 53 | End{ 54 | #Nothing to do here 55 | } 56 | } 57 | 58 | #--------------------------------------------------- 59 | # Function to create WorkSheet 60 | #--------------------------------------------------- 61 | 62 | function Create-WorkSheet{ 63 | [cmdletbinding()] 64 | Param ( 65 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 66 | [Alias('SheetName')] 67 | [String]$Title 68 | ) 69 | Begin{ 70 | $WorkSheet = $WorkBook.Worksheets.Add() 71 | $WorkSheet.Activate() | Out-Null 72 | } 73 | Process{ 74 | $WorkSheet.Name = $Title 75 | $WorkSheet.Select() 76 | $Excel.ActiveWindow.Displaygridlines = $false 77 | } 78 | End{ 79 | Set-Variable WorkSheet -Value $WorkSheet -Scope Global -Force 80 | } 81 | } 82 | 83 | #--------------------------------------------------- 84 | # Function to create table through CSV data 85 | #--------------------------------------------------- 86 | 87 | function Create-CSV2Table{ 88 | [cmdletbinding()] 89 | Param ( 90 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 91 | [Alias('MyData')] 92 | [Object]$Data, 93 | 94 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 95 | [Alias('SheetName')] 96 | [String]$Title, 97 | 98 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 99 | [Alias('TableName')] 100 | [String]$TableTitle, 101 | 102 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 103 | [Alias('Style')] 104 | [String]$TableStyle, 105 | 106 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 107 | [Alias('ColumnName')] 108 | [String]$iconColumnName, 109 | 110 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 111 | [Alias('TableIsFreeze')] 112 | [Bool]$isFreeze 113 | ) 114 | Begin{ 115 | if ($Data -ne $null){ 116 | #Create tmp file and store all content 117 | $CSVFile = ($env:temp + "\" + ([System.Guid]::NewGuid()).ToString() + ".csv") 118 | $Data | Export-Csv -path $CSVFile -noTypeInformation 119 | #Create new Sheet in Excel 120 | Create-WorkSheet $Title 121 | #Define the connection string and where the data is supposed to go 122 | $TxtConnector = ("TEXT;" + $CSVFile) 123 | $CellRef = $worksheet.Range("A1") 124 | #Build, use and remove the text file connector 125 | $Connector = $worksheet.QueryTables.add($TxtConnector,$CellRef) 126 | $worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True 127 | $worksheet.QueryTables.item($Connector.name).TextFileParseType = 1 128 | $worksheet.QueryTables.item($Connector.name).Refresh() 129 | $worksheet.QueryTables.item($Connector.name).delete() 130 | $worksheet.UsedRange.EntireColumn.AutoFit() 131 | $listObject = $worksheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceType]::xlSrcRange,` 132 | $worksheet.UsedRange, $null,[Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes,$null) 133 | $listObject.Name = $TableTitle 134 | if($TableStyle){ 135 | # Style Cheat Sheet: https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.tablestyle.aspx 136 | # Table styles https://msdn.microsoft.com/en-us/library/office/dn535872.aspx 137 | $listObject.TableStyle = $TableStyle 138 | } 139 | $worksheet.Activate(); 140 | $worksheet.Application.ActiveWindow.SplitRow = 1; 141 | $worksheet.Application.ActiveWindow.FreezePanes = $isFreeze 142 | 143 | } 144 | } 145 | Process{ 146 | #Remove Tmp file 147 | Remove-Item -Path $CSVFile -Force 148 | if($iconColumnName){ 149 | #Add colors to ACL Column 150 | #Need to solve try catch 151 | Add-Icon -SheetName $Title -ColumnName $iconColumnName | Out-Null 152 | } 153 | } 154 | End{ 155 | #Nothing to do here 156 | } 157 | } 158 | 159 | #--------------------------------------------------- 160 | # Function to release OBJ Excel 161 | #--------------------------------------------------- 162 | 163 | function Release-ExcelObject{ 164 | Begin{ 165 | $Excel.DisplayAlerts = $false 166 | $Excel.ActiveWorkBook.Close | Out-Null 167 | $Excel.Quit() 168 | } 169 | Process{ 170 | #[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$WorBook) | Out-Null 171 | #[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$WorkSheet) | Out-Null 172 | [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$Excel) | Out-Null 173 | } 174 | End{ 175 | $Excel = $null 176 | $WorkBook = $null 177 | #Remove vars 178 | remove-item -Path "variable:Excel" -Force -ErrorAction SilentlyContinue 179 | remove-item -Path "variable:WorkBook" -Force -ErrorAction SilentlyContinue 180 | [GC]::Collect() 181 | [GC]::WaitForPendingFinalizers() 182 | } 183 | } 184 | 185 | #--------------------------------------------------- 186 | # Function to Save Excel 187 | #--------------------------------------------------- 188 | 189 | function Save-Excel{ 190 | [cmdletbinding()] 191 | Param ( 192 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 193 | [Alias('RootPath')] 194 | [String]$Path 195 | ) 196 | Begin{ 197 | #Get Date 198 | $date = Get-Date -format "yyyyMMdd" 199 | $SaveExcelIn = "$($RootPath)\ExcelReport\"+"Voyeur_Report_$($date)" 200 | } 201 | Process{ 202 | # http://msdn.microsoft.com/en-us/library/bb241279.aspx 203 | $WorkBook.SaveAs($SaveExcelIn,51) 204 | } 205 | End{ 206 | $WorkBook.Saved = $true 207 | } 208 | } 209 | 210 | #--------------------------------------------------- 211 | # Function Create Table 212 | #--------------------------------------------------- 213 | 214 | function Create-Table{ 215 | [cmdletbinding()] 216 | Param ( 217 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 218 | [Alias('Totals')] 219 | [Bool]$ShowTotals, 220 | 221 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 222 | [Alias('MyData')] 223 | [Object]$Data, 224 | 225 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 226 | [Alias('AddHeader')] 227 | [Object]$Header, 228 | 229 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 230 | [Alias('AddSheetName')] 231 | [String]$SheetName, 232 | 233 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 234 | [Alias('AddTableTitle')] 235 | [String]$TableTitle, 236 | 237 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 238 | [Alias('AddFormatTable')] 239 | [String]$FormatTable, 240 | 241 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 242 | [Alias('AddPosition')] 243 | [Object]$Position, 244 | 245 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 246 | [Alias('Headers')] 247 | [Bool]$ShowHeaders, 248 | 249 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 250 | [Alias('isNewSheet')] 251 | [Bool]$NewSheet, 252 | 253 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 254 | [Alias('isNewChart')] 255 | [Bool]$AddNewChart, 256 | 257 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 258 | [Alias('NewchartType')] 259 | [String]$ChartType, 260 | 261 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 262 | [Alias('MyHeaderStyle')] 263 | [String]$HeaderStyle, 264 | 265 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 266 | [Alias('isDataTable')] 267 | [Bool]$HasDataTable, 268 | 269 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 270 | [Alias('NewChartStyle')] 271 | [Int]$ChartStyle, 272 | 273 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 274 | [Alias('AddChartTitle')] 275 | [String]$ChartTitle 276 | ) 277 | Begin{ 278 | 279 | if($NewSheet -and $SheetName){ 280 | #Create new worksheet 281 | Create-WorkSheet -SheetName $SheetName 282 | } 283 | $Cells = $WorkSheet.Cells 284 | $Row=$Position[0] 285 | $InitialRow = $Row 286 | $Col=$Position[1] 287 | $InitialCol = $Col 288 | #Check for headers 289 | if ($Header){ 290 | #insert column headings 291 | $Header | foreach{ 292 | $cells.item($row,$col)=$_ 293 | $cells.item($row,$col).font.bold=$True 294 | $Col++ 295 | } 296 | } 297 | # Add table content 298 | foreach ($Key in $Data.Keys){ 299 | $Row++ 300 | $Col = $InitialCol 301 | $cells.item($Row,$Col) = $Key 302 | $nbItems = $Data[$Key].Count 303 | for ( $i=0; $i -lt $nbItems; $i++ ){ 304 | $Col++ 305 | $cells.item($Row,$Col) = $Data[$Key][$i] 306 | $cells.item($Row,$Col).NumberFormat ="0" 307 | } 308 | } 309 | } 310 | Process{ 311 | # Apply Styles to table 312 | $Range = $WorkSheet.Range($WorkSheet.Cells.Item($InitialRow,$InitialCol),$WorkSheet.Cells.Item($Row,$Col)) 313 | $listObject = $worksheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceType]::xlSrcRange, $Range, $null,[Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes,$null) 314 | $listObject.Name = $TableTitle 315 | $listObject.ShowTotals = $ShowTotals 316 | $listObject.ShowHeaders = $ShowHeaders 317 | # Style Cheat Sheet: https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.tablestyle.aspx 318 | # Table styles https://msdn.microsoft.com/en-us/library/office/dn535872.aspx 319 | $listObject.TableStyle = $FormatTable 320 | 321 | # Sort data based on the 2nd column 322 | $MyPosition = $WorkSheet.Cells.Item($InitialRow+1,$InitialCol+1).Address($False,$False) 323 | $SortRange = $WorkSheet.Range($MyPosition) # address: Convert cells position 1,1 -> A:1 324 | $WorkSheet.Sort.SortFields.Clear() 325 | [void]$WorkSheet.Sort.SortFields.Add($SortRange,0,1,0) 326 | $WorkSheet.Sort.SetRange($Range) 327 | $WorkSheet.Sort.Header = 1 # exclude header 328 | $WorkSheet.Sort.Orientation = 1 329 | $WorkSheet.Sort.Apply() 330 | 331 | # Apply Styles to Title 332 | $cells.item(1,$InitialCol) = $TableTitle 333 | $RangeTitle = $WorkSheet.Range($WorkSheet.Cells.Item(1,$InitialCol),$WorkSheet.Cells.Item(1,$Col)) 334 | $RangeTitle.MergeCells = $true 335 | $RangeTitle.Style = $HeaderStyle 336 | # http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.constants.aspx 337 | $RangeTitle.HorizontalAlignment = -4108 338 | $RangeTitle.ColumnWidth = 20 339 | } 340 | End{ 341 | #Add chart 342 | if($AddNewChart -and $ChartType){ 343 | Create-MyChart -DataRange $Range -ChartType $ChartType ` 344 | -ChartTitle $ChartTitle -HasDataTable $HasDataTable ` 345 | -Style $ChartStyle -ChartRange $Position | Out-Null 346 | } 347 | } 348 | } 349 | 350 | 351 | #--------------------------------------------------- 352 | # Create a chart in Excel 353 | # http://www.alexwinner.com/articles/powershell/115-posh-excel-part4.html 354 | #--------------------------------------------------- 355 | function Create-MyChart{ 356 | [cmdletbinding()] 357 | Param ( 358 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 359 | [Alias('MyDataRange')] 360 | [Object]$DataRange, 361 | 362 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 363 | [Alias('AddChartType')] 364 | [String]$chartType, 365 | 366 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 367 | [Alias('AddDataTable')] 368 | [Bool]$HasDataTable, 369 | 370 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 371 | [Alias('AddStyle')] 372 | [Int]$Style, 373 | 374 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 375 | [Alias('MyTitle')] 376 | [String]$ChartTitle, 377 | 378 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 379 | [Alias('ChartRange')] 380 | [Object]$Position 381 | ) 382 | Begin{ 383 | #Add Types 384 | Add-Type -AssemblyName Microsoft.Office.Interop.Excel 385 | $MyChartType=[Microsoft.Office.Interop.Excel.XLChartType]$chartType 386 | # Add the chart 387 | $Chart = $WorkSheet.Shapes.AddChart().Chart 388 | $Chart.ChartType = $MyChartType 389 | #$Chart | gm 390 | } 391 | Process{ 392 | # Apply a specific style for each type 393 | If( $ChartType -like "xlPie" ){ 394 | $Chart.ApplyLayout(2,$Chart.ChartType) 395 | $Chart.Legend.Position = -4107 396 | if ( $ChartTitle ){ 397 | $Chart.HasTitle = $true 398 | $Chart.ChartTitle.Text = $ChartTitle 399 | } 400 | # http://msdn.microsoft.com/fr-fr/library/microsoft.office.interop.excel._chart.setsourcedata(v=office.11).aspx 401 | $Chart.SetSourceData($DataRange) 402 | } 403 | else{ 404 | $Chart.SetSourceData($DataRange,[Microsoft.Office.Interop.Excel.XLRowCol]::xlRows) 405 | 406 | # http://msdn.microsoft.com/en-us/library/office/bb241345(v=office.12).aspx 407 | $Chart.Legend.Position = -4107 408 | $Chart.ChartStyle = $Style 409 | $Chart.ApplyLayout(2,$Chart.ChartType) 410 | if ($HasDataTable){ 411 | $Chart.HasDataTable = $true 412 | $Chart.DataTable.HasBorderOutline = $true 413 | } 414 | 415 | $NbSeries = $Chart.SeriesCollection().Count 416 | 417 | # Define data labels 418 | for ( $i=1 ; $i -le $NbSeries; ++$i ){ 419 | $Chart.SeriesCollection($i).HasDataLabels = $true 420 | $Chart.SeriesCollection($i).DataLabels(0).Position = 3 421 | } 422 | 423 | $Chart.HasAxis([Microsoft.Office.Interop.Excel.XlAxisType]::xlCategory) = $false 424 | $Chart.HasAxis([Microsoft.Office.Interop.Excel.XlAxisType]::xlValue) = $false 425 | if ( $ChartTitle ){ 426 | $Chart.HasTitle = $true 427 | $Chart.ChartTitle.Text = $ChartTitle 428 | } 429 | } 430 | } 431 | End{ 432 | #Extract Row $ Col 433 | $Row=$Position[0] 434 | $Col = $Position[1] 435 | 436 | $ChartRange = $WorkSheet.Range($WorkSheet.Cells.Item(1,$col),$WorkSheet.Cells.Item(18,6)) 437 | 438 | # Define the position of the chart 439 | $ChartObj = $Chart.Parent 440 | 441 | $ChartObj.Height = $DataRange.Height * 4 442 | $ChartObj.Width = $DataRange.Width + 100 443 | 444 | $ChartObj.Top = $DataRange.Top 445 | $ChartObj.Left = $DataRange.Left 446 | 447 | #Save image 448 | If($saveImage){ 449 | $ImageFile = ($env:temp + "\" + ([System.Guid]::NewGuid()).ToString() + ".png") 450 | $Chart.Export($ImageFile) 451 | return $ImageFile 452 | } 453 | } 454 | } 455 | 456 | #--------------------------------------------------- 457 | # Read Headers in Excel 458 | # Return Hash Table of each cell 459 | #--------------------------------------------------- 460 | 461 | Function Read-Headers{ 462 | [cmdletbinding()] 463 | Param ( 464 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 465 | [Alias('Sheet')] 466 | [Object]$MyWorkSheet 467 | ) 468 | Begin{ 469 | $Headers =@{} 470 | $column = 1 471 | } 472 | Process{ 473 | Do { 474 | $Header = $MyWorkSheet.cells.item(1,$column).text 475 | If ($Header) { 476 | $Headers.add($Header, $column) 477 | $column++ 478 | } 479 | } until (!$Header) 480 | } 481 | End{ 482 | return $Headers 483 | } 484 | } 485 | 486 | #--------------------------------------------------- 487 | # Format Cells 488 | # Add Icon format for each ACL value 489 | #--------------------------------------------------- 490 | 491 | Function Add-Icon{ 492 | [cmdletbinding()] 493 | Param ( 494 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 495 | [Alias('Sheet')] 496 | [String]$SheetName, 497 | 498 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 499 | [Alias('Column')] 500 | [String]$ColumnName 501 | ) 502 | Begin{ 503 | #Charts Variables 504 | $xlConditionValues=[Microsoft.Office.Interop.Excel.XLConditionValueTypes] 505 | $xlIconSet=[Microsoft.Office.Interop.Excel.XLIconSet] 506 | $xlDirection=[Microsoft.Office.Interop.Excel.XLDirection] 507 | $MyWorkSheet = $Excel.WorkSheets.Item($SheetName) 508 | $Headers = Read-Headers $MyWorkSheet 509 | } 510 | Process{ 511 | #Add Icons 512 | try{ 513 | $range = [char]($Headers[$ColumnName]+64) 514 | $start=$WorkSheet.range($range+"2") 515 | #get the last cell 516 | $Selection=$WorkSheet.Range($start,$start.End($xlDirection::xlDown)) 517 | } 518 | catch{ 519 | Write-Host "[Voyeur problem][Function Add-Icon]...$($_.Exception)" -ForegroundColor Red 520 | break 521 | } 522 | } 523 | End{ 524 | #add the icon set 525 | $Selection.FormatConditions.AddIconSetCondition() | Out-Null 526 | $Selection.FormatConditions.item($($Selection.FormatConditions.Count)).SetFirstPriority() 527 | $Selection.FormatConditions.item(1).ReverseOrder = $True 528 | $Selection.FormatConditions.item(1).ShowIconOnly = $True 529 | $Selection.FormatConditions.item(1).IconSet = $xlIconSet::xl3TrafficLights1 530 | $Selection.FormatConditions.item(1).IconCriteria.Item(2).Type=$xlConditionValues::xlConditionValueNumber 531 | $Selection.FormatConditions.item(1).IconCriteria.Item(2).Value=60 532 | $Selection.FormatConditions.item(1).IconCriteria.Item(2).Operator=7 533 | $Selection.FormatConditions.item(1).IconCriteria.Item(3).Type=$xlConditionValues::xlConditionValueNumber 534 | $Selection.FormatConditions.item(1).IconCriteria.Item(3).Value=90 535 | $Selection.FormatConditions.item(1).IconCriteria.Item(3).Operator=7 536 | } 537 | 538 | } 539 | 540 | #--------------------------------------------------- 541 | # Create Report Index 542 | # Function to create report index with HyperLinks 543 | #--------------------------------------------------- 544 | 545 | Function Create-Index{ 546 | [cmdletbinding()] 547 | Param ( 548 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 549 | [Alias('Settings')] 550 | [Object]$ExcelSettings 551 | ) 552 | Begin{ 553 | #Set Constants 554 | Set-Variable msoFalse 0 -Option Constant -ErrorAction SilentlyContinue 555 | Set-Variable msoTrue 1 -Option Constant -ErrorAction SilentlyContinue 556 | 557 | Set-Variable cellWidth 48 -Option Constant -ErrorAction SilentlyContinue 558 | Set-Variable cellHeight 15 -Option Constant -ErrorAction SilentlyContinue 559 | 560 | $CompanyLogo = "$($MyPath){0}{1}" -f "\",$ExcelSettings["CompanyLogoFront"] 561 | $CompanyLogoTopLeft = "$($MyPath){0}{1}" -f "\",$ExcelSettings["CompanyUserTopLeft"] 562 | 563 | <# 564 | [Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 565 | #Background Image 566 | $bytes = [System.Convert]::FromBase64String($IndexImage) 567 | $ImgFile = ($env:temp + "\" + ([System.Guid]::NewGuid()).ToString() + ".jpg") 568 | $p = New-Object IO.MemoryStream($bytes, 0, $bytes.length) 569 | $p.Write($bytes, 0, $bytes.length) 570 | $picture = [System.Drawing.Image]::FromStream($p, $true) 571 | $picture.Save($ImgFile) 572 | #Innotec Image 573 | $bytes = [System.Convert]::FromBase64String($InnotecImage) 574 | $InnotecFile = ($env:temp + "\" + ([System.Guid]::NewGuid()).ToString() + ".png") 575 | $p = New-Object IO.MemoryStream($bytes, 0, $bytes.length) 576 | $p.Write($bytes, 0, $bytes.length) 577 | $picture = [System.Drawing.Image]::FromStream($p, $true) 578 | $picture.Save($InnotecFile) 579 | #> 580 | } 581 | Process{ 582 | try{ 583 | #Main Report Index 584 | $row = 08 585 | $col = 1 586 | $WorkSheet = $WorkBook.WorkSheets.Add() 587 | $WorkSheet.Name = "Index" 588 | $WorkSheet.Tab.ColorIndex = 8 589 | foreach ($Sheet in $WorkBook.WorkSheets){ 590 | $format = [char](64+$row) 591 | $r=$format+"$row" 592 | $range = $WorkSheet.Range($r) 593 | #$v = $WorkSheet.Hyperlinks.Add($WorkSheet.Cells.Item($row,$col),"","'$($_.Name)'"+"!$($r)","","$($_.Name)") 594 | $v = $WorkSheet.Hyperlinks.Add($WorkSheet.Cells.Item($row,$col),"","'$($Sheet.Name)'"+"!$($r)","",$Sheet.Name) 595 | $row++ 596 | } 597 | $CellRange = $WorkSheet.Range("A1:A40") 598 | #$CellRange.Interior.ColorIndex = 9 599 | $CellRange.Font.ColorIndex = 9 600 | $CellRange.Font.Size = 14 601 | $CellRange.Font.Bold = $true 602 | $WorkSheet.columns.item("A").EntireColumn.AutoFit() | out-null 603 | $Excel.ActiveWindow.Displaygridlines = $false 604 | } 605 | catch{ 606 | Write-Host "[Voyeur problem][Function Create-Index]...$($_.Exception)" -ForegroundColor Red 607 | } 608 | 609 | # add image to the Sheet 610 | #Image format and properties 611 | $LinkToFile = $msoFalse 612 | $SaveWithDocument = $msoTrue 613 | $Left = 470 614 | $Top = 200 615 | $Width = 954 616 | $Height = 222 617 | $img = $WorkSheet.Shapes.AddPicture($CompanyLogo, $LinkToFile, $SaveWithDocument, 618 | $Left, $Top, $Width, $Height) 619 | 620 | # add image to the Sheet 621 | #Image format and properties 622 | $LinkToFile = $msoFalse 623 | $SaveWithDocument = $msoTrue 624 | $Left = 0 625 | $Top = 0 626 | $Width = 70 627 | $Height = 70 628 | $img = $WorkSheet.Shapes.AddPicture($CompanyLogoTopLeft, $LinkToFile, $SaveWithDocument, 629 | $Left, $Top, $Width, $Height) 630 | 631 | #Add AuditorName 632 | $WorkSheet.Cells.Item(5,1).FormulaLocal = $AuditorName 633 | 634 | 635 | } 636 | End{ 637 | #Nothing to do Here 638 | } 639 | } 640 | 641 | #--------------------------------------------------- 642 | # Create About page 643 | # Function to create About page with HyperLinks 644 | #--------------------------------------------------- 645 | Function Create-About{ 646 | [cmdletbinding()] 647 | Param ( 648 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 649 | [Alias('Settings')] 650 | [Object]$ExcelSettings 651 | ) 652 | Begin{ 653 | #Main Report Index 654 | [Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 655 | $WorkSheet = $WorkBook.WorkSheets.Add() 656 | $WorkSheet.Name = "About" 657 | $WorkSheet.Cells.Item(1,1).Value() = $ExcelSettings["ReportName"] 658 | $WorkSheet.Cells.Item(1,1).Font.Size = 25 659 | $WorkSheet.Cells.Item(1,1).Font.Bold = $true 660 | $cnt = 1 661 | foreach ($webpage in $ExcelSettings["HyperLinkcompanyName"]){ 662 | $WorkSheet.Cells.Item(24+$cnt,4).Value() = $webpage 663 | $r = $WorkSheet.Range("D"+(24+$cnt)) 664 | $v = $WorkSheet.Hyperlinks.Add($r,$webpage) 665 | $WorkSheet.Cells.Item(24+$cnt,4).Font.Size = 14 666 | $WorkSheet.Cells.Item(24+$cnt,4).Font.Bold = $true 667 | $cnt+=2 668 | } 669 | } 670 | Process{ 671 | #Set Constants 672 | Set-Variable msoFalse 0 -Option Constant -ErrorAction SilentlyContinue 673 | Set-Variable msoTrue 1 -Option Constant -ErrorAction SilentlyContinue 674 | 675 | Set-Variable cellWidth 48 -Option Constant -ErrorAction SilentlyContinue 676 | Set-Variable cellHeight 15 -Option Constant -ErrorAction SilentlyContinue 677 | 678 | $CompanyLogo = "$($MyPath){0}{1}" -f "\",$ExcelSettings["CompanyLogo"] 679 | #Image format and properties 680 | $LinkToFile = $msoFalse 681 | $SaveWithDocument = $msoTrue 682 | $Left = 400 683 | $Top = 40 684 | $Width = 616 685 | $Height = 210 686 | 687 | # add image to the Sheet 688 | $img = $WorkSheet.Shapes.AddPicture($CompanyLogo, $LinkToFile, $SaveWithDocument, 689 | $Left, $Top, $Width, $Height) 690 | 691 | #Remove GridLines 692 | $Excel.ActiveWindow.Displaygridlines = $false 693 | $CellRange = $WorkSheet.Range("A1:G30") 694 | #Color palette 695 | #http://dmcritchie.mvps.org/excel/colors.htm 696 | #$CellRange.Interior.ColorIndex = 1 697 | $CellRange.Font.ColorIndex = 30 698 | <# 699 | #$WorkSheet.Cells.Item(2,5).Value() = "" 700 | #Background Image 701 | $bytes = [System.Convert]::FromBase64String($EntelgyImage2) 702 | $EntelgyFile = ($env:temp + "\" + ([System.Guid]::NewGuid()).ToString() + ".png") 703 | $p = New-Object IO.MemoryStream($bytes, 0, $bytes.length) 704 | $p.Write($bytes, 0, $bytes.length) 705 | $picture = [System.Drawing.Image]::FromStream($p, $true) 706 | $picture.Save($EntelgyFile) 707 | $v= $WorkSheet.Shapes.AddPicture($EntelgyFile,0,1,0,10,616,290) 708 | $ShapeRange = $WorkSheet.Shapes.Range(1) 709 | #$ShapeRange.Left = -999996 #Right constant 710 | $ShapeRange.Left = 372 711 | $Excel.ActiveWindow.Displaygridlines = $false 712 | $CellRange = $WorkSheet.Range("A1:G20") 713 | $CellRange.Interior.ColorIndex = 14 714 | $CellRange.Font.ColorIndex = 2 715 | $row = 1 716 | $col = 1 717 | #> 718 | } 719 | End{ 720 | #nothing to do here 721 | } 722 | } -------------------------------------------------------------------------------- /Common/Runspace.ps1: -------------------------------------------------------------------------------- 1 | Function Get-RunSpaceADObject 2 | { 3 | [CmdletBinding()] 4 | Param 5 | ( 6 | [Parameter(HelpMessage="Computer or computers to gather information from", 7 | ValueFromPipeline=$true, 8 | ValueFromPipelineByPropertyName=$true, 9 | Position=0)] 10 | [ValidateNotNullOrEmpty()] 11 | [Alias('IpAddress','Server')] 12 | [Object] 13 | $ComputerName=$env:computername, 14 | 15 | [Parameter(HelpMessage="Plugin to retrieve data", 16 | Position=1)] 17 | [Array] 18 | $Plugins = $false, 19 | 20 | [Parameter(HelpMessage="Object with AD valuable data")] 21 | [Object] 22 | $ADObject = $false, 23 | 24 | [Parameter(HelpMessage="Maximum number of concurrent threads")] 25 | [ValidateRange(1,65535)] 26 | [int32] 27 | $Throttle = 32, 28 | 29 | [Parameter(HelpMessage="Timeout before a thread stops trying to gather the information")] 30 | [ValidateRange(1,65535)] 31 | [int32] 32 | $Timeout = 120, 33 | 34 | [Parameter(HelpMessage="Set this if you want run Single Query")] 35 | [switch] 36 | $SingleQuery, 37 | 38 | [Parameter(HelpMessage="Set this if you want to provide your own alternate credentials")] 39 | [System.Management.Automation.Credential()] 40 | $Credential = [System.Management.Automation.PSCredential]::Empty 41 | ) 42 | 43 | Begin{ 44 | 45 | #Function Get-RunSpaceData 46 | #http://learn-powershell.net/2012/05/10/speedy-network-information-query-using-powershell/ 47 | Function Get-RunspaceData{ 48 | [cmdletbinding()] 49 | param( 50 | [switch]$Wait, 51 | [String]$message = "Running Jobs" 52 | ) 53 | Do { 54 | $more = $false 55 | $i = 0 56 | $total = $runspaces.Count 57 | Foreach($runspace in $runspaces){ 58 | $StartTime = $runspacetimers[$runspace.ID] 59 | $i++ 60 | If ($runspace.Runspace.isCompleted) { 61 | #Write-Host $runspace.PluginName -ForegroundColor Yellow 62 | $runspace.powershell.EndInvoke($runspace.Runspace) 63 | $runspace.powershell.dispose() 64 | $runspace.Runspace = $null 65 | $runspace.powershell = $null 66 | } ElseIf ($runspace.Runspace -ne $null) { 67 | $more = $true 68 | } 69 | } 70 | If ($more -AND $PSBoundParameters['Wait']) { 71 | Start-Sleep -Milliseconds 100 72 | } 73 | #Clean out unused runspace jobs 74 | $temphash = $runspaces.clone() 75 | $temphash | Where { 76 | $_.runspace -eq $Null 77 | } | ForEach { 78 | $Runspaces.remove($_) 79 | } 80 | #Write-Progress -Activity $message -Status "Percent Complete" -PercentComplete $(($i/$total) * 100) 81 | } while ($more -AND $PSBoundParameters['Wait']) 82 | } 83 | 84 | #Inicializar variables 85 | $SyncServer = [HashTable]::Synchronized(@{}) 86 | $Global:ReturnServer = New-Object -TypeName PSCustomObject 87 | $runspacetimers = [HashTable]::Synchronized(@{}) 88 | $runspaces = New-Object -TypeName System.Collections.ArrayList 89 | $Counter = 0 90 | $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() 91 | foreach ($EntryVars in ('runspacetimers')){ 92 | $sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $EntryVars, (Get-Variable -Name $EntryVars -ValueOnly), '')) 93 | } 94 | $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) 95 | $runspacepool.ApartmentState = 'STA' 96 | $runspacepool.ThreadOptions = "ReuseThread" 97 | $runspacepool.Open() 98 | } 99 | Process{ 100 | If($Plugins -ne $false){ 101 | foreach ($Plugin in $Plugins){ 102 | #Add plugin data to ADObject 103 | $PluginFullPath = $Plugin.FullName 104 | $PluginName = [io.path]::GetFileNameWithoutExtension($Plugin.FullName) 105 | Write-Verbose "Adding $PluginName to queue..." 106 | $NewPlugin = $ADObject | Select-Object * 107 | $NewPlugin | Add-Member -type NoteProperty -name PluginName -value $PluginName -Force 108 | #End plugin work 109 | $ScriptBlockPlugin = [ScriptBlock]::Create($(Get-Content $PluginFullPath | Out-String)) 110 | $Counter++ 111 | $PowerShell = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlockPlugin) 112 | $null = $PowerShell.AddParameter('bgRunspaceID',$Counter) 113 | $null = $PowerShell.AddParameter('SyncServer',$SyncServer) 114 | $null = $PowerShell.AddParameter('ADObject',$NewPlugin) 115 | $null = $PowerShell.AddParameter('ReturnServerObject',$ReturnServer) 116 | $PowerShell.RunspacePool = $runspacepool 117 | 118 | [void]$runspaces.Add(@{ 119 | runspace = $PowerShell.BeginInvoke() 120 | PowerShell = $PowerShell 121 | Computer = $ComputerName.ComputerName 122 | PluginName = $PluginName 123 | ID = $Counter 124 | }) 125 | } 126 | } 127 | Get-RunspaceData 128 | } 129 | End{ 130 | Get-RunspaceData -Wait 131 | $runspacepool.Close() 132 | $runspacepool.Dispose() 133 | #$SyncServer 134 | return $ReturnServer 135 | } 136 | } 137 | 138 | 139 | -------------------------------------------------------------------------------- /Common/Vars.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | The script scan Active Directory and retrieve huge data of many sources like: 4 | -User objects 5 | -Computer objects 6 | -Organizational Unit 7 | -Forest 8 | -Access control list 9 | -Etc. 10 | 11 | Ability to export data to CSV and Excel format. Default save format is CSV 12 | 13 | .NOTES 14 | Author : Juan Garrido (http://windowstips.wordpress.com) 15 | Twitter : @tr1ana 16 | Company : http://www.innotecsystem.com 17 | File Name : voyeur.ps1 18 | 19 | #> 20 | 21 | #GroupTypes 22 | 23 | #A few colors for format cells 24 | $ACLColors = @{ 25 | "CreateChild, DeleteChild" = 44; #Warning 26 | "CreateChild, Self, WriteProperty, ExtendedRight, Delete, GenericRead, WriteDacl, WriteOwner" = 44;#Warning 27 | "CreateChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner" = 44;#Warning 28 | "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, Delete, WriteDacl, WriteOwner" = 44;#Warning 29 | "GenericAll" = 9;#High 30 | "GenericRead" = 10; #Low 31 | "ReadProperty, WriteProperty, ExtendedRight" = 44;#Warning 32 | "ListChildren" = 10;#Low 33 | "ReadProperty" = 10;#Low 34 | "ReadProperty, WriteProperty" = 44;#Warning 35 | "ExtendedRight" = 44;#Warning 36 | "WriteProperty" = 44;#Warning 37 | "ListChildren, ReadProperty, ListObject" = 10; #Low 38 | "GenericRead, WriteDacl" = 10; #Low 39 | "ReadProperty, GenericExecute" = 44;#Warning 40 | "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, Delete, GenericRead, WriteDacl, WriteOwner" = 44;#Warning 41 | "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner" = 44;#Warning 42 | "DeleteChild" = 66;#Warning 43 | "DeleteTree, Delete" = 66;#Warning 44 | "DeleteChild, DeleteTree, Delete" = 66;#Warning 45 | "Self, WriteProperty, GenericRead" = 66; #Warning 46 | "CreateChild, ReadProperty, GenericExecute" = 66; #Warning 47 | "CreateChild, ReadProperty, WriteProperty, GenericExecute" = 66; #Warning 48 | "ListChildren, ReadProperty, GenericWrite" = 66;#Warning 49 | "CreateChild, ListChildren, ReadProperty, GenericWrite" = 66;#Warning 50 | "CreateChild" = 10;#Low 51 | 52 | 53 | } 54 | 55 | # Set Global variables 56 | Set-Variable -Name InactiveDays -Value 180 -Scope Global 57 | Set-Variable -Name CurrentDate -Value (Get-Date).ToFileTimeUTC() -Scope Global 58 | Set-Variable -Name InactiveDate -Value (Get-Date).AddDays(-[int]$InactiveDays).ToFileTimeUtc() -Scope Global 59 | Set-Variable -Name PasswordAge -Value 120 -Scope Global 60 | Set-Variable -Name PasswordLastSet -Value (Get-Date).AddDays(-[int]$PasswordAge).ToFileTimeUtc() -Scope Global 61 | 62 | 63 | #Count of roles 64 | $Roles = @{ 65 | SQLServer=0; 66 | TerminalServer=0; 67 | Exchange=0; 68 | SCOM=0; 69 | Cluster=0; 70 | GlobalCatalog=0; 71 | DNS=0; 72 | } -------------------------------------------------------------------------------- /Common/getconfig.ps1: -------------------------------------------------------------------------------- 1 | Function Get-VoyeurConf{ 2 | Param ( 3 | [parameter(Mandatory=$true, HelpMessage="Path to voyeur config file")] 4 | [string]$Path, 5 | [parameter(Mandatory=$true, HelpMessage="Path to voyeur config file")] 6 | [string]$Node 7 | ) 8 | Begin{ 9 | $global:appSettings = @{} 10 | try{ 11 | [xml]$config = Get-Content $Path.ToString() 12 | foreach ($addNode in $config.configuration.$($Node).add){ 13 | if ($addNode.Value.Contains(‘,’)){ 14 | # Array case 15 | $value = $addNode.Value.Split(‘,’) 16 | for ($i = 0; $i -lt $value.length; $i++){ 17 | $value[$i] = $value[$i].Trim() 18 | } 19 | } 20 | else{ 21 | # Scalar case 22 | $value = $addNode.Value 23 | } 24 | $global:appSettings[$addNode.Key] = $value 25 | } 26 | } 27 | catch{ 28 | throw ("{0}: {1}" -f "Fatal error in config file",$_.Exception.Message) 29 | } 30 | } 31 | Process{ 32 | if($global:appSettings.Count -ge 0){ return $global:appSettings } 33 | else{ 34 | return $false 35 | } 36 | } 37 | End{ 38 | #Nothing to do here 39 | } 40 | } -------------------------------------------------------------------------------- /Config/Voyeur.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Images/NCC-logoFront.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverhack/voyeur/40879f1cb8e780fead483f8b6e9577da86e26d0a/Images/NCC-logoFront.png -------------------------------------------------------------------------------- /Images/NCC-logoFront_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverhack/voyeur/40879f1cb8e780fead483f8b6e9577da86e26d0a/Images/NCC-logoFront_.png -------------------------------------------------------------------------------- /Images/NCCLogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverhack/voyeur/40879f1cb8e780fead483f8b6e9577da86e26d0a/Images/NCCLogo.jpg -------------------------------------------------------------------------------- /Images/NCCLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverhack/voyeur/40879f1cb8e780fead483f8b6e9577da86e26d0a/Images/NCCLogo.png -------------------------------------------------------------------------------- /Images/User.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverhack/voyeur/40879f1cb8e780fead483f8b6e9577da86e26d0a/Images/User.png -------------------------------------------------------------------------------- /Images/ncc-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverhack/voyeur/40879f1cb8e780fead483f8b6e9577da86e26d0a/Images/ncc-logo.png -------------------------------------------------------------------------------- /Images/ncc-logo_lab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverhack/voyeur/40879f1cb8e780fead483f8b6e9577da86e26d0a/Images/ncc-logo_lab.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 silverhack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Plugins/ACL.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | The script scan Active Directory and retrieve huge data of many sources like: 4 | -User objects 5 | -Computer objects 6 | -Organizational Unit 7 | -Forest 8 | -Access control list 9 | -Etc. 10 | 11 | Ability to export data to CSV and Excel format. Default save format is CSV 12 | 13 | .NOTES 14 | Author : Juan Garrido (http://windowstips.wordpress.com) 15 | Twitter : @tr1ana 16 | Company : http://www.innotecsystem.com 17 | File Name : voyeur.ps1 18 | 19 | #> 20 | 21 | #Plugin extract computers from AD 22 | [cmdletbinding()] 23 | Param ( 24 | [Parameter(HelpMessage="Background Runspace ID")] 25 | [int] 26 | $bgRunspaceID, 27 | 28 | [Parameter(HelpMessage="Not used in this version")] 29 | [HashTable] 30 | $SyncServer, 31 | 32 | [Parameter(HelpMessage="Object with AD valuable data")] 33 | [Object] 34 | $ADObject, 35 | 36 | [Parameter(HelpMessage="Object to return data")] 37 | [Object] 38 | $ReturnServerObject, 39 | 40 | [Parameter(HelpMessage="Not used in this version")] 41 | [String] 42 | $sAMAccountName, 43 | 44 | [Parameter(HelpMessage="Organizational Unit filter")] 45 | [String] 46 | $Filter, 47 | 48 | [Parameter(HelpMessage="Attributes for user object")] 49 | [Array] 50 | $MyProperties = @("*") 51 | 52 | ) 53 | Begin{ 54 | 55 | #--------------------------------------------------- 56 | # Get Icon of ACL 57 | #--------------------------------------------------- 58 | 59 | Function Get-Icon{ 60 | Param ( 61 | [parameter(Mandatory=$true, HelpMessage="Color")] 62 | [String]$Color 63 | ) 64 | #A few colors for format cells 65 | $ACLIcons = @{ 66 | "CreateChild, DeleteChild" = 66; #Warning 67 | "DeleteTree, WriteDacl" = 66;#Warning 68 | "Delete" = 99#Warning 69 | "CreateChild, DeleteChild, ListChildren" = 66; #Warning 70 | "CreateChild, Self, WriteProperty, ExtendedRight, Delete, GenericRead, WriteDacl, WriteOwner" = 66;#Warning 71 | "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, Delete, WriteDacl, WriteOwner" = 66;#Warning 72 | "CreateChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner" = 66;#Warning 73 | "ListChildren, ReadProperty, ListObject" = 10; #Low 74 | "GenericAll" = 99;#High 75 | "GenericRead" = 10; #Low 76 | "ReadProperty, WriteProperty, ExtendedRight" = 66;#Warning 77 | "ListChildren" = 10;#Low 78 | "ReadProperty" = 10;#Low 79 | "ReadProperty, WriteProperty" = 66;#Warning 80 | "ReadProperty, GenericExecute" = 66;#Warning 81 | "GenericRead, WriteDacl" = 10; #Low 82 | "ExtendedRight" = 66;#Warning 83 | "WriteProperty" = 66;#Warning 84 | "DeleteChild" = 66;#Warning 85 | "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, Delete, GenericRead, WriteDacl, WriteOwner" = 66;#Warning 86 | "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner" = 66;#Warning 87 | "DeleteTree, Delete" = 66;#Warning 88 | "DeleteChild, DeleteTree, Delete" = 66;#Warning 89 | "CreateChild, ReadProperty, GenericExecute" = 66; #Warning 90 | "Self, WriteProperty, GenericRead" = 66; #Warning 91 | "CreateChild, ReadProperty, WriteProperty, GenericExecute" = 66; #Warning 92 | "ListChildren, ReadProperty, GenericWrite" = 66;#Warning 93 | "CreateChild, ListChildren, ReadProperty, GenericWrite" = 66;#Warning 94 | "CreateChild" = 10;#Low 95 | } 96 | try{ 97 | return $ACLIcons[$Color] 98 | } 99 | catch{ 100 | Write-Host "ACL color not found" -ForegroundColor Red 101 | } 102 | } 103 | 104 | #--------------------------------------------------- 105 | # Search ACL in domain 106 | #--------------------------------------------------- 107 | function TranslateInheritanceType{ 108 | Param ( 109 | [parameter(Mandatory=$true, HelpMessage="InheritanceType")] 110 | [String]$Type 111 | ) 112 | $AppliesTo = "" 113 | $InheritanceType = @{"None"="This object only";"Descendents"="All child objects";` 114 | "SelfAndChildren"="This object and one level Of child objects";` 115 | "Children"="One level of child objects";"All"="This object and all child objects"} 116 | try{ 117 | $AppliesTo = $InheritanceType[[String]::Format($type)] 118 | } 119 | catch{ 120 | $AppliesTo = "Flag not resolved" 121 | Write-Debug $AppliesTo 122 | } 123 | return $AppliesTo 124 | } 125 | #--------------------------------------------------- 126 | # Translate SID 127 | #--------------------------------------------------- 128 | function TranslateSid{ 129 | Param ( 130 | [parameter(Mandatory=$true, HelpMessage="SID object to translate")] 131 | [Object]$SID 132 | ) 133 | $Account = "" 134 | $KnowSID = @{"S-1-0" ="Null Authority";"S-1-0-0"="Nobody";"S-1-1"="World Authority";"S-1-1-0"="Everyone";` 135 | "S-1-2"="Local Authority";"S-1-2-0"="Local";"S-1-2-1"="Console Logon";"S-1-3"="Creator Authority";` 136 | "S-1-3-0"="Creator Owner";"S-1-3-1"="Creator Group";"S-1-3-4"="Owner Rights";"S-1-5-80-0"="All Services";` 137 | "S-1-4"="Non Unique Authority";"S-1-5"="NT Authority";"S-1-5-1"="Dialup";"S-1-5-2"="Network";` 138 | "S-1-5-3"="Batch";"S-1-5-4"="Interactive";"S-1-5-6"="Service";"S-1-5-7" ="Anonymous";"S-1-5-9"="Enterprise Domain Controllers";` 139 | "S-1-5-10"="Self";"S-1-5-11"="Authenticated Users";"S-1-5-12"="Restricted Code";"S-1-5-13"="Terminal Server Users";` 140 | "S-1-5-14"="Remote Interactive Logon";"S-1-5-15"="This Organization";"S-1-5-17"="This Organization";"S-1-5-18"="Local System";` 141 | "S-1-5-19"="NT Authority Local Service";"S-1-5-20"="NT Authority Network Service";"S-1-5-32-544"="Administrators";` 142 | "S-1-5-32-545"="Users";"S-1-5-32-546"="Guests";"S-1-5-32-547"="Power Users";"S-1-5-32-548"="Account Operators";` 143 | "S-1-5-32-549"="Server Operators";"S-1-5-32-550"="Print Operators";"S-1-5-32-551"="Backup Operators";"S-1-5-32-552"="Replicators";` 144 | "S-1-5-32-554"="Pre-Windows 2000 Compatibility Access";"S-1-5-32-555"="Remote Desktop Users";"S-1-5-32-556"="Network Configuration Operators";` 145 | "S-1-5-32-557"="Incoming forest trust builders";"S-1-5-32-558"="Performance Monitor Users";"S-1-5-32-559"="Performance Log Users";` 146 | "S-1-5-32-560"="Windows Authorization Access Group";"S-1-5-32-561"="Terminal Server License Servers";"S-1-5-32-562"="Distributed COM Users";` 147 | "S-1-5-32-569"="Cryptographic Operators";"S-1-5-32-573"="Event Log Readers";"S-1-5-32-574"="Certificate Services DCOM Access";` 148 | "S-1-5-32-575"="RDS Remote Access Servers";"S-1-5-32-576"="RDS Endpoint Servers";"S-1-5-32-577"="RDS Management Servers";` 149 | "S-1-5-32-578"="Hyper-V Administrators";"S-1-5-32-579"="Access Control Assistance Operators";"S-1-5-32-580"="Remote Management Users"} 150 | try{ 151 | $Account = $SID.Translate([System.Security.Principal.NTAccount]) 152 | #Write-Host $Account -ForegroundColor Red 153 | } 154 | catch{ 155 | $Account = $KnowSID[[String]::Format($SID)] 156 | #Write-Host $Account -ForegroundColor Yellow 157 | } 158 | return $Account 159 | } 160 | } 161 | Process{ 162 | #Extract all users from domain 163 | $Domain = $ADObject.Domain 164 | $Connection = $ADObject.Domain.ADConnection 165 | $UseCredentials = $ADObject.UseCredentials 166 | $PluginName = $ADObject.PluginName 167 | #Extract action from object 168 | $Action = $ADObject.OUExtract 169 | $FullACLExtract = $Action.FullACL 170 | $Query = $Action.Query 171 | $ReportName = $Action.Name 172 | #End action retrieve 173 | if($Connection){ 174 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 175 | $Searcher.SearchScope = "subtree" 176 | $Searcher.SearchRoot = $Connection 177 | } 178 | if($Searcher){ 179 | $Searcher.PageSize = 200 180 | } 181 | if($Action){ 182 | Write-Host ("{0}: {1}" -f "ACL task ID $bgRunspaceID", "Retrieve $($Action.Name) ACL and data from $($Domain.Name)")` 183 | -ForegroundColor Magenta 184 | $Searcher.filter = $Query 185 | [void]$Searcher.PropertiesToLoad.AddRange("DistinguishedName") 186 | [void]$Searcher.PropertiesToLoad.AddRange("name") 187 | $Results = $Searcher.FindAll() 188 | $ListOU = @() 189 | if($FullACLExtract -and $Results){ 190 | foreach ($result in $Results){ 191 | #$ListOU += $result.properties.item("DistinguishedName") 192 | $data = $result.GetDirectoryEntry() 193 | $aclObject = $data.Get_ObjectSecurity() 194 | $aclList = $aclObject.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier]) 195 | foreach($acl in $aclList){ 196 | $objSID = New-Object System.Security.Principal.SecurityIdentifier($acl.IdentityReference) 197 | $AccountName = TranslateSID($objSID) 198 | $AppliesTo = TranslateInheritanceType($acl.InheritanceType) 199 | $OUACL = @{ 200 | "AccountName" = [String]$AccountName 201 | "DN" = [String]::Format($result.properties.item('DistinguishedName')) 202 | "Rights" = $acl.ActiveDirectoryRights 203 | "Risk" = (Get-Icon $acl.ActiveDirectoryRights).ToString() 204 | "AppliesTo" = [String]$AppliesTo 205 | "AccessControlType" = [String]$acl.AccessControlType 206 | "InheritanceFlags" = [String]$acl.InheritanceFlags 207 | } 208 | $obj = New-Object -TypeName PSObject -Property $OUACL 209 | $obj.PSObject.typenames.insert(0,'Arsenal.AD.$($ReportName)') 210 | $ListOU +=$obj 211 | #Write-Output $obj 212 | } 213 | } 214 | #Add Full OU data to object 215 | #Create custom object for store data 216 | $TmpObject = New-Object -TypeName PSCustomObject 217 | $TmpObject | Add-Member -type NoteProperty -name Data -value $ListOU 218 | 219 | #Formatting Excel Data 220 | $Excelformatting = New-Object -TypeName PSCustomObject 221 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $ListOU 222 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value $ReportName 223 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value $ReportName 224 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 225 | $Excelformatting | Add-Member -type NoteProperty -name IconSet -value $True 226 | $Excelformatting | Add-Member -type NoteProperty -name IconColumnName -value "Risk" 227 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 228 | 229 | #Add Excel formatting into psobject 230 | $TmpObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 231 | 232 | #Add ACL data to object 233 | if($ListOU){ 234 | $ReturnServerObject | Add-Member -type NoteProperty -name $ReportName -value $TmpObject 235 | } 236 | 237 | #Add Full OU to report 238 | $CustomReportFields = $ReturnServerObject.Report 239 | $NewCustomReportFields = [array]$CustomReportFields+=$ReportName 240 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 241 | #Work with Synchronized hash 242 | $SyncServer.FullOrganizationalUnit=$ListOU 243 | } 244 | if(!$FullACLExtract -and $Results){ 245 | foreach ($result in $Results){ 246 | $OUProperties = @{ 247 | "Name" = [String]::Format($result.properties.item('name')) 248 | "DN" = [String]::Format($result.properties.item('DistinguishedName')) 249 | } 250 | $obj = New-Object -TypeName PSObject -Property $OUProperties 251 | $obj.PSObject.typenames.insert(0,'Arsenal.AD.$($ReportName)') 252 | $ListOU +=$obj 253 | } 254 | #Add Simple OU data to object 255 | #Create custom object for store data 256 | $TmpObject = New-Object -TypeName PSCustomObject 257 | $TmpObject | Add-Member -type NoteProperty -name Data -value $ListOU 258 | 259 | #Formatting Excel Data 260 | $Excelformatting = New-Object -TypeName PSCustomObject 261 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $ListOU 262 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value $ReportName 263 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value $ReportName 264 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 265 | #$Excelformatting | Add-Member -type NoteProperty -name IconSet -value $True 266 | #$Excelformatting | Add-Member -type NoteProperty -name IconColumnName -value "Risk" 267 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 268 | 269 | #Add Excel formatting into psobject 270 | $TmpObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 271 | 272 | #Add ACL data to object 273 | if($ListOU){ 274 | $ReturnServerObject | Add-Member -type NoteProperty -name $ReportName -value $TmpObject 275 | } 276 | 277 | 278 | #Add SimpleOU to report 279 | $CustomReportFields = $ReturnServerObject.Report 280 | $NewCustomReportFields = [array]$CustomReportFields+="$ReportName" 281 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 282 | #Work with Synchronized hash 283 | $SyncServer.OrganizationalUnit=$ListOU 284 | } 285 | } 286 | 287 | 288 | } 289 | End{ 290 | #Nothing to do here 291 | #End 292 | } -------------------------------------------------------------------------------- /Plugins/Computers.ps1: -------------------------------------------------------------------------------- 1 | #Plugin extract computers from AD 2 | [cmdletbinding()] 3 | Param ( 4 | [Parameter(HelpMessage="Background Runspace ID")] 5 | [int] 6 | $bgRunspaceID, 7 | 8 | [Parameter(HelpMessage="Not used in this version")] 9 | [HashTable] 10 | $SyncServer, 11 | 12 | [Parameter(HelpMessage="Object with AD valuable data")] 13 | [Object] 14 | $ADObject, 15 | 16 | [Parameter(HelpMessage="Object to return data")] 17 | [Object] 18 | $ReturnServerObject, 19 | 20 | [Parameter(HelpMessage="Not used in this version")] 21 | [String] 22 | $sAMAccountName, 23 | 24 | [Parameter(HelpMessage="Organizational Unit filter")] 25 | [String] 26 | $Filter, 27 | 28 | [Parameter(HelpMessage="Attributes for user object")] 29 | [Array] 30 | $MyProperties = @("*") 31 | 32 | ) 33 | Begin{ 34 | #--------------------------------------------------- 35 | # General Status of Active Directory objects 36 | #--------------------------------------------------- 37 | 38 | Function Get-ComputerStatus{ 39 | Param ( 40 | [parameter(Mandatory=$true, HelpMessage="Computers Object")] 41 | [Object]$Computers 42 | ) 43 | #Populate Array with OS & Service Pack 44 | $AllOS= @() 45 | for ($i=0; $i -lt $Computers.Count; $i++){ 46 | $OS = "{0} {1}" -f $Computers[$i].operatingSystem, $Computers[$i].operatingSystemServicePack 47 | $NewOS = @{"OS"=$OS} 48 | $obj = New-Object -TypeName PSObject -Property $NewOS 49 | $obj.PSObject.typenames.insert(0,'Arsenal.AD.OperatingSystem') 50 | $AllOS+=$obj 51 | } 52 | $OSChart = @{} 53 | $AllOS | Group OS | ForEach-Object {$OSChart.Add($_.Name,@($_.Count))} 54 | if($OSChart){ 55 | return $OSChart 56 | } 57 | 58 | } 59 | 60 | #--------------------------------------------------- 61 | # Construct detailed PSObject 62 | #--------------------------------------------------- 63 | 64 | function Set-PSObject ([Object] $Object, [String] $type){ 65 | $FinalObject = @() 66 | foreach ($Obj in $Object){ 67 | $NewObject = New-Object -TypeName PSObject -Property $Obj 68 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 69 | $FinalObject +=$NewObject 70 | } 71 | return $FinalObject 72 | } 73 | #Start computers plugin 74 | $DomainName = $ADObject.Domain.Name 75 | $PluginName = $ADObject.PluginName 76 | Write-Host ("{0}: {1}" -f "Computers task ID $bgRunspaceID", "Retrieve computers data from $DomainName")` 77 | -ForegroundColor Magenta 78 | } 79 | Process{ 80 | #Extract computers from domain 81 | $Domain = $ADObject.Domain 82 | $Connection = $ADObject.Domain.ADConnection 83 | $UseSSL = $ADObject.UseSSL 84 | $Computers = @() 85 | if($Connection){ 86 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 87 | $Searcher.SearchScope = "subtree" 88 | $Searcher.SearchRoot = $Connection 89 | } 90 | if($Searcher){ 91 | $Searcher.PageSize = 200 92 | } 93 | # Add Attributes 94 | $ComputersProperties = $ADObject.ComputersFilter 95 | if($ComputersProperties){ 96 | foreach($property in $ComputersProperties){ 97 | $Searcher.PropertiesToLoad.Add([String]::Format($property)) > $Null 98 | } 99 | } 100 | else{ 101 | $ComputersProperties = "*" 102 | } 103 | if (!$sAMAccountName){ 104 | $Searcher.Filter = "(objectCategory=Computer)" 105 | $Results = $Searcher.FindAll() 106 | Write-Verbose "Computers search return $($Results.Count)" -Verbose 107 | } 108 | else{ 109 | $Searcher.filter = "(&(objectClass=Computer)(sAMAccountName= $sAMAccountName))" 110 | $Results = $Searcher.FindAll() 111 | #Write-Verbose "The user search return $($Results.Count)" -Verbose 112 | } 113 | if($Results){ 114 | ForEach ($Result In $Results){ 115 | $record = @{} 116 | ForEach ($Property in $ComputersProperties){ 117 | if ($Property -eq "lastLogon"){ 118 | if ([String]::IsNullOrEmpty($Result.Properties.Item([String]::Format($Property)))){ 119 | $record.Add($Property,"Never") 120 | } 121 | else{ 122 | $lastLogon = [datetime]::FromFileTime([Int64]::Parse($Result.Properties.Item([String]::Format($Property)))) 123 | $record.Add($Property,$lastLogon) 124 | } 125 | } 126 | elseif ($Property -eq "pwdLastSet"){ 127 | if ([String]::IsNullOrEmpty($Result.Properties.Item([String]::Format($Property)))){ 128 | $record.Add($Property,0) 129 | } 130 | else{ 131 | $pwdLastSet = [datetime]::FromFileTime([Int64]::Parse($Result.Properties.Item([String]::Format($Property)))) 132 | $record.Add($Property,$pwdLastSet) 133 | } 134 | } 135 | else{ 136 | $record.Add($Property,[String] $Result.Properties.Item([String]::Format($Property))) 137 | } 138 | } 139 | $Computers +=$record 140 | } 141 | } 142 | } 143 | End{ 144 | #Get status from all computers 145 | $ComputersStatus = Get-ComputerStatus -Computers $Computers 146 | #$OSCount = $ComputersStatus | Format-Table @{label='OS';expression={$_.Name}},@{label='Count';expression={$_.Value}} 147 | #Set PsObject from computers object 148 | $AllComputers = Set-PSObject -Object $Computers -type "AD.Arsenal.Computers" 149 | #Work with SyncHash 150 | $SyncServer.$($PluginName)=$AllComputers 151 | $SyncServer.ComputersStatus=$ComputersStatus 152 | #Add computers data to object 153 | #$ReturnServerObject | Add-Member -type NoteProperty -name ComputersStatus -value $ComputersStatus 154 | 155 | #Create custom object for store data 156 | $ComputersData = New-Object -TypeName PSCustomObject 157 | $ComputersData | Add-Member -type NoteProperty -name Data -value $AllComputers 158 | 159 | #Formatting Excel Data 160 | $Excelformatting = New-Object -TypeName PSCustomObject 161 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $AllComputers 162 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "All Computers" 163 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "All Computers" 164 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 165 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 166 | 167 | #Add Excel formatting into psobject 168 | $ComputersData | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 169 | 170 | #Add Groups data to object 171 | $ReturnServerObject | Add-Member -type NoteProperty -name Computers -value $ComputersData 172 | 173 | #Add Computer status Chart 174 | if($ComputersStatus){ 175 | $TmpCustomObject = New-Object -TypeName PSCustomObject 176 | 177 | #Formatting Excel Data 178 | $Excelformatting = New-Object -TypeName PSCustomObject 179 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $ComputersStatus 180 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Computer status" 181 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Dashboard Computers" 182 | $Excelformatting | Add-Member -type NoteProperty -name isnewSheet -value $True 183 | $Excelformatting | Add-Member -type NoteProperty -name showTotals -value $True 184 | $Excelformatting | Add-Member -type NoteProperty -name showHeaders -value $false 185 | $Excelformatting | Add-Member -type NoteProperty -name addHeader -value $false 186 | $Excelformatting | Add-Member -type NoteProperty -name position -value @(2,1) 187 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "NewTable" 188 | 189 | #Add chart 190 | $Excelformatting | Add-Member -type NoteProperty -name addChart -value $True 191 | $Excelformatting | Add-Member -type NoteProperty -name chartType -value "xlColumnClustered" 192 | $Excelformatting | Add-Member -type NoteProperty -name ChartTitle -value "OS versions" 193 | $Excelformatting | Add-Member -type NoteProperty -name style -value 34 194 | $Excelformatting | Add-Member -type NoteProperty -name hasDataTable -value $true 195 | 196 | $TmpCustomObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 197 | 198 | #Add Computers chart 199 | $ReturnServerObject | Add-Member -type NoteProperty -name GlobalComputers -value $TmpCustomObject 200 | 201 | } 202 | 203 | 204 | #Add Computers to report 205 | $CustomReportFields = $ReturnServerObject.Report 206 | $NewCustomReportFields = [array]$CustomReportFields+="Computers, ComputerStatus" 207 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 208 | #End 209 | } -------------------------------------------------------------------------------- /Plugins/GppPasswords.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | Param ( 3 | [Parameter(HelpMessage="Background Runspace ID")] 4 | [int] 5 | $bgRunspaceID, 6 | 7 | [Parameter(HelpMessage="Not used in this version")] 8 | [HashTable] 9 | $SyncServer, 10 | 11 | [Parameter(HelpMessage="Object with AD valuable data")] 12 | [Object] 13 | $ADObject, 14 | 15 | [Parameter(HelpMessage="Object to return data")] 16 | [Object] 17 | $ReturnServerObject, 18 | 19 | [Parameter(HelpMessage="Not used in this version")] 20 | [String] 21 | $sAMAccountName, 22 | 23 | [Parameter(HelpMessage="Organizational Unit filter")] 24 | [String] 25 | $Filter, 26 | 27 | [Parameter(HelpMessage="Attributes for user object")] 28 | [Array] 29 | $MyProperties = @("*") 30 | 31 | ) 32 | Begin{ 33 | #define helper function that decodes and decrypts password 34 | function Get-DecryptedCpassword { 35 | Param ( 36 | [string] $Cpassword 37 | ) 38 | 39 | try { 40 | #Append appropriate padding based on string length 41 | $Mod = ($Cpassword.length % 4) 42 | if ($Mod -ne 0) {$Cpassword += ('=' * (4 - $Mod))} 43 | 44 | $Base64Decoded = [Convert]::FromBase64String($Cpassword) 45 | 46 | #Create a new AES .NET Crypto Object 47 | $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider 48 | [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8, 49 | 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b) 50 | 51 | #Set IV to all nulls to prevent dynamic generation of IV value 52 | $AesIV = New-Object Byte[]($AesObject.IV.Length) 53 | $AesObject.IV = $AesIV 54 | $AesObject.Key = $AesKey 55 | $DecryptorObject = $AesObject.CreateDecryptor() 56 | [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length) 57 | 58 | return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) 59 | } 60 | 61 | catch {Write-Error $Error[0]} 62 | } 63 | #Check if part of domain 64 | function Check-IfDomain{ 65 | [CmdletBinding()] 66 | param ( 67 | [parameter(Position=0, 68 | Mandatory=$true, 69 | ValueFromPipeline=$true, 70 | ValueFromPipelineByPropertyName=$true)] 71 | [string]$computer="." 72 | ) 73 | PROCESS{ 74 | $DomainData = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computer | 75 | select Name, Domain, partofdomain 76 | 77 | #Return Object 78 | return $DomainData 79 | }#process 80 | } 81 | #Get potential files containing passwords from Group Policy objects 82 | Function Get-XMLFiles{ 83 | $AllFiles = $false 84 | if($ADObject.Domain.DistinguishedName -or $ADObject.Domain.DNSRoot -or $ADObject.Domain.DNSHostName){ 85 | try{ 86 | $DomainName = $ADObject.Domain.DNSRoot 87 | $AllFiles = Get-ChildItem "\\$DomainName\SYSVOL" -Recurse -ErrorAction Stop -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Drives.xml' 88 | #Push-Location 89 | #Start-Sleep -Milliseconds 100 90 | #Pop-Location 91 | } 92 | catch [System.Management.Automation.ItemNotFoundException]{ 93 | Write-Warning "Item not found" 94 | } 95 | catch [System.UnauthorizedAccessException]{ 96 | Write-Warning "Access denied" 97 | } 98 | catch{ 99 | Write-Host "Function Get-XMLFiles $($_.Exception.Message)" -ForegroundColor Red 100 | } 101 | } 102 | return $AllFiles 103 | } 104 | } 105 | Process{ 106 | #Start GPP plugin 107 | $DomainName = $ADObject.Domain.Name 108 | $PluginName = $ADObject.PluginName 109 | Write-Host ("{0}: {1}" -f "Group Policy task ID $bgRunspaceID", "Retrieve potential files with Username:passwords from $DomainName")` 110 | -ForegroundColor Magenta 111 | $AllItems = Get-XMLFiles 112 | if($AllItems){ 113 | $GPPLeaks = @() 114 | foreach ($File in $AllItems){ 115 | $Filename = $File.Name 116 | $Filepath = $File.VersionInfo.FileName 117 | #put filename in $XmlFile 118 | [xml] $Xml = Get-Content($File) 119 | 120 | #declare blank variables 121 | $Cpassword = '' 122 | $UserName = '' 123 | $NewName = '' 124 | $Changed = '' 125 | 126 | switch ($Filename) { 127 | 128 | 'Groups.xml' { 129 | $Cpassword = $Xml.Groups.User.Properties.cpassword 130 | $UserName = $Xml.Groups.User.Properties.userName 131 | $NewName = $Xml.Groups.User.Properties.newName 132 | $Changed = $Xml.Groups.User.changed 133 | } 134 | 135 | 'Services.xml' { 136 | $Cpassword = $Xml.NTServices.NTService.Properties.cpassword 137 | $UserName = $Xml.NTServices.NTService.Properties.accountName 138 | $Changed = $Xml.NTServices.NTService.changed 139 | } 140 | 141 | 'Scheduledtasks.xml' { 142 | $Cpassword = $Xml.ScheduledTasks.Task.Properties.cpassword 143 | $UserName = $Xml.ScheduledTasks.Task.Properties.runAs 144 | $Changed = $Xml.ScheduledTasks.Task.changed 145 | } 146 | 147 | 'DataSources.xml' { 148 | $Cpassword = $Xml.DataSources.DataSource.Properties.cpassword 149 | $UserName = $Xml.DataSources.DataSource.Properties.username 150 | $Changed = $Xml.DataSources.DataSource.changed 151 | } 152 | 153 | 'Drives.xml' { 154 | $Cpassword = $Xml.Drives.Drive.Properties.cpassword 155 | $UserName = $Xml.Drives.Drive.Properties.username 156 | $Changed = $Xml.Drives.Drive.changed 157 | } 158 | } 159 | if ($Cpassword) {$Password = Get-DecryptedCpassword $Cpassword} 160 | else {Write-Verbose "No encrypted passwords found in $Filepath"} 161 | #Create custom object to output results 162 | $ObjectProperties = @{'Password' = [String]$Password; 163 | 'UserName' = [String]$UserName; 164 | 'Changed' = [String]$Changed; 165 | 'NewName' = [String]$NewName 166 | 'File' = [String]$Filepath} 167 | 168 | $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties 169 | $ResultsObject.PSObject.typenames.insert(0,'Arsenal.AD.GetPasswords') 170 | $GPPLeaks += $ResultsObject 171 | } 172 | } 173 | else{ 174 | Write-Warning 'No group policy preferences found on Domain....' 175 | Break 176 | } 177 | } 178 | End{ 179 | #Work with SyncHash 180 | $SyncServer.$($PluginName)=$GPPLeaks 181 | #Add Group policy passwords data to object 182 | 183 | #Create custom object for store data 184 | $GroupPolicyLeaks = New-Object -TypeName PSCustomObject 185 | $GroupPolicyLeaks | Add-Member -type NoteProperty -name Data -value $GPPLeaks 186 | 187 | #Formatting Excel Data 188 | $Excelformatting = New-Object -TypeName PSCustomObject 189 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $GPPLeaks 190 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "GPPLeaks" 191 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "GPPLeaks" 192 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 193 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 194 | 195 | #Add Excel formatting into psobject 196 | $GroupPolicyLeaks | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 197 | 198 | #Add Groups data to object 199 | if($GPPLeaks){ 200 | $ReturnServerObject | Add-Member -type NoteProperty -name GppLeaks -value $GroupPolicyLeaks 201 | } 202 | 203 | #Add GPP to report 204 | $CustomReportFields = $ReturnServerObject.Report 205 | $NewCustomReportFields = [array]$CustomReportFields+="GPPleaks" 206 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 207 | #End 208 | } -------------------------------------------------------------------------------- /Plugins/GroupMonitor.ps1: -------------------------------------------------------------------------------- 1 | #Plugin extract roles from AD 2 | [cmdletbinding()] 3 | Param ( 4 | [Parameter(HelpMessage="Background Runspace ID")] 5 | [int] 6 | $bgRunspaceID, 7 | 8 | [Parameter(HelpMessage="Not used in this version")] 9 | [HashTable] 10 | $SyncServer, 11 | 12 | [Parameter(HelpMessage="Object with AD valuable data")] 13 | [Object] 14 | $ADObject, 15 | 16 | [Parameter(HelpMessage="Object to return data")] 17 | [Object] 18 | $ReturnServerObject, 19 | 20 | [Parameter(HelpMessage="Not used in this version")] 21 | [String] 22 | $sAMAccountName, 23 | 24 | [Parameter(HelpMessage="Organizational Unit filter")] 25 | [String] 26 | $Filter, 27 | 28 | [Parameter(HelpMessage="Attributes for user object")] 29 | [Array] 30 | $MyProperties = @("*") 31 | 32 | ) 33 | Begin{ 34 | 35 | #------------------------------------------------------- 36 | # Function to convert msds-replvaluemetadata in XML data 37 | #------------------------------------------------------- 38 | function ConvertReplTo-XML{ 39 | Param ( 40 | [parameter(Mandatory=$true, HelpMessage="msds-replvaluemetadata object")] 41 | [Object]$replmetadata 42 | ) 43 | Begin{ 44 | #Initialize vars 45 | $NewXmlData = $Null 46 | } 47 | Process{ 48 | if($replmetadata -ne $Null){ 49 | $NewXmlData = $replmetadata.Replace("&","&") 50 | } 51 | } 52 | End{ 53 | if($NewXmlData){ 54 | return [XML]$NewXmlData 55 | } 56 | } 57 | } 58 | 59 | #--------------------------------------------------- 60 | # Function to analyze msds-replvaluemetadata info 61 | #--------------------------------------------------- 62 | function Get-MetadataInfo{ 63 | Param ( 64 | [parameter(Mandatory=$true, HelpMessage="Groups object")] 65 | [Object]$AllGroups 66 | ) 67 | Begin{ 68 | $Threshold = 180 69 | $Today = Get-Date 70 | $AllObj = @() 71 | } 72 | Process{ 73 | for ($i=0; $i -lt $AllGroups.Count; $i++){ 74 | $distinguishedname = [string] $AllGroups[$i].Properties.distinguishedname 75 | $sAMAccountName = [string] $AllGroups[$i].Properties.samaccountname 76 | $replmetatada = $AllGroups[$i].Properties.'msds-replvaluemetadata' 77 | # Show progress in the script 78 | Write-Progress -Activity "Collecting protected groups" ` 79 | -Status "$($sAMAccountName): processing of $i/$($AllGroups.Count) Groups"` 80 | -Id 0 -CurrentOperation "Parsing group: $distinguishedname"` 81 | -PercentComplete ( ($i / $AllGroups.Count) * 100 ) 82 | #Iterate for each object 83 | foreach($metadata in $replmetatada){ 84 | if($metadata){ 85 | [XML]$XML = ConvertReplTo-XML -replmetadata $metadata 86 | $Match= $xml.DS_REPL_VALUE_META_DATA | Where-Object {$_.pszAttributeName -eq "member"} | 87 | Select-Object @{name="Attribute"; expression={$_.pszAttributeName}}, 88 | @{name="objectDN";expression={$_.pszObjectDn}}, 89 | @{name="pszObjectDn";expression={$_.ftimeCreated}}, 90 | @{name="dwVersion";expression={$_.dwVersion}}, 91 | @{name="ftimeDeleted";expression={$_.ftimeDeleted}}, 92 | @{name="ftimeCreated";expression={$_.ftimeCreated}} 93 | try{ 94 | #Check if object has been added or removed 95 | If ( ([datetime] $Match.ftimeDeleted) -ge $Today.AddDays(-$Threshold)` 96 | -or ([datetime] $Match.ftimeCreated) -ge $Today.AddDays(-$Threshold)){ 97 | #Check if added or removed 98 | If ( $Match.ftimeDeleted -ne "1601-01-01T00:00:00Z"){ 99 | #Deleted user 100 | $Flag = "removed" 101 | $Datemodified = $Match.ftimeDeleted 102 | } 103 | else{ 104 | #User added 105 | $Flag = "added" 106 | $Datemodified = $Match.ftimeCreated 107 | } 108 | #Create Object 109 | $record = @{} 110 | $record.Add("distinguishedName", $distinguishedname) 111 | $record.Add("sAMAccountName", $sAMAccountName) 112 | $record.Add("MemberDN", $Match.ObjectDn) 113 | $record.Add("Operation", $Flag) 114 | $record.Add("FirstTime", $Match.ftimeCreated) 115 | $record.Add("ModifiedTime", $Datemodified) 116 | $record.Add("Version", $Match.dwVersion) 117 | 118 | #Add to array 119 | $AllObj += $record 120 | } 121 | } 122 | catch{ 123 | #Nothing to do here 124 | } 125 | } 126 | } 127 | 128 | } 129 | } 130 | End{ 131 | if($AllObj){ 132 | return $AllObj 133 | } 134 | } 135 | } 136 | 137 | #--------------------------------------------------- 138 | # Construct detailed PSObject 139 | #--------------------------------------------------- 140 | 141 | function Set-PSObject{ 142 | Param ( 143 | [parameter(Mandatory=$true, HelpMessage="Object")] 144 | [Object]$Object, 145 | 146 | [parameter(Mandatory=$true, HelpMessage="Object")] 147 | [String]$Type 148 | ) 149 | Begin{ 150 | #Declare vars 151 | $FinalObject = @() 152 | } 153 | Process{ 154 | foreach ($Obj in $Object){ 155 | $NewObject = New-Object -TypeName PSObject -Property $Obj 156 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 157 | $FinalObject +=$NewObject 158 | } 159 | } 160 | End{ 161 | if($FinalObject){ 162 | return $FinalObject 163 | } 164 | } 165 | } 166 | 167 | #Start computers plugin 168 | $DomainName = $ADObject.Domain.Name 169 | $PluginName = $ADObject.PluginName 170 | Write-Host ("{0}: {1}" -f "Group membership task ID $bgRunspaceID", "Retrieve group membership change information from $DomainName")` 171 | -ForegroundColor Magenta 172 | } 173 | Process{ 174 | #Extract information about connection 175 | $Connection = $ADObject.Domain.ADConnection 176 | if($Connection){ 177 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 178 | $Searcher.SearchScope = "subtree" 179 | $Searcher.SearchRoot = $Connection 180 | } 181 | if($Searcher){ 182 | $Searcher.PageSize = 200 183 | #Add filter 184 | $Searcher.Filter = "(&(objectClass=group)(adminCount=1))" 185 | #Add properties to query 186 | [void]$Searcher.PropertiesToLoad.Add("distinguishedName") 187 | [void]$Searcher.PropertiesToLoad.Add("msDS-ReplValueMetaData") 188 | [void]$Searcher.PropertiesToLoad.Add("sAMAccountName") 189 | $PriviledgeGroups = $Searcher.FindAll() 190 | if($PriviledgeGroups){ 191 | $RawObj = Get-MetadataInfo -AllGroups $PriviledgeGroups 192 | } 193 | } 194 | } 195 | End{ 196 | if($RawObj){ 197 | $MonitoredGroups = Set-PSObject $RawObj "Arsenal.AD.MonitoredGroups" 198 | #Work with SyncHash 199 | $SyncServer.$($PluginName)=$MonitoredGroups 200 | 201 | #Create custom object for store data 202 | $TmpObject = New-Object -TypeName PSCustomObject 203 | $TmpObject | Add-Member -type NoteProperty -name Data -value $MonitoredGroups 204 | 205 | #Formatting Excel Data 206 | $Excelformatting = New-Object -TypeName PSCustomObject 207 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $MonitoredGroups 208 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Administrative Group changes" 209 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Administrative Group changes" 210 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 211 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 212 | 213 | #Add Excel formatting into psobject 214 | $TmpObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 215 | 216 | #Add roles data to object 217 | $ReturnServerObject | Add-Member -type NoteProperty -name GroupChange -value $TmpObject 218 | } 219 | } -------------------------------------------------------------------------------- /Plugins/GroupPolicy.ps1: -------------------------------------------------------------------------------- 1 | #Plugin extract computers from AD 2 | [cmdletbinding()] 3 | Param ( 4 | [Parameter(HelpMessage="Background Runspace ID")] 5 | [int] 6 | $bgRunspaceID, 7 | 8 | [Parameter(HelpMessage="Not used in this version")] 9 | [HashTable] 10 | $SyncServer, 11 | 12 | [Parameter(HelpMessage="Object with AD valuable data")] 13 | [Object] 14 | $ADObject, 15 | 16 | [Parameter(HelpMessage="Object to return data")] 17 | [Object] 18 | $ReturnServerObject, 19 | 20 | [Parameter(HelpMessage="Not used in this version")] 21 | [String] 22 | $sAMAccountName, 23 | 24 | [Parameter(HelpMessage="Organizational Unit filter")] 25 | [String] 26 | $Filter, 27 | 28 | [Parameter(HelpMessage="Attributes for user object")] 29 | [Array] 30 | $MyProperties = @("*") 31 | 32 | ) 33 | Begin{ 34 | #--------------------------------------------------- 35 | # Construct detailed PSObject 36 | #--------------------------------------------------- 37 | 38 | function Set-PSObject ([Object] $Object, [String] $type){ 39 | $FinalObject = @() 40 | foreach ($Obj in $Object){ 41 | $NewObject = New-Object -TypeName PSObject -Property $Obj 42 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 43 | $FinalObject +=$NewObject 44 | } 45 | return $FinalObject 46 | } 47 | #---------------------- 48 | #Start computers plugin 49 | $AllGPO = @() 50 | $DomainName = $ADObject.Domain.Name 51 | $PluginName = $ADObject.PluginName 52 | Write-Host ("{0}: {1}" -f "Group Policy task ID $bgRunspaceID", "Retrieve GPO from $DomainName")` 53 | -ForegroundColor Magenta 54 | } 55 | Process{ 56 | #Extract groups from domain 57 | $Domain = $ADObject.Domain 58 | $Connection = $ADObject.Domain.ADConnection 59 | $UseCredentials = $ADObject.UseCredentials 60 | if($Connection){ 61 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 62 | $Searcher.SearchScope = "subtree" 63 | $Searcher.SearchRoot = $Connection 64 | } 65 | if($Searcher){ 66 | $Searcher.PageSize = 200 67 | #Retrieve all GPO which displayname like $DisplayName value 68 | #$Searcher.Filter = "(&(objectClass=groupPolicyContainer)(displayname=$DisplayName))" 69 | #Retrieve all GPO 70 | $Searcher.Filter = '(objectClass=groupPolicyContainer)' 71 | $GPos = $Searcher.FindAll() 72 | foreach ($gpo in $GPos){ 73 | #https://msdn.microsoft.com/en-us/library/ms682264(v=vs.85).aspx 74 | $TmpGPO = @{ 75 | "DisplayName" = $gpo.properties.displayname -join '' 76 | "DistinguishedName" = $gpo.properties.distinguishedname -join '' 77 | "CommonName" = $gpo.properties.cn -join '' 78 | "whenCreated" = $gpo.properties.whencreated -join '' 79 | "whenChanged" = $gpo.properties.whenchanged -join '' 80 | "FilePath" = $gpo.properties.gpcfilesyspath -join '' 81 | } 82 | $AllGPO +=$TmpGPO 83 | } 84 | } 85 | } 86 | End{ 87 | if($AllGPO){ 88 | #Set PsObject from All Groups 89 | $FormattedGPO = Set-PSObject -Object $AllGPO -type "AD.Arsenal.GroupPolicyObjects" 90 | #Work with SyncHash 91 | $SyncServer.$($PluginName)=$FormattedGPO 92 | 93 | #Create custom object for store data 94 | $GroupPolicyObjects = New-Object -TypeName PSCustomObject 95 | $GroupPolicyObjects | Add-Member -type NoteProperty -name Data -value $FormattedGPO 96 | 97 | #Formatting Excel Data 98 | $Excelformatting = New-Object -TypeName PSCustomObject 99 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $FormattedGPO 100 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Group Policy" 101 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Group Policy" 102 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 103 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 104 | 105 | #Add Excel formatting into psobject 106 | $GroupPolicyObjects | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 107 | 108 | #Add Group Policy data to object 109 | $ReturnServerObject | Add-Member -type NoteProperty -name GroupPolicy -value $GroupPolicyObjects 110 | 111 | #Add Groups data to object 112 | $ReturnServerObject | Add-Member -type NoteProperty -name GroupPolicy -value $FormattedGPO 113 | 114 | #Add Groups to report 115 | $CustomReportFields = $ReturnServerObject.Report 116 | $NewCustomReportFields = [array]$CustomReportFields+="GroupPolicy" 117 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 118 | #End 119 | } 120 | } -------------------------------------------------------------------------------- /Plugins/Groups.ps1: -------------------------------------------------------------------------------- 1 | #Plugin extract computers from AD 2 | [cmdletbinding()] 3 | Param ( 4 | [Parameter(HelpMessage="Background Runspace ID")] 5 | [int] 6 | $bgRunspaceID, 7 | 8 | [Parameter(HelpMessage="Not used in this version")] 9 | [HashTable] 10 | $SyncServer, 11 | 12 | [Parameter(HelpMessage="Object with AD valuable data")] 13 | [Object] 14 | $ADObject, 15 | 16 | [Parameter(HelpMessage="Object to return data")] 17 | [Object] 18 | $ReturnServerObject, 19 | 20 | [Parameter(HelpMessage="Not used in this version")] 21 | [String] 22 | $sAMAccountName, 23 | 24 | [Parameter(HelpMessage="Organizational Unit filter")] 25 | [String] 26 | $Filter, 27 | 28 | [Parameter(HelpMessage="Attributes for user object")] 29 | [Array] 30 | $MyProperties = @("*") 31 | 32 | ) 33 | Begin{ 34 | #--------------------------------------------------- 35 | # Construct detailed PSObject 36 | #--------------------------------------------------- 37 | 38 | function Set-PSObject ([Object] $Object, [String] $type){ 39 | $FinalObject = @() 40 | foreach ($Obj in $Object){ 41 | $NewObject = New-Object -TypeName PSObject -Property $Obj 42 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 43 | $FinalObject +=$NewObject 44 | } 45 | return $FinalObject 46 | } 47 | #---------------------- 48 | #Start computers plugin 49 | #---------------------- 50 | $GroupType = @{ 51 | "-2147483646" = "Global Security Group"; 52 | "-2147483644" = "Local Security Group"; 53 | "-2147483643" = "BuiltIn Group"; 54 | "-2147483640" = "Universal Security Group"; 55 | "2" = "Global Distribution Group"; 56 | "4" = "Local Distribution Group"; 57 | "8" = "Universal Distribution Group";} 58 | #---------------------- 59 | #Start computers plugin 60 | #---------------------- 61 | $DomainName = $ADObject.Domain.Name 62 | $PluginName = $ADObject.PluginName 63 | Write-Host ("{0}: {1}" -f "Groups task ID $bgRunspaceID", "Retrieve groups from $DomainName")` 64 | -ForegroundColor Magenta 65 | } 66 | Process{ 67 | 68 | #Extract groups from domain 69 | $Domain = $ADObject.Domain 70 | $Connection = $ADObject.Domain.ADConnection 71 | $UseCredentials = $ADObject.UseCredentials 72 | $Filter = $ADObject.SearchRoot 73 | $Groups = @() 74 | if($Connection){ 75 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 76 | $Searcher.SearchScope = "subtree" 77 | $Searcher.SearchRoot = $Connection 78 | } 79 | if($Searcher){ 80 | $Searcher.PageSize = 200 81 | } 82 | # Add Attributes 83 | $GroupProperties = $ADObject.GroupFilter 84 | if($GroupProperties){ 85 | foreach($property in $GroupProperties){ 86 | [void]$Searcher.PropertiesToLoad.Add([String]::Format($property)) 87 | } 88 | } 89 | else{ 90 | $GroupProperties = "*" 91 | } 92 | if (!$sAMAccountName){ 93 | $Searcher.Filter = "(&(objectCategory=group)(objectClass=group))" 94 | $Results = $Searcher.FindAll() 95 | Write-Verbose "Group search return $($Results.Count)" -Verbose 96 | } 97 | else{ 98 | $Searcher.filter = "(&(objectClass=group)(sAMAccountName= $sAMAccountName))" 99 | $Results = $Searcher.FindAll() 100 | } 101 | if($Results){ 102 | foreach ($result in $Results){ 103 | foreach ($Property in $GroupProperties){ 104 | $record = @{} 105 | if($Property -eq "groupType"){ 106 | [String]$tmp = $Result.Properties.Item($Property) 107 | $record.Add($Property,$GroupType[$tmp]) 108 | } 109 | else{ 110 | $record.Add($Property,[String]$Result.Properties.Item([String]::Format($Property))) 111 | } 112 | } 113 | $Groups +=$record 114 | } 115 | } 116 | } 117 | End{ 118 | #Set PsObject from All Groups 119 | $AllGroups = Set-PSObject -Object $Groups -type "AD.Arsenal.Groups" 120 | #Work with SyncHash 121 | $SyncServer.$($PluginName)=$AllGroups 122 | 123 | #Create custom object for store data 124 | $GroupData = New-Object -TypeName PSCustomObject 125 | $GroupData | Add-Member -type NoteProperty -name Data -value $AllGroups 126 | 127 | #Formatting Excel Data 128 | $Excelformatting = New-Object -TypeName PSCustomObject 129 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $AllGroups 130 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "All Groups" 131 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "All Groups" 132 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 133 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 134 | 135 | #Add Excel formatting into psobject 136 | $GroupData | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 137 | 138 | #Add Groups data to object 139 | $ReturnServerObject | Add-Member -type NoteProperty -name Groups -value $GroupData 140 | 141 | #Add Groups to report 142 | $CustomReportFields = $ReturnServerObject.Report 143 | $NewCustomReportFields = [array]$CustomReportFields+="AllGroups" 144 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 145 | #End 146 | } -------------------------------------------------------------------------------- /Plugins/PasswordPolicy.ps1: -------------------------------------------------------------------------------- 1 | #Plugin extract Domain password policy from AD 2 | [cmdletbinding()] 3 | Param ( 4 | [Parameter(HelpMessage="Background Runspace ID")] 5 | [int] 6 | $bgRunspaceID, 7 | 8 | [Parameter(HelpMessage="Not used in this version")] 9 | [HashTable] 10 | $SyncServer, 11 | 12 | [Parameter(HelpMessage="Object with AD valuable data")] 13 | [Object] 14 | $ADObject, 15 | 16 | [Parameter(HelpMessage="Object to return data")] 17 | [Object] 18 | $ReturnServerObject, 19 | 20 | [Parameter(HelpMessage="Not used in this version")] 21 | [String] 22 | $sAMAccountName, 23 | 24 | [Parameter(HelpMessage="Organizational Unit filter")] 25 | [String] 26 | $Filter, 27 | 28 | [Parameter(HelpMessage="Attributes for user object")] 29 | [Array] 30 | $MyProperties = @("*") 31 | 32 | ) 33 | Begin{ 34 | #--------------------------------------------------- 35 | # Construct detailed PSObject 36 | #--------------------------------------------------- 37 | 38 | function Set-PSObject ([Object] $Object, [String] $type){ 39 | $FinalObject = @() 40 | foreach ($Obj in $Object){ 41 | $NewObject = New-Object -TypeName PSObject -Property $Obj 42 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 43 | $FinalObject +=$NewObject 44 | } 45 | return $FinalObject 46 | } 47 | #Start computers plugin 48 | $DomainName = $ADObject.Domain.Name 49 | $PluginName = $ADObject.PluginName 50 | Write-Host ("{0}: {1}" -f "Domain Password Policy task ID $bgRunspaceID", "Retrieve info data data from $DomainName")` 51 | -ForegroundColor Magenta 52 | } 53 | Process{ 54 | #Extract Domain password policy from domain 55 | $Domain = $ADObject.Domain.DomainContext.GetDirectoryEntry() 56 | $UseCredentials = $ADObject.UseCredentials 57 | if($Domain){ 58 | $DomainDetailmaxPwdAgeValue = $Domain.maxPwdAge.Value 59 | $DomainDetailminPwdAgeValue = $Domain.minPwdAge.Value 60 | $DomainDetailmaxPwdAgeInt64 = $Domain.ConvertLargeIntegerToInt64($DomainDetailmaxPwdAgeValue) 61 | $DomainDetailminPwdAgeInt64 = $Domain.ConvertLargeIntegerToInt64($DomainDetailminPwdAgeValue) 62 | 63 | $MaxPwdAge = -$DomainDetailmaxPwdAgeInt64/(600000000 * 1440) 64 | $MinPwdAge = -$DomainDetailminPwdAgeInt64/(600000000 * 1440) 65 | 66 | $DomainDetailminPwdLength = $Domain.minPwdLength 67 | $DomainDetailpwdHistoryLength = $Domain.pwdHistoryLength 68 | #Construct object 69 | $DomainPasswordPolicy = @{ 70 | "MaxPasswordAge" = $MaxPwdAge 71 | "MinPasswordAge" = $MinPwdAge 72 | "MinPasswordLength" = $DomainDetailminPwdLength.ToString() 73 | "PasswordHistoryLength" = $DomainDetailpwdHistoryLength.ToString() 74 | } 75 | } 76 | 77 | } 78 | End{ 79 | #Set PsObject from Password Policy object 80 | $DPP = Set-PSObject -Object $DomainPasswordPolicy -type "AD.Arsenal.DomainPasswordPolicy" 81 | #Work with SyncHash 82 | $SyncServer.$($PluginName)=$DPP 83 | 84 | #Create custom object for store data 85 | $DomainPasswordPolicy = New-Object -TypeName PSCustomObject 86 | $DomainPasswordPolicy | Add-Member -type NoteProperty -name Data -value $DPP 87 | 88 | #Formatting Excel Data 89 | $Excelformatting = New-Object -TypeName PSCustomObject 90 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $DPP 91 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Domain Password Policy" 92 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Domain Password Policy" 93 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 94 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 95 | 96 | #Add Excel formatting into psobject 97 | $DomainPasswordPolicy | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 98 | 99 | #Add DPP data to object 100 | if($DPP){ 101 | $ReturnServerObject | Add-Member -type NoteProperty -name DomainPasswordPolicy -value $DomainPasswordPolicy 102 | } 103 | 104 | #Add DPP to report 105 | $CustomReportFields = $ReturnServerObject.Report 106 | $NewCustomReportFields = [array]$CustomReportFields+="DomainPasswordPolicy" 107 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 108 | #End 109 | } 110 | -------------------------------------------------------------------------------- /Plugins/Roles.ps1: -------------------------------------------------------------------------------- 1 | #Plugin extract roles from AD 2 | [cmdletbinding()] 3 | Param ( 4 | [Parameter(HelpMessage="Background Runspace ID")] 5 | [int] 6 | $bgRunspaceID, 7 | 8 | [Parameter(HelpMessage="Not used in this version")] 9 | [HashTable] 10 | $SyncServer, 11 | 12 | [Parameter(HelpMessage="Object with AD valuable data")] 13 | [Object] 14 | $ADObject, 15 | 16 | [Parameter(HelpMessage="Object to return data")] 17 | [Object] 18 | $ReturnServerObject, 19 | 20 | [Parameter(HelpMessage="Not used in this version")] 21 | [String] 22 | $sAMAccountName, 23 | 24 | [Parameter(HelpMessage="Organizational Unit filter")] 25 | [String] 26 | $Filter, 27 | 28 | [Parameter(HelpMessage="Attributes for user object")] 29 | [Array] 30 | $MyProperties = @("*") 31 | 32 | ) 33 | Begin{ 34 | 35 | #----------------------------------------------------------- 36 | # Function to count Services 37 | #----------------------------------------------------------- 38 | Function Get-ServiceStats{ 39 | Param ( 40 | [parameter(Mandatory=$true, HelpMessage="Object")] 41 | [Object]$AllServices 42 | ) 43 | Begin{ 44 | #Declare var 45 | $RolesChart = @{} 46 | } 47 | Process{ 48 | #Group for each object for count values 49 | $AllServices | Group Service | ForEach-Object {$RolesChart.Add($_.Name,@($_.Count))} 50 | } 51 | End{ 52 | if($RolesChart){ 53 | return $RolesChart 54 | } 55 | } 56 | } 57 | #Common Service Principal Names 58 | $Services = @(@{Service="SQLServer";SPN="MSSQLSvc"},@{Service="TerminalServer";SPN="TERMSRV"}, 59 | @{Service="Exchange";SPN="IMAP4"},@{Service="Exchange";SPN="IMAP"}, 60 | @{Service="Exchange";SPN="SMTPSVC"},@{Service="SCOM";SPN="AdtServer"}, 61 | @{Service="SCOM";SPN="MSOMHSvc"},@{Service="Cluster";SPN="MSServerCluster"}, 62 | @{Service="Cluster";SPN="MSServerClusterMgmtAPI"};@{Service="GlobalCatalog";SPN="GC"}, 63 | @{Service="DNS";SPN="DNS"},@{Service="Exchange";SPN="exchangeAB"}, 64 | @{Service ="WebServer";SPN="tapinego"},@{Service ="WinRemoteAdministration";SPN="WSMAN"}, 65 | @{Service ="ADAM";SPN="E3514235-4B06-11D1-AB04-00C04FC2DCD2-ADAM"}, 66 | @{Service ="Exchange";SPN="exchangeMDB"} 67 | ) 68 | #--------------------------------------------------- 69 | # Construct detailed PSObject 70 | #--------------------------------------------------- 71 | 72 | function Set-PSObject ([Object] $Object, [String] $type){ 73 | $FinalObject = @() 74 | foreach ($Obj in $Object){ 75 | $NewObject = New-Object -TypeName PSObject -Property $Obj 76 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 77 | $FinalObject +=$NewObject 78 | } 79 | return $FinalObject 80 | } 81 | #Start computers plugin 82 | $DomainName = $ADObject.Domain.Name 83 | $PluginName = $ADObject.PluginName 84 | Write-Host ("{0}: {1}" -f "Roles task ID $bgRunspaceID", "Retrieve role information from $DomainName")` 85 | -ForegroundColor Magenta 86 | } 87 | Process{ 88 | #Extract roles from domain 89 | $Domain = $ADObject.Domain 90 | $UseCredentials = $ADObject.UseCredentials 91 | $Connection = $ADObject.Domain.ADConnection 92 | $FinalObject = @() 93 | if($Connection){ 94 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 95 | $Searcher.SearchScope = "subtree" 96 | $Searcher.SearchRoot = $Connection 97 | } 98 | if($Searcher){ 99 | $Searcher.PageSize = 200 100 | } 101 | #Search roles in AD 102 | Foreach ($service in $Services){ 103 | $spn = [String]::Format($service.SPN) 104 | $Searcher.filter = "(servicePrincipalName=$spn*/*)" 105 | $Results = $Searcher.FindAll() 106 | if ($Results){ 107 | Write-Verbose "Role Service Found....." 108 | foreach ($r in $Results){ 109 | $account = $r.GetDirectoryEntry() 110 | $record = @{} 111 | $record.Add("Name", [String]$account.Name) 112 | $record.Add("AccountName", [String]$account.sAMAccountName) 113 | $record.Add("GUID", [String]$account.guid) 114 | $record.Add("DN", [String]$account.DistinguishedName) 115 | $record.Add("Service", [String]$service.Service) 116 | $FinalObject +=$record 117 | } 118 | } 119 | } 120 | } 121 | End{ 122 | if($FinalObject){ 123 | #Set PsObject from roles found 124 | $AllRoles = Set-PSObject $FinalObject "Arsenal.AD.Roles" 125 | #Work with SyncHash 126 | $SyncServer.$($PluginName)=$AllRoles 127 | 128 | #Create custom object for store data 129 | $RoleData = New-Object -TypeName PSCustomObject 130 | $RoleData | Add-Member -type NoteProperty -name Data -value $AllRoles 131 | 132 | #Formatting Excel Data 133 | $Excelformatting = New-Object -TypeName PSCustomObject 134 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $AllRoles 135 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "All roles" 136 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "All Roles" 137 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 138 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 139 | 140 | #Add Excel formatting into psobject 141 | $RoleData | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 142 | 143 | #Add roles data to object 144 | $ReturnServerObject | Add-Member -type NoteProperty -name Roles -value $RoleData 145 | 146 | #Add Chart for roles 147 | $RoleChart = Get-ServiceStats -AllServices $AllRoles 148 | if($RoleChart){ 149 | $TmpCustomObject = New-Object -TypeName PSCustomObject 150 | 151 | #Formatting Excel Data 152 | $Excelformatting = New-Object -TypeName PSCustomObject 153 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $RoleChart 154 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Service status" 155 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Role Dashboard" 156 | $Excelformatting | Add-Member -type NoteProperty -name isnewSheet -value $True 157 | $Excelformatting | Add-Member -type NoteProperty -name showTotals -value $True 158 | $Excelformatting | Add-Member -type NoteProperty -name showHeaders -value $false 159 | $Excelformatting | Add-Member -type NoteProperty -name addHeader -value @('Type of role','Count') 160 | $Excelformatting | Add-Member -type NoteProperty -name position -value @(2,1) 161 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "NewTable" 162 | 163 | #Add chart 164 | $Excelformatting | Add-Member -type NoteProperty -name addChart -value $True 165 | $Excelformatting | Add-Member -type NoteProperty -name chartType -value "xlColumnClustered" 166 | $Excelformatting | Add-Member -type NoteProperty -name ChartTitle -value "Role status" 167 | $Excelformatting | Add-Member -type NoteProperty -name style -value 34 168 | $Excelformatting | Add-Member -type NoteProperty -name hasDataTable -value $true 169 | 170 | $TmpCustomObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 171 | 172 | #Add Computers chart 173 | $ReturnServerObject | Add-Member -type NoteProperty -name GlobalServices -value $TmpCustomObject 174 | } 175 | 176 | #Add Computers to report 177 | $CustomReportFields = $ReturnServerObject.Report 178 | $NewCustomReportFields = [array]$CustomReportFields+="AllRoles" 179 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 180 | #End 181 | } 182 | } -------------------------------------------------------------------------------- /Plugins/users.ps1: -------------------------------------------------------------------------------- 1 | #Plugin extract users from AD 2 | [cmdletbinding()] 3 | Param ( 4 | [Parameter(HelpMessage="Background Runspace ID")] 5 | [int] 6 | $bgRunspaceID, 7 | 8 | [Parameter(HelpMessage="Not used in this version")] 9 | [HashTable] 10 | $SyncServer, 11 | 12 | [Parameter(HelpMessage="Object with AD valuable data")] 13 | [Object] 14 | $ADObject, 15 | 16 | [Parameter(HelpMessage="Object to return data")] 17 | [Object] 18 | $ReturnServerObject, 19 | 20 | [Parameter(HelpMessage="Not used in this version")] 21 | [String] 22 | $sAMAccountName, 23 | 24 | [Parameter(HelpMessage="Organizational Unit filter")] 25 | [String] 26 | $Filter, 27 | 28 | [Parameter(HelpMessage="Attributes for user object")] 29 | [Array] 30 | $MyProperties = @("*") 31 | 32 | ) 33 | Begin{ 34 | 35 | #----------------------------------------------------------- 36 | # Function to get some stats about users 37 | #----------------------------------------------------------- 38 | Function Get-UsersStats{ 39 | Param ( 40 | [parameter(Mandatory=$true, HelpMessage="Object users")] 41 | [Object]$Status 42 | ) 43 | 44 | # Dashboard users 45 | $UsersVolumetry = @{ 46 | Actives = @($Status['isActive']['Count']); 47 | Inactives = @($Status['isInActive']['Count']) 48 | } 49 | 50 | $UsersStatus = @{ 51 | Locked = @($Status['isActive']['isLocked'],$Status['isInActive']['isLocked']); 52 | "Password Expired" = @($Status['isActive']['isPwdExpired'],$Status['isInActive']['isPwdExpired']); 53 | Expired = @($Status['isActive']['isExpired'],$Status['isInActive']['isExpired']); 54 | Disabled = @($Status['isActive']['isDisabled'],$Status['isInActive']['isDisabled']); 55 | } 56 | 57 | $UsersConfig = @{ 58 | "No delegated privilege" = @($Status['isActive']['isPTH'],$Status['isInActive']['isPTH']); 59 | "Password older than $($ADObject.InactiveDays) days" = @($Status['isActive']['isPwdOld'],$Status['isInActive']['isPwdOld']); 60 | "Password not required" = @($Status['isActive']['isPwdNotRequired'],$Status['isInActive']['isPwdNotRequired']); 61 | "Password never expires" = @($Status['isActive']['isPwdNeverExpires'],$Status['isInActive']['isPwdNeverExpires']); 62 | } 63 | #Return data 64 | return $UsersVolumetry, $UsersStatus, $UsersConfig 65 | 66 | } 67 | 68 | #----------------------------------------------------------- 69 | # Function to count High privileged users 70 | #----------------------------------------------------------- 71 | Function Get-PrivilegedStats{ 72 | Param ( 73 | [parameter(Mandatory=$true, HelpMessage="Object users")] 74 | [Object]$HighUsers 75 | ) 76 | Begin{ 77 | #Declare var 78 | $HighChart = @{} 79 | 80 | } 81 | Process{ 82 | #Group for each object for count values 83 | $HighUsers | Group Group | ForEach-Object {$HighChart.Add($_.Name,@($_.Count))} 84 | } 85 | End{ 86 | if($HighChart){ 87 | return $HighChart 88 | } 89 | } 90 | } 91 | 92 | #----------------------------------------------------------- 93 | # Determine High Privileges in Active Directory object users 94 | #----------------------------------------------------------- 95 | 96 | function Get-HighPrivileges{ 97 | Param ( 98 | [parameter(Mandatory=$true, HelpMessage="Object users")] 99 | [Object]$Users 100 | ) 101 | $Domain = $ADObject.Domain 102 | $Connection = $ADObject.Domain.ADConnection 103 | Write-Host "Trying to enumerate high privileges in $($Domain.Name)..." -ForegroundColor Magenta 104 | #Extract info from ADObject 105 | $DomainSID = $ADObject.DomainSID 106 | #End extraction data 107 | #http://support.microsoft.com/kb/243330 108 | $PrivGroups = @("S-1-5-32-552","S-1-5-32-544";"S-1-5-32-548";"S-1-5-32-549";"S-1-5-32-551";` 109 | "$DomainSID-519";"$DomainSID-518";"$DomainSID-512";"$DomainSID-521") 110 | $HighPrivileges = @() 111 | #Create connection for search in AD 112 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 113 | $Searcher.SearchScope = "subtree" 114 | $Searcher.SearchRoot = $Connection 115 | $Searcher.PageSize = 200 116 | try{ 117 | $ListPrivGroups = @{} 118 | foreach ($group in $PrivGroups){ 119 | $Searcher.filter = "(objectSID=$group)" 120 | $Results = $Searcher.FindOne() 121 | ForEach ($result in $Results){ 122 | $dn = $result.properties.distinguishedname 123 | $cn = $result.properties.cn 124 | $ListPrivGroups.Add($cn,$dn) 125 | } 126 | } 127 | $ListPrivGroups.GetEnumerator() | ForEach-Object ` 128 | { 129 | try{ 130 | $ErrorActionPreference = "Continue" 131 | $Searcher.PageSize = 200 132 | $Searcher.Filter = "(&(objectCategory=group)(name=$($_.Key)))" 133 | $Items = $Searcher.FindAll() 134 | } 135 | catch{ 136 | $ErrorActionPreference = "Continue" 137 | Write-Host "Function Get-HighPrivileges. Error in group $($_.Key)" -ForegroundColor Red 138 | } 139 | foreach ($Item in $Items){ 140 | $Group = $Item.GetDirectoryEntry() 141 | $Members = $Group.member 142 | } 143 | try{ 144 | if (($Group.Member).Count -gt 0){ 145 | foreach ($Member in $Members){ 146 | $Properties = @("pwdLastSet","whenCreated") 147 | $sAMAccountName = ($Member.replace('\','') -split ",*..=")[1] 148 | $Searcher.Filter = "(&(objectCategory=group)(name=$($_.Key)))" 149 | $UserFound = $Searcher.FindAll() 150 | $Match = $Users | Where-Object {$_.distinguishedName -eq $Member} 151 | if($Match.sAMAccountName){ 152 | $HighPrivilege = @{ 153 | "AccountName" = [String]::Format($sAMAccountName) 154 | "DN" = [String]::Format($Member) 155 | "MemberOf" = [String]::Format($_.Value) 156 | "PasswordLastSet" = $Match.pwdLastSet 157 | "whenCreated" = $Match.whenCreated 158 | "Group" = [String]::Format($_.Key) 159 | } 160 | $obj = New-Object -TypeName PSObject -Property $HighPrivilege 161 | $obj.PSObject.typenames.insert(0,'Arsenal.AD.HighPrivileges') 162 | $HighPrivileges += $obj 163 | } 164 | 165 | } 166 | } 167 | } 168 | catch{ 169 | Write-Warning ("{0}: {1}" -f "Error in function Get-HighPrivileges",$_.Exception.Message) 170 | } 171 | } 172 | return $HighPrivileges 173 | } 174 | catch{ 175 | Write-Warning ("{0}: {1}" -f "Error in function Get-HighPrivileges",$_.Exception.Message) 176 | } 177 | } 178 | 179 | #--------------------------------------------------- 180 | # General Status of Active Directory object users 181 | #--------------------------------------------------- 182 | 183 | function Get-UserStatus{ 184 | Param ( 185 | [parameter(Mandatory=$true, HelpMessage="Object users")] 186 | [Object]$Users 187 | ) 188 | # Count user info 189 | $UsersCount = @{ 190 | isActive = @{ 191 | Count = 0; 192 | isExpired = 0; 193 | isDisabled = 0; 194 | isPwdOld = 0; 195 | isLocked = 0; 196 | isPwdNeverExpires = 0; 197 | isPwdNotRequired = 0; 198 | isPwdExpired = 0 199 | isPTH = 0; 200 | } 201 | isInactive = @{ 202 | Count = 0; 203 | isExpired = 0; 204 | isDisabled = 0; 205 | isPwdOld = 0; 206 | isLocked = 0; 207 | isPwdNeverExpires = 0; 208 | isPwdNotRequired = 0; 209 | isPwdExpired = 0 210 | isPTH = 0; 211 | } 212 | } 213 | $DN = $ADObject.Domain.GetDirectoryEntry().distinguishedName 214 | for ($i=0; $i -lt $Users.Count; $i++){ 215 | Write-Progress -activity "Collecting Users metrics..." -status "$($DN): processing of $i/$($Users.Count) Users" -PercentComplete (($i / $Users.Count) * 100) 216 | # Active accounts 217 | if (([string]::IsNullOrEmpty($Users[$i].lastlogontimestamp)) -or (([DateTime]$Users[$i].lastlogontimestamp).ticks -lt $InactiveDate)){ 218 | $Users[$i].isActive = $false 219 | $UsersCount['isInactive']['Count']++ 220 | } 221 | else{ 222 | $Users[$i].isActive = $true 223 | $UsersCount['isActive']['Count']++ 224 | } 225 | #Password Expires 226 | if ($Users[$i].ADS_UF_PASSWORD_EXPIRED -eq $true){ 227 | if ($Users[$i].isActive){ 228 | $UsersCount['isActive']['isPwdExpired']++ 229 | } 230 | else{ 231 | $UsersCount['isInactive']['isPwdExpired']++ 232 | } 233 | } 234 | if ($Users[$i].accountExpires -lt $CurrentDate){ 235 | $Users[$i].isExpired = $true 236 | if ($Users[$i].isActive){ 237 | $UsersCount['isActive']['isExpired']++ 238 | } 239 | else{ 240 | $UsersCount['isInactive']['isExpired']++ 241 | } 242 | } 243 | else{ 244 | $Users[$i].isExpired = $false 245 | } 246 | if ($Users[$i].isDisabled){ 247 | if ($Users[$i].isActive){ 248 | $UsersCount['isActive']['isDisabled']++ 249 | } 250 | else{ 251 | $UsersCount['isInactive']['isDisabled']++ 252 | } 253 | } 254 | if ($Users[$i].isPwdNoRequired){ 255 | if ($Users[$i].isActive){ 256 | $UsersCount['isActive']['isPwdNotRequired']++ 257 | } 258 | else{ 259 | $UsersCount['isInactive']['isPwdNotRequired']++ 260 | } 261 | } 262 | if ($Users[$i].isNoDelegation -eq $false){ 263 | if ($Users[$i].isActive){ 264 | $UsersCount['isActive']['isPTH']++ 265 | } 266 | else{ 267 | $UsersCount['isInactive']['isPTH']++ 268 | } 269 | } 270 | # Identify pwdLastSet older than $PasswordAge days 271 | if ( $Users[$i].pwdLastSet -lt $PasswordLastSet ){ 272 | $Users[$i].isPwdOld = $true 273 | if ( $UserObj.isActive ){ 274 | $UsersCount['isActive']['isPwdOld']++ 275 | } 276 | else{ 277 | $UsersCount['isInactive']['isPwdOld']++ 278 | } 279 | } 280 | # Identify password never expires 281 | if ( $Users[$i].isPwdNeverExpires){ 282 | if ( $Users[$i].isActive ){ 283 | $UsersCount['isActive']['isPwdNeverExpires']++ 284 | } 285 | else{ 286 | $UsersCount['isInactive']['isPwdNeverExpires']++ 287 | } 288 | } 289 | } 290 | return $UsersCount 291 | } 292 | #--------------------------------------------------- 293 | # Construct detailed PSObject 294 | #--------------------------------------------------- 295 | 296 | function Set-PSObject ([Object] $Object, [String] $type) 297 | { 298 | $FinalObject = @() 299 | foreach ($Obj in $Object) 300 | { 301 | $NewObject = New-Object -TypeName PSObject -Property $Obj 302 | $NewObject.PSObject.typenames.insert(0,[String]::Format($type)) 303 | $FinalObject +=$NewObject 304 | } 305 | return $FinalObject 306 | } 307 | 308 | #--------------------------------------------------- 309 | # Resolv Bits of UserAccountControl 310 | #--------------------------------------------------- 311 | 312 | function UACBitResolv{ 313 | Param ( 314 | [parameter(Mandatory=$true, HelpMessage="Users Object")] 315 | [Object]$Users 316 | ) 317 | #Bits of UserAccountControl 318 | $UAC_USER_FLAG_ENUM = @{ 319 | isLocked = 16; 320 | isDisabled = 2; 321 | isPwdNeverExpires = 65536; 322 | isNoDelegation = 1048576; 323 | isPwdNoRequired = 32; 324 | isPwdExpired = 8388608; 325 | } 326 | Write-Host "Resolving UAC bits..." -ForegroundColor Green 327 | $FinalUsers = @() 328 | foreach ($User in $Users){ 329 | foreach ($key in $UAC_USER_FLAG_ENUM.Keys){ 330 | if ([int][string]$User.userAccountControl -band $UAC_USER_FLAG_ENUM[$key]){ 331 | $User.$key = $true 332 | } 333 | else{ 334 | $User.$key = $false} 335 | } 336 | $FinalUsers += $User 337 | } 338 | return $FinalUsers 339 | } 340 | #--------------------------------------------------- 341 | # Resolv Bits of msds-user-account-control-computed 342 | #--------------------------------------------------- 343 | 344 | function Get-MsDSUACResolv{ 345 | Param ( 346 | [parameter(Mandatory=$true, HelpMessage="Users object")] 347 | [Object]$Users 348 | ) 349 | $ADS_USER_FLAG_ENUM = @{ 350 | ADS_UF_SCRIPT = 1; #// 0x1 351 | ADS_UF_ACCOUNTDISABLE = 2; #// 0x2 352 | ADS_UF_HOMEDIR_REQUIRED = 8; #// 0x8 353 | ADS_UF_LOCKOUT = 16; #// 0x10 354 | ADS_UF_PASSWD_NOTREQD = 32; #// 0x20 355 | ADS_UF_PASSWD_CANT_CHANGE = 64; #// 0x40 356 | ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128; #// 0x80 357 | ADS_UF_TEMP_DUPLICATE_ACCOUNT = 256; #// 0x100 358 | ADS_UF_NORMAL_ACCOUNT = 512; #// 0x200 359 | ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 2048; #// 0x800 360 | ADS_UF_WORKSTATION_TRUST_ACCOUNT = 4096; #// 0x1000 361 | ADS_UF_SERVER_TRUST_ACCOUNT = 8192; #// 0x2000 362 | ADS_UF_DONT_EXPIRE_PASSWD = 65536; #// 0x10000 363 | ADS_UF_MNS_LOGON_ACCOUNT = 131072; #// 0x20000 364 | ADS_UF_SMARTCARD_REQUIRED = 262144; #// 0x40000 365 | ADS_UF_TRUSTED_FOR_DELEGATION = 524288; #// 0x80000 366 | ADS_UF_NOT_DELEGATED = 1048576; #// 0x100000 367 | ADS_UF_USE_DES_KEY_ONLY = 2097152; #// 0x200000 368 | ADS_UF_DONT_REQUIRE_PREAUTH = 4194304; #// 0x400000 369 | ADS_UF_PASSWORD_EXPIRED = 8388608; #// 0x800000 370 | ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216; #// 0x1000000 371 | } 372 | Write-Host "Resolving mds-UAC bits..." -ForegroundColor Green 373 | $FinalUsers = @() 374 | try{ 375 | foreach ($User in $Users){ 376 | $Attribute = "msds-user-account-control-computed" 377 | $CheckUser = $Result.GetDirectoryEntry() 378 | $CheckUser.RefreshCache($Attribute) 379 | $UserAccountFlag = $CheckUser.Properties[$Attribute].Value 380 | foreach ($key in $ADS_USER_FLAG_ENUM.Keys){ 381 | if ($UserAccountFlag -band $ADS_USER_FLAG_ENUM[$key]){ 382 | $User.$key = $true 383 | } 384 | else{ 385 | $User.$key = $false 386 | } 387 | } 388 | $FinalUsers +=$User 389 | } 390 | return $FinalUsers 391 | } 392 | catch{ 393 | Write-Warning ("{0}: {1}" -f "Error in function Get-MsDSUACResolv",$_.Exception.Message) 394 | } 395 | } 396 | 397 | #--------------------------------------------------- 398 | # Get user information from AD 399 | #--------------------------------------------------- 400 | 401 | function Get-UsersInfo{ 402 | Param ( 403 | [parameter(Mandatory=$false, HelpMessage="DN or sAMAccountName to search")] 404 | [String]$AccountName, 405 | 406 | [parameter(Mandatory=$false, HelpMessage="Psobject with AD data")] 407 | [Object]$MyADObject, 408 | 409 | [parameter(Mandatory=$false, HelpMessage="Extract All properties")] 410 | [Switch]$ExtractAll 411 | ) 412 | 413 | Begin{ 414 | #Extract data from ADObject 415 | $Connection = $MyADObject.Domain.ADConnection 416 | $Filter = $MyADObject.SearchRoot 417 | $UsersProperties = $MyADObject.UsersFilter 418 | 419 | #Create Connection 420 | if($Connection){ 421 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 422 | $Searcher.SearchScope = "subtree" 423 | $Searcher.SearchRoot = $Connection 424 | } 425 | #Add Pagesize property 426 | if($Searcher){ 427 | $Searcher.PageSize = 200 428 | } 429 | #Add properties to load for each user 430 | if(!$ExtractAll){ 431 | foreach($property in $UsersProperties){ 432 | $Searcher.PropertiesToLoad.Add([String]::Format($property)) > $Null 433 | } 434 | } 435 | else{ 436 | $UsersProperties = "*" 437 | } 438 | if (!$sAMAccountName){ 439 | $Searcher.Filter = "(&(objectCategory=person)(objectClass=user))" 440 | $Results = $Searcher.FindAll() 441 | } 442 | else{ 443 | $Searcher.filter = "(&(objectClass=user)(sAMAccountName= $($sAMAccountName)))" 444 | $Results = $Searcher.FindAll() 445 | } 446 | 447 | } 448 | Process{ 449 | if($Results){ 450 | Write-Verbose "The users search return $($Results.Count)" -Verbose 451 | return $Results 452 | } 453 | 454 | } 455 | End{ 456 | #Nothing to do here... 457 | } 458 | 459 | } 460 | 461 | #Start users plugin 462 | $DomainName = $ADObject.Domain.Name 463 | $PluginName = $ADObject.PluginName 464 | Write-Host ("{0}: {1}" -f "Users task ID $bgRunspaceID", "Retrieve users data from $DomainName")` 465 | -ForegroundColor Magenta 466 | } 467 | Process{ 468 | #Extract all users from domain 469 | <# 470 | $Connection = $ADObject.Domain.ADConnection 471 | $Filter = $ADObject.SearchRoot 472 | $UseCredentials = $ADObject.UseCredentials 473 | $Users = @() 474 | if($Connection){ 475 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $Connection 476 | $Searcher.SearchScope = "subtree" 477 | $Searcher.SearchRoot = $Connection 478 | } 479 | if($Searcher){ 480 | $Searcher.PageSize = 200 481 | } 482 | # Add Attributes 483 | $UsersProperties = $ADObject.UsersFilter 484 | if($UsersProperties){ 485 | foreach($property in $UsersProperties){ 486 | $Searcher.PropertiesToLoad.Add([String]::Format($property)) > $Null 487 | } 488 | } 489 | else{ 490 | $UsersProperties = "*" 491 | } 492 | if (!$sAMAccountName){ 493 | $Searcher.Filter = "(&(objectCategory=person)(objectClass=user))" 494 | $Results = $Searcher.FindAll() 495 | Write-Verbose "The users search return $($Results.Count)" -Verbose 496 | } 497 | else{ 498 | $Searcher.filter = "(&(objectClass=user)(sAMAccountName= $($sAMAccountName)))" 499 | $Results = $Searcher.FindAll() 500 | #Write-Verbose "The user search return $($Results.Count)" -Verbose 501 | } 502 | #> 503 | $RawUsers = @() 504 | $Properties = $ADObject.UsersFilter 505 | $RawResults = Get-UsersInfo -MyADObject $ADObject 506 | if($RawResults){ 507 | ForEach ($Result In $RawResults){ 508 | $record = @{} 509 | ForEach ($Property in $Properties){ 510 | if ($property -eq "pwdLastSet"){ 511 | if([string]::IsNullOrEmpty($Result.Properties.Item($property))){ 512 | $record.Add($Property,"never") 513 | } 514 | else{ 515 | $pwlastSet = [datetime]::FromFileTime([Int64]::Parse($Result.Properties.Item($Property))) 516 | $record.Add($Property,$pwlastSet) 517 | } 518 | } 519 | elseif ($Property -eq "accountExpires"){ 520 | if([string]::IsNullOrEmpty($Result.Properties.Item($property))){ 521 | $record.Add($Property,"never") 522 | } 523 | elseif ($Result.Properties.Item($Property) -gt [datetime]::MaxValue.Ticks -or [Int64]::Parse($Result.Properties.Item($Property)) -eq 0){ 524 | $record.Add($Property,"Never") 525 | } 526 | else{ 527 | $Date = [Datetime]([Int64]::Parse($Result.Properties.Item($Property))) 528 | $accountExpires = $Date.AddYears(1600).ToLocalTime() 529 | $record.Add($Property,$accountExpires) 530 | } 531 | } 532 | elseif ($Property -eq "lastLogonTimestamp"){ 533 | $value = ($Result.Properties.Item($Property)).Count 534 | switch($value){ 535 | 0 536 | { 537 | $date = $Result.Properties.Item("WhenCreated") 538 | $record.Add($Property,$null) 539 | } 540 | default 541 | { 542 | $date = [datetime]::FromFileTime([Int64]::Parse($Result.Properties.Item($Property))) 543 | $record.Add($Property,[String]::Format($date)) 544 | } 545 | } 546 | } 547 | else{ 548 | $record.Add($Property,[String]$Result.Properties.Item([String]::Format($Property))) 549 | 550 | } 551 | 552 | } 553 | $RawUsers +=$record 554 | } 555 | } 556 | 557 | } 558 | End{ 559 | #Resolv UAC bits for each user 560 | $UsersWithUACResolv = UACBitResolv -Users $RawUsers 561 | 562 | #Resolv mds-UAC bits for each user 563 | $UsersWithmdsUACResolv = Get-MsDSUACResolv -Users $UsersWithUACResolv 564 | 565 | #Resolv High privileges in Domain 566 | $HighPrivileges = Get-HighPrivileges -Users $UsersWithmdsUACResolv 567 | 568 | #Resolv Status for each user 569 | $UsersStatus = Get-UserStatus -Users $UsersWithmdsUACResolv 570 | 571 | #Set PsObject from users object 572 | $FinalUsers = Set-PSObject -Object $UsersWithmdsUACResolv -type "AD.Arsenal.Users" 573 | 574 | #Work with SyncHash 575 | $SyncServer.$($PluginName)=$FinalUsers 576 | $SyncServer.UsersStatus=$UsersStatus 577 | 578 | ###########################################Reporting Options################################################### 579 | 580 | #Create custom object for store data 581 | $AllDomainUsers = New-Object -TypeName PSCustomObject 582 | $AllDomainUsers | Add-Member -type NoteProperty -name Data -value $FinalUsers 583 | 584 | #Formatting Excel Data 585 | $Excelformatting = New-Object -TypeName PSCustomObject 586 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $FinalUsers 587 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Domain Users" 588 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Domain Users" 589 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 590 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 591 | 592 | #Add Excel formatting into psobject 593 | $AllDomainUsers | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 594 | 595 | #Add Users data to object 596 | if($FinalUsers){ 597 | $ReturnServerObject | Add-Member -type NoteProperty -name DomainUsers -value $AllDomainUsers 598 | } 599 | 600 | #Add High privileges account to report 601 | $HighPrivilegedUsers = New-Object -TypeName PSCustomObject 602 | $HighPrivilegedUsers | Add-Member -type NoteProperty -name Data -value $HighPrivileges 603 | 604 | #Formatting Excel Data 605 | $Excelformatting = New-Object -TypeName PSCustomObject 606 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $HighPrivileges 607 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "High Privileged Users" 608 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "High Privileged Users" 609 | $Excelformatting | Add-Member -type NoteProperty -name isFreeze -value $True 610 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "CSVTable" 611 | 612 | #Add Excel formatting into psobject 613 | $HighPrivilegedUsers | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 614 | 615 | #Add Users data to object 616 | if($HighPrivileges){ 617 | $ReturnServerObject | Add-Member -type NoteProperty -name HighPrivileged -value $HighPrivilegedUsers 618 | } 619 | 620 | 621 | #######################################Add Table options Users Status########################################## 622 | #Add new table for High privileges account to report 623 | $PrivilegedStats = Get-PrivilegedStats -HighUsers $HighPrivileges 624 | 625 | if($PrivilegedStats){ 626 | $HighPrivilegedUsers = New-Object -TypeName PSCustomObject 627 | $HighPrivilegedUsers | Add-Member -type NoteProperty -name Data -value $PrivilegedStats 628 | 629 | #Formatting Excel Data 630 | $Excelformatting = New-Object -TypeName PSCustomObject 631 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $PrivilegedStats 632 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Privileged Users" 633 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Chart Privileged Users" 634 | $Excelformatting | Add-Member -type NoteProperty -name isnewSheet -value $True 635 | $Excelformatting | Add-Member -type NoteProperty -name showTotals -value $True 636 | $Excelformatting | Add-Member -type NoteProperty -name showHeaders -value $True 637 | $Excelformatting | Add-Member -type NoteProperty -name addHeader -value @('Type of group','Count') 638 | $Excelformatting | Add-Member -type NoteProperty -name position -value @(2,1) 639 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "NewTable" 640 | 641 | #Add chart 642 | $Excelformatting | Add-Member -type NoteProperty -name addChart -value $True 643 | $Excelformatting | Add-Member -type NoteProperty -name chartType -value "xlColumnClustered" 644 | $Excelformatting | Add-Member -type NoteProperty -name ChartTitle -value "High Privileges Accounts in Groups" 645 | $Excelformatting | Add-Member -type NoteProperty -name style -value 34 646 | $Excelformatting | Add-Member -type NoteProperty -name hasDataTable -value $true 647 | 648 | $HighPrivilegedUsers | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 649 | 650 | #Add Users status account 651 | $ReturnServerObject | Add-Member -type NoteProperty -name HighPrivilegedChart -value $HighPrivilegedUsers 652 | } 653 | 654 | 655 | #Add new table for Users account to report 656 | ($GlobalUserStats, $GlobalStatus, $GlobalUsersConfig) = Get-UsersStats -Status $UsersStatus 657 | if($GlobalUserStats){ 658 | $TmpCustomObject = New-Object -TypeName PSCustomObject 659 | #$GlobalStats | Add-Member -type NoteProperty -name Data -value $GlobalUserStats 660 | 661 | #Formatting Excel Data 662 | $Excelformatting = New-Object -TypeName PSCustomObject 663 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $GlobalUserStats 664 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Status of user Accounts" 665 | $Excelformatting | Add-Member -type NoteProperty -name SheetName -value "Users volumetry" 666 | $Excelformatting | Add-Member -type NoteProperty -name isnewSheet -value $true 667 | $Excelformatting | Add-Member -type NoteProperty -name showTotals -value $True 668 | $Excelformatting | Add-Member -type NoteProperty -name showHeaders -value $True 669 | $Excelformatting | Add-Member -type NoteProperty -name addHeader -value @('Type of account','Count') 670 | $Excelformatting | Add-Member -type NoteProperty -name position -value @(3,1) 671 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "NewTable" 672 | 673 | #Add chart 674 | $Excelformatting | Add-Member -type NoteProperty -name addChart -value $True 675 | $Excelformatting | Add-Member -type NoteProperty -name chartType -value "xlPie" 676 | $Excelformatting | Add-Member -type NoteProperty -name ChartTitle -value "Volumetry of user accounts" 677 | $Excelformatting | Add-Member -type NoteProperty -name style -value 34 678 | $Excelformatting | Add-Member -type NoteProperty -name hasDataTable -value $true 679 | 680 | $TmpCustomObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 681 | 682 | #Add Users status account 683 | $ReturnServerObject | Add-Member -type NoteProperty -name UsersVolumetry -value $TmpCustomObject 684 | } 685 | #Add status in the same sheet 686 | if($GlobalStatus){ 687 | $TmpCustomObject = New-Object -TypeName PSCustomObject 688 | #$GlobalStats | Add-Member -type NoteProperty -name Data -value $GlobalUserStats 689 | 690 | #Formatting Excel Data 691 | $Excelformatting = New-Object -TypeName PSCustomObject 692 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $GlobalStatus 693 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "User Account Status" 694 | $Excelformatting | Add-Member -type NoteProperty -name showTotals -value $True 695 | $Excelformatting | Add-Member -type NoteProperty -name showHeaders -value $True 696 | $Excelformatting | Add-Member -type NoteProperty -name addHeader -value @('Status','Active Accounts','Inactive Accounts') 697 | $Excelformatting | Add-Member -type NoteProperty -name isnewSheet -value $False 698 | $Excelformatting | Add-Member -type NoteProperty -name position -value @(3,6) 699 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "NewTable" 700 | 701 | #Add chart 702 | $Excelformatting | Add-Member -type NoteProperty -name addChart -value $True 703 | $Excelformatting | Add-Member -type NoteProperty -name chartType -value "xlBarClustered" 704 | $Excelformatting | Add-Member -type NoteProperty -name ChartTitle -value "Status of active user accounts" 705 | $Excelformatting | Add-Member -type NoteProperty -name style -value 34 706 | $Excelformatting | Add-Member -type NoteProperty -name hasDataTable -value $true 707 | 708 | $TmpCustomObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 709 | #Add Users status account 710 | $ReturnServerObject | Add-Member -type NoteProperty -name GlobalUsersStatus -value $TmpCustomObject 711 | } 712 | 713 | if($GlobalUsersConfig){ 714 | $TmpCustomObject = New-Object -TypeName PSCustomObject 715 | #$GlobalStats | Add-Member -type NoteProperty -name Data -value $GlobalUserStats 716 | 717 | #Formatting Excel Data 718 | $Excelformatting = New-Object -TypeName PSCustomObject 719 | $Excelformatting | Add-Member -type NoteProperty -name Data -value $GlobalUsersConfig 720 | $Excelformatting | Add-Member -type NoteProperty -name TableName -value "Configuration of user accounts" 721 | $Excelformatting | Add-Member -type NoteProperty -name isnewSheet -value $false 722 | $Excelformatting | Add-Member -type NoteProperty -name showTotals -value $True 723 | $Excelformatting | Add-Member -type NoteProperty -name showHeaders -value $True 724 | $Excelformatting | Add-Member -type NoteProperty -name addHeader -value @('Options','Active Accounts','Inactive Accounts') 725 | $Excelformatting | Add-Member -type NoteProperty -name position -value @(3,12) 726 | $Excelformatting | Add-Member -type NoteProperty -name Type -value "NewTable" 727 | 728 | #Add chart 729 | $Excelformatting | Add-Member -type NoteProperty -name addChart -value $True 730 | $Excelformatting | Add-Member -type NoteProperty -name chartType -value "xlColumnClustered" 731 | $Excelformatting | Add-Member -type NoteProperty -name ChartTitle -value "Configuration of active user accounts" 732 | $Excelformatting | Add-Member -type NoteProperty -name style -value 34 733 | $Excelformatting | Add-Member -type NoteProperty -name hasDataTable -value $true 734 | 735 | $TmpCustomObject | Add-Member -type NoteProperty -name Excelformat -Value $Excelformatting 736 | 737 | #Add Users status account 738 | $ReturnServerObject | Add-Member -type NoteProperty -name GlobalUsersConfig -value $TmpCustomObject 739 | } 740 | #Add users to report 741 | $CustomReportFields = $ReturnServerObject.Report 742 | $NewCustomReportFields = [array]$CustomReportFields+="Users, HighPrivileges, UserStatus" 743 | $ReturnServerObject | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 744 | #End 745 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

Introduction

3 | 4 | VOYEUR's main purpose is to automate several tasks of an Active Directory build review or security assessment. Also, the tool is able to create a fast (and pretty) Active Directory report. The tool is developed entirely in PowerShell (a powerful scripting language) without dependencies like Microsoft Remote Administration tools. (Just .Net Framework 2.0 and Office Excel if you want a useful and pretty report). The generated report is a perfect starting point for well-established forensic, incident response team, security consultants or security researchers who want to quickly analyze threats in Active Directory Services. 5 | 6 |

Features

7 | 8 | * Return a huge number of attributes on computers, users, containers/OUs, groups, ACL, etc... 9 | * Search for locked accounts, expired password, no password policy, etc... 10 | * Return a list of all privileged account in domain. (The script search in SID value instead in a group name) 11 | * Return a list of group’s modification (Users added/deleted for a specific group, etc...) 12 | * Multi-Threading support 13 | * Plugin Support 14 | 15 |

Usage scenarios

16 | 17 | VOYEUR can be used in two ways scenarios: 18 | 19 | * Able for using on local or remote computer 20 | * Able for using on joined machine or workgroup machine 21 | 22 |

Screenshots

23 | 24 | ![voyeur](https://cloud.githubusercontent.com/assets/5271640/17643817/19b20958-6143-11e6-8e3a-41a4bec132df.png) 25 | 26 |

Reporting

27 | 28 | Support for exporting data driven to several formats like CSV, XML or JSON (Experimental). 29 | 30 | The following screenshot shows an example report in JSON format 31 | 32 | ![voyeur_jsonreport](https://cloud.githubusercontent.com/assets/5271640/17643845/d5a545da-6143-11e6-8769-32008d19f638.PNG) 33 | 34 |

Office Support

35 | 36 | Support for exporting data driven to EXCEL format. The tool also support table style modification, chart creation, company logo or independent language support. At the moment only Office Excel 2010 and Office Excel 2013 are supported by the tool. 37 | 38 | ![voyeurexcelreport](https://cloud.githubusercontent.com/assets/5271640/17643864/55bfa3dc-6144-11e6-8266-29487b4f167f.PNG) 39 | 40 |

Sample reports

41 | 42 | An example of report generated by Voyeur can be downloaded from [Voyeur_Report_20160609.xlsx](https://github.com/silverhack/voyeur/files/416616/Voyeur_Report_20160609.xlsx) 43 | 44 |

Prerequisites

45 | 46 | Voyeur works out of the box with PowerShell version 2.x. You can check your Windows PowerShell version executing the command $PsVersionTable: 47 | 48 | ```powershell 49 | PS C:\Users\silverhack> $psversiontable 50 | 51 | Name Value 52 | ---- ----- 53 | CLRVersion 2.0.50727.5420 54 | BuildVersion 6.1.7601.17514 55 | PSVersion 2.0 56 | WSManStackVersion 2.0 57 | PSCompatibleVersions {1.0, 2.0} 58 | SerializationVersion 1.1.0.1 59 | PSRemotingProtocolVersion 2.1 60 | ``` 61 | 62 |

Installation

63 | 64 | You can download the latest zip by clicking [https://github.com/silverhack/voyeur/archive/master.zip here]. 65 | 66 | Preferably, you can download voyeur by cloning the [https://github.com/silverhack/voyeur] repository: 67 | 68 |
 69 | git clone https://github.com/silverhack/voyeur
 70 | 
71 | 72 |

Usage

73 | 74 | To get a list of basic options and switches use: 75 | 76 | ```powershell 77 | get-help .\voyeur.ps1 78 | ``` 79 | 80 | To get a list of examples use: 81 | 82 | ```powershell 83 | get-help .\voyeur.ps1 -Examples 84 | ``` 85 | 86 | To get a list of all options and examples with detailed info use: 87 | 88 | ```powershell 89 | get-help .\voyeur.ps1 -Detailed 90 | ``` 91 | 92 |

Examples

93 | 94 | This example retrieve information of an Active Directory for a specific domain. Also, retrieve Organizational Unit 95 | information and ACL values. Next, export all data driven to JSON format. 96 | 97 | ```powershell 98 | .\voyeur.ps1 -Domain "test.example.local" -ExportACL -ExportOU -ExportTo JSON 99 | ``` 100 | 101 | This example retrieve information of an Active Directory for a specific domain with explicit credentials and 102 | export results to CSV format. 103 | 104 | ```powershell 105 | .\voyeur.ps1 -Domain "test.example.local" -AlternateCredential -ExportTo CSV 106 | ``` 107 | 108 | This example retrieve information of an Active Directory for a specific domain and for specific Organizational 109 | Unit (OU). Also, retrieve Organizational Unit information and ACL values. Next, export all data driven to all supported formats 110 | ```powershell 111 | .\voyeur.ps1 -Domain "test.example.local" -SearchRoot "OU=Domain,DC=test,DC=example,DC=local" -ExportACL -ExportOU -ExportTo XML,JSON,CSV,EXCEL 112 | ``` 113 | -------------------------------------------------------------------------------- /Utils/CsvReport.ps1: -------------------------------------------------------------------------------- 1 | Function Generate-CSV{ 2 | [cmdletbinding()] 3 | Param ( 4 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 5 | [Object]$ServerObject, 6 | 7 | [parameter()] 8 | [string]$RootPath 9 | 10 | ) 11 | 12 | Begin{ 13 | Function Create-CSVFolderReport{ 14 | [cmdletbinding()] 15 | Param ( 16 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 17 | [Alias('IPAddress','Server','IPAddresses','Servers')] 18 | [String]$Computername, 19 | 20 | [parameter()] 21 | [string]$RootPath 22 | 23 | ) 24 | $target = "$($RootPath)\CSVReport" 25 | if (!(Test-Path -Path $target)){ 26 | $tmpdir = New-Item -ItemType Directory -Path $target 27 | Write-Host "Folder Reports created in $target...." -ForegroundColor Yellow 28 | return $target} 29 | else{ 30 | Write-Host "Directory already exists...." -ForegroundColor Red 31 | return $target 32 | } 33 | } 34 | ##End of function 35 | } 36 | Process{ 37 | if($ServerObject){ 38 | Write-Host "Create report folder...." -ForegroundColor Green 39 | $ReportPath = Create-CSVFolderReport -RootPath $RootPath -Computername $ServerObject.ComputerName 40 | Write-Host "Report folder created in $($ReportPath)..." -ForegroundColor Green 41 | } 42 | if($ServerObject -and $ReportPath){ 43 | Write-Host ("{0}: {1}" -f "CSV Task", "Generating CSV report for data retrieved from $($Domain.Name)")` 44 | -ForegroundColor Magenta 45 | $ServerObject | %{ 46 | foreach ($query in $_.psobject.Properties){ 47 | if($query.Name -and $query.Value){ 48 | Write-Host "Export $($query.Name) to CSV file" -ForegroundColor Green 49 | $CSVFile = ($ReportPath + "\" + ([System.Guid]::NewGuid()).ToString() +$query.Name+ ".csv") 50 | try{ 51 | if($query.value.Data){ 52 | $query.value.Data | Export-Csv -NoTypeInformation -Path $CSVFile 53 | } 54 | } 55 | catch{ 56 | Write-Host "Function Generate-CSV. Error in $($query.name)" -ForegroundColor Red 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | End{ 64 | #Nothing to do here 65 | } 66 | } -------------------------------------------------------------------------------- /Utils/ExcelReport.ps1: -------------------------------------------------------------------------------- 1 | Function Generate-Excel{ 2 | [cmdletbinding()] 3 | Param ( 4 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 5 | [Alias('ServerObject')] 6 | [Object]$AllData, 7 | 8 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 9 | [Alias('Formatting')] 10 | [Object]$TableFormatting, 11 | 12 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 13 | [Alias('Style')] 14 | [Object]$HeaderStyle, 15 | 16 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 17 | [Alias('Settings')] 18 | [Object]$ExcelSettings, 19 | 20 | [parameter()] 21 | [string]$RootPath 22 | 23 | ) 24 | Begin{ 25 | Function Create-ExcelFolderReport{ 26 | [cmdletbinding()] 27 | Param ( 28 | [parameter()] 29 | [string]$RootPath 30 | ) 31 | $target = "$($RootPath)\ExcelReport" 32 | if (!(Test-Path -Path $target)){ 33 | $tmpdir = New-Item -ItemType Directory -Path $target 34 | Write-Host "Folder Reports created in $target...." -ForegroundColor Yellow 35 | return $target} 36 | else{ 37 | Write-Host "Directory already exists...." -ForegroundColor Red 38 | return $target 39 | } 40 | } 41 | 42 | } 43 | Process{ 44 | if($AllData -and $ExcelSettings -and $TableFormatting){ 45 | #Add Types 46 | Add-Type -AssemblyName Microsoft.Office.Interop.Excel 47 | #Create Excel object 48 | $isDebug = [System.Convert]::ToBoolean($ExcelSettings["Debug"]) 49 | Create-Excel -ExcelDebugging $isDebug 50 | if($ExcelSettings){ 51 | # Create About Page 52 | Create-About -ExcelSettings $ExcelSettings 53 | } 54 | #Get Language Settings 55 | $Language = Get-ExcelLanguage 56 | if ($Language){ 57 | #Try to get config data 58 | if($HeaderStyle["$Language"]){ 59 | #Detected language 60 | Write-Host "Locale detected..." -ForegroundColor Magenta 61 | $FormatTable = $TableFormatting["Style"] 62 | $Header = $HeaderStyle["$Language"] 63 | } 64 | else{ 65 | $FormatTable = $false 66 | $Header = $false 67 | } 68 | } 69 | #Populate data into Excel sheets 70 | $AllData | % { 71 | Foreach ($newDataSheet in $_.psobject.Properties){ 72 | if($newDataSheet.Value.Excelformat){ 73 | Write-Host "Add $($newDataSheet.Name) data into Excel..." -ForegroundColor Magenta 74 | #Extract format of sheet (New Table, CSV2Table, Chart, etc...) 75 | $TypeOfData = $newDataSheet.Value.ExcelFormat.Type 76 | #Switch cases 77 | switch($TypeOfData){ 78 | 'CSVTable' 79 | { 80 | $Data = $newDataSheet.Value.ExcelFormat.Data 81 | $Title = $newDataSheet.Value.ExcelFormat.SheetName 82 | $TableTitle = $newDataSheet.Value.ExcelFormat.TableName 83 | $freeze = $newDataSheet.Value.ExcelFormat.isFreeze 84 | if($newDataSheet.Value.ExcelFormat.IconSet){ 85 | $columnName = $newDataSheet.Value.ExcelFormat.IconColumnName 86 | } 87 | else{$columnName=$null} 88 | Create-CSV2Table -Data $Data -Title $Title -TableTitle $TableTitle ` 89 | -TableStyle $FormatTable -isFreeze $freeze ` 90 | -iconColumnName $columnName| Out-Null 91 | } 92 | 'NewTable' 93 | { 94 | #Extract data from psobject 95 | $Data = $newDataSheet.Value.ExcelFormat.Data 96 | $ShowHeaders = $newDataSheet.Value.ExcelFormat.showHeaders 97 | $MyHeaders = $newDataSheet.Value.ExcelFormat.addHeader 98 | $ShowTotals = $newDataSheet.Value.ExcelFormat.showTotals 99 | $Position = $newDataSheet.Value.ExcelFormat.position 100 | $Title = $newDataSheet.Value.ExcelFormat.SheetName 101 | $TableName = $newDataSheet.Value.ExcelFormat.TableName 102 | $isnewSheet = [System.Convert]::ToBoolean($newDataSheet.Value.ExcelFormat.isnewSheet) 103 | $addNewChart = [System.Convert]::ToBoolean($newDataSheet.Value.ExcelFormat.addChart) 104 | $ChartType = $newDataSheet.Value.ExcelFormat.chartType 105 | $HasDatatable = [System.Convert]::ToBoolean($newDataSheet.Value.ExcelFormat.hasDataTable) 106 | $chartStyle = $newDataSheet.Value.ExcelFormat.Style 107 | $chartTitle = $newDataSheet.Value.ExcelFormat.ChartTitle 108 | 109 | #Create new table with data 110 | Create-Table -ShowTotals $ShowTotals -ShowHeaders $ShowHeaders -Data $Data ` 111 | -SheetName $Title -TableTitle $TableName -Position $Position ` 112 | -Header $MyHeaders -isNewSheet $isnewSheet -addNewChart $addNewChart ` 113 | -ChartType $ChartType -ChartTitle $chartTitle ` 114 | -ChartStyle $chartStyle -HasDataTable $HasDatatable ` 115 | -HeaderStyle $Header | Out-Null 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | #Delete Sheet1 and create index 123 | $Excel.WorkSheets.Item($Excel.WorkSheets.Count).Delete() | Out-Null 124 | Create-Index -ExcelSettings $ExcelSettings 125 | } 126 | End{ 127 | #Create Report Folder 128 | Write-Host "Creating report folder...." -ForegroundColor Green 129 | $ReportPath = Create-ExcelFolderReport -RootPath $RootPath 130 | Write-Host "Report folder created in $($ReportPath)..." -ForegroundColor Green 131 | 132 | #Save Excel 133 | Save-Excel -Path $ReportPath 134 | #Release Excel Object 135 | Release-ExcelObject 136 | } 137 | } -------------------------------------------------------------------------------- /Utils/JsonReport.ps1: -------------------------------------------------------------------------------- 1 | Function Generate-Json{ 2 | [cmdletbinding()] 3 | Param ( 4 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 5 | [Alias('IPAddress','Server','IPAddresses','Servers')] 6 | [Object]$ServerObject, 7 | 8 | [parameter()] 9 | [string]$RootPath 10 | 11 | ) 12 | 13 | Begin{ 14 | function ConvertTo-Json20([object] $AllItems){ 15 | add-type -assembly system.web.extensions 16 | $JavaScriptSerializer = new-object system.web.script.serialization.javascriptSerializer 17 | $JavaScriptSerializer.MaxJsonLength = [System.Int32]::MaxValue 18 | $AllJsonResults = @() 19 | foreach ($item in $AllItems){ 20 | $TmpDict = @{} 21 | $item.psobject.properties | Foreach { $TmpDict[$_.Name] = $_.Value } 22 | 23 | $AllJsonResults+=$JavaScriptSerializer.Serialize($TmpDict) 24 | } 25 | #Return Data 26 | return $AllJsonResults 27 | } 28 | Function Create-JsonFolderReport{ 29 | [cmdletbinding()] 30 | Param ( 31 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 32 | [Alias('IPAddress','Server','IPAddresses','Servers')] 33 | [String]$Computername, 34 | 35 | [parameter()] 36 | [string]$RootPath 37 | 38 | ) 39 | $target = "$($RootPath)\JsonReport" 40 | if (!(Test-Path -Path $target)){ 41 | $tmpdir = New-Item -ItemType Directory -Path $target 42 | Write-Host "Folder Reports created in $target...." -ForegroundColor Yellow 43 | return $target} 44 | else{ 45 | Write-Host "Directory already exists...." -ForegroundColor Red 46 | return $target 47 | } 48 | } 49 | ##End of function 50 | if($ServerObject){ 51 | Write-Host "Create report folder...." -ForegroundColor Green 52 | $ReportPath = Create-JsonFolderReport -RootPath $RootPath -Computername $ServerObject.ComputerName 53 | Write-Host "Report folder created in $($ReportPath)..." -ForegroundColor Green 54 | } 55 | } 56 | Process{ 57 | if($ServerObject -and $ReportPath){ 58 | Write-Host ("{0}: {1}" -f "JSON Task", "Generating XML report for data retrieved from $($Domain.Name)")` 59 | -ForegroundColor Magenta 60 | $ServerObject | %{ 61 | foreach ($query in $_.psobject.Properties){ 62 | if($query.Name -and $query.Value){ 63 | Write-Host "Export $($query.Name) to JSON file" -ForegroundColor Green 64 | $JSONFile = ($ReportPath + "\" + ([System.Guid]::NewGuid()).ToString() +$query.Name+ ".json") 65 | try{ 66 | if($query.value.Data){ 67 | $output = ConvertTo-Json20 $query.value.Data 68 | Set-Content $JSONFile $output 69 | #$output | Out-File -FilePath $JSONFile 70 | } 71 | } 72 | catch{ 73 | Write-Host "Function Generate-Json. Error in $($query.name)" -ForegroundColor Red 74 | Write-Host $_.Exception 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /Utils/XmlReport.ps1: -------------------------------------------------------------------------------- 1 | Function Generate-XML{ 2 | [cmdletbinding()] 3 | Param ( 4 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 5 | [Alias('IPAddress','Server','IPAddresses','Servers')] 6 | [Object]$ServerObject, 7 | 8 | [parameter()] 9 | [string]$RootPath 10 | 11 | ) 12 | 13 | Begin{ 14 | Function Create-XMLFolderReport{ 15 | [cmdletbinding()] 16 | Param ( 17 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 18 | [Alias('IPAddress','Server','IPAddresses','Servers')] 19 | [String]$Computername, 20 | 21 | [parameter()] 22 | [string]$RootPath 23 | 24 | ) 25 | $target = "$($RootPath)\XMLReport" 26 | if (!(Test-Path -Path $target)){ 27 | $tmpdir = New-Item -ItemType Directory -Path $target 28 | Write-Host "Folder Reports created in $target...." -ForegroundColor Yellow 29 | return $target} 30 | else{ 31 | Write-Host "Directory already exists...." -ForegroundColor Red 32 | return $target 33 | } 34 | } 35 | ##End of function 36 | if($ServerObject){ 37 | Write-Host "Create report folder...." -ForegroundColor Green 38 | $ReportPath = Create-XMLFolderReport -RootPath $RootPath -Computername $ServerObject.ComputerName 39 | Write-Host "Report folder created in $($ReportPath)..." -ForegroundColor Green 40 | } 41 | } 42 | Process{ 43 | if($ServerObject -and $ReportPath){ 44 | Write-Host ("{0}: {1}" -f "XML Task", "Generating XML report for data retrieved from $($Domain.Name)")` 45 | -ForegroundColor Magenta 46 | $ServerObject | %{ 47 | foreach ($query in $_.psobject.Properties){ 48 | if($query.Name -and $query.Value){ 49 | Write-Host "Export $($query.Name) to XML file" -ForegroundColor Green 50 | $XMLFile = ($ReportPath + "\" + ([System.Guid]::NewGuid()).ToString() +$query.Name+ ".xml") 51 | try{ 52 | if($query.value.Data){ 53 | ($query.value.Data | ConvertTo-Xml).Save($XMLFile) 54 | } 55 | } 56 | catch{ 57 | Write-Host "Function Generate-XML. Error in $($query.name)" -ForegroundColor Red 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /voyeur.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | 4 | 5 | .DESCRIPTION 6 | The main features included in this version are: 7 | 8 | Return a huge number of attributes on computers, users, containers/OUs, groups, ACL, etc... 9 | Search for locked accounts, expired password, no password policy, etc... 10 | Return a list of all privileged account in domain. (The script search in SID value instead in a group name) 11 | Return a list of group’s modification (Users added/deleted for a specific group, etc...) 12 | Multi-Threading support 13 | Plugin Support 14 | 15 | VOYEUR can be used in two ways scenarios: 16 | Able for using on local or remote computer 17 | Able for using on joined machine or workgroup machine 18 | 19 | With VOYEUR, there is also support for exporting data driven to popular formats like CSV, XML or JSON. 20 | 21 | Office Support 22 | Support for exporting data driven to EXCEL format. The tool also support table style modification, chart creation, company logo or independent language support. At the moment only Office Excel 2010 and Office Excel 2013 are supported by the tool. 23 | 24 | .NOTES 25 | Author : Juan Garrido (http://windowstips.wordpress.com) 26 | Twitter : @tr1ana 27 | Company : https://www.nccgroup.trust 28 | File Name : voyeur.ps1 29 | 30 | .LINK 31 | 32 | https://www.blackhat.com/us-16/arsenal.html#juan-garrido 33 | 34 | .EXAMPLE 35 | .\voyeur.ps1 -ExportTo PRINT 36 | 37 | This example retrieve information of an Active Directory and print results. If no credential passed, the script will try to connect using the token for logged user 38 | 39 | .EXAMPLE 40 | .\voyeur.ps1 -ExportTo CSV,JSON,XML 41 | 42 | This example retrieve information of an Active Directory and export data driven to CSV, JSON and XML format into Reports folder. If no credential passed, the script will try to connect using the token for logged user 43 | 44 | .EXAMPLE 45 | .\voyeur.ps1 -Domain "test.example.local" -AlternateCredential -ExportTo CSV 46 | 47 | This example retrieve information of an Active Directory for a specific domain with explicit credentials and export results to CSV format. 48 | 49 | .EXAMPLE 50 | .\voyeur.ps1 -Domain "test.example.local" -ExportACL -ExportOU -ExportTo JSON 51 | 52 | This example retrieve information of an Active Directory for a specific domain. Also, retrieve Organizational Unit information and ACL values. Next, export all data driven to JSON format. 53 | 54 | .EXAMPLE 55 | .\voyeur.ps1 -Domain "test.example.local" -SearchRoot "OU=NCCGroup,DC=test,DC=example,DC=local" -ExportACL -ExportOU -ExportTo XML 56 | 57 | This example retrieve information of an Active Directory for a specific domain and for specific Organizational Unit (OU). Also, retrieve Organizational Unit information and ACL values. Next, export all data driven to XML format. 58 | 59 | .EXAMPLE 60 | .\voyeur.ps1 -Domain "test.example.local" -UseSSL -ExportTo CSV 61 | 62 | This example retrieve information of an Active Directory through SSL and export data driven to CSV. 63 | Note: You must first make sure that LDAP over SSL (also known as LDAPS or LDAP over TLS) is enabled on Active Directory server and you have imported imported a CA certificate for Active Directory server to your machine. 64 | 65 | .PARAMETER Domain 66 | Collect data from the specified domain. 67 | 68 | .PARAMETER SearchRoot 69 | Collect data from specified Organizational Unit. 70 | 71 | .PARAMETER UseSSL 72 | For SSL connection an valid username/password and domain passed is neccesary to passed 73 | 74 | .PARAMETER ExportTo 75 | Export all data to multiple formats. Supported XML, JSON, CSV, EXCEL 76 | 77 | .PARAMETER AlternateCredential 78 | Run Voyeur with alternate credential 79 | 80 | .PARAMETER ExportACL 81 | Export ACL information from Organizational Units 82 | 83 | .PARAMETER AdminSDHolder 84 | Collect data from AdminSDHolder object. 85 | 86 | .PARAMETER AuditorName 87 | Sets the name of security auditor. Used for Excel report 88 | #> 89 | 90 | [CmdletBinding()] 91 | param 92 | ( 93 | [Parameter(Mandatory=$false)] 94 | [String] $DomainName= $null, 95 | 96 | [Parameter(Mandatory=$false, HelpMessage="Prompt for alternate credentials")] 97 | [switch] 98 | $AlternateCredential, 99 | 100 | [Parameter(Mandatory=$false, HelpMessage="Export ACL from Organizational Units")] 101 | [switch] 102 | $ExportACL, 103 | 104 | [Parameter(Mandatory=$false, HelpMessage="Export Organizational Units from Domain")] 105 | [switch] 106 | $ExportOU, 107 | 108 | [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Export AdminSDHolder ACL")] 109 | [switch] 110 | $AdminSDHolder, 111 | 112 | [Parameter(Mandatory=$false, HelpMessage="Get Organizational Unit Volumetry")] 113 | [Switch] $OUVolumetry, 114 | 115 | [Parameter(Mandatory=$false)] 116 | [String] $SearchRoot = $null, 117 | 118 | [Parameter(Mandatory=$false, HelpMessage="Name of auditor")] 119 | [String] $AuditorName = $env:username, 120 | 121 | [Parameter(Mandatory=$false, HelpMessage="User/Password and Domain required")] 122 | [Switch] $UseSSL, 123 | 124 | [parameter(ValueFromPipelineByPropertyName=$true, Mandatory= $false, HelpMessage= "Export data to multiple formats")] 125 | [ValidateSet("CSV","JSON","XML","PRINT","EXCEL")] 126 | [Array]$ExportTo=@() 127 | ) 128 | 129 | $MyParams = $PSBoundParameters 130 | 131 | #Export voyeur data to multiple formats 132 | Function Export-ResultQuery{ 133 | Param ( 134 | [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] 135 | [Object]$Dataset, 136 | 137 | [parameter()] 138 | [ValidateSet("CSV","JSON","XML","Print","EXCEL")] 139 | [String]$ExportTo="CSV" 140 | 141 | ) 142 | 143 | #Export data 144 | switch ($ExportTo) { 145 | 'CSV' 146 | { 147 | Generate-CSV -ServerObject $AllVoyeurData -RootPath $Report 148 | } 149 | 'JSON' 150 | { 151 | Generate-Json -ServerObject $AllVoyeurData -RootPath $Report 152 | } 153 | 'XML' 154 | { 155 | Generate-XML -ServerObject $AllVoyeurData -RootPath $Report 156 | } 157 | 'EXCEL' 158 | { 159 | Generate-Excel -ServerObject $AllVoyeurData -Settings $ExcelSettings ` 160 | -Formatting $TableFormatting -HeaderStyle $HeaderStyle -RootPath $Report 161 | } 162 | 'Print' 163 | { 164 | $AllVoyeurData | %{ 165 | foreach ($node in $_.psobject.Properties){ 166 | [pscustomobject]@{$node.Name=$node.Value.Data} 167 | } 168 | } 169 | } 170 | } 171 | } 172 | 173 | #Function to create new ADObject 174 | Function New-VoyeurADObject{ 175 | try{ 176 | #Create and return a new PsObject 177 | $ObjectData = New-Object -TypeName PSCustomObject 178 | $ObjectData | Add-Member -type NoteProperty -name Forest -value $Global:Forest 179 | $ObjectData | Add-Member -type NoteProperty -name Credential -value $Global:credential 180 | $ObjectData | Add-Member -type NoteProperty -name Domain -value $Global:AllDomainData 181 | $ObjectData | Add-Member -type NoteProperty -name DomainSID -value $Global:DomainSID 182 | $ObjectData | Add-Member -type NoteProperty -name UseCredentials -value $Global:UseCredentials 183 | $ObjectData | Add-Member -type NoteProperty -name SearchRoot -value $Global:SearchRoot 184 | $ObjectData | Add-Member -type NoteProperty -name UseSSL -value $Global:UseSSL 185 | $ObjectData | Add-Member -type NoteProperty -name InactiveDays -value $InactiveDays 186 | $ObjectData | Add-Member -type NoteProperty -name KerberosEncType -value $Global:KerberosEncType 187 | $ObjectData | Add-Member -type NoteProperty -name Report -value @() 188 | return $ObjectData 189 | } 190 | catch{ 191 | throw ("{0}: {1}" -f "Unable to create new object",$_.Exception.Message) 192 | } 193 | } 194 | 195 | #Region Import Modules 196 | #--------------------------------------------------- 197 | # Import Modules 198 | #--------------------------------------------------- 199 | $ScriptPath = $PWD.Path #Split-Path $MyInvocation.MyCommand.Path -Parent 200 | . $ScriptPath\Common\Domain.ps1 201 | . $ScriptPath\Common\Runspace.ps1 202 | . $ScriptPath\Common\getconfig.ps1 203 | . $ScriptPath\Common\Functions.ps1 204 | . $ScriptPath\Common\Vars.ps1 205 | . $ScriptPath\Common\Office\Excel\ExcelObject.ps1 206 | . $ScriptPath\Utils\CsvReport.ps1 207 | . $ScriptPath\Utils\JsonReport.ps1 208 | . $ScriptPath\Utils\XmlReport.ps1 209 | . $ScriptPath\Utils\ExcelReport.ps1 210 | 211 | 212 | #Load Plugins and config file 213 | $Plugins = Get-ChildItem "$ScriptPath\Plugins\*.ps1" | Select-Object FullName 214 | $appConfig = Get-VoyeurConf -path "$($ScriptPath)\Config\Voyeur.config" -Node "filterSettings" 215 | $appConfig+= Get-VoyeurConf -path "$($ScriptPath)\Config\Voyeur.config" -Node "eventSettings" 216 | $ExcelSettings = Get-VoyeurConf -path "$($ScriptPath)\Config\Voyeur.config" -Node "excelSettings" 217 | $TableFormatting = Get-VoyeurConf -path "$($ScriptPath)\Config\Voyeur.config" -Node "tableFormatting" 218 | $HeaderStyle = Get-VoyeurConf -path "$($ScriptPath)\Config\Voyeur.config" -Node "HeaderStyle" 219 | 220 | #Set-Variable credential -Value (Get-Credential) -Scope Global 221 | #Set-Variable UseCredentials -Value $true -Scope Global 222 | 223 | #EndRegion 224 | #Start Time 225 | $starttimer = Get-Date 226 | #End Start Time 227 | #Main Vars 228 | Set-Variable MyPath -Value $ScriptPath -Scope Global 229 | Set-Variable isConnected -Value $false -Scope Global 230 | Set-Variable Domain -Value $false -Scope Global 231 | Set-Variable DomainName -Value $DomainName -Scope Global 232 | Set-Variable UseSSL -Value $UseSSL -Scope Global 233 | Set-Variable SearchRoot -Value $SearchRoot -Scope Global 234 | Set-Variable AuditorName -Value $AuditorName -Scope Global 235 | Set-Variable KerberosEncType -Value ([System.DirectoryServices.AuthenticationTypes]::Sealing -bor [System.DirectoryServices.AuthenticationTypes]::Secure) -Scope Global 236 | #Region Main 237 | 238 | if($MyParams['AlternateCredential']){ 239 | Set-Variable credential -Value (Get-Credential) -Scope Global 240 | Set-Variable UseCredentials -Value $true -Scope Global 241 | } 242 | else{ 243 | Set-Variable UseCredentials -Value $false -Scope Global 244 | } 245 | $AllDomainData = Get-DomainInfo 246 | 247 | #$AllDomainData | fl 248 | Set-Variable AllDomainData -Value $AllDomainData -Scope Global 249 | #Get Identifiers from Domain 250 | if ($Global:AllDomainData.Name){ 251 | Set-Variable -Name Forest -Value (Get-CurrentForest) -Scope Global 252 | Set-Variable -Name DomainSID -Value (Get-DomainSID) -Scope Global 253 | 254 | $ObjectData = New-VoyeurADObject 255 | $AllData = New-Object -TypeName PSCustomObject 256 | 257 | #Add filter data to Users and Computers queries 258 | if($appConfig["UsersFilter"]){ 259 | $ObjectData | Add-Member -type NoteProperty -name UsersFilter -value $appConfig["UsersFilter"] 260 | } 261 | if($appConfig["ComputersFilter"]){ 262 | $ObjectData | Add-Member -type NoteProperty -name ComputersFilter -value $appConfig["ComputersFilter"] 263 | } 264 | if($appConfig["GroupFilter"]){ 265 | $ObjectData | Add-Member -type NoteProperty -name GroupFilter -value $appConfig["GroupFilter"] 266 | } 267 | #Populate jobs with plugins 268 | $AllData = Get-RunSpaceADObject -Plugins $Plugins -ADObject $ObjectData 269 | 270 | if($MyParams['ExportOU']){ 271 | #Create New object 272 | $MyPlugin = Get-ChildItem "$ScriptPath\Plugins\ACL.ps1" | Select-Object FullName 273 | $OUExtract = New-Object -TypeName PSCustomObject 274 | $OUExtract | Add-Member -type NoteProperty -name Name -value "OrganizationalUnit" 275 | $OUExtract | Add-Member -type NoteProperty -name Query -value $appConfig["OU"] 276 | $OUExtract | Add-Member -type NoteProperty -name FullACL -value $false 277 | #Send object to plugin 278 | $NewObjectData = New-VoyeurADObject 279 | $NewObjectData | Add-Member -type NoteProperty -name OUExtract -value $OUExtract -Force 280 | #Send Unit plugin 281 | $OUExport = Get-RunSpaceADObject -Plugins $MyPlugin -ADObject $NewObjectData 282 | #Add new values to PsObject 283 | $CustomReportFields = $AllData.Report 284 | $Data = $OUExport.OrganizationalUnit 285 | if($Data){ 286 | $NewCustomReportFields = [array]$CustomReportFields+="OrganizationalUnit" 287 | $AllData | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 288 | $AllData | Add-Member -type NoteProperty -name OrganizationalUnit -value $Data -Force 289 | } 290 | 291 | } 292 | if($MyParams['ExportACL']){ 293 | #Create New object 294 | $MyPlugin = Get-ChildItem "$ScriptPath\Plugins\ACL.ps1" | Select-Object FullName 295 | $OUExtract = New-Object -TypeName PSCustomObject 296 | $OUExtract | Add-Member -type NoteProperty -name Name -value "FullOrganizationalUnit" 297 | $OUExtract | Add-Member -type NoteProperty -name Query -value $appConfig["OU"] 298 | $OUExtract | Add-Member -type NoteProperty -name FullACL -value $true 299 | #Send object to plugin 300 | $NewObjectData = New-VoyeurADObject 301 | $NewObjectData | Add-Member -type NoteProperty -name OUExtract -value $OUExtract -Force 302 | #Send Unit plugin 303 | $ACLExport = Get-RunSpaceADObject -Plugins $MyPlugin -ADObject $NewObjectData 304 | #Add data to psObject 305 | $CustomReportFields = $AllData.Report 306 | $Data = $ACLExport.FullOrganizationalUnit 307 | if($Data){ 308 | $NewCustomReportFields = [array]$CustomReportFields+="FullOrganizationalUnit" 309 | $AllData | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 310 | $AllData | Add-Member -type NoteProperty -name FullOrganizationalUnit -value $Data -Force 311 | } 312 | 313 | } 314 | if($MyParams['SearchRoot']){ 315 | $ObjectData | Add-Member -type NoteProperty -name SearchRoot -value $SearchRoot -Force 316 | } 317 | if($MyParams['AdminSDHolder']){ 318 | #Create New object 319 | $MyPlugin = Get-ChildItem "$ScriptPath\Plugins\ACL.ps1" | Select-Object FullName 320 | $OUExtract = New-Object -TypeName PSCustomObject 321 | $OUExtract | Add-Member -type NoteProperty -name Name -value "AdminSDHolder" 322 | $OUExtract | Add-Member -type NoteProperty -name Query -value $appConfig["AdminSDHolder"] 323 | $OUExtract | Add-Member -type NoteProperty -name FullACL -value $true 324 | #Send object to plugin 325 | $NewObjectData = New-VoyeurADObject 326 | $NewObjectData | Add-Member -type NoteProperty -name OUExtract -value $OUExtract -Force 327 | #Send Unit plugin 328 | $AdminSDHolderExport = Get-RunSpaceADObject -Plugins $MyPlugin -ADObject $NewObjectData 329 | #Add data to PSObject 330 | $CustomReportFields = $AllData.Report 331 | $Data = $AdminSDHolderExport.AdminSDHolder 332 | if($Data){ 333 | $NewCustomReportFields = [array]$CustomReportFields+="AdminSDHolder" 334 | $AllData | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 335 | $AllData | Add-Member -type NoteProperty -name AdminSDHolder -value $Data -Force 336 | } 337 | } 338 | if($MyParams['OUVolumetry']){ 339 | #Try to extract OU Volumetry 340 | $AllUsers = $AllData.DomainUsers.Data 341 | $AllOU = $AllData.OrganizationalUnit.Data 342 | if($AllUsers -and $AllOU){ 343 | $VolumetryACL = Get-OUVolumetry -ACL $AllOU -Users $AllUsers 344 | $CustomReportFields = $AllData.Report 345 | $NewCustomReportFields = [array]$CustomReportFields+="OUVolumetry" 346 | $AllData | Add-Member -type NoteProperty -name Report -value $NewCustomReportFields -Force 347 | $AllData | Add-Member -type NoteProperty -name OUVolumetry -value $VolumetryACL -Force 348 | } 349 | } 350 | #Remove unnecessary data 351 | $excludes = @("Report","UsersStatus") 352 | $AllVoyeurData = New-Object -TypeName PSCustomObject 353 | $AllData.psobject.Properties| %{` 354 | foreach ($exclude in $excludes){ 355 | if ($_.Name -eq $exclude){ 356 | return} 357 | } 358 | $AllVoyeurData | Add-Member -type NoteProperty -name $_.Name -value $_.Value 359 | } 360 | 361 | #Prepare data and export results to multiple formats 362 | if($MyParams['ExportTo']){ 363 | if($AllVoyeurData){ 364 | if($ExportTo -ne "print"){ 365 | Write-Host "Create report folder...." -ForegroundColor Green 366 | $ReportPath = New-Report $ScriptPath $Domain.name 367 | Set-Variable -Name Report -Value $ReportPath -Scope Global 368 | Write-Host "Report folder created in $Report..." -ForegroundColor Green 369 | } 370 | $ExportTo | %{$Output = $_.split(","); 371 | Export-ResultQuery -Dataset $AllVoyeurData -ExportTo $Output[0] 372 | } 373 | } 374 | } 375 | 376 | } 377 | else{ 378 | throw "Unable to connect..." 379 | } 380 | 381 | #End main script. Remove Vars 382 | try{ 383 | remove-item -Path "variable:Report" -Force -ErrorAction SilentlyContinue 384 | remove-item -Path "variable:DomainName" -Force -ErrorAction SilentlyContinue 385 | remove-item -Path "variable:Forest" -Force -ErrorAction SilentlyContinue 386 | remove-item -Path "variable:DomainSID" -Force -ErrorAction SilentlyContinue 387 | remove-item -Path "Variable:RootDomainSID" -Force -ErrorAction SilentlyContinue 388 | remove-item -Path "Variable:credential" -Force -ErrorAction SilentlyContinue 389 | remove-item -Path "Variable:Domain" -Force -ErrorAction SilentlyContinue 390 | remove-item -Path "Variable:UseCredentials" -Force -ErrorAction SilentlyContinue 391 | remove-item -Path "Variable:isConnected" -Force -ErrorAction SilentlyContinue 392 | remove-item -Path "Variable:AllDomainData" -Force -ErrorAction SilentlyContinue 393 | remove-item -Path "Variable:SearchRoot" -Force -ErrorAction SilentlyContinue 394 | remove-item -Path "Variable:UseSSL"-Force -ErrorAction SilentlyContinue 395 | remove-item -Path "Variable:MyPath"-Force -ErrorAction SilentlyContinue 396 | remove-item -Path "Variable:AuditorName"-Force -ErrorAction SilentlyContinue 397 | $ObjectData = $null 398 | $AllData = $null 399 | } 400 | catch{ 401 | #Nothing to do here 402 | } 403 | #Stop timer 404 | $stoptimer = Get-Date 405 | "Total time for JOBs: {0} Minutes" -f [math]::round(($stoptimer – $starttimer).TotalMinutes , 2) 406 | --------------------------------------------------------------------------------