sg-utilities.psm1
# This file contains random Safeguard utilities required by some modules # Nothing is exported from here function Out-SafeguardExceptionIfPossible { [CmdletBinding()] Param( [Parameter(Mandatory=$true,Position=0)] [object]$ThrownException ) if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } if (-not ([System.Management.Automation.PSTypeName]"Ex.SafeguardMethodException").Type) { Add-Type -TypeDefinition @" using System; using System.Runtime.Serialization; namespace Ex { public class SafeguardMethodException : System.Exception { public SafeguardMethodException() : base("Unknown SafeguardMethodException") {} public SafeguardMethodException(int httpCode, string httpMessage, int errorCode, string errorMessage, string errorJson) : base(httpCode + ": " + httpMessage + " -- " + errorCode + ": " + errorMessage) { HttpStatusCode = httpCode; ErrorCode = errorCode; ErrorMessage = errorMessage; ErrorJson = errorJson; } public SafeguardMethodException(string message, Exception innerException) : base(message, innerException) {} protected SafeguardMethodException (SerializationInfo info, StreamingContext context) : base(info, context) {} public int HttpStatusCode { get; set; } public int ErrorCode { get; set; } public string ErrorMessage { get; set; } public string ErrorJson { get; set; } } } "@ } $local:ExceptionToThrow = $ThrownException if ($ThrownException.Response) { Write-Verbose "---Response Status---" if ($ThrownException.Response | Get-Member StatusDescription -MemberType Properties) { $local:StatusDescription = $ThrownException.Response.StatusDescription } elseif ($ThrownException.Response | Get-Member ReasonPhrase -MemberType Properties) { $local:StatusDescription = $ThrownException.Response.ReasonPhrase } Write-Verbose "$([int]$ThrownException.Response.StatusCode) $($local:StatusDescription)" Write-Verbose "---Response Body---" if ($ThrownException.Response | Get-Member GetResponseStream -MemberType Methods) { $local:Stream = $ThrownException.Response.GetResponseStream() $local:Reader = New-Object System.IO.StreamReader($local:Stream) $local:Reader.BaseStream.Position = 0 $local:Reader.DiscardBufferedData() $local:ResponseBody = $local:Reader.ReadToEnd() $local:Reader.Dispose() } elseif ($ThrownException.Response | Get-Member Content -MemberType Properties) { # different properties and methods on net core try { $local:ResponseBody = $ThrownException.Response.Content.ReadAsStringAsync().Result } catch {} } if ($local:ResponseBody) { Write-Verbose $local:ResponseBody try # try/catch is a workaround for this bug in PowerShell: { # https://stackoverflow.com/questions/41272128/does-convertfrom-json-respect-erroraction $local:ResponseObject = (ConvertFrom-Json $local:ResponseBody) # -ErrorAction SilentlyContinue } catch {} if ($local:ResponseObject.Code) # Safeguard error { $local:ExceptionToThrow = (New-Object Ex.SafeguardMethodException -ArgumentList @( [int]$ThrownException.Response.StatusCode, $local:StatusDescription, $local:ResponseObject.Code, $local:ResponseObject.Message, $local:ResponseBody )) } elseif ($local:ResponseObject.error_description) # rSTS error { $local:ExceptionToThrow = (New-Object Ex.SafeguardMethodException -ArgumentList @( [int]$ThrownException.Response.StatusCode, $local:StatusDescription, 0, $local:ResponseObject.error_description, $local:ResponseBody )) } else # ?? { $local:ExceptionToThrow = (New-Object Ex.SafeguardMethodException -ArgumentList @( [int]$ThrownException.Response.StatusCode, $local:StatusDescription, 0, "", $local:ResponseBody )) } } else # ?? { $local:ExceptionToThrow = (New-Object Ex.SafeguardMethodException -ArgumentList @( [int]$ThrownException.Response.StatusCode, $local:StatusDescription, 0, "", "<unable to retrieve response content>" )) } } Write-Verbose "---Exception---" $ThrownException | Format-List * -Force | Out-String | Write-Verbose if ($ThrownException.InnerException) { Write-Verbose "---Inner Exception---" $ThrownException.InnerException | Format-List * -Force | Out-String | Write-Verbose } throw $local:ExceptionToThrow } function Test-SafeguardMinVersionInternal { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$true)] [ValidatePattern("^\d+\.\d+")] [string]$MinVersion ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } [int]$local:Major,[int]$local:Minor = $MinVersion.split(".") $local:CurrentVersion = (Invoke-SafeguardMethod -Anonymous -Appliance $Appliance -Insecure:$Insecure Appliance GET Version -RetryVersion 2) if (([int]$local:CurrentVersion.Major) -gt $local:Major ` -or (([int]$local:CurrentVersion.Major) -eq $local:Major -and ([int]$local:CurrentVersion.Minor) -ge $local:Minor)) { $true } else { $false } } function Wait-ForSafeguardStatus { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$false)] [int]$Timeout = 600, [Parameter(Mandatory=$true)] [string]$DesiredStatus ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } Start-Sleep 5 # up front wait to solve new transition timing issues $local:StartTime = (Get-Date) $local:Status = "Unreachable" $local:TimeElapsed = 10 do { Write-Progress -Activity "Waiting for $DesiredStatus Status" -Status "Current: $($local:Status)" -PercentComplete (($local:TimeElapsed / $Timeout) * 100) try { $local:Status = (Get-SafeguardStatus -Appliance $Appliance -Insecure:$Insecure).ApplianceCurrentState } catch {} Start-Sleep 2 $local:TimeElapsed = (((Get-Date) - $local:StartTime).TotalSeconds) if ($local:TimeElapsed -gt $Timeout) { throw "Timed out waiting for $DesiredStatus Status, timeout was $Timeout seconds" } } until ($local:Status -ieq $DesiredStatus) Write-Progress -Activity "Waiting for $DesiredStatus Status" -Status "Current: $($local:Status)" -PercentComplete 100 } function Wait-ForSafeguardOnlineStatus { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$false)] [int]$Timeout = 600 ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } Wait-ForSafeguardStatus -Appliance $Appliance -Insecure:$Insecure -Timeout $Timeout -DesiredStatus "Online" Write-Host "Safeguard is back online." } function Wait-ForSessionModuleState { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [object]$AccessToken, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$false,Position=0)] [string]$ContainerState, [Parameter(Mandatory=$false,Position=1)] [string]$ModuleState, [Parameter(Mandatory=$false,Position=2)] [int]$Timeout = 180 ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } $local:StartTime = (Get-Date) if ($ContainerState -and $ModuleState) { $local:DesiredStatus = "$ContainerState, $ModuleState" } elseif ($ContainerState) { $local:DesiredStatus = "$ContainerState, Any" } elseif ($ModuleState) { $local:DesiredStatus = "Any, $ModuleState" } else { $local:DesiredStatus = "Any, Any" } $local:StatusString = "Unreachable, Unreachable" $local:TimeElapsed = 4 do { Write-Progress -Activity "Waiting for Session Module Status: $($local:DesiredStatus)" -Status "Current: $($local:StatusString)" -PercentComplete (($local:TimeElapsed / $Timeout) * 100) try { $local:StateFound = $false $local:Status = (Get-SafeguardSessionContainerStatus -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure) $local:StatusString = "$($local:Status.SessionContainerState), $($local:Status.SessionModuleState)" if ($ContainerState -and $ModuleState) { $local:StateFound = ($local:Status.SessionContainerState -eq $ContainerState -and $local:Status.SessionModuleState -eq $ModuleState) } elseif ($ContainerState) { $local:StateFound = ($local:Status.SessionContainerState -eq $ContainerState) } elseif ($ModuleState) { $local:StateFound = ($local:Status.SessionModuleState -eq $ModuleState) } else { $local:StateFound = $true } } catch { $local:StatusString = "Unreachable, Unreachable" } Start-Sleep 2 $local:TimeElapsed = (((Get-Date) - $local:StartTime).TotalSeconds) if ($local:TimeElapsed -gt $Timeout) { throw "Timed out waiting for Session Module Status, timeout was $Timeout seconds" } } until ($local:StateFound) Write-Progress -Activity "Waiting for Session Module Status: $($local:DesiredStatus)" -Status "Current: $($local:StatusString)" -PercentComplete 100 } function Wait-ForClusterOperation { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [object]$AccessToken, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$false)] [int]$Timeout = 600 ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } $local:StartTime = (Get-Date) $local:Status = "Unknown" $local:TimeElapsed = 10 do { Write-Progress -Activity "Waiting for cluster operation to finish" -Status "Cluster Operation: $($local:Status)" -PercentComplete (($local:TimeElapsed / $Timeout) * 100) try { $local:Status = (Invoke-SafeguardMethod -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure Core GET ClusterStatus).Operation } catch {} Start-Sleep 2 $local:TimeElapsed = (((Get-Date) - $local:StartTime).TotalSeconds) if ($local:TimeElapsed -gt $Timeout) { throw "Timed out waiting for cluster operation to finish, timeout was $Timeout seconds" } } until ($local:Status -eq "None") Write-Progress -Activity "Waiting for cluster operation to finish" -Status "Current: $($local:Status)" -PercentComplete 100 Write-Host "Safeguard cluster operation completed...~$($local:TimeElapsed) seconds" } function Wait-ForPatchDistribution { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [object]$AccessToken, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$false)] [int]$Timeout = 600 ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } $local:TimeElapsed = 0 if ((Invoke-SafeguardMethod -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure Core GET "Cluster/Members" ` -RetryUrl "ClusterMembers").Count -gt 1) { $local:StartTime = (Get-Date) $local:Status = "Unknown" $local:TimeElapsed = 10 do { Write-Progress -Activity "Waiting for patch distribution" -Status "Cluster Operation: $($local:Status)" -PercentComplete (($local:TimeElapsed / $Timeout) * 100) try { $local:Members = (Invoke-SafeguardMethod -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure Core GET ClusterStatus/PatchDistribution).Members $local:StagingStatuses = ($local:Members.StagingStatus | Sort-Object) $local:Status = $local:StagingStatuses -join "," } catch {} Start-Sleep 2 $local:TimeElapsed = (((Get-Date) - $local:StartTime).TotalSeconds) if ($local:TimeElapsed -gt $Timeout) { throw "Timed out waiting for cluster operation to finish, timeout was $Timeout seconds" } } until (@($local:StagingStatuses | Select-Object -Unique).Count -eq 1 -and $local:StagingStatuses[0] -eq "Staged") Write-Progress -Activity "Waiting for patch distribution" -Status "Current: $($local:Status)" -PercentComplete 100 } Write-Host "Safeguard patch distribution completed...~$($local:TimeElapsed) seconds" } function Resolve-SafeguardSystemId { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [object]$AccessToken, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$true,Position=0)] [object]$System ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } try { Import-Module -Name "$PSScriptRoot\assets.psm1" -Scope Local Resolve-SafeguardAssetId -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure $System } catch { Write-Verbose "Unable to resolve to asset ID, trying directories" try { Import-Module -Name "$PSScriptRoot\directories.psm1" -Scope Local Resolve-SafeguardDirectoryId -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure $System } catch { Write-Verbose "Unable to resolve to directory ID" throw "Cannot determine system ID for '$System'" } } } function Resolve-SafeguardAccountIdWithoutSystemId { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [object]$AccessToken, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$true,Position=0)] [object]$Account ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } try { Import-Module -Name "$PSScriptRoot\assets.psm1" -Scope Local Resolve-SafeguardAssetAccountId -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure $Account } catch { Write-Verbose "Unable to resolve to asset account ID, trying directories" try { Import-Module -Name "$PSScriptRoot\directories.psm1" -Scope Local Resolve-SafeguardDirectoryAccountId -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure $Account } catch { Write-Verbose "Unable to resolve to directory account ID" throw "Cannot determine account ID for '$Account'" } } } function Resolve-SafeguardAccountIdWithSystemId { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [object]$AccessToken, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$true,Position=0)] [int]$SystemId, [Parameter(Mandatory=$true,Position=1)] [object]$Account ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } try { Import-Module -Name "$PSScriptRoot\assets.psm1" -Scope Local Resolve-SafeguardAssetAccountId -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure -AssetId $SystemId $Account } catch { Write-Verbose "Unable to resolve to asset account ID, trying directories" try { Import-Module -Name "$PSScriptRoot\directories.psm1" -Scope Local Resolve-SafeguardDirectoryAccountId -Appliance $Appliance -AccessToken $AccessToken -Insecure:$Insecure -DirectoryId $SystemId $Account } catch { Write-Verbose "Unable to resolve to directory account ID" throw "Cannot determine system ID for '$System'" } } } function Resolve-ReasonCodeId { [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Appliance, [Parameter(Mandatory=$false)] [object]$AccessToken, [Parameter(Mandatory=$false)] [switch]$Insecure, [Parameter(Mandatory=$true,Position=0)] [object]$ReasonCode ) $ErrorActionPreference = "Stop" if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") } if (-not ($ReasonCode -as [int])) { try { $local:ReasonCodes = (Invoke-SafeguardMethod -AccessToken $AccessToken -Appliance $Appliance -Insecure:$Insecure ` Core GET ReasonCodes -Parameters @{ filter = "Name ieq '$ReasonCode'" }) } catch { Write-Verbose $_ Write-Verbose "Caught exception with ieq filter, trying with q parameter" $local:ReasonCodes = (Invoke-SafeguardMethod -AccessToken $AccessToken -Appliance $Appliance -Insecure:$Insecure ` Core GET ReasonCodes -Parameters @{ q = $ReasonCode }) } if (-not $local:ReasonCodes) { throw "Unable to find reason code registration matching '$ReasonCode'" } if ($local:ReasonCodes.Count -ne 1) { throw "Found $($local:ReasonCodes.Count) reason code registration matching '$ReasonCode'" } $local:ReasonCodes[0].Id } else { $ReasonCode } } # Helper function for formatting dates (useful for passing to audit log query parameters) function Format-DateTimeAsString { [CmdletBinding()] Param( [Parameter(Mandatory=$true,Position=0)] [DateTime]$DateTime ) "$($DateTime.ToString("yyyy-MM-ddTHH:mm:sszzz"))" } # Helper function to get begin time for audit log function Get-EntireAuditLogStartDateAsString { [CmdletBinding()] Param( ) Format-DateTimeAsString ((Get-Date -Month 1 -Day 1 -Year 2017 -Hour 0 -Minute 0 -Second 0).ToUniversalTime()) } |