functions/image/Remove-PSDCImage.ps1
function Remove-PSDCImage { <# .SYNOPSIS Remove-PSDCImage removes one or more images .DESCRIPTION The command will remove an image from PSDatabaseClone. It will also remove all the clones associated with it on the hosts. .PARAMETER ImageID Remove images based on the image id .PARAMETER ImageName Remove images based on the image name .PARAMETER ImageLocation Location of the image as it's saved in the database or can be seen on the file system. .PARAMETER Database Remove images based on the database .PARAMETER ExcludeDatabase Filter the images based on the excluded database .PARAMETER Unused Remove images not used by any clones .PARAMETER Keep When used with the Unused parameter, sets the number of most recent images to keep .PARAMETER PSDCSqlCredential Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted. This works similar as SqlCredential but is only meant for authentication to the PSDatabaseClone database server and database. .PARAMETER Credential Allows you to login to servers using Windows Auth/Integrated/Trusted. To use: $scred = Get-Credential, then pass $scred object to the -Credential parameter. .PARAMETER Force Forcefully remove the items. .PARAMETER InputObject The input object that is used for pipeline use .PARAMETER EnableException By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .NOTES Author: Sander Stad (@sqlstad, sqlstad.nl) Website: https://psdatabaseclone.org Copyright: (C) Sander Stad, sander@sqlstad.nl License: MIT https://opensource.org/licenses/MIT .LINK https://psdatabaseclone.org/ .EXAMPLE Remove-PSDCImage -ImageLocation "\\server1\images\DB1_20180703193345.vhdx" Remove an image .EXAMPLE Get-PSDCImage -Database DB1 | Remove-PSDCImage Remove all images and clones based on database DB1 #> [CmdLetBinding(DefaultParameterSetName = "ImageLocation", SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( [int[]]$ImageID, [string[]]$ImageName, [parameter(ParameterSetName = "ImageLocation")] [string[]]$ImageLocation, [string[]]$Database, [string[]]$ExcludeDatabase, [switch]$Unused, [int]$Keep = 0, [PSCredential]$PSDCSqlCredential, [PSCredential]$Credential, [switch]$Force, [parameter(ValueFromPipeline = $true, ParameterSetName = "Image")] [object[]]$InputObject, [switch]$EnableException ) begin { # Check if the console is run in Administrator mode if ( -not (Test-PSDCElevated) ) { Stop-PSFFunction -Message "Module requires elevation. Please run the console in Administrator mode" } # Check if the setup has ran if (-not (Get-PSFConfigValue -FullName psdatabaseclone.setup.status)) { Stop-PSFFunction -Message "The module setup has NOT yet successfully run. Please run 'Set-PSDCConfiguration'" return } # Get the information store $informationStore = Get-PSFConfigValue -FullName psdatabaseclone.informationstore.mode if ($informationStore -eq 'SQL') { # Get the module configurations $pdcSqlInstance = Get-PSFConfigValue -FullName psdatabaseclone.database.Server $pdcDatabase = Get-PSFConfigValue -FullName psdatabaseclone.database.name if (-not $PSDCSqlCredential) { $pdcCredential = Get-PSFConfigValue -FullName psdatabaseclone.informationstore.credential -Fallback $null } else { $pdcCredential = $PSDCSqlCredential } # Test the module database setup try { Test-PSDCConfiguration -SqlCredential $pdcCredential -EnableException } catch { Stop-PSFFunction -Message "Something is wrong in the module configuration" -ErrorRecord $_ -Continue } } # Get all the items $items = @() $clones = @() $items += Get-PSDCImage $clones += Get-PSDCClone if ($ImageID) { Write-PSFMessage -Message "Filtering image ids" -Level Verbose $items = $items | Where-Object { $_.ImageID -in $ImageID } } if ($ImageName) { Write-PSFMessage -Message "Filtering image name" -Level Verbose $items = $items | Where-Object { $_.ImageName -in $ImageName } } if ($ImageLocation) { Write-PSFMessage -Message "Filtering image locations" -Level Verbose $items = $items | Where-Object { $_.ImageLocation -in $ImageLocation } } if ($Database) { Write-PSFMessage -Message "Filtering databases" -Level Verbose $items = $items | Where-Object { $_.DatabaseName -in $Database } } if ($ExcludeDatabase) { Write-PSFMessage -Message "Filtering excluded databases" -Level Verbose $items = $items | Where-Object { $_.DatabaseName -notin $Database } } if ($Unused) { Write-PSFMessage -Message "Filtering images with associated clones, keeping latest $Keep images." -Level Verbose $images = $items foreach ($item in $items) { if ($clones.ImageID -contains $item.ImageID) { $images = $images | Where-Object { $_.ImageID -ne $item.ImageID } } } $items = $images | Sort-Object -Property CreatedOn -Descending | Select-Object -Skip $Keep } # Append the items $InputObject += $items } process { # Test if there are any errors if (Test-PSFFunctionInterrupt) { return } Write-PSFMessage -Message "Started removing database images" -Level Verbose # Group the objects to make it easier to go through $images = $InputObject | Group-Object ImageID foreach ($image in $images) { # Loop through each of the results foreach ($item in $image.Group) { # Make up the data from the network path try { [uri]$uri = New-Object System.Uri($item.ImageLocation) $uriHost = $uri.Host $item.ImageLocation } catch { Stop-PSFFunction -Message "The image location $ImageNetworkPath is not valid" -ErrorRecord $_ -Target $ImageNetworkPath return } # Setup the computer object $computer = [PsfComputer]$uriHost if (-not $computer.IsLocalhost) { # Get the result for the remote test $resultPSRemote = Test-PSDCRemoting -ComputerName $uriHost -Credential $Credential # Check the result if ($resultPSRemote.Result) { $command = [scriptblock]::Create("Import-Module PSDatabaseClone -Force") try { Invoke-PSFCommand -ComputerName $computer -ScriptBlock $command -Credential $Credential } catch { Stop-PSFFunction -Message "Couldn't import module remotely" -Target $command return } } else { Stop-PSFFunction -Message "Couldn't connect to host remotely.`nVerify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from this computer" -Target $resultPSRemote -Continue } } # Get the clones associated with the image $results = @() $results = Get-PSDCClone -ImageID $item.ImageID # Check the results if ($results.Count -ge 1) { # Loop through the results foreach ($result in $results) { if ($PSCmdlet.ShouldProcess($item.CloneID, "Removing clone $($result.CloneLocation) from $($result.HostName)")) { # Remove the clones for the host try { Write-PSFMessage -Message "Removing clones for host $($result.HostName) and database $($result.DatabaseName)" -Level Verbose Remove-PSDCClone -HostName $result.HostName -Database $result.DatabaseName -PSDCSqlCredential $pdcCredential -Credential $Credential -Confirm:$false } catch { Stop-PSFFunction -Message "Couldn't remove clones from host $($result.HostName)" -ErrorRecord $_ -Target $result -Continue } } } } else { Write-PSFMessage -Message "No clones were found created with image $($image.Name)" -Level Verbose } if ($PSCmdlet.ShouldProcess($item.ImageLocation, "Removing image from system")) { # Remove the image from the file system try { if ($computer.IsLocalhost) { if (Test-Path -Path $item.ImageLocation -Credential $Credential) { Write-PSFMessage -Message "Removing image '$($item.ImageLocation)' from file system" -Level Verbose $null = Remove-Item -Path $item.ImageLocation -Credential $Credential -Force:$Force } else { Write-PSFMessage -Message "Couldn't find image $($item.ImageLocation)" -Level Verbose } } else { $command = [scriptblock]::Create("Test-Path -Path '$($item.ImageLocation)'") $result = Invoke-PSFCommand -ComputerName $computer -ScriptBlock $command -Credential $Credential if ($result) { $command = [scriptblock]::Create("Remove-Item -Path '$($item.ImageLocation)' -Force") $result = Invoke-PSFCommand -ComputerName $computer -ScriptBlock $command -Credential $Credential } else { Write-PSFMessage -Message "Couldn't find image '$($item.ImageLocation)'" -Level Verbose } } } catch { Stop-PSFFunction -Message "Couldn't remove image '$($item.ImageLocation)' from file system" -ErrorRecord $_ -Target $result } } if ($PSCmdlet.ShouldProcess($item.ImageLocation, "Removing image from database")) { if ($informationStore -eq 'SQL') { # Remove the image from the database try { $query = "DELETE FROM dbo.Image WHERE ImageID = $($item.ImageID)" $null = Invoke-DbaQuery -SqlInstance $pdcSqlInstance -SqlCredential $pdcCredential -Database $pdcDatabase -Query $query } catch { Stop-PSFFunction -Message "Couldn't remove image '$($item.ImageLocation)' from database" -ErrorRecord $_ -Target $query } } elseif ($informationStore -eq 'File') { $imageData = Get-PSDCImage | Where-Object { $_.ImageID -ne $item.ImageID } # Set the image file $jsonImageFile = "PSDCJSONFolder:\images.json" # Convert the data back to JSON if ($imageData) { $imageData | ConvertTo-Json | Set-Content $jsonImageFile } else { Clear-Content -Path $jsonImageFile } } } } # End for each item in group } # End for each image } # End process end { # Test if there are any errors if (Test-PSFFunctionInterrupt) { return } Write-PSFMessage -Message "Finished removing database image(s)" -Level Verbose } } |