Copy and paste this sample script and modify as needed for your environment:

<# .SYNOPSIS     Deploys the Secure Boot Rollout Orchestrator as a Windows Scheduled Task.

.DESCRIPTION     Creates a scheduled task that runs the orchestrator continuously in the background.     The task runs with bypass execution policy so no security prompts appear.          The orchestrator will:     - Poll for device updates on the specified interval     - Automatically generate waves and deploy GPOs     - Continue until all eligible devices are updated          Monitor progress using: Get-SecureBootRolloutStatus.ps1

.PARAMETER AggregationInputPath     UNC path to JSON device data (from detection GPO)

.PARAMETER ReportBasePath     Local path for reports and state files

.PARAMETER TargetOU     OU to link GPOs (optional - defaults to domain root)

.PARAMETER PollIntervalMinutes     Minutes between status checks. Default: 30

.PARAMETER UseWinCS     Use WinCS (Windows Configuration System) instead of AvailableUpdatesPolicy GPO.     When enabled, deploys WinCsFlags.exe scheduled task to endpoints instead of registry GPO.

.PARAMETER WinCSKey     The WinCS key for Secure Boot configuration. Default: F33E0C8E002

.PARAMETER ServiceAccount     Account to run the task. Default: SYSTEM     For domain operations, use a domain admin service account.

.PARAMETER ScriptPath     Path to the orchestrator script. Default: Same folder as this script.

.PARAMETER Uninstall     Remove the scheduled task

.EXAMPLE     .\Deploy-OrchestratorTask.ps1 -AggregationInputPath "\\server\SecureBootData$" -ReportBasePath "C:\SecureBootReports" -ServiceAccount "DOMAIN\svc_secureboot"

.EXAMPLE     .\Deploy-OrchestratorTask.ps1 -AggregationInputPath "\\server\SecureBootData$" -ReportBasePath "C:\SecureBootReports"

.EXAMPLE     # Deploy using WinCS method instead of AvailableUpdatesPolicy     .\Deploy-OrchestratorTask.ps1 -AggregationInputPath "\\server\SecureBootData$" -ReportBasePath "C:\SecureBootReports" -UseWinCS

.EXAMPLE     .\Deploy-OrchestratorTask.ps1 -Uninstall #>

[CmdletBinding()] param(     [Parameter(Mandatory = $false)]     [string]$AggregationInputPath,          [Parameter(Mandatory = $false)]     [string]$ReportBasePath,          [Parameter(Mandatory = $false)]     [string]$TargetOU,          [Parameter(Mandatory = $false)]     [int]$PollIntervalMinutes = 30,          [Parameter(Mandatory = $false)]     [switch]$UseWinCS,          [Parameter(Mandatory = $false)]     [string]$WinCSKey = "F33E0C8E002",          [Parameter(Mandatory = $false)]     [string]$ServiceAccount = "SYSTEM",          [Parameter(Mandatory = $false)]     [string]$ScriptPath,          [Parameter(Mandatory = $false)]     [switch]$Uninstall )

$ErrorActionPreference = "Stop" $TaskName = "SecureBoot-Rollout-Orchestrator" $DownloadUrl = "https://aka.ms/getsecureboot" $DownloadSubPage = "Deployment and Monitoring Samples"

# ============================================================================ # DEPENDENCY VALIDATION # ============================================================================

function Test-ScriptDependencies {     <#     .SYNOPSIS         Validates that all required scripts are present.     .DESCRIPTION         Checks for required script dependencies and provides download instructions if missing.     #>     param(         [Parameter(Mandatory = $true)]         [string]$ScriptDirectory,                  [Parameter(Mandatory = $true)]         [string[]]$RequiredScripts     )          $missingScripts = @()          foreach ($script in $RequiredScripts) {         $scriptPath = Join-Path $ScriptDirectory $script         if (-not (Test-Path $scriptPath)) {             $missingScripts += $script         }     }          if ($missingScripts.Count -gt 0) {         Write-Host ""         Write-Host ("=" * 70) -ForegroundColor Red         Write-Host "  MISSING DEPENDENCIES" -ForegroundColor Red         Write-Host ("=" * 70) -ForegroundColor Red         Write-Host ""         Write-Host "The following required scripts were not found:" -ForegroundColor Yellow         foreach ($script in $missingScripts) {             Write-Host "  - $script" -ForegroundColor White         }         Write-Host ""         Write-Host "Please download the latest scripts from:" -ForegroundColor Cyan         Write-Host "  URL: $DownloadUrl" -ForegroundColor White         Write-Host "  Navigate to: '$DownloadSubPage'" -ForegroundColor White         Write-Host ""         Write-Host "Extract all scripts to the same directory and run again." -ForegroundColor Yellow         Write-Host ""         return $false     }          return $true }

# Required scripts for orchestrator deployment $requiredScripts = @(     "Start-SecureBootRolloutOrchestrator.ps1",     "Aggregate-SecureBootData.ps1",     "Deploy-GPO-SecureBootCollection.ps1",     "Detect-SecureBootCertUpdateStatus.ps1",     "Get-SecureBootRolloutStatus.ps1",     "Enable-SecureBootUpdateTask.ps1" )

if (-not (Test-ScriptDependencies -ScriptDirectory $PSScriptRoot -RequiredScripts $requiredScripts)) {     exit 1 }

# ============================================================================ # UNINSTALL # ============================================================================

if ($Uninstall) {     Write-Host ""     Write-Host "Removing scheduled task: $TaskName" -ForegroundColor Yellow          $existingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue     if ($existingTask) {         Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue         Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false         Write-Host "Task removed successfully." -ForegroundColor Green     } else {         Write-Host "Task not found." -ForegroundColor Gray     }     exit 0 }

# ============================================================================ # VALIDATION # ============================================================================

if (-not $AggregationInputPath -or -not $ReportBasePath) {     Write-Host "ERROR: -AggregationInputPath and -ReportBasePath are required." -ForegroundColor Red     Write-Host ""     Write-Host "Example:" -ForegroundColor Yellow     Write-Host '  .\Deploy-OrchestratorTask.ps1 -AggregationInputPath "\\server\SecureBootData$" -ReportBasePath "C:\SecureBootReports"'     exit 1 }

# Find orchestrator script if (-not $ScriptPath) {     $ScriptPath = Join-Path $PSScriptRoot "Start-SecureBootRolloutOrchestrator.ps1" }

if (-not (Test-Path $ScriptPath)) {     Write-Host "ERROR: Orchestrator script not found: $ScriptPath" -ForegroundColor Red     exit 1 }

# Find aggregation script (needed by orchestrator) $aggregateScript = Join-Path $PSScriptRoot "Aggregate-SecureBootData.ps1" if (-not (Test-Path $aggregateScript)) {     Write-Host "WARNING: Aggregate-SecureBootData.ps1 not found in script directory" -ForegroundColor Yellow     Write-Host "         Orchestrator may fail if it cannot find this script." -ForegroundColor Yellow }

Write-Host "" Write-Host ("=" * 70) -ForegroundColor Cyan Write-Host "  Secure Boot Rollout Orchestrator - Task Deployment" -ForegroundColor Cyan Write-Host ("=" * 70) -ForegroundColor Cyan Write-Host ""

# For display, show relative paths (script names only) $displayScriptPath = Split-Path $ScriptPath -Leaf

Write-Host "Configuration:" -ForegroundColor Yellow Write-Host "  Task Name:         $TaskName" Write-Host "  Orchestrator:      $displayScriptPath" Write-Host "  Input Path:        $AggregationInputPath" Write-Host "  Report Path:       $ReportBasePath" Write-Host "  Target OU:         $(if ($TargetOU) { $TargetOU } else { '(domain root)' })" Write-Host "  Poll Interval:     $PollIntervalMinutes minutes" Write-Host "  Service Account:   $ServiceAccount" Write-Host "  Deployment Method: $(if ($UseWinCS) { "WinCS (WinCsFlags.exe)" } else { "AvailableUpdatesPolicy (GPO)" })" if ($UseWinCS) {     Write-Host "  WinCS Key:         $WinCSKey" } Write-Host ""

# ============================================================================ # GPO DETECTION - AUTO-DEPLOY IF MISSING # ============================================================================

$CollectionGPOName = "SecureBoot-EventCollection" $deployGpoScript = Join-Path $PSScriptRoot "Deploy-GPO-SecureBootCollection.ps1"

# Check if GroupPolicy module is available if (Get-Module -ListAvailable -Name GroupPolicy) {     Import-Module GroupPolicy -ErrorAction SilentlyContinue          Write-Host "Checking for Detection GPO..." -ForegroundColor Yellow          try {         # Get the domain from the AggregationInputPath (e.g., \\domain\share)         $domainFromPath = if ($AggregationInputPath -match '^\\\\([^\\]+)\\') {             $matches[1]         } else {             $env:USERDNSDOMAIN         }                  # Check if GPO exists         $existingGpo = Get-GPO -Name $CollectionGPOName -ErrorAction SilentlyContinue                  if ($existingGpo) {             Write-Host "  Detection GPO found: $CollectionGPOName" -ForegroundColor Green         } else {             Write-Host ""             Write-Host ("=" * 70) -ForegroundColor Yellow             Write-Host "  DETECTION GPO NOT FOUND" -ForegroundColor Yellow             Write-Host ("=" * 70) -ForegroundColor Yellow             Write-Host ""             Write-Host "The detection GPO '$CollectionGPOName' was not found." -ForegroundColor Yellow             Write-Host "This GPO is required to collect device status data." -ForegroundColor Yellow             Write-Host ""                          # Ask user if they want to deploy the GPO now             Write-Host "Would you like to deploy the Detection GPO now? (Y/N)" -ForegroundColor Cyan             $response = Read-Host                          if ($response -match '^[Yy]') {                 Write-Host ""                 Write-Host "Launching GPO Deployment..." -ForegroundColor Cyan                 Write-Host ""                                  # Build parameters for GPO deployment                 $gpoParams = @{                     DomainName = $domainFromPath                     CollectionSharePath = $AggregationInputPath                     ScriptSourcePath = Join-Path $PSScriptRoot "Detect-SecureBootCertUpdateStatus.ps1"                 }                                  if ($TargetOU) {                     $gpoParams.OUPath = $TargetOU                 } else {                     # Use AutoDetectOU to let user select                     $gpoParams.AutoDetectOU = $true                 }                                  # Run the GPO deployment script                 & $deployGpoScript @gpoParams                                  if ($LASTEXITCODE -ne 0) {                     Write-Host "GPO deployment may have encountered issues. Review the output above." -ForegroundColor Yellow                     Write-Host "You can continue with orchestrator deployment or press Ctrl+C to abort." -ForegroundColor Yellow                     Write-Host ""                     Read-Host "Press Enter to continue"                 } else {                     Write-Host ""                     Write-Host "Detection GPO deployed successfully!" -ForegroundColor Green                     Write-Host ""                 }             } else {                 Write-Host ""                 Write-Host "Skipping GPO deployment. The orchestrator will not receive device data" -ForegroundColor Yellow                 Write-Host "until the Detection GPO is deployed manually." -ForegroundColor Yellow                 Write-Host ""                 Write-Host "To deploy the Detection GPO later, run:" -ForegroundColor Cyan                 Write-Host "  .\Deploy-GPO-SecureBootCollection.ps1 -DomainName `"$domainFromPath`" -AutoDetectOU" -ForegroundColor White                 Write-Host ""             }         }     } catch {         Write-Host "  Unable to check for GPO: $($_.Exception.Message)" -ForegroundColor Yellow         Write-Host "  Continuing with orchestrator deployment..." -ForegroundColor Gray     } } else {     Write-Host "  GroupPolicy module not available - skipping GPO check" -ForegroundColor Gray     Write-Host "  Ensure Detection GPO is deployed separately." -ForegroundColor Gray }

Write-Host ""

# ============================================================================ # BUILD ARGUMENTS # ============================================================================

$arguments = @(     "-NoProfile"     "-ExecutionPolicy Bypass"     "-File `"$ScriptPath`""     "-AggregationInputPath `"$AggregationInputPath`""     "-ReportBasePath `"$ReportBasePath`""     "-PollIntervalMinutes $PollIntervalMinutes" )

if ($TargetOU) {     $arguments += "-TargetOU `"$TargetOU`"" }

if ($UseWinCS) {     $arguments += "-UseWinCS"     $arguments += "-WinCSKey `"$WinCSKey`"" }

$argumentString = $arguments -join " "

# Don't display raw arguments with full paths - it's confusing for published scripts # The task will use the full path internally

# ============================================================================ # CREATE SCHEDULED TASK # ============================================================================

# Check for existing task $existingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue if ($existingTask) {     Write-Host "Task already exists. Updating..." -ForegroundColor Yellow     Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue     Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false }

# Create task action $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $argumentString -WorkingDirectory $PSScriptRoot

# Create trigger - run once, immediately (orchestrator loops internally) $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1)

# Create principal if ($ServiceAccount -eq "SYSTEM") {     $principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest } else {     # Prompt for password for domain account     Write-Host "Enter password for $ServiceAccount" -ForegroundColor Yellow     $cred = Get-Credential -UserName $ServiceAccount -Message "Service account credentials for scheduled task"     $principal = New-ScheduledTaskPrincipal -UserId $ServiceAccount -LogonType Password -RunLevel Highest }

# Task settings $settings = New-ScheduledTaskSettingsSet `     -AllowStartIfOnBatteries `     -DontStopIfGoingOnBatteries `     -StartWhenAvailable `     -RunOnlyIfNetworkAvailable `     -RestartCount 3 `     -RestartInterval (New-TimeSpan -Minutes 5) `     -ExecutionTimeLimit (New-TimeSpan -Days 30)  # Allow long-running

# Register task try {     if ($ServiceAccount -eq "SYSTEM") {         Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "Secure Boot Certificate Rollout - Automated GPO deployment"     } else {         Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "Secure Boot Certificate Rollout - Automated GPO deployment" -User $ServiceAccount -Password $cred.GetNetworkCredential().Password     }     Write-Host "Scheduled task created successfully!" -ForegroundColor Green } catch {     Write-Host "Failed to create scheduled task: $($_.Exception.Message)" -ForegroundColor Red     exit 1 }

# ============================================================================ # CREATE STATUS SHORTCUT # ============================================================================

$statusScript = Join-Path $PSScriptRoot "Get-SecureBootRolloutStatus.ps1" if (Test-Path $statusScript) {     Write-Host ""     Write-Host "To check rollout status, run:" -ForegroundColor Yellow     Write-Host "  .\Get-SecureBootRolloutStatus.ps1 -ReportBasePath `"$ReportBasePath`"" -ForegroundColor Cyan }

# ============================================================================ # OUTPUT # ============================================================================

Write-Host "" Write-Host ("=" * 70) -ForegroundColor Green Write-Host "  DEPLOYMENT COMPLETE" -ForegroundColor Green Write-Host ("=" * 70) -ForegroundColor Green Write-Host "" Write-Host "The orchestrator will start in approximately 1 minute." -ForegroundColor White Write-Host "" Write-Host "MONITORING:" -ForegroundColor Yellow Write-Host "  View task status:    Get-ScheduledTask -TaskName '$TaskName' | Select State" Write-Host "  View task log:       Get-Content '$ReportBasePath\RolloutState\Orchestrator_$(Get-Date -Format 'yyyyMMdd').log' -Tail 50" Write-Host "  View rollout state:  Get-Content '$ReportBasePath\RolloutState\RolloutState.json' | ConvertFrom-Json" Write-Host "  View dashboard:      Start '$ReportBasePath\Aggregation_*\SecureBoot_Dashboard*.html'" Write-Host "  Quick status:        .\Get-SecureBootRolloutStatus.ps1 -ReportBasePath '$ReportBasePath'" Write-Host "" Write-Host "MANAGEMENT:" -ForegroundColor Yellow Write-Host "  Start manually:      Start-ScheduledTask -TaskName '$TaskName'" Write-Host "  Stop:                Stop-ScheduledTask -TaskName '$TaskName'" Write-Host "  Remove:              .\Deploy-OrchestratorTask.ps1 -Uninstall" Write-Host ""  

Need more help?

Want more options?

Explore subscription benefits, browse training courses, learn how to secure your device, and more.