Files
2026-08-02 19:43:02 -04:00

173 lines
10 KiB
PowerShell

#region Remove-RackpadOrphanedObject
Function Remove-RackpadOrphanedObject
{
<#
.SYNOPSIS
Deletes objects that this solution previously created and that are no longer present within the desired state.
.DESCRIPTION
Pruning is deliberately narrow. Only objects recorded within the managed state document are eligible, which means that any
object created by hand within the Rackpad user interface is never touched.
An entry becomes eligible when it exists within the state document but was not registered as observed during the current run.
Deletion is performed in the supplied order so that dependent objects are removed before the objects that they reference.
A status code of 404 is treated as success because the target object has already been removed, which keeps the operation
idempotent.
.PARAMETER Session
The session object produced by Connect-RackpadInstance.
.PARAMETER ManagedState
The state object produced by Get-RackpadManagedState.
.PARAMETER PruneOrder
An ordered dictionary mapping entity type to collection route. Entity types are processed in the order supplied.
.EXAMPLE
$PruneOrder = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$PruneOrder.'ip-assignment' = 'ip-assignments'
$PruneOrder.'port-link' = 'port-links'
$PruneOrder.'port' = 'ports'
$PruneResultList = Remove-RackpadOrphanedObject -Session $RackpadSession -ManagedState $ManagedState -PruneOrder $PruneOrder
.NOTES
Author: Grace Solutions
Version: 2026.08.02.0000
#>
[CmdletBinding(SupportsShouldProcess=$True)]
Param
(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[PSObject]$Session,
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[PSObject]$ManagedState,
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[System.Collections.Specialized.OrderedDictionary]$PruneOrder
)
Try
{
$PruneResultList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
#region Build the list of orphaned entries in the supplied deletion order
$OrphanedEntryList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
ForEach ($PruneOrderEntry In $PruneOrder.GetEnumerator())
{
$EntityTypeKey = "$($PruneOrderEntry.Key)".ToLowerInvariant()
Switch ($ManagedState.EntryTable.ContainsKey($EntityTypeKey))
{
{($_ -eq $True)}
{
$ObservedKeySet = New-Object -TypeName 'System.Collections.Generic.HashSet[System.String]'
Switch ($ManagedState.ObservedTable.ContainsKey($EntityTypeKey))
{
{($_ -eq $True)}
{
$ObservedKeySet = $ManagedState.ObservedTable[$EntityTypeKey]
}
}
ForEach ($RecordedEntry In $ManagedState.EntryTable[$EntityTypeKey].GetEnumerator())
{
Switch ($ObservedKeySet.Contains("$($RecordedEntry.Key)"))
{
{($_ -eq $False)}
{
$OrphanedEntryProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OrphanedEntryProperties.EntityType = "$($EntityTypeKey)"
$OrphanedEntryProperties.CollectionRoute = "$($PruneOrderEntry.Value)"
$OrphanedEntryProperties.NaturalKey = "$($RecordedEntry.Key)"
$OrphanedEntryProperties.Identifier = "$($RecordedEntry.Value)"
$OrphanedEntryList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($OrphanedEntryProperties)))
}
}
}
}
}
}
#endregion
Write-Verbose -Message "[$([System.DateTime]::UtcNow.ToString('yyyy/MM/dd HH:mm:ss.FFF'))] - [INFO] - $($OrphanedEntryList.Count) previously created object(s) are eligible for pruning."
#region Delete each orphaned entry
$OrphanedEntryListCount = $OrphanedEntryList.Count
For ($OrphanedEntryIndex = 0; $OrphanedEntryIndex -lt $OrphanedEntryListCount; $OrphanedEntryIndex++)
{
$OrphanedEntry = $OrphanedEntryList[$OrphanedEntryIndex]
$WriteProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$WriteProgressParameters.Activity = 'Pruning previously created Rackpad objects'
$WriteProgressParameters.Status = "Removing $($OrphanedEntry.EntityType) `"$($OrphanedEntry.NaturalKey)`" ($($OrphanedEntryIndex + 1) of $($OrphanedEntryListCount))"
$WriteProgressParameters.PercentComplete = [System.Math]::Round((($OrphanedEntryIndex + 1) / $OrphanedEntryListCount) * 100, 2)
$WriteProgressParameters.Id = 30
$Null = Write-Progress @WriteProgressParameters
$DeletionRoute = "$($OrphanedEntry.CollectionRoute)/$($OrphanedEntry.Identifier)"
$PruneResultProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$PruneResultProperties.EntityType = "$($OrphanedEntry.EntityType)"
$PruneResultProperties.NaturalKey = "$($OrphanedEntry.NaturalKey)"
$PruneResultProperties.Identifier = "$($OrphanedEntry.Identifier)"
$PruneResultProperties.Action = 'Deleted'
$PruneResultProperties.ErrorMessage = $Null
Switch ($PSCmdlet.ShouldProcess("$($OrphanedEntry.EntityType) `"$($OrphanedEntry.NaturalKey)`"", "Delete using DELETE /api/$($DeletionRoute)"))
{
{($_ -eq $True)}
{
$DeletionResponse = Invoke-RackpadRequest -Session ($Session) -Route ($DeletionRoute) -Method 'DELETE' -AcceptableStatusCodeList @('404')
Switch ($DeletionResponse.IsSuccess)
{
{($_ -eq $True)}
{
$Null = $ManagedState.EntryTable["$($OrphanedEntry.EntityType)"].Remove("$($OrphanedEntry.NaturalKey)")
Write-Verbose -Message "[$([System.DateTime]::UtcNow.ToString('yyyy/MM/dd HH:mm:ss.FFF'))] - [INFO] - Pruned $($OrphanedEntry.EntityType) `"$($OrphanedEntry.NaturalKey)`". [Identifier: $($OrphanedEntry.Identifier)]"
}
{($_ -eq $False)}
{
$PruneResultProperties.Action = 'Failed'
$PruneResultProperties.ErrorMessage = "$($DeletionResponse.StatusDescription) $($DeletionResponse.ErrorMessage)".Trim()
Write-Warning -Message "[$([System.DateTime]::UtcNow.ToString('yyyy/MM/dd HH:mm:ss.FFF'))] - [WARNING] - Failed to prune $($OrphanedEntry.EntityType) `"$($OrphanedEntry.NaturalKey)`". [Status Code: $($DeletionResponse.StatusCode)] [$($PruneResultProperties.ErrorMessage)]"
}
}
}
{($_ -eq $False)}
{
$PruneResultProperties.Action = 'WhatIf'
}
}
$PruneResultList.Add((New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($PruneResultProperties)))
}
$Null = Write-Progress -Activity 'Pruning previously created Rackpad objects' -Id 30 -Completed
#endregion
Write-Output -InputObject ($PruneResultList)
}
Catch
{
Throw
}
}
#endregion