Public/Invoke-AtomicRunner.ps1
. "$PSScriptRoot\Invoke-RunnerScheduleMethods.ps1" function Invoke-AtomicRunner { [CmdletBinding( SupportsShouldProcess = $true, PositionalBinding = $false, ConfirmImpact = 'Medium')] Param( [Parameter(Mandatory = $false)] [switch] $ShowDetails, [Parameter(Mandatory = $false)] [switch] $CheckPrereqs, [Parameter(Mandatory = $false)] [switch] $GetPrereqs, [Parameter(Mandatory = $false)] [switch] $Cleanup, [Parameter(Mandatory = $false)] [switch] $ShowDetailsBrief, [Parameter(Mandatory = $false)] [String] $LoggingModule, [Parameter(Mandatory = $false)] $ListOfAtomics, [parameter(Mandatory = $false)] [ValidateRange(0, [int]::MaxValue)] [int] $PauseBetweenAtomics, [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)] $OtherArgs ) Begin { } Process { function Get-GuidFromHostName( $basehostname ) { $guid = [System.Net.Dns]::GetHostName() -replace $($basehostname + "-"), "" if (!$guid) { LogRunnerMsg "Hostname has not been updated or could not parse out the Guid: " + $guid return } # Confirm hostname contains a guid [regex]$guidRegex = '(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$' if ($guid -match $guidRegex) { return $guid } else { return "" } } function Invoke-AtomicTestFromScheduleRow ($tr, $Cleanup = $false) { $theArgs = $tr.InputArgs if ($theArgs.GetType().Name -ne "Hashtable") { $tr.InputArgs = ConvertFrom-StringData -StringData $theArgs } $sc = $tr.AtomicsFolder #Run the Test based on if scheduleContext is 'private' or 'public' if (($sc -eq 'public') -or ($null -eq $sc)) { Invoke-AtomicTest $tr.Technique -TestGuids $tr.auto_generated_guid -InputArgs $tr.InputArgs -TimeoutSeconds $tr.TimeoutSeconds -ExecutionLogPath $artConfig.execLogPath -PathToAtomicsFolder $artConfig.PathToPublicAtomicsFolder @htvars -Cleanup:$Cleanup -supressPathToAtomicsFolder } elseif ($sc -eq 'private') { Invoke-AtomicTest $tr.Technique -TestGuids $tr.auto_generated_guid -InputArgs $tr.InputArgs -TimeoutSeconds $tr.TimeoutSeconds -ExecutionLogPath $artConfig.execLogPath -PathToAtomicsFolder $artConfig.PathToPrivateAtomicsFolder @htvars -Cleanup:$Cleanup -supressPathToAtomicsFolder } if ($timeToPause -gt 0) { Write-Host "Sleeping for $timeToPause seconds..." Start-Sleep $timeToPause } elseif ($timeToPause -eq 0) { Write-Host 'Press any key to continue...'; $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'); } } function Rename-ThisComputer ($tr, $basehostname) { $hash = $tr.auto_generated_guid $newHostName = "$basehostname-$hash" $shouldRename = $true if ( $newHostName -eq [System.Net.Dns]::GetHostName()) { $shouldRename = $false } if ($artConfig.verbose) { LogRunnerMsg "Setting hostname to $newHostName" } If (Test-Path $artConfig.stopFile) { LogRunnerMsg "exiting script because $($artConfig.stopFile) exists" exit } if ($IsLinux) { if ($shouldRename) { Invoke-Expression $("hostnamectl set-hostname $newHostName") } Invoke-Expression $("shutdown -r now") } if ($IsMacOS) { if ($shouldRename) { Invoke-Expression $("/usr/sbin/scutil --set HostName $newHostName") Invoke-Expression $("/usr/sbin/scutil --set ComputerName $newHostName") Invoke-Expression $("/usr/sbin/scutil --set LocalHostName $newHostName") } Invoke-Expression $("/sbin/shutdown -r now") } else { if ($debug) { LogRunnerMsg "Debug: pretending to rename the computer to $newHostName"; exit } if (-not $shouldRename) { Restart-Computer -Force } if ($artConfig.gmsaAccount) { $retry = $true; $count = 0 while ($retry) { # add retry loop to avoid this occassional error "The verification of the MSA failed with error 1355" Invoke-Command -ComputerName '127.0.0.1' -ConfigurationName 'RenameRunnerEndpoint' -ScriptBlock { Rename-Computer -NewName $Using:newHostName -Force -Restart } Start-Sleep 120; $count = $count + 1 LogRunnerMsg "Retrying computer rename $count" if ($count -gt 15) { $retry = $false } } } else { $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $artConfig.user, (Get-Content $artConfig.credFile | ConvertTo-SecureString) try { Rename-Computer -NewName $newHostName -Force -DomainCredential $cred -Restart -ErrorAction stop } catch { if ($artConfig.verbose) { LogRunnerMsg $_ } try { Rename-Computer -NewName $newHostName -Force -LocalCredential $cred -Restart -ErrorAction stop } catch { if ($artConfig.verbose) { LogRunnerMsg $_ } } } } Start-Sleep -seconds 30 LogRunnerMsg "uh oh, still haven't restarted - should never get to here" $retry = $true; $count = 0 while ($retry) { Restart-Computer -Force Start-Sleep 300; $count = $count + 1 LogRunnerMsg "Rename retry $count" if ($count -gt 60) { $retry = $false } } exit } } function Get-TimingVariable ($sched) { $atcount = $sched.Count if ($null -eq $atcount) { $atcount = 1 } $scheduleTimeSpanSeconds = $artConfig.scheduleTimeSpan.TotalSeconds $secondsForAllTestsToComplete = $scheduleTimeSpanSeconds $sleeptime = ($secondsForAllTestsToComplete / $atcount) - 120 - $artConfig.kickOffDelay.TotalSeconds # 1 minute for restart and 1 minute delay for scheduled task and an optional kickoff delay if ($sleeptime -lt 120) { $sleeptime = 120 } # minimum 2 minute sleep time return $sleeptime } # Convert OtherArgs to hashtable so we can pass it through to the call to Invoke-AtomicTest $htvars = @{} if ($OtherArgs) { $OtherArgs | ForEach-Object { if ($_ -match '^-') { #New parameter $lastvar = $_ -replace '^-' $htvars[$lastvar] = $true } else { #Value $htvars[$lastvar] = $_ } } } if ($PSBoundParameters.ContainsKey("PauseBetweenAtomics")) { $timeToPause = $PauseBetweenAtomics } else { $timeToPause = $null } $htvars += [Hashtable]$PSBoundParameters $htvars.Remove('listOfAtomics') | Out-Null $htvars.Remove('OtherArgs') | Out-Null $htvars.Remove('Cleanup') | Out-Null $htvars.Remove('PauseBetweenAtomics') | Out-Null $schedule = Get-Schedule $listOfAtomics # If the schedule is empty, end process if (-not $schedule) { LogRunnerMsg "No test guid's or enabled tests." return } # timing variables $SleepTillCleanup = Get-TimingVariable $schedule # Perform cleanup, Showdetails or Prereq stuff for all scheduled items and then exit if ($Cleanup -or $ShowDetails -or $CheckPrereqs -or $ShowDetailsBrief -or $GetPrereqs -or $listOfAtomics) { $schedule | ForEach-Object { Invoke-AtomicTestFromScheduleRow $_ $Cleanup } return } # exit if file stop.txt is found If (Test-Path $artConfig.stopFile) { LogRunnerMsg "exiting script because $($artConfig.stopFile) does exist" Write-Host -ForegroundColor Yellow "Exiting script because $($artConfig.stopFile) does exist."; Start-Sleep 10; exit } # Find current test to run $guid = Get-GuidFromHostName $artConfig.basehostname if ([string]::IsNullOrWhiteSpace($guid)) { LogRunnerMsg "Test Guid ($guid) was null, using next item in the schedule" } else { if ($artConfig.verbose) { LogRunnerMsg "Found Test: $guid specified in hostname" } $sp = [Collections.Generic.List[Object]]$schedule $currentIndex = $sp.FindIndex( { $args[0].auto_generated_guid -eq $guid }) if (($null -ne $currentIndex) -and ($currentIndex -ne -1)) { $tr = $schedule[$currentIndex] } if ($null -ne $tr) { # run the atomic test and exit Invoke-AtomicTestFromScheduleRow $tr # Cleanup after running test Write-Host -Fore cyan "Sleeping for $SleepTillCleanup seconds before cleaning up for $($tr.Technique) $($tr.auto_generated_guid) "; Start-Sleep -Seconds $SleepTillCleanup Invoke-AtomicTestFromScheduleRow $tr $true } else { LogRunnerMsg "Could not find Test: $guid in schedule. Please update schedule to run this test." } } # Load next scheduled test before renaming computer $nextIndex += $currentIndex + 1 if ($nextIndex -ge ($schedule.count)) { $tr = $schedule[0] } else { $tr = $schedule[$nextIndex] } if ($null -eq $tr) { LogRunnerMsg "Could not determine the next row to execute from the schedule, Starting from 1st row"; $tr = $schedule[0] } #Rename Computer and Restart Rename-ThisComputer $tr $artConfig.basehostname } } |