Providers/AD/Public/Get-ADEnvironmentReport.ps1
|
function Get-ADEnvironmentReport { <# .SYNOPSIS Generates a comprehensive report of the AD test environment .DESCRIPTION Reads the organizational units, users, service accounts, devices and groups under the seed OU, with every group's members, and reports them. Console output is for a person; JSON, CSV and HTML are for a file, written by the one writer every provider shares, as UTF-8, with CSV as a folder of one file per section. The report object is the shape every provider returns: Provider, Target, GeneratedOn, the domain, Counts, Sections, and one property per section. .PARAMETER OutputFormat Console, JSON, HTML or CSV. .PARAMETER OutputPath The file to write, or for CSV the folder. Required for anything but Console. .PARAMETER PassThru Returns the report object as well. .EXAMPLE Get-ADEnvironmentReport Displays a console report with complete details .EXAMPLE Get-ADEnvironmentReport -OutputFormat HTML -OutputPath "C:\Reports\ADTestReport.html" Writes one HTML page with a table per section .EXAMPLE Get-ADEnvironmentReport -OutputFormat CSV -OutputPath "C:\Reports\" Writes one ADLab<Section>.csv per section, group members included .EXAMPLE $report = Get-ADEnvironmentReport -PassThru The report object, for a script .OUTPUTS ADEnvironmentReport, the shape every provider returns, when -PassThru is supplied .NOTES Author: Jeffrey Stuhr Blog: https://www.techbyjeff.net LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Colour-coded console progress is intentional; results are returned as objects.')] [CmdletBinding()] [OutputType([PSCustomObject], ParameterSetName = 'PassThru')] [OutputType([void], ParameterSetName = 'Default')] param( [ValidateSet('Console', 'JSON', 'HTML', 'CSV')] [string]$OutputFormat = 'Console', [string]$OutputPath, [switch]$PassThru ) begin { $correlationId = [System.Guid]::NewGuid() Write-Verbose "Starting Get-ADEnvironmentReport - CorrelationId: $correlationId" # Get domain information $domain = Get-ADTestDomain if ($OutputFormat -ne 'Console' -and -not $OutputPath) { throw "-OutputPath is required for the $OutputFormat format." } } process { try { Write-TestMessage -Message "Generating Active Directory Test Data Report" -Type Header # Initialize report data structure $reportData = @{ GeneratedOn = Get-Date Domain = $domain.DNSName CorrelationId = $correlationId TestOUs = @() TestUsers = @() TestServiceAccounts = @() TestDevices = @() TestGroups = @() GroupMembers = @() } # Collect Test OUs Write-TestMessage -Message "Collecting OU information..." -Type Info try { $searchBase = "OU=$($script:ADTestRootName),$($domain.DomainDN)" $ouProps = 'Description', 'ProtectedFromAccidentalDeletion' $testOUs = Get-ADOrganizationalUnit -Filter '*' -SearchBase $searchBase -Properties $ouProps | Select-Object Name, DistinguishedName, Description, ProtectedFromAccidentalDeletion, @{Name='ParentOU';Expression={($_.DistinguishedName -split ',',2)[1]}} $reportData.TestOUs = $testOUs } catch { Write-Warning "Error collecting OU data: $($_.Exception.Message)" $reportData.TestOUs = @() } # Collect Test Users Write-TestMessage -Message "Collecting user information..." -Type Info try { # Get ALL properties from AD $searchBase = "OU=Users,OU=$($script:ADTestRootName),$($domain.DomainDN)" $testUsers = Get-ADUser -Filter '*' -SearchBase $searchBase ` -Properties DisplayName, Department, Title, Office, Manager, Description $reportData.TestUsers = $testUsers } catch { Write-Warning "Error collecting user data: $($_.Exception.Message)" $reportData.TestUsers = @() } # Collect Test Service Accounts Write-TestMessage -Message "Collecting service account information..." -Type Info try { # Get ALL properties from AD $searchBase = "OU=ServiceAccounts,OU=$($script:ADTestRootName),$($domain.DomainDN)" $testServiceAccounts = Get-ADUser -Filter '*' -SearchBase $searchBase ` -Properties Description, ServicePrincipalNames, TrustedForDelegation $reportData.TestServiceAccounts = $testServiceAccounts } catch { Write-Warning "Error collecting service account data: $($_.Exception.Message)" $reportData.TestServiceAccounts = @() } # Collect Test Devices Write-TestMessage -Message "Collecting device information..." -Type Info try { # Get ALL properties from AD $searchBase = "OU=Devices,OU=$($script:ADTestRootName),$($domain.DomainDN)" $testDevices = Get-ADComputer -Filter '*' -SearchBase $searchBase ` -Properties OperatingSystem, OperatingSystemVersion, Description $reportData.TestDevices = $testDevices } catch { Write-Warning "Error collecting device data: $($_.Exception.Message)" $reportData.TestDevices = @() } # Collect Test Groups Write-TestMessage -Message "Collecting group information..." -Type Info try { # Get ALL properties from AD $searchBase = "OU=Groups,OU=$($script:ADTestRootName),$($domain.DomainDN)" $testGroups = Get-ADGroup -Filter '*' -SearchBase $searchBase -Properties Description # Always collect group members and add member counts $groupMembers = @() foreach ($group in $testGroups) { try { $getADGroupMemberArgs1 = @{ Identity = $group.DistinguishedName ErrorAction = 'SilentlyContinue' } $members = Get-ADGroupMember @getADGroupMemberArgs1 $countProp = @{ MemberType = 'NoteProperty' Name = 'MemberCount' # Wrapped, because on Windows PowerShell a single member has no Count # and a group of one was reported as empty. Value = @($members).Count Force = $true } $group | Add-Member @countProp $memberSummary = $members | Select-Object Name, objectClass $memberProp = @{ MemberType = 'NoteProperty' Name = 'Members' Value = $memberSummary Force = $true } $group | Add-Member @memberProp # Create individual member records for the GroupMembers collection foreach ($member in $members) { $groupMembers += [PSCustomObject]@{ GroupName = $group.Name GroupDistinguishedName = $group.DistinguishedName MemberName = $member.Name MemberType = $member.objectClass MemberDistinguishedName = $member.DistinguishedName MemberSamAccountName = $member.SamAccountName } } } catch { $group | Add-Member -MemberType NoteProperty -Name 'MemberCount' -Value 0 -Force $group | Add-Member -MemberType NoteProperty -Name 'Members' -Value @() -Force } } $reportData.GroupMembers = $groupMembers $reportData.TestGroups = $testGroups } catch { Write-Warning "Error collecting group data: $($_.Exception.Message)" $reportData.TestGroups = @() } # One shape and one file writer, shared with every provider. The AD objects are # projected to the columns a report reader wants rather than exported whole, because # a full ADUser is several hundred properties of which a handful say anything. $sections = [ordered]@{ OrganizationalUnits = @($reportData.TestOUs) Users = @($reportData.TestUsers | ForEach-Object { [PSCustomObject]@{ Name = $_.Name SamAccountName = $_.SamAccountName UserPrincipalName = $_.UserPrincipalName DisplayName = $_.DisplayName Department = $_.Department Title = $_.Title Office = $_.Office Enabled = $_.Enabled Manager = $_.Manager Description = $_.Description DistinguishedName = $_.DistinguishedName } }) ServiceAccounts = @($reportData.TestServiceAccounts | ForEach-Object { [PSCustomObject]@{ Name = $_.Name SamAccountName = $_.SamAccountName Description = $_.Description Enabled = $_.Enabled ServicePrincipalNames = @($_.ServicePrincipalNames) TrustedForDelegation = [bool]$_.TrustedForDelegation DistinguishedName = $_.DistinguishedName } }) Devices = @($reportData.TestDevices | ForEach-Object { [PSCustomObject]@{ Name = $_.Name OperatingSystem = $_.OperatingSystem OperatingSystemVersion = $_.OperatingSystemVersion Enabled = $_.Enabled Description = $_.Description DistinguishedName = $_.DistinguishedName } }) Groups = @($reportData.TestGroups | ForEach-Object { [PSCustomObject]@{ Name = $_.Name GroupScope = $_.GroupScope GroupCategory = $_.GroupCategory Description = $_.Description MemberCount = [int]$_.MemberCount DistinguishedName = $_.DistinguishedName } }) GroupMembers = @($reportData.GroupMembers) } $report = New-TestEnvironmentReport -Provider 'AD' -Target $domain.DNSName -TypeName 'ADEnvironmentReport' -Section $sections ` -Property ([ordered]@{ Domain = $domain.DNSName; CorrelationId = $correlationId }) if ($OutputFormat -eq 'Console') { if ($report.Counts.OrganizationalUnits -gt 0) { Write-Host "=== ORGANIZATIONAL UNITS ===" -ForegroundColor Yellow $report.OrganizationalUnits | Format-Table Name, Description, ProtectedFromAccidentalDeletion -AutoSize | Out-String -Width 200 | Write-Host } if ($report.Counts.Users -gt 0) { Write-Host "=== USERS ===" -ForegroundColor Yellow $report.Users | Format-Table Name, Department, Title, Enabled -AutoSize | Out-String -Width 200 | Write-Host } if ($report.Counts.ServiceAccounts -gt 0) { Write-Host "=== SERVICE ACCOUNTS ===" -ForegroundColor Yellow $report.ServiceAccounts | Format-Table Name, SamAccountName, Description, Enabled -AutoSize | Out-String -Width 200 | Write-Host } if ($report.Counts.Devices -gt 0) { Write-Host "=== DEVICES ===" -ForegroundColor Yellow $report.Devices | Format-Table Name, OperatingSystem, Enabled -AutoSize | Out-String -Width 200 | Write-Host } if ($report.Counts.Groups -gt 0) { Write-Host "=== SECURITY GROUPS ===" -ForegroundColor Yellow $report.Groups | Format-Table Name, GroupScope, MemberCount -AutoSize | Out-String -Width 200 | Write-Host } Write-TestMessage -Message "Active Directory Test Data Report" -Type Success Write-Host "" Write-Host "Domain: $($report.Domain)" -ForegroundColor Cyan Write-Host "Generated: $($report.GeneratedOn)" -ForegroundColor Cyan Write-Host "" Write-Host "=== SUMMARY ===" -ForegroundColor Yellow foreach ($property in $report.Counts.PSObject.Properties) { Write-Host ("Total {0}: {1}" -f $property.Name, $property.Value) -ForegroundColor Green } Write-Host "" } else { Export-TestEnvironmentReport -Report $report -OutputFormat $OutputFormat -OutputPath $OutputPath ` -FilePrefix 'ADLab' -Title 'Active Directory Test Environment Report' -Note @("Domain $($domain.DNSName)") } if ($PassThru) { return $report } } catch { Write-Error "Failed to generate report: $($_.Exception.Message)" -ErrorAction Stop } } end { Write-Verbose "Completed Get-ADEnvironmentReport - CorrelationId: $correlationId" } } |