classes/PSCronJobObject.psm1
using namespace System.IO using namespace System.Collections using namespace System.Management.Automation using namespace Microsoft.PowerShell using module '.\PSCronDateTime.psm1' class PSCronJobObject { [ValidatePattern('^(?:\S+\s?){5}(?<!\s)$')][string]$Schedule [string]$Name [string]$Description [string]$Source [string[]]$IncludeScripts hidden [System.IO.FileInfo]$__FilePath hidden [ExecutionPolicy]$__ExecutionPolicy = ( Get-ExecutionPolicy ) [scriptblock]$Definition [System.IO.DirectoryInfo]$WorkingDirectory = ( [Environment]::SystemDirectory ) [PSCronDateTime]$ReferenceDate = ( Get-PSCronDate ) [PSCronDateTime]$StartDate = ( Get-PSCronDate -Resolution Millisecond ) [PSCronDateTIme]$EndDate [nullable[timespan]]$RunTime [PSInvocationState]$State hidden [ArrayList]$__Log = @() [FileInfo]$LogPath hidden [bool]$__Append = $false [int]$TimeOut = 60 [PSDataCollection[pscustomobject]]$Output [PSDataCollection[ErrorRecord]]$Errors [Exception]$TerminatingError [bool]$HadErrors [ActionPreference]$JobInformationPreference = 'Continue' [ActionPreference]$JobDebugPreference = 'SilentlyContinue' [ActionPreference]$JobWarningPreference = 'Continue' [ActionPreference]$JobErrorActionPreference = 'Stop' PSCronJobObject( [hashtable]$Hashtable ) { $this.__Init( $Hashtable ) } hidden [void] __Init( [hashtable]$Hashtable ) { # get all object properties [string[]]$MyProperties = $this | Get-Member -MemberType Property -Force | Select-Object -ExpandProperty Name # set initial property values and property readers for hidden # properties $MyProperties | ForEach-Object { $KeyName = $_ -replace '^__' # initialize property values if ( $KeyName -in $Hashtable.Keys ) { $this.$_ = $Hashtable.$KeyName } # configure read only properties, except Log which is handled # below if ( $KeyName -ne $_ ) { $this | Add-Member -MemberType ScriptProperty -Name $KeyName -Value ( [scriptblock]::Create( "`$this.$_" ) ) } } # configure the SigningStatus property $this | Add-Member -Name SigningStatus -MemberType ScriptProperty -Value { if ( -not $this.FilePath ) { [SignatureStatus]::NotSupportedFileFormat } return ( Get-AuthenticodeSignature $this.FilePath ).Status } # configure the SignatureRequired property $this | Add-Member -Name SignatureRequired -MemberType ScriptProperty -Value { return $this.__SignatureRequired( $this.FilePath, $this.ExecutionPolicy ) } # override the Log property since we handle that in a special way $this | Add-Member -Name Log -MemberType ScriptProperty -Value { $this.__Log | Out-String } -Force # override the FilePath property since we handle that in a special way $this | Add-Member -Name FilePath -MemberType ScriptProperty -Value { $this.__FilePath } -SecondValue { param( [ValidateNotNullOrEmpty()] [System.IO.FileInfo] $FilePath ) $this.__FilePath = $FilePath if ( -not $this.SignatureRequired -or $this.SigningStatus -eq 'Valid' ) { $this.Definition = [scriptblock]::Create( ( Get-Content $this.__FilePath | Out-String ) ) } else { Write-Warning 'Failed authenticode signature validation for file:' Write-Warning $this.__FilePath $this.Definition = { throw [System.Management.Automation.PSSecurityException]::new( 'Invalid Authenticode Signature' ) } } } -Force # rerun assignment for $FilePath to initialize properly if ( $Hashtable.FilePath ) { $this.FilePath = $Hashtable.FilePath } # setup the default display property set [string[]]$DefaultProperties = 'Name', 'Description', 'Schedule', 'Source', 'StartDate', 'EndDate', 'RunTime', 'Output', 'State', 'HadErrors' $DefaultDisplayPropertySet = [PSPropertySet]::new( 'DefaultDisplayPropertySet', $DefaultProperties ) $PSStandardMembers = [PSMemberInfo[]]$DefaultDisplayPropertySet $this | Add-Member -MemberType MemberSet -Name 'PSStandardMembers' -Value $PSStandardMembers } [string] ToString() { return $this.Log } [void] LogRaw( [string]$MessageData ) { # the first time this function is called ( $this-__Log.Count -eq 0 ) # and we are overwritting the log ( -not $this.__Append ) and the log # path is set we overwrite any existing log $Append = -not ( $this.__Log.Count -eq 0 -and -not $this.__Append -and $this.LogPath ) $this.__Log.Add( $MessageData ) if ( $this.LogPath ) { $MessageData | Out-File -FilePath $this.LogPath.FullName -Append:$Append -Encoding UTF8 } } [void] LogMessage( [datetime]$TimeStamp, [string]$OutputStream, [string[]]$MessageData ) { $MessageData | ForEach-Object { $_ -split '[\r\n]+' } | ForEach-Object { '[{0:HH:mm:ss}] {1,-7} {2}' -f $TimeStamp, $OutputStream, $_ } | ForEach-Object { $this.LogRaw( $_ ) } } hidden [bool] __SignatureRequired( [FileInfo]$FilePath, [ExecutionPolicy]$ExecutionPolicy ) { switch ( $ExecutionPolicy ) { 'Bypass' { return $false } 'Unrestricted' { return $false } 'Default' { [bool]$ServerOS = Get-WmiObject -Query 'SELECT ProductType FROM Win32_OperatingSystem WHERE ProductType > 1' return $this.SignatureRequired( $FilePath, ( 'Restricted', 'RemoteSigned' )[ $ServerOS ] ) } 'RemoteSigned' { # if the file is downloaded then we will require a signature if ( [bool]( Get-Item $FilePath -Stream * | Where-Object { $_.Stream -eq 'Zone.Identifier' } ) ) { return $true } # otherwise we check if the file is on a UNC path $Uri = $null if ( [System.Uri]::TryCreate( $FilePath.FullName, [System.UriKind]::Absolute, ( [ref]$Uri ) ) -and $Uri.IsUnc ) { return $true } # in other cases file is local file and we should return false return $false } } # AllSigned / Restricted / Undefined return $true } } |