PoshIssues.psm1
enum IssueFixStatus { Ready Pending Complete Error Canceled } enum IssueCheckStatus { Enabled Disabled } <# .SYNOPSIS Creates a new IssueFix object with the passed parameters .DESCRIPTION Creates a new IssueFix object with the passed parameters, using defaults as needed. .PARAMETER FixCommand A ScriptBlock to be added to that fix that will be executed to fix the issue. .PARAMETER FixCommandString A String that can be converted to a ScriptBlock to be added to that fix that will be executed to fix the issue. .PARAMETER FixDescription A user friendly description of what the fix does, prefereble specific to this instance. .PARAMETER CheckName Name of the issue check that generated this fix. .PARAMETER Status The status of this fix. See IssueFixStatus enum. Default is Ready. .PARAMETER NotificationCount Set the number of times notices is sent about this fix. Usefull for scheduled notifications of pending fixes. Each time a notificaton is sent for a fix the notificationCount is decremented by one. By default, only fixes with a notification count greater then 0 are sent. This allows for control over how often a fix is notified about. Default is 10000. .PARAMETER SequenceNumber Fix sort order. Default is 1. .INPUTS ScriptBlock representing the script that will be invoked by the fix String representing the script that will be invoked by the fix .OUTPUTS IssueFix The fix object(s) created by the cmdlet #> function New-IssueFix { [CmdletBinding(SupportsShouldProcess=$false,DefaultParameterSetName="Block")] [OutputType("PoshIssues.Fix")] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false, ParameterSetName="Block")] [ScriptBlock] $FixCommand, [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false, ParameterSetName="String")] [String] $FixCommandString, [Parameter(Mandatory=$false,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [String] $FixDescription = "", [Parameter(Mandatory=$false,Position=2,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [String] $CheckName = "", [Parameter(Mandatory=$false,Position=3,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [IssueFixStatus] $Status = 0, [Parameter(Mandatory=$false,Position=4,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [System.Int64] $NotificationCount = 10000, [Parameter(Mandatory=$false,Position=5,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [System.Int64] $SequenceNumber = 1 ) Process { $_return = New-Object -TypeName PSObject $_return.PSObject.TypeNames.Insert(0,'PoshIssues.Fix') If ($FixCommandString) { $FixCommand = [scriptblock]::Create($FixCommandString) } Add-Member -InputObject $_return -MemberType NoteProperty -Name "fixCommand" -Value $FixCommand Add-Member -InputObject $_return -MemberType NoteProperty -Name "fixDescription" -Value $FixDescription Add-Member -InputObject $_return -MemberType NoteProperty -Name "checkName" -Value $CheckName Add-Member -InputObject $_return -MemberType NoteProperty -Name "_status" -Value ([Int64] $Status) Add-Member -InputObject $_return -MemberType NoteProperty -Name "notificationCount" -Value ([Int64] $NotificationCount) Add-Member -InputObject $_return -MemberType NoteProperty -Name "sequenceNumber" -Value ([Int64] $SequenceNumber) Add-Member -InputObject $_return -MemberType NoteProperty -Name "creationDateTime" -Value ([DateTime] (Get-Date)) Add-Member -InputObject $_return -MemberType NoteProperty -Name "statusDateTime" -Value ([DateTime] (Get-Date)) #Calculate iD $StringBuilder = New-Object System.Text.StringBuilder [System.Security.Cryptography.HashAlgorithm]::Create('MD5').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($FixCommand.ToString())) | ForEach-Object{ [Void]$StringBuilder.Append($_.ToString("x2")) } Add-Member -InputObject $_return -MemberType NoteProperty -Name "iD" -Value $StringBuilder.ToString() Add-Member -InputObject $_return -MemberType ScriptProperty -Name "status" -Value ` { #Get return [IssueFixStatus]::([enum]::getValues([IssueFixStatus]) | Where-Object value__ -eq $this._status) } ` { #Set param ( [IssueFixStatus] $status ) $this._status = ([IssueFixStatus]::$Status).value__ } Write-Output $_return } } <# .SYNOPSIS Writes (saves) an IssueFix object to the file system as a JSON file. .DESCRIPTION Writes (saves) an IssueFix object to the file system as a JSON file. Supports saving to a specific Path or to a Database folder structure. .PARAMETER Fix IssueFix object(s), typically passed via the pipeline, to be written to the file system as a JSON object. .PARAMETER DatabasePath A string path representing the folder to use as a simple database. The IssueFix files will be saved as JSON files using their iD value into a Fixes folder. Folders will be created as needed. If the IssueFix has already been saved once, the cmdlet can get the value from the pipeline object. .PARAMETER Path A string path representing the path and file name to save the JSON content as. If the IssueFix has already been saved once, the cmdlet can get the value from the pipeline object. .PARAMETER NoClobber Switch to prevent an existing file from being overwritten, otherwise by default, the existing file is overwritten. .INPUTS IssueFix .OUTPUTS IssueFix The fix object(s) passed through the cmdlet #> function Write-IssueFix { [CmdletBinding(SupportsShouldProcess=$false,DefaultParameterSetName="DatabasePath")] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="DatabasePath")] [String] $DatabasePath, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="Path")] [String] $Path, [Parameter(Mandatory=$false,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false)] [Switch] $NoClobber ) Process { #Create an object to save as JSON $_fix = @{ "id" = $Fix.id; "sequenceNumber" = $Fix.sequenceNumber; "checkName" = $Fix.checkName; "fixCommandBase64" = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($Fix.fixCommand)); "fixDescription" = $Fix.fixDescription; "fixResults" = $Fix.fixResults; "statusInt" = $Fix._status; "notificationCount" = $Fix.notificationCount; "creationDateTime" = $Fix.creationDateTime; "statusDateTime" = $Fix.statusDateTime } $_path = "" If ($DatabasePath) { #Save to database path, overwriting only if Force $_fix.Add("databasePath", $DatabasePath) Add-Member -InputObject $Fix -MemberType NoteProperty -Name "databasePath" -Value $DatabasePath -Force if (!(Test-Path $DatabasePath)) { New-Item $DatabasePath -ItemType Directory } if (!(Test-Path "$($DatabasePath)\Fixes")) { New-Item "$($DatabasePath)\Fixes" -ItemType Directory } $_path = "$($DatabasePath)\Fixes\$($Fix.id).json" } else { #Save to path, overwriting only if Force $_fix.Add("path", $Path) Add-Member -InputObject $Fix -MemberType NoteProperty -Name "path" -Value $Path -Force $_path = $Path } #If the file exists AND NoClobber is true not write the file if ((Test-Path $_path) -and ($NoClobber)) { Write-Verbose "JSON file already exists at '$_path' and NoClobber is set." } else { $_json = ConvertTo-Json -InputObject $_fix Out-File -FilePath $_path -Force:$true -InputObject $_json Write-Verbose "JSON saved to '$_path'." } Write-Output $Fix } } <# .SYNOPSIS Removes (deletes) an IssueFix object from the file system. .DESCRIPTION Removes (deletes) an IssueFix object from the file system. Can use the path information from the fix if present and passed through pipeline. Just performs a remove-item. .PARAMETER Fix IssueFix object(s), typically passed via the pipeline, to be removed from the file system. .PARAMETER DatabasePath A string path representing the folder to use as a simple database. The IssueFix files will be remvoed using their iD value from a Fixes folder. Folders will be created as needed. If the IssueFix has already been saved once, the cmdlet can get the value from the pipeline object. .PARAMETER Path A string path representing the path and file name to remove. If the IssueFix has already been saved once, the cmdlet can get the value from the pipeline object. .INPUTS IssueFix .OUTPUTS IssueFix The fix object(s) passed through the cmdlet #> function Remove-IssueFix { [CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName="DatabasePath")] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="DatabasePath")] [String] $DatabasePath, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="Path")] [String] $Path ) Process { $_path = "" #Calculate path If ($DatabasePath) { $_path = "$($DatabasePath)\Fixes\$($Fix.id).json" } else { $_path = $Path } if ($_path -eq "") { Write-Error "Unable to determine path to saved Fix" } else { if (Test-Path $_path) { if ($PSCmdlet.ShouldProcess("Remove $($Fix.fixDescription) from file/database?")) { #Delete the JSON file Write-Verbose "Removed $_path" Remove-Item $_path } } else { Write-Warning "Saved Fix JSON file not found at $_path" } } Write-Output $Fix } } <# .SYNOPSIS Archives (moves) an IssueFix object in the file system. .DESCRIPTION Archives (moves) an IssueFix object in the file system. File must have previousely been written to file system. Can use the path information from the fix if present and passed through pipeline. Just performas a move-item. .PARAMETER Fix IssueFix object(s), typically passed via the pipeline, to be moved to archive location. .PARAMETER DatabasePath A string path representing the folder to use as a simple database. The IssueFix files will be moved to an Archive folder under the Fixes folder and the filename will be appended with the current datatime. Folders will be created as needed. If the IssueFix has already been saved once, the cmdlet can get the value from the pipeline object. .PARAMETER Path A string path representing the path and file name to current JSON file. If the IssueFix has already been saved once, the cmdlet can get the value from the pipeline object. .PARAMETER Path A string path representing the path and file name to move the file to. .PARAMETER Force Switch to force overwritting any existing file. .INPUTS IssueFix .OUTPUTS IssueFix The fix object(s) passed through the cmdlet #> function Archive-IssueFix { [CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName="DatabasePath")] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="DatabasePath")] [String] $DatabasePath, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="Path")] [String] $Path, [Parameter(Mandatory=$true,Position=2,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="Path")] [String] $ArchivePath, [Parameter(Mandatory=$false,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false)] [Switch] $Force ) Process { $_path = "" $_destinationPath = "" #Calculate path If ($DatabasePath) { $_path = "$($DatabasePath)\Fixes\$($Fix.id).json" $_destinationPath = "$($DatabasePath)\Fixes\Archive\$($Fix.id)_$(Get-Date -Format yyyyMMddHHmmss).json" if (!(Test-Path $DatabasePath)) { New-Item $DatabasePath -ItemType Directory } if (!(Test-Path "$($DatabasePath)\Fixes")) { New-Item "$($DatabasePath)\Fixes" -ItemType Directory } if (!(Test-Path "$($DatabasePath)\Fixes\Archive")) { New-Item "$($DatabasePath)\Fixes\Archive" -ItemType Directory } } else { $_path = $Path $_destinationPath = $ArchivePath } if ($_path -eq "") { Write-Error "Unable to determine path to saved Fix" } else { if (Test-Path $_path) { if ($PSCmdlet.ShouldProcess("Move $($Fix.fixDescription) to $_destinationPath?")) { #Move the JSON file Write-Verbose "Moved $_path to $_destinationPath" Move-Item -Path $_path -Destination $_destinationPath -Force:$Force } } else { Write-Warning "Saved Fix JSON file not found at $_path" } } Write-Output $Fix } } function Read-IssueFix { [CmdletBinding(SupportsShouldProcess=$true,DefaultParameterSetName="DatabasePath")] Param( [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="DatabasePath")] [String] $DatabasePath, [Parameter(Mandatory=$false,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false,ParameterSetName="DatabasePath")] [Switch] $IncludeArchive, [Parameter(Mandatory=$false,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false,ParameterSetName="DatabasePath")] [Switch] $OnlyArchive, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, ParameterSetName="Path")] [String] $Path ) Process { $items = @() if ($Path) { $items = Get-Item $Path } elseif ($DatabasePath) { $_folder = "$($DatabasePath)\Fixes" if ($OnlyArchive) { $_folder = "$($_folder)\Archive" } if ($IncludeArchive) { $_recurse = $true } else { $_recurse = $false } $items = Get-ChildItem -Path $_folder -Recurse:$_recurse -Filter "*.json" } $items | Get-Content -Raw | ConvertFrom-Json | ForEach-Object { #Take the object from the JSON import and build a fix object $_fix = $_ $_return = New-Object -TypeName PSObject $_return.PSObject.TypeNames.Insert(0,'PoshIssues.Fix') [ScriptBlock] $_script = [ScriptBlock]::Create([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($_fix.fixCommandBase64))) Add-Member -InputObject $_return -MemberType NoteProperty -Name "fixCommand" -Value $_script Add-Member -InputObject $_return -MemberType NoteProperty -Name "fixDescription" -Value $_fix.fixDescription Add-Member -InputObject $_return -MemberType NoteProperty -Name "checkName" -Value $_fix.checkName Add-Member -InputObject $_return -MemberType NoteProperty -Name "_status" -Value ([Int64] $_fix.statusInt) Add-Member -InputObject $_return -MemberType NoteProperty -Name "notificationCount" -Value ([Int64] $_fix.notificationCount) Add-Member -InputObject $_return -MemberType NoteProperty -Name "sequenceNumber" -Value ([Int64] $_fix.sequenceNumber) Add-Member -InputObject $_return -MemberType NoteProperty -Name "iD" -Value $_fix.id Add-Member -InputObject $_return -MemberType NoteProperty -Name "databasePath" -Value $DatabasePath -Force Add-Member -InputObject $_return -MemberType NoteProperty -Name "creationDateTime" -Value ([DateTime] $_fix.creationDateTime) -Force Add-Member -InputObject $_return -MemberType NoteProperty -Name "statusDateTime" -Value ([DateTime] $_fix.creationDateTime) -Force if ("fixResults" -in $_fix.PSobject.Properties.Name) { Add-Member -InputObject $_return -MemberType NoteProperty -Name "fixResults" -Value $_fix.fixResults -Force } Add-Member -InputObject $_return -MemberType ScriptProperty -Name "status" -Value ` { #Get return [IssueFixStatus]::([enum]::getValues([IssueFixStatus]) | Where-Object value__ -eq $this._status) } ` { #Set param ( [IssueFixStatus] $status ) $this._status = ([IssueFixStatus]::$Status).value__ } Write-Output $_return } | Write-Output } } function Set-IssueFix { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix, [Parameter(Mandatory=$false,Position=1,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [String] $FixDescription, [Parameter(Mandatory=$false,Position=2,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [IssueFixStatus] $Status, [Parameter(Mandatory=$false,Position=3,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [System.Int64] $NotificationCount, [Parameter(Mandatory=$false,Position=4,ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true)] [System.Int64] $SequenceNumber ) Begin { #Put begining stuff here } Process { if ($PSCmdlet.ShouldProcess("Change $($Fix.fixDescription)?")) { if ($FixDescription) { $Fix.fixDescription = $FixDescription } if ($Status) { if (($Status -ge 0) -and ($Status -le 4)) { $Fix._status = $Status $Fix.statusDateTime = Get-Date } else { Write-Warning "Invalid status value" } } If ($NotificationCount) { $Fix.notificationCount = $NotificationCount } if ($SequenceNumber) { $Fix.sequenceNumber = $SequenceNumber } } Write-Output $Fix } End { #Put end here } } function Approve-IssueFix { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix ) Process { if ($PSCmdlet.ShouldProcess("Change $($Fix.fixDescription) from $($Fix.status) to Complete?")) { $Fix._status = 0 $Fix.statusDateTime = Get-Date } Write-Output $Fix } } function Deny-IssueFix { [CmdletBinding(SupportsShouldProcess=$true)] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix ) Process { if ($PSCmdlet.ShouldProcess("Change $($Fix.fixDescription) from $($Fix.status) to Complete?")) { $Fix._status = 4 $Fix.statusDateTime = Get-Date } Write-Output $Fix } } function Invoke-IssueFix { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix, [Parameter()] [Switch] $Force, [Parameter()] [Switch] $NoNewScope ) Process { if (($Fix.status -eq 0) -or $Force) { if ($PSCmdlet.ShouldProcess("Invoke $($Fix.fixDescription) from $($Fix.checkName) by running $($Fix.fixCommand)?")) { Add-Member -InputObject $Fix -MemberType NoteProperty -Name "fixResults" -Value "" -Force try { $Fix.fixResults = [String] (Invoke-Command -ScriptBlock $fix.fixCommand -NoNewScope:$NoNewScope) $Fix.status = 2 #Complete Write-Verbose "$($Fix.checkName): $($Fix.fixDescription) complete with following results: $($Fix.fixResults)" } catch { #Error $Fix.fixResults = [String] $_.Exception.Message $Fix.status = 3 #Error Write-Verbose "$($Fix.checkName): $($Fix.fixDescription) errored with following error: $($Fix.fixResults)" } finally { $Fix.statusDateTime = Get-Date } } } Write-Output $Fix } } function Limit-IssueFix { [CmdletBinding()] Param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$false)] [PSObject] $Fix ) End { $_fixes = $input #Sort the fixes by iD and creationDateTime $_fixes = $_fixes | Sort-Object -Property @("iD", "creationDateTime") -Descending #Iterate resutls of sort writing out the first instance of each iD $_iD = "" forEach ($_fix in $_fixes) { if ($_fix.iD -ne $_iD) { $_iD = $_fix.iD Write-Output $_fix } else { Write-Verbose "Removed from pipelin fix with iD: $($_fix.iD) and creation date/time of $($_fix.creationDateTime)" } } } } |