Public/DomainReport/Security/Get-ADSecurityConfiguration.ps1
|
function Get-ADSecurityConfiguration { try { Write-Log "Retrieving AD security configuration..." -Level Info # Retrieve Organizational Units (OUs) $OUs = Get-ADOrganizationalUnit -Filter * -Properties Name, DistinguishedName -Credential $script:adminCreds -ErrorAction Stop # Retrieve Domain Controllers (DCs) $DCs = Get-ADDomainController -Filter * -Properties HostName -Credential $script:adminCreds -ErrorAction Stop # TODO: Retrieve Users (for SPN Configuration) # $Users = Get-ADUser -Filter * -Properties SamAccountName, Enabled, ServicePrincipalNames -Credential $script:adminCreds -ErrorAction Stop # Compile the security configuration into a PSCustomObject $securityConfig = [PSCustomObject]@{ ObjectACLs = Get-CriticalObjectACLs -OUs $OUs FileShareACLs = Get-CriticalShareACLs -DCs $DCs SPNConfiguration = Get-CriticalShareACLs -DCs $DCs } # Add ToString method to securityConfig Add-Member -InputObject $securityConfig -MemberType ScriptMethod -Name "ToString" -Value { "ObjectACLs=$($this.ObjectACLs.Count); FileShareACLs=$($this.FileShareACLs.Count)" } -Force Write-Log "Successfully retrieved AD security configuration." -Level Info return $securityConfig } catch { Write-Log "Error retrieving AD security configuration: $($_.Exception.Message)" -Level Error return $null } } function Get-CriticalObjectACLs { [CmdletBinding()] param( [Parameter(Mandatory)] [array]$OUs ) try { Write-Log "Collecting ACLs for critical AD objects..." -Level Info # Define a processing scriptblock for each OU $processingScript = { param($ou) try { # Retrieve ACL for the OU $acl = Get-Acl -Path ("AD:" + $ou.DistinguishedName) # Process Access Rules $accessRules = $acl.Access | ForEach-Object { [PSCustomObject]@{ Principal = $_.IdentityReference.Value AccessType = $_.AccessControlType.ToString() Rights = $_.ActiveDirectoryRights.ToString() Inherited = $_.IsInherited } } # Construct the ACL object [PSCustomObject]@{ OU = $ou.Name Path = $ou.DistinguishedName Owner = $acl.Owner AccessRules = $accessRules } } catch { Write-Log "Error getting ACL for $($ou.DistinguishedName): $($_.Exception.Message)" -Level Warning return $null } } # Use the helper to process each OU with progress reporting $objectACLs = Invoke-ADRetrievalWithProgress -ObjectType "OUs" ` -Filter "*" ` -Properties @('Name', 'DistinguishedName') ` -Credential $null ` -ProcessingScript $processingScript ` -ActivityName "Retrieving OU ACLs" ` -InputData $OUs # Remove any null entries resulting from errors $objectACLs = $objectACLs | Where-Object { $_ -ne $null } # Add ToString method to each ACL object foreach ($aclObj in $objectACLs) { Add-Member -InputObject $aclObj -MemberType ScriptMethod -Name "ToString" -Value { "OU=$($this.OU); Owner=$($this.Owner); Rules=$($this.AccessRules.Count)" } -Force } Write-Log "Successfully collected ACLs for critical AD objects." -Level Info return $objectACLs } catch { Write-Log "Error collecting critical object ACLs: $($_.Exception.Message)" -Level Error return @() } } function Get-CriticalShareACLs { [CmdletBinding()] param( [Parameter(Mandatory)] [array]$DCs ) try { Write-Log "Collecting ACLs for SYSVOL and NETLOGON shares..." -Level Info # Define a processing scriptblock for each share $processingScript = { param($shareInfo) try { # Construct the share path $path = "\\$($shareInfo.HostName)\$($shareInfo.ShareName)" # Retrieve ACL for the share $acl = Get-Acl -Path $path # Process Access Rules $accessRules = $acl.Access | ForEach-Object { [PSCustomObject]@{ Principal = $_.IdentityReference.Value AccessType = $_.AccessControlType.ToString() Rights = $_.FileSystemRights.ToString() Inherited = $_.IsInherited } } # Construct the Share ACL object [PSCustomObject]@{ ShareName = $shareInfo.ShareName Path = $path Owner = $acl.Owner AccessRules = $accessRules } } catch { Write-Log "Error getting ACL for $($shareInfo.ShareName): $($_.Exception.Message)" -Level Warning return $null } } # Define the shares to retrieve $shares = @("SYSVOL", "NETLOGON") # Prepare share information from all DCs $shareInfos = foreach ($dc in $DCs) { foreach ($share in $shares) { [PSCustomObject]@{ HostName = $dc.HostName ShareName = $share } } } # Use the helper to process each share with progress reporting $shareACLs = Invoke-ADRetrievalWithProgress -ObjectType "Shares" ` -Filter "*" ` -Properties @('HostName', 'ShareName') ` -Credential $null ` -ProcessingScript $processingScript ` -ActivityName "Retrieving Share ACLs" ` -InputData $shareInfos # Remove any null entries resulting from errors $shareACLs = $shareACLs | Where-Object { $_ -ne $null } # Add ToString method to each Share ACL object foreach ($shareAclObj in $shareACLs) { Add-Member -InputObject $shareAclObj -MemberType ScriptMethod -Name "ToString" -Value { "Share=$($this.ShareName); Owner=$($this.Owner); Rules=$($this.AccessRules.Count)" } -Force } Write-Log "Successfully collected ACLs for SYSVOL and NETLOGON shares." -Level Info return $shareACLs } catch { Write-Log "Error collecting share ACLs: $($_.Exception.Message)" -Level Error return @() } } function Get-CriticalObjectACLs { try { Write-Log "Collecting ACLs for critical AD objects..." -Level Info if (-not $script:AllOUs -or $script:AllOUs.Count -eq 0) { Write-Log "No OU data available in cache." -Level Warning return $null } $acls = @() foreach ($ou in $script:AllOUs) { try { # Getting ACL from AD is still required $acl = Get-Acl -Path ("AD:" + $ou.DistinguishedName) # Convert ACL.Access to a collection of custom objects $accessRules = @() foreach ($rule in $acl.Access) { $accessRules += [PSCustomObject]@{ Principal = $rule.IdentityReference.Value AccessType = $rule.AccessControlType.ToString() Rights = $rule.ActiveDirectoryRights.ToString() Inherited = $rule.IsInherited } } $aclObject = [PSCustomObject]@{ OU = $ou.Name Path = $ou.DistinguishedName Owner = $acl.Owner AccessRules = $accessRules } # Add ToString method to each ACL object Add-Member -InputObject $aclObject -MemberType ScriptMethod -Name "ToString" -Value { "OU=$($this.OU); Owner=$($this.Owner); Rules=$($this.AccessRules.Count)" } -Force $acls += $aclObject } catch { Write-Log "Error getting ACL for $($ou.DistinguishedName) : $($_.Exception.Message)" -Level Warning } } return $acls } catch { Write-Log "Error collecting critical object ACLs: $($_.Exception.Message)" -Level Error return $null } } function Get-CriticalShareACLs { try { Write-Log "Collecting ACLs for SYSVOL and NETLOGON shares..." -Level Info # Use cached DC data if (-not $script:AllDCs -or $script:AllDCs.Count -eq 0) { Write-Log "No domain controller data available in cache. Cannot retrieve share ACLs." -Level Error return $null } # Pick the first DC from the cached list (or add logic to choose a specific one) $dc = $script:AllDCs[0] if (-not $dc.HostName) { Write-Log "No DC HostName available to form share paths." -Level Error return $null } $shares = @("SYSVOL", "NETLOGON") $shareAcls = @() foreach ($share in $shares) { try { $path = "\\$($dc.HostName)\$share" $acl = Get-Acl -Path $path $accessRules = @() foreach ($rule in $acl.AccessRules) { $accessRules += [PSCustomObject]@{ Principal = $rule.IdentityReference.Value AccessType = $rule.AccessControlType.ToString() Rights = $rule.FileSystemRights.ToString() Inherited = $rule.IsInherited } } $shareAclObject = [PSCustomObject]@{ ShareName = $share Path = $path Owner = $acl.Owner AccessRules = $accessRules } # Add ToString method to each share ACL object Add-Member -InputObject $shareAclObject -MemberType ScriptMethod -Name "ToString" -Value { "Share=$($this.ShareName); Owner=$($this.Owner); Rules=$($this.AccessRules.Count)" } -Force $shareAcls += $shareAclObject } catch { Write-Log "Error getting ACL for $share : $($_.Exception.Message)" -Level Warning } } return $shareAcls } catch { Write-Log "Error collecting share ACLs: $($_.Exception.Message)" -Level Error return $null } } # Helper Function: Get-SPNConfiguration function Get-SPNConfiguration { [CmdletBinding()] param( [Parameter(Mandatory)] [array]$Users ) try { Write-Log "Collecting SPN configuration from AD users..." -Level Info # Define a processing scriptblock for each user $processingScript = { param($user) try { if ($user.ServicePrincipalNames -and $user.ServicePrincipalNames.Count -gt 0) { [PSCustomObject]@{ UserName = $user.SamAccountName Enabled = $user.Enabled SPNs = $user.ServicePrincipalNames IsDuplicate = $false } } else { return $null } } catch { Write-Log "Error processing SPNs for user $($user.SamAccountName): $($_.Exception.Message)" -Level Warning return $null } } # Use the helper to process each user with progress reporting $spnUsers = Invoke-ADRetrievalWithProgress -ObjectType "Users" ` -Filter "*" ` -Properties @('SamAccountName', 'Enabled', 'ServicePrincipalNames') ` -Credential $null ` -ProcessingScript $processingScript ` -ActivityName "Retrieving SPN Configurations" ` -InputData $Users # Remove any null entries resulting from errors or users without SPNs $spnUsers = $spnUsers | Where-Object { $_ -ne $null } if ($spnUsers.Count -eq 0) { Write-Log "No users with SPNs found." -Level Info return @() } # Check for duplicate SPNs # Create a hashtable to track SPN counts $spnTable = @{} foreach ($spnObj in $spnUsers) { foreach ($spn in $spnObj.SPNs) { if ($spnTable.ContainsKey($spn)) { $spnTable[$spn]++ } else { $spnTable[$spn] = 1 } } } # Mark duplicates foreach ($spnObj in $spnUsers) { foreach ($spn in $spnObj.SPNs) { if ($spnTable[$spn] -gt 1) { $spnObj.IsDuplicate = $true break # No need to check further SPNs for this user } } } # Add ToString method to each SPN configuration object foreach ($spnObj in $spnUsers) { Add-Member -InputObject $spnObj -MemberType ScriptMethod -Name "ToString" -Value { "User=$($this.UserName); Enabled=$($this.Enabled); SPNCount=$($this.SPNs.Count); Duplicate=$($this.IsDuplicate)" } -Force } Write-Log "Successfully collected SPN configurations." -Level Info return $spnUsers } catch { Write-Log "Error collecting SPN configuration: $($_.Exception.Message)" -Level Error return @() } } |