$ErrorActionPreference = 'Stop' $taskRoot = 'C:\laragon\www\portal-investidor' $planOutput = Join-Path $taskRoot 'outputs\investmoz-workflow-service-plan-20261011' $privateOutput = Join-Path $taskRoot 'storage\app\private\investmoz-v2\portal-audit\workflow-service-plan-20261011' New-Item -ItemType Directory -Path $planOutput,$privateOutput -Force | Out-Null $utf8 = New-Object System.Text.UTF8Encoding($false) $phpBinary = 'C:\laragon\bin\php\php-8.5.11-Win32-vs17-x64\php.exe' $clamDirectory = Join-Path $taskRoot 'storage\app\private\investmoz-v2\portal-audit\clamav' $clamBinary = Join-Path $clamDirectory 'runtime\clamav-1.5.4.win.x64' $taskService = New-Object -ComObject 'Schedule.Service' $taskService.Connect() function New-ProposedTaskXml { param([string]$Name,[string]$Executable,[string]$Arguments,[string]$WorkingDirectory,[string]$Interval,[string]$Limit) # NewTask returns an in-memory definition. No RegisterTaskDefinition or task/service mutation. $definition = $taskService.NewTask(0) $definition.RegistrationInfo.Description = 'PROPOSAL ONLY. Not installed. InvestMoz B1/B2 review required.' $definition.Principal.Id = 'Service' $definition.Actions.Context = 'Service' $definition.Principal.UserId = 'S-1-5-19' $definition.Principal.LogonType = 5 $definition.Principal.RunLevel = 0 $definition.Settings.MultipleInstances = 2 $definition.Settings.DisallowStartIfOnBatteries = $false $definition.Settings.StopIfGoingOnBatteries = $false $definition.Settings.AllowHardTerminate = $true $definition.Settings.StartWhenAvailable = $true $definition.Settings.ExecutionTimeLimit = $Limit if ($Name -ne 'InvestMoz Portal Documentary Worker') { $definition.Settings.RestartInterval = 'PT1M' $definition.Settings.RestartCount = 3 } $boot = $definition.Triggers.Create(8) $boot.Enabled = $true $boot.Delay = 'PT1M' $timer = $definition.Triggers.Create(1) $timer.StartBoundary = (Get-Date).AddMinutes(1).ToString('yyyy-MM-ddTHH:mm:ss') $timer.Repetition.Interval = $Interval $timer.Enabled = $true $action = $definition.Actions.Create(0) $action.Path = $Executable $action.Arguments = $Arguments $action.WorkingDirectory = $WorkingDirectory $xml = $definition.XmlText.Replace('encoding="UTF-16"','encoding="UTF-8"') $validation = $taskService.NewTask(0) $validation.XmlText = $definition.XmlText [System.IO.File]::WriteAllText((Join-Path $planOutput ($Name.Replace(' ','-')+'.xml')), $xml, $utf8) } $priorTasks = @() foreach ($name in @('InvestMoz ClamAV Loopback Scanner','InvestMoz ClamAV Signature Update')) { $task = Get-ScheduledTask -TaskName $name -TaskPath '\' $priorXml = Export-ScheduledTask -TaskName $name -TaskPath '\' $file = Join-Path $privateOutput ($name.Replace(' ','-')+'-before.xml') [System.IO.File]::WriteAllText($file,$priorXml.Replace('encoding="UTF-16"','encoding="UTF-8"'),$utf8) $info = Get-ScheduledTaskInfo -TaskName $name -TaskPath '\' $priorTasks += [pscustomobject]@{ name=$name; state=[string]$task.State; originalXmlSha256=(Get-FileHash -LiteralPath $file -Algorithm SHA256).Hash.ToLowerInvariant() actions=@($task.Actions | ForEach-Object { [pscustomobject]@{exe=$_.Execute;args=$_.Arguments;cwd=$_.WorkingDirectory} }) logon=[string]$task.Principal.LogonType; restartCount=$task.Settings.RestartCount; lastResult=$info.LastTaskResult } } New-ProposedTaskXml 'InvestMoz ClamAV Loopback Scanner' (Join-Path $clamBinary 'clamd.exe') ('--config-file="'+(Join-Path $clamDirectory 'clamd.conf')+'" --foreground') $clamBinary 'PT1M' 'PT0S' New-ProposedTaskXml 'InvestMoz ClamAV Signature Update' (Join-Path $clamBinary 'freshclam.exe') ('--config-file="'+(Join-Path $clamDirectory 'freshclam.conf')+'"') $clamBinary 'PT2H' 'PT10M' New-ProposedTaskXml 'InvestMoz Portal Documentary Worker' $phpBinary '-c "C:\laragon\bin\php\php-8.5.11-Win32-vs17-x64\php.ini" -d memory_limit=256M artisan queue:work database --queue=default --once --tries=3 --timeout=150 --memory=256 --no-interaction' $taskRoot 'PT1M' 'PT3M' $paths = @( (Join-Path $clamDirectory 'clamd.conf'), (Join-Path $clamDirectory 'freshclam.conf'), (Join-Path $clamBinary 'clamd.exe'), (Join-Path $clamBinary 'freshclam.exe'), $phpBinary,'C:\laragon\bin\php\php-8.5.11-Win32-vs17-x64\php.ini', 'C:\laragon\etc\apache2\mod_php.conf','C:\laragon\etc\apache2\sites-enabled\portal-investidor.test.conf' ) $hashes = @($paths | ForEach-Object { [pscustomobject]@{path=$_;sha256=(Get-FileHash -LiteralPath $_ -Algorithm SHA256).Hash.ToLowerInvariant()} }) $proof = [pscustomobject]@{ at=[DateTime]::UtcNow.ToString('o');scope='Read-only OS inventory and in-memory Task Scheduler proposal definitions' tasks_before=$priorTasks; source_files=$hashes listeners=@(Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -in @(3310,13310) } | Select-Object LocalAddress,LocalPort,OwningProcess) active_services_changes=0;registered_tasks=0;candidate_changed=$false } [System.IO.File]::WriteAllText((Join-Path $privateOutput 'os-before.json'),($proof | ConvertTo-Json -Depth 7),$utf8) @{result='proposal_files_prepared';tasks_registered=0;services_changed=0;output=$planOutput} | ConvertTo-Json -Compress