diff --git a/foam_sync.ps1 b/foam_sync.ps1 index 6ee4d96..8ea2d0d 100644 --- a/foam_sync.ps1 +++ b/foam_sync.ps1 @@ -50,30 +50,43 @@ $taskDescription = "Periodically synchronizes the Git repository at $scriptDir u $taskPrincipal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive # Trigger configuration -$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $frequencyMinutes) +$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $frequencyMinutes) -RepetitionDuration ([TimeSpan]::MaxValue) # Action configuration: # We use an indirection technique to ensure the window is truly hidden. # The scheduled task launches an initial PowerShell. # This initial PowerShell then uses Start-Process to launch the *actual* script in a new, hidden PowerShell process. -# 1. Path to the script, with internal double quotes escaped as "" (for the -File parameter of the innermost PowerShell) +# 1. Innermost PowerShell's -File argument: Path to the script, with internal double quotes escaped as "" +# e.g., \"C:\path\to your script.ps1\" $scriptPathForInnermostFileParam = $scriptPath.Replace('"', '""') +$innermostFileArg = "\`"$scriptPathForInnermostFileParam\`"" # 2. Argument string for the innermost PowerShell instance (the one executing the actual script) -$innermostPSArgs = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \`"$scriptPathForInnermostFileParam\`"" +# e.g., -NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\" +$innermostPSArgs = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File $innermostFileArg" # 3. The innermostPSArgs string needs its single quotes escaped (as '') because it will be wrapped in single quotes for Start-Process's -ArgumentList $innermostPSArgsEscapedForStartProcess = $innermostPSArgs.Replace("'", "''") -# 4. Command to be executed by the intermediate PowerShell (launched by Task Scheduler). This command uses Start-Process. -$commandForIntermediatePS = "Start-Process -FilePath powershell.exe -ArgumentList '$innermostPSArgsEscapedForStartProcess' -WindowStyle Hidden" -$taskActionArgument = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command \`"$($commandForIntermediatePS.Replace('"', '`"'))\`"" # Escape " in command as `"` +# 4. Command string that the first PowerShell instance (launched by Task Scheduler) will execute using Start-Process. +# e.g., Start-Process -FilePath powershell.exe -ArgumentList '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"' -WindowStyle Hidden +$commandToRunViaStartProcess = "Start-Process -FilePath powershell.exe -ArgumentList '$innermostPSArgsEscapedForStartProcess' -WindowStyle Hidden" -$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $taskActionArgument +# 5. Argument string FOR THE TASK SCHEDULER to pass to the first powershell.exe. +# The $commandToRunViaStartProcess is the value for -Command, and needs to be quoted for registration. +# e.g., -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "Start-Process -FilePath powershell.exe -ArgumentList '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"C:\path\to your script.ps1\"' -WindowStyle Hidden" +$actionArgumentForRegistration = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command \`"$commandToRunViaStartProcess\`"" + +# 6. Expected argument string WHEN RETRIEVED by Get-ScheduledTask. +# Task Scheduler/PowerShell often strips the outermost quotes from the -Command value when retrieved. +$expectedRetrievedActionArgument = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -Command $commandToRunViaStartProcess" + +$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $actionArgumentForRegistration # Task settings -$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 1) +$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Hours 1) ` + -StopIfGoingOnBatteries:$false # Explicitly ensure it doesn't stop # Check and configure the scheduled task try { @@ -85,16 +98,18 @@ try { $currentTrigger = $existingTask.Triggers[0] $currentAction = $existingTask.Actions[0] + # Check Trigger $triggerMatches = $false if ($currentTrigger -is [Microsoft.Management.Infrastructure.CimInstance] ` - -and $currentTrigger.RepetitionInterval.TotalMinutes -eq $frequencyMinutes) { + -and $currentTrigger.RepetitionInterval.TotalMinutes -eq $frequencyMinutes ` + -and $currentTrigger.RepetitionDuration -eq ([TimeSpan]::MaxValue)) { + # Check duration too $triggerMatches = $true } - $actionMatches = $false if ($currentAction -is [Microsoft.Management.Infrastructure.CimInstance] ` -and $currentAction.Execute -eq "powershell.exe" ` - -and $currentAction.Argument -eq $taskActionArgument) { + -and $currentAction.Argument -eq $expectedRetrievedActionArgument) { # Compare with the expected retrieved format $actionMatches = $true }