core/Remove-VolatileKeys.ps1
|
#Requires -Version 7.0 <# .SYNOPSIS Removes volatile/non-configuration keys from a PowerShell object. .DESCRIPTION Strips out volatile keys like timestamps, IDs, and metadata that should not be backed up. Works recursively on nested objects and arrays. Creates a deep clone to avoid mutating originals. Supports pipeline input of individual objects (each piped item is processed separately). .PARAMETER InputObject The object to clean. Can be a PSCustomObject, hashtable, array, or null. .PARAMETER AdditionalExcludedProperties Additional property names to exclude in addition to the built-in volatile keys. .EXAMPLE $cleanObj = $obj | Remove-VolatileKeys .EXAMPLE $arr | Remove-VolatileKeys # processes each array element individually via pipeline .EXAMPLE Remove-VolatileKeys -InputObject $obj -AdditionalExcludedProperties @('id', 'description') #> $script:VolatileKeys = @( 'version', 'topicIdentifier', 'certificate', 'createdDateTime', 'lastModifiedDateTime', 'isAssigned', '@odata.context', 'sourceId', 'supportsScopeTags', 'companyCodes', 'isGlobalScript', 'highestAvailableVersion', 'token', 'lastSyncDateTime', 'isReadOnly', 'secretReferenceValueId', 'isEncrypted', 'modifiedDateTime', 'deployedAppCount', 'intunecd_name', 'deviceHealthScriptType', 'scheduledActionsForRule@odata.context', 'scheduledActionConfigurations@odata.context', 'assignments@odata.context' ) function Remove-VolatileKeysFromObject { [CmdletBinding()] param( $obj, [System.Collections.Generic.HashSet[int]]$RecursionStack, [System.Collections.Generic.HashSet[string]]$ExcludedKeys ) if ($null -eq $obj) { return $null } # keep primitives as-is if ($obj.GetType().IsValueType -or $obj -is [string]) { return $obj } if ($null -eq $RecursionStack) { $RecursionStack = [System.Collections.Generic.HashSet[int]]::new() } # guard against circular references in object graphs $objectId = [System.Runtime.CompilerServices.RuntimeHelpers]::GetHashCode($obj) if ($RecursionStack.Contains($objectId)) { return $null } $addedToStack = $RecursionStack.Add($objectId) try { # handle hashtables if ($obj -is [hashtable]) { $cleaned = [ordered]@{} foreach ($key in $obj.Keys) { if (-not $ExcludedKeys.Contains([string]$key)) { $cleaned[$key] = Remove-VolatileKeysFromObject -obj $obj[$key] -RecursionStack $RecursionStack -ExcludedKeys $ExcludedKeys } } return $cleaned } # handle PSCustomObject if ($obj -is [PSCustomObject]) { $cleaned = [PSCustomObject]@{} foreach ($prop in $obj.PSObject.Properties) { if (-not $ExcludedKeys.Contains($prop.Name)) { $cleaned | Add-Member -MemberType NoteProperty -Name $prop.Name -Value (Remove-VolatileKeysFromObject -obj $prop.Value -RecursionStack $RecursionStack -ExcludedKeys $ExcludedKeys) } } return $cleaned } # handle arrays (but not strings or dictionaries) if ($obj -is [System.Collections.IEnumerable] -and $obj -isnot [string] -and $obj -isnot [System.Collections.IDictionary]) { $array = [System.Collections.Generic.List[object]]::new() foreach ($item in $obj) { $array.Add((Remove-VolatileKeysFromObject -obj $item -RecursionStack $RecursionStack -ExcludedKeys $ExcludedKeys)) } return , $array.ToArray() } # primitive/other object types — return as-is return $obj } finally { if ($addedToStack) { [void]$RecursionStack.Remove($objectId) } } } function Remove-VolatileKeys { [CmdletBinding()] param( [Parameter(ValueFromPipeline = $true)] [AllowNull()] $InputObject, [string[]]$AdditionalExcludedProperties = @() ) begin { $collected = [System.Collections.Generic.List[object]]::new() $excludedKeys = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($key in $script:VolatileKeys + $AdditionalExcludedProperties) { if (-not [string]::IsNullOrWhiteSpace($key)) { [void]$excludedKeys.Add($key) } } } process { $stack = [System.Collections.Generic.HashSet[int]]::new() $collected.Add((Remove-VolatileKeysFromObject -obj $InputObject -RecursionStack $stack -ExcludedKeys $excludedKeys)) } end { if ($collected.Count -eq 0) { return } elseif ($collected.Count -eq 1) { return $collected[0] } else { return , $collected.ToArray() } } } Export-ModuleMember -Function Remove-VolatileKeys |