Public/Miscellaneous/Get-AttributeSchemaHashTable.ps1
Function Get-AttributeSchemaHashTable { <# .SYNOPSIS Gets all GUIDs from the Active Directory schema and stores them in a hashtable. .DESCRIPTION This function queries the Active Directory schema and retrieves all schema object GUIDs, creating a hashtable that maps LDAP display names to their corresponding GUIDs. The hashtable is stored in the $Variables.GuidMap variable for later use. This function is essential for performing GUID-based operations in Active Directory, particularly for security descriptors and permission management. .PARAMETER Force Forces the function to rebuild the GUID map even if it already exists. .PARAMETER Server Specifies the domain controller to use for the query. If not provided, the function will use the default domain controller. .EXAMPLE Get-AttributeSchemaHashTable Retrieves all schema GUIDs and stores them in $Variables.GuidMap. .EXAMPLE Get-AttributeSchemaHashTable -Force Forces the function to rebuild the GUID map even if it already exists. .EXAMPLE Get-AttributeSchemaHashTable -Server 'DC01.EguibarIT.local' Retrieves all schema GUIDs from the specified domain controller. .OUTPUTS System.Collections.Hashtable A hashtable mapping LDAP display names to their corresponding GUIDs. .NOTES Used Functions: Name ║ Module/Namespace ═══════════════════════════════════════════╬══════════════════════════════ Get-ADObject ║ ActiveDirectory Write-Progress ║ Microsoft.PowerShell.Utility Write-Verbose ║ Microsoft.PowerShell.Utility Write-Error ║ Microsoft.PowerShell.Utility Write-Debug ║ Microsoft.PowerShell.Utility .NOTES Version: 1.2 DateModified: 21/Mar/2025 LastModifiedBy: Vicente Rodriguez Eguibar vicente@eguibar.com Eguibar IT http://www.eguibarit.com .LINK https://github.com/vreguibar/EguibarIT.DelegationPS/blob/main/Public/Get-AttributeSchemaHashTable.ps1 #> [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low' )] [OutputType([System.Collections.Hashtable])] Param( [Parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, Position = 0, HelpMessage = 'Forces the function to rebuild the GUID map even if it already exists.' )] [Alias('Rebuild')] [switch] $Force, [Parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, Position = 1, HelpMessage = 'Specifies the domain controller to use for the query.' )] [ValidateNotNullOrEmpty()] [Alias('DC', 'DomainController')] [string]$Server ) Begin { Set-StrictMode -Version Latest # Display function header if variables exist if ($null -ne $Variables -and $null -ne $Variables.HeaderDelegation) { $txt = ($Variables.HeaderDelegation -f (Get-Date).ToString('dd/MMM/yyyy'), $MyInvocation.Mycommand, (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False) ) Write-Verbose -Message $txt } #end if ############################## # Variables Definition [hashtable]$TmpMap = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) [hashtable]$ProgressSplat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) [bool]$FillUp = $false [int32]$ProcessedItems = 0 [int32]$BatchSize = 1000 # Optimized batch size for AD queries # Prepare AD query splatting parameters $Splat = @{ SearchBase = $Variables.SchemaNamingContext LDAPFilter = '(schemaidguid=*)' Properties = 'lDAPDisplayName', 'schemaIDGUID' } # Add server if specified if ($PSBoundParameters.ContainsKey('Server')) { $Splat.Add('Server', $Server) Write-Debug -Message ('Using specified server: {0}' -f $Server) } #end if } #end Begin Process { # Check if $Variables.GuidMap is Null or Empty If ($Force -or [string]::IsNullOrEmpty($Variables.GuidMap) -or $Variables.GuidMap.Count -eq 0) { # We have to fill it up $FillUp = $true Write-Debug -Message 'The GUID map is null, empty, or Force parameter was specified.' } else { Write-Debug -Message 'GUID map already exists. Use -Force to rebuild it.' } #end If If ( $FillUp ) { try { Write-Debug -Message ' The GUID map is null, empty, zero, or false. Getting the GUID value of each schema class and attribute' # Add pagination parameters for large environments $Splat.Add('ResultPageSize', $BatchSize) # Execute the AD query with optimized filter Write-Debug -Message ('Executing AD query with parameters: {0}' -f ($ADSplat | ConvertTo-Json -Compress)) $AllSchema = Get-ADObject @Splat if ($null -eq $AllSchema) { Write-Warning -Message 'No schema objects were found.' } $SchemaCount = ($AllSchema | Measure-Object).Count Write-Debug -Message ('Found {0} schema objects' -f $SchemaCount) # Process schema objects Foreach ($item in $AllSchema) { $ProcessedItems++ # Update progress bar $ProgressSplat = @{ Activity = 'Adding Schema attributes to Hashtable' Status = ('Processing: {0}/{1} ({2:P2} complete)' -f $ProcessedItems, $SchemaCount, ($ProcessedItems / $SchemaCount)) PercentComplete = [math]::Round(($ProcessedItems / $SchemaCount) * 100, 2) CurrentOperation = ('Processing attribute: {0}' -f $Item.lDAPDisplayName) } Write-Progress @ProgressSplat # Convert schemaIDGUID to string GUID format and add to hashtable try { $GuidValue = ([System.GUID]$Item.schemaIDGUID).GUID # Check if the lDAPDisplayName already exists in the hashtable if (-not $TmpMap.ContainsKey($Item.lDAPDisplayName)) { $TmpMap.Add($Item.lDAPDisplayName, $GuidValue) Write-Debug -Message ('Added {0}: {1}' -f $SchemaItem.lDAPDisplayName, $GuidValue) } else { Write-Warning -Message (' Duplicate lDAPDisplayName found: {0}. Using first occurrence only.' -f $SchemaItem.lDAPDisplayName ) } } catch { Write-Warning -Message (' Error processing schema item {0}: {1}' -f $SchemaItem.lDAPDisplayName, $_.Exception.Message ) } #end Try-Catch } #end ForEach # Add the "All" entry with null GUID if (-not [string]::IsNullOrEmpty($Constants.guidNull)) { $TmpMap.Add('All', $Constants.guidNull) Write-Debug -Message ('Added All: {0}' -f $Constants.guidNull) } else { # Add a null GUID if $Constants.guidNull is not defined $TmpMap.Add('All', '00000000-0000-0000-0000-000000000000') Write-Debug -Message 'Added All: 00000000-0000-0000-0000-000000000000' } #end If-Else # Update the module-level variable with our new hashtable Write-Verbose -Message 'Updating $Variables.GuidMap with new values' $Variables.GuidMap = $TmpMap } catch [System.DirectoryServices.ActiveDirectory.ActiveDirectoryOperationException] { Write-Error -Message ('Active Directory operation error: {0}' -f $_.Exception.Message) throw } catch [System.UnauthorizedAccessException] { Write-Error -Message ('Access denied error: {0}' -f $_.Exception.Message) throw } catch { Write-Error -Message ('Error filling GUID map: {0}' -f $_.Exception.Message) throw } Finally { # Complete the progress bar $ProgressSplat = @{ Activity = 'Adding Schema attributes to Hashtable' Status = 'Completed' CurrentOperation = 'Finished' PercentComplete = 100 Completed = $true } Write-Progress @ProgressSplat } #end Try-Catch-Finally } #end If } #end Process End { # Display function footer if variables exist if ($null -ne $Variables -and $null -ne $Variables.FooterDelegation) { $txt = ($Variables.FooterDelegation -f $MyInvocation.InvocationName, 'filling up GuidMap variable.' ) Write-Verbose -Message $txt } #end if } # end END } #end Function Get-AttributeSchemaHashTable |