private/workflow/Invoke-OSDCloudWorkflowTask.ps1
|
function Invoke-OSDCloudWorkflowTask { [CmdletBinding()] param ( [switch] $Test ) #================================================= $Error.Clear() Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" #================================================= # Re-apply the OSDCloud Property override layer to $global:OSDCloudDeploy so authoritative # overrides win over any changes made by the workflow UI, and so the snapshot below # inherits the final values. <# if ($global:OSDCloudEnv -and (Get-Command -Name 'Set-OSDCloudEnvOverride' -ErrorAction Ignore)) { Set-OSDCloudEnvOverride -Target $global:OSDCloudDeploy -ResolveOperatingSystem -AddMissingKeys } #> #================================================= $operatingSystemCloudObject = $global:OSDCloudDeploy.OperatingSystemCloudObject $workflowSettingsUser = $global:OSDCloudWorkflowSettingsUser if (-not $workflowSettingsUser -and (Get-Command -Name 'Initialize-OSDCloudWorkflowSettingsUser' -ErrorAction Ignore)) { $settingsArchitecture = if ($global:OSDCloudDeploy.OSArchitecture -ieq 'arm64') { 'ARM64' } else { 'AMD64' } Initialize-OSDCloudWorkflowSettingsUser -WorkflowName $global:OSDCloudDeploy.WorkflowName -Architecture $settingsArchitecture | Out-Null $workflowSettingsUser = $global:OSDCloudWorkflowSettingsUser } $global:OSDCloudWorkflowInvoke = $null $global:OSDCloudWorkflowInvoke = [ordered]@{ DeploymentDisk = $global:OSDCloudDeploy.DeploymentDisk DiskPartition = [pscustomobject]@{ DiskNumber = $global:OSDCloudDeploy.DeploymentDiskNumber } FileInfoWindowsImage = $null DriverPackCacheObject = $global:OSDCloudDeploy.DriverPackCacheObject DriverPackName = $global:OSDCloudDeploy.DriverPackName DriverPackCloudObject = $global:OSDCloudDeploy.DriverPackCloudObject DriverPackCloudTest = $global:OSDCloudDeploy.DriverPackCloudTest Force = $global:OSDCloudDeploy.Force LaunchMethod = $global:OSDCloudDeploy.LaunchMethod LogsPath = "$env:TEMP\osdcloud-logs" OperatingSystem = $global:OSDCloudDeploy.OperatingSystem OperatingSystemCacheObject = $global:OSDCloudDeploy.OperatingSystemCacheObject OperatingSystemCloudObject = $operatingSystemCloudObject OperatingSystemCloudTest = $global:OSDCloudDeploy.OperatingSystemCloudTest OSBuild = $global:OSDCloudDeploy.OSBuild OSEditionId = $global:OSDCloudDeploy.OSEditionId RecoveryPartition = $workflowSettingsUser.RecoveryPartition SkipFirmwareUpdate = $global:OSDCloudDeploy.SkipFirmwareUpdate TimeEnd = $null TimeSpan = $null TimeStart = [datetime](Get-Date) ParamsExpandWindowsImage = $null USBPartitions = $null WindowsEdition = $null WindowsImage = $null WindowsImageIndex = $null WindowsImagePath = $null WinpeRestart = [bool]$workflowSettingsUser.WinpeRestart WinpeShutdown = [bool]$workflowSettingsUser.WinpeShutdown } #================================================= #region OSDCloud Deployment Analytics $eventName = 'Deploy-OSDCloud' function Send-EventDeployOSDCloud { param( [Parameter(Mandatory)] [string]$EventName, [Parameter(Mandatory)] [string]$ApiKey, [Parameter(Mandatory)] [string]$DistinctId, [Parameter()] [hashtable]$Properties ) try { $payload = [ordered]@{ api_key = $ApiKey event = $EventName properties = $Properties + @{ distinct_id = $DistinctId } timestamp = (Get-Date).ToString('o') } $body = $payload | ConvertTo-Json -Depth 4 -Compress Invoke-RestMethod -Method Post ` -Uri 'https://us.i.posthog.com/capture/' ` -Body $body ` -ContentType 'application/json' ` -TimeoutSec 2 ` -ErrorAction Stop | Out-Null Write-Verbose "[$(Get-Date -format s)] [OSDCloud] Event sent: $EventName" } catch { Write-Verbose "[$(Get-Date -format s)] [OSDCloud] Failed to send event: $($_.Exception.Message)" } } # UUID $deviceUUID = [System.String]$global:OSDCoreDevice.UUID # Convert the UUID to a hash value to protect user privacy and ensure a consistent identifier across events [string]$distinctId = $global:OSDCoreDevice.EndpointSHA if ([string]::IsNullOrWhiteSpace($distinctId) -and -not [string]::IsNullOrWhiteSpace($deviceUUID)) { $distinctId = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($deviceUUID))).Replace("-", "") } if ([string]::IsNullOrWhiteSpace($distinctId)) { $distinctId = [System.Guid]::NewGuid().ToString() } $computerInfo = Get-ComputerInfo -ErrorAction Ignore if ($env:SystemDrive -eq 'X:') { $deploymentPhase = 'WinPE' $osName = 'Microsoft WindowsPE' } else { $deploymentPhase = 'Windows' $osName = [string]$computerInfo.OsName } $eventProperties = @{ OSDManufacturer = $OSDCoreDevice.OSDManufacturer OSDModel = $OSDCoreDevice.OSDModel OSDProduct = $OSDCoreDevice.OSDProduct deviceBaseBoardProduct = $OSDCoreDevice.BaseBoardProduct deviceBiosReleaseDate = $OSDCoreDevice.BiosReleaseDate deviceBiosVersion = $OSDCoreDevice.BiosVersion deviceComputerManufacturer = $OSDCoreDevice.ComputerManufacturer deviceComputerModel = $OSDCoreDevice.ComputerModel deviceComputerSystemFamily = $OSDCoreDevice.ComputerSystemFamily deviceComputerSystemProduct = $OSDCoreDevice.ComputerSystemProduct deviceComputerSystemSKU = $OSDCoreDevice.ComputerSystemSKU deviceComputerSystemType = $OSDCoreDevice.ComputerSystemType deviceKeyboardLayout = $OSDCoreDevice.KeyboardLayout deviceKeyboardName = $OSDCoreDevice.KeyboardName deviceOSArchitecture = $OSDCoreDevice.OSArchitecture deviceOSVersion = $OSDCoreDevice.OSVersion deviceProcessorArchitecture = $OSDCoreDevice.ProcessorArchitecture deviceSystemFirmwareHardwareId = $OSDCoreDevice.SystemFirmwareHardwareId deviceTimeZone = $OSDCoreDevice.TimeZone deviceTotalPhysicalMemoryGB = $OSDCoreDevice.TotalPhysicalMemoryGB winArchitecture = [string]$env:PROCESSOR_ARCHITECTURE winBuildLabEx = [string]$computerInfo.WindowsBuildLabEx winBuildNumber = [string]$computerInfo.OsBuildNumber winCountryCode = [string]$computerInfo.OsCountryCode winEditionId = [string]$computerInfo.WindowsEditionId winInstallationType = [string]$computerInfo.WindowsInstallationType winLanguage = [string]$computerInfo.OsLanguage winName = [string]$osName winTimeZone = [string]$computerInfo.TimeZone winVersion = [string]$computerInfo.OsVersion osdcloudModuleVersion = [string]$($MyInvocation.MyCommand.Module.Version) osdcloudWorkflowName = [string]$global:OSDCloudDeploy.WorkflowName osdcloudWorkflowTaskName = [string]$global:OSDCloudDeploy.WorkflowTaskName osdcloudDriverPackName = [string]$global:OSDCloudDeploy.DriverPackName osdcloudOSName = [string]$operatingSystemCloudObject.OSName osdcloudOSVersion = [string]$operatingSystemCloudObject.OSVersion osdcloudOSActivationStatus = [string]$operatingSystemCloudObject.OSActivation osdcloudOSBuild = [string]$operatingSystemCloudObject.OSBuild osdcloudOSBuildVersion = [string]$operatingSystemCloudObject.OSBuildVersion osdcloudOSLanguageCode = [string]$operatingSystemCloudObject.OSLanguageCode deploymentPhase = [string]$deploymentPhase idOSDeployDevice = [string]$global:OSDCoreDevice.idOSDeployDevice # OSDeploy Device Hash idOSDeployBoot = [string]$global:OSDCoreDevice.idOSDeployBoot # OSDeploy Boot GUID # idRegisteredEmail = [string]$global:OSDCoreDevice.idRegisteredEmail idRegisteredLicense = [string]$global:OSDCoreDevice.idRegisteredLicense } $postApi = 'phc_2h7nQJCo41Hc5C64B2SkcEBZOvJ6mHr5xAHZyjPl3ZK' Send-EventDeployOSDCloud -EventName $eventName -ApiKey $postApi -DistinctId $distinctId -Properties $eventProperties #endregion #================================================= # Start Workflow Task Execution if ($null -ne $global:OSDCloudDeploy.WorkflowTaskObject) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO]" foreach ($step in $global:OSDCloudDeploy.WorkflowTaskObject.steps) { # Set the current step in the global variable $global:OSDCloudCurrentStep = $step #================================================= # Should we skip this step? (support both 'skip' and legacy 'disable') if (($step.skip -eq $true) -or ($step.disable -eq $true)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] [Skip:True] $($step.name)" continue } #================================================= # Can we test this step in full Windows OS (not WinPE)? if (($env:SystemDrive -ne 'X:') -and ($step.testinfullos -ne $true)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] [Skip:FullOS] $($step.name)" continue } #================================================= # Can we pause before this step? if ($step.pause -eq $true) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] [Pause:True] $($step.name)" Pause } #================================================= # Command or ScriptBlock $command = $null $commandline = $null if ($step.command) { $command = $step.command if (($command -is [string]) -and ($command.Contains(" "))) { $commandline = $command } elseif (-not (Get-Command $command -ErrorAction Ignore)) { Write-Host -ForegroundColor DarkRed "[$(Get-Date -format s)] [Step command does not exist] $($step.command)" continue } } elseif ($step.scriptblock) { $command = [scriptblock]::Create($step.scriptblock) } else { Write-Host -ForegroundColor DarkRed "[$(Get-Date -format s)] [Step does not contain a command] $($step.name)" continue } #================================================= # Arguments $arguments = @() if ($step.args) { # Only process if args is an array of strings, not an empty object if ($step.args -is [array]) { [array]$arguments = @($step.args) $arguments = $arguments | Where-Object { $_ -is [string] } | ForEach-Object { $_.Trim() } # Trim whitespace from arguments $arguments = $arguments | Where-Object { $_ -ne "" } # Remove empty arguments } } #================================================= # Parameters $parameters = $null if ($step.parameters) { $parameters = $null $parameters = [ordered]@{} ($step.parameters).psobject.properties | ForEach-Object { $parameters[$_.Name] = $_.Value } if ($parameters.Count -eq 0) { $parameters = $null } } # Execute if ($step.scriptblock) { Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] $($step.name) [ScriptBlock:$($step.scriptblock)]" if ($Test) { continue } & $command } elseif ($commandline) { Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] $($step.name)" if ($Test) { continue } # Parse the command line into a command and arguments to avoid Invoke-Expression $parseErrors = $null $tokens = [System.Management.Automation.PSParser]::Tokenize($commandline, [ref]$parseErrors) if ($parseErrors -and $parseErrors.Count -gt 0) { Write-Error "Failed to parse command line for step '$($step.name)': $commandline" continue } if (-not $tokens -or $tokens.Count -eq 0) { Write-Error "Empty or invalid command line for step '$($step.name)': $commandline" continue } # First token is the command; subsequent CommandArgument tokens are arguments $exeToken = $tokens | Where-Object { $_.Type -eq 'Command' } | Select-Object -First 1 if (-not $exeToken) { Write-Error "No executable command found in command line for step '$($step.name)': $commandline" continue } $exe = $exeToken.Content $cmdArgs = @() foreach ($tok in $tokens) { if ($tok -eq $exeToken) { continue } if ($tok.Type -eq 'CommandArgument') { $cmdArgs += $tok.Content } } & $exe @cmdArgs } elseif ($command -and ($arguments.Count -ge 1) -and ($parameters.Count -ge 1)) { Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] $($step.name) [Arguments:$arguments]" ($parameters | Out-String).Trim() if ($Test) { continue } & $command @parameters @arguments } elseif ($command -and ($arguments.Count -ge 1)) { Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] $($step.name) [Arguments:$arguments]" if ($Test) { continue } & $command @arguments } elseif ($command -and ($parameters.Count -ge 1)) { Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] $($step.name)" ($parameters | Out-String).Trim() if ($Test) { continue } & $command @parameters } elseif ($command) { Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] $($step.name)" if ($Test) { continue } & $command } else { Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] No command to execute." continue } } # End of workflow steps Write-Host -ForegroundColor Green "[$(Get-Date -format s)] Workflow Task execution done." #================================================= # End the function $Message = "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" Write-Verbose -Message $Message; Write-Debug -Message $Message #================================================= } } |