WTS Setup Script (Unified)
This document details the setup of the Windows Task Scheduler (WTS) for all ErpCrystal jobs using the Unified folder structure on the E: drive.
Overview
- Deployment Path:
E:\MFGReports\SchedulerLog - Setup Script:
E:\MFGReports\SchedulerLog\setup-scheduler.ps1 - Unified Wrapper:
E:\MFGReports\SchedulerLog\run-job.ps1 - Logs:
E:\MFGReports\SchedulerLog\scheduler.log - Task Naming:
ErpCrystal_<JobName>
Prerequisites
- PowerShell Administrator: Run scripts as Administrator.
- API Running: Ensure the API is accessible at
http://localhost:5116. - Permissions: Ensure the user account has rights to write to
E:\.
Unified Setup Script (setup-scheduler.ps1)
This script configures all 7 jobs. It creates the directories, generates a single generic wrapper script (run-job.ps1), and registers the scheduled tasks to use it.
# ==========================================
# ErpCrystal Development Scheduler Setup (Unified)
# Target: E:\MFGReports\SchedulerLog
# ==========================================
$SchedulerDir = "E:\MFGReports\SchedulerLog"
$LogDir = "E:\MFGReports\SchedulerLog"
$UserAccount = "SYSTEM"
$BaseUrl = "http://localhost:5116"
$ApiKey = "ApiKey"
# --- CONFIGURATION ---
$UnifiedLogFile = "$LogDir\scheduler.log"
$GenericWrapper = "$SchedulerDir\run-job.ps1"
# --- JOB DEFINITIONS ---
# Available Jobs (Uncomment to enable):
# @{ Name = "ErpCrystal_InvoiceEmail"; Endpoint = "invoice-email"; Time = "09:30" },
# @{ Name = "ErpCrystal_ArEmail"; Endpoint = "ar-email"; Time = "10:30" },
# @{ Name = "ErpCrystal_VoucherPayment"; Endpoint = "voucher-payment-email"; Time = "11:00" },
# @{ Name = "ErpCrystal_VoucherReceipt"; Endpoint = "voucher-receipt-email"; Time = "11:30" },
$Jobs = @(
@{ Name = "ErpCrystal_IndentEmail"; Endpoint = "indent-email"; Time = "11:34" },
@{ Name = "ErpCrystal_SalesOrderEmail"; Endpoint = "sales-order-email"; Time = "11:37" },
@{ Name = "ErpCrystal_AIInsights"; Endpoint = "ai-insights"; Time = "11:40" }
)
# 1. Create Directories (Ignore errors if drive mapped differently in session, rely on script location)
if (-not (Test-Path $SchedulerDir)) {
try { New-Item -ItemType Directory -Path $SchedulerDir -Force | Out-Null } catch {}
}
# 2. Create the Single Generic Wrapper Script
Write-Host "`nCreating Generic Wrapper: $GenericWrapper" -ForegroundColor Cyan
$WrapperContent = @"
# ======================================================
# ErpCrystal Generic Job Wrapper
# SYSTEM-safe, HTTPS-safe, localhost-safe
# ======================================================
param(
[string]`$EndpointSuffix,
[string]`$TaskName
)
`$ErrorActionPreference = "Stop"
# ---------- CONFIG ----------
`$BaseUrl = "$BaseUrl"
`$ApiKey = "$ApiKey"
`$LogFile = "$UnifiedLogFile"
`$FullEndpoint = "`$BaseUrl/api/jobs/`$EndpointSuffix"
# ---------- HELPERS ----------
function Log(`$msg) {
`$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"`$timestamp | [`$TaskName] | `$msg" | Out-File -FilePath `$LogFile -Append -Encoding UTF8
}
Log "STARTING..."
Log "User: `$(whoami) | API: `$FullEndpoint"
try {
# ---- LOAD REQUIRED ASSEMBLY ----
Add-Type -AssemblyName System.Net.Http
# ---- TLS ----
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# ---- HTTP CLIENT ----
`$handler = New-Object System.Net.Http.HttpClientHandler
`$handler.ServerCertificateCustomValidationCallback = { `$true }
`$client = New-Object System.Net.Http.HttpClient(`$handler)
`$client.Timeout = [TimeSpan]::FromMinutes(10)
# FORCE HTTP/1.1
`$request = New-Object System.Net.Http.HttpRequestMessage ``
([System.Net.Http.HttpMethod]::Post, `$FullEndpoint)
`$request.Version = [Version]::Parse("1.1")
`$request.Headers.Add("ApiKey", `$ApiKey)
`$response = `$client.SendAsync(`$request).GetAwaiter().GetResult()
if (-not `$response.IsSuccessStatusCode) {
throw "HTTP `$(`$response.StatusCode) - `$(`$response.ReasonPhrase)"
}
`$body = `$response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
# Truncate body for log if too long
`$logBody = if (`$body.Length -gt 500) { `$body.Substring(0, 500) + "..." } else { `$body }
Log "SUCCESS: `$logBody"
exit 0
}
catch {
Log "ERROR: `$_.Exception.Message"
exit 1
}
finally {
if (`$client) { `$client.Dispose() }
}
"@
$WrapperContent | Out-File -FilePath $GenericWrapper -Encoding UTF8 -Force
# 3. Register Each Task
foreach ($Job in $Jobs) {
$TaskName = $Job.Name
$EndpointSuffix = $Job.Endpoint
$JobTime = $Job.Time
Write-Host "Registering Task: $TaskName ($JobTime)"
$Action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$GenericWrapper`" -EndpointSuffix `"$EndpointSuffix`" -TaskName `"$TaskName`""
$Trigger = New-ScheduledTaskTrigger -Daily -At $JobTime
$Settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-MultipleInstances IgnoreNew `
-RestartCount 3 `
-RestartInterval (New-TimeSpan -Minutes 1)
try {
Register-ScheduledTask `
-Action $Action `
-Trigger $Trigger `
-Settings $Settings `
-TaskName $TaskName `
-Description "ErpCrystal Job: $TaskName" `
-User $UserAccount `
-Force | Out-Null
Write-Host " -> OK" -ForegroundColor Green
}
catch {
Write-Error " -> FAILED: $_"
}
}
Write-Host "`nAll Jobs Configured Successfully." -ForegroundColor GreenQuickly Updating Task Times
To update the scheduled times without re-running the full setup script (and potentially overwriting the wrapper), run the following command in PowerShell as Administrator:
# Define your new times here
$Updates = @{
"ErpCrystal_IndentEmail" = "11:47"
"ErpCrystal_SalesOrderEmail" = "11:50"
"ErpCrystal_AIInsights" = "11:45"
}
foreach ($TaskName in $Updates.Keys) {
$Time = $Updates[$TaskName]
Write-Host "Updating $TaskName to $Time..."
$Trigger = New-ScheduledTaskTrigger -Daily -At $Time
Set-ScheduledTask -TaskName $TaskName -Trigger $Trigger | Out-Null
Write-Host "Success!" -ForegroundColor Green
}Maintenance
- Manual Run: Open Task Scheduler, right-click any
ErpCrystal_*task, and select “Run”. - View Logs: Check
E:\MFGReports\SchedulerLog\scheduler.log(all logs are consolidated here). - Edit Script: Modify the single wrapper script at
E:\MFGReports\SchedulerLog\run-job.ps1.
Testing Steps
Testers should follow these detailed steps to verify the WTS setup:
-
Verify Configuration:
- Ensure the API is running at
http://localhost:5116. - Ensure
appsettings.Development.jsonhasApiKeyset to"ApiKey"(or match the script).
- Ensure the API is running at
-
Create Setup Script:
- Ensure the directory
E:\MFGReports\SchedulerLogexists. - Create a new file named
setup-scheduler.ps1in that location. - Copy the script content from the “Unified Setup Script” section above.
- Paste it into your new
setup-scheduler.ps1file and save.
- Ensure the directory
-
Run Setup Script:
- Open PowerShell as Administrator.
- Execute the command:
E:\MFGReports\SchedulerLog\setup-scheduler.ps1 - Success Criteria: Look for the message
"All Jobs Configured Successfully"in green at the end of the output. - Verify Files: Confirm that a new file
E:\MFGReports\SchedulerLog\run-job.ps1has been created.
-
Verify Task Scheduler:
- Open Windows Task Scheduler (search for
taskschd.msc). - Click on Task Scheduler Library in the left pane.
- Check: You should see the following tasks listed:
ErpCrystal_IndentEmailErpCrystal_SalesOrderEmailErpCrystal_AIInsights
- Status: Ensure their status is Ready.
- Open Windows Task Scheduler (search for
-
Manual Job Test:
- In Task Scheduler, right-click
ErpCrystal_IndentEmailand select Run. - Verify Logs: Open
E:\MFGReports\SchedulerLog\scheduler.log. - Success Criteria: Look for a new entry starting with
STARTING...followed bySUCCESS: ....
- In Task Scheduler, right-click
-
Scheduled Run Test:
- Wait for the scheduled trigger time (e.g., 19:50) or use the “Quick Update” script below to set a test time.
- Verify the logs update automatically at the specified time.
Configuration Changes for Testing
The following configuration changes have been applied to facilitate testing and local development. Testers should verify these settings if connectivity issues arise.
1. VS Code Launch Configuration (.vscode/launch.json)
- API Project: Configured to run on HTTP port 5116.
ASPNETCORE_URLS:"http://localhost:5116;"- This ensures the Scheduler scripts (which use HTTP by default) can connect without SSL certificate errors.
2. API Launch Settings (ErpCrystal_MFG.Api/Properties/launchSettings.json)
- Profile
ErpCrystal_MFG.Api:applicationUrl:http://localhost:5116- Matches the VS Code configuration to ensure consistency regardless of how the project is launched.
3. Web App HTTP Client (ErpCrystal_MFG.Web/Program.cs)
- Named Client “mfgapi”:
- Logic updated to read
BaseAddressandApiKeydirectly fromappsettings.json(Section:ApiConfiguration). - Important: Ensure your local
appsettings.Development.jsonhas the correct values:"ApiConfiguration": { "BaseAddress": "http://localhost:5116", "ApiKey": "ApiKey" } - Comment the app.UseHttpsRedirection() line to avoid forced HTTPS redirection during local testing.
- Logic updated to read