├── 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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------