Adds handling to add scheduled task background task

This commit is contained in:
Lord Hepipud 2024-03-04 17:30:02 +01:00
parent 5d7824ba5c
commit ed3e8dc8fd
15 changed files with 369 additions and 0 deletions

View file

@ -20,6 +20,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
### Enhancements
* [#679](https://github.com/Icinga/icinga-powershell-framework/pull/679) Adds a new data provider for fetching process information of Windows systems, while sorting all objects based on a process name and their process id
* [#688](https://github.com/Icinga/icinga-powershell-framework/pull/688) Adds new handling to add scheduled tasks in Windows for interacting with Icinga for Windows core functionality as well as an auto renewal task for the Icinga for Windows certificate generation
## 1.11.2 (tbd)

View file

@ -0,0 +1,32 @@
param (
[string]$ServiceName = '',
[string]$TmpFilePath = ''
);
Use-Icinga -Minimal;
[string]$ErrMsg = "";
[hashtable]$ServiceData = @{
'Status' = '';
'Present' = $FALSE;
'Name' = 'Unknown';
'DisplayName' = 'Unknown';
};
try {
$SvcData = Get-Service "$ServiceName" -ErrorAction Stop;
$ServiceData.Status = [string]$SvcData.Status;
$ServiceData.Name = $SvcData.Name;
$ServiceData.DisplayName = $SvcData.DisplayName;
$ServiceData.Present = $TRUE;
} catch {
$ErrMsg = [string]::Format('Failed to get data for service "{0}": {1}', $ServiceName, $_.Exception.Message);
}
Write-IcingaFileSecure -File "$TmpFilePath" -Value (
@{
'Service' = $ServiceData;
'Message' = [string]::Format('Successfully fetched data for service "{0}"', $ServiceName);
'ErrMsg' = $ErrMsg;
} | ConvertTo-Json -Depth 100
);

33
jobs/RenewCertificate.ps1 Normal file
View file

@ -0,0 +1,33 @@
Use-Icinga -Minimal;
# This script will simply install the Icinga for Windows certificate everyime the
# scheduled task is running. This does not impact our system at all, because we
# can update the certificate at any time without having to worry about the state
# To make the configuration of the task as easy as possible, we should fetch
# the current configuration of our REST-Api and check if we provide a custom
# certificate file or thumbprint. In case we do, ensure we use this certificate
# for the icingaforwindows.pfx creation instead of the auto lookup
[hashtable]$RegisteredBackgroundDaemons = Get-IcingaBackgroundDaemons;
[string]$CertificatePath = '';
[string]$CertificateThumbprint = '';
if ($RegisteredBackgroundDaemons.ContainsKey('Start-IcingaWindowsRESTApi')) {
if ($RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi'].ContainsKey('CertFile')) {
$CertificatePath = $RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi']['CertFile'];
}
if ($RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi'].ContainsKey('-CertFile')) {
$CertificatePath = $RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi']['-CertFile'];
}
if ($RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi'].ContainsKey('CertThumbprint')) {
$CertificateThumbprint = $RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi']['CertThumbprint'];
}
if ($RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi'].ContainsKey('-CertThumbprint')) {
$CertificateThumbprint = $RegisteredBackgroundDaemons['Start-IcingaWindowsRESTApi']['-CertThumbprint'];
}
}
Install-IcingaForWindowsCertificate -CertFile $CertificatePath -CertThumbprint $CertificateThumbprint;
# Tell the Task-Scheduler that the script was executed fine
exit 0;

View file

@ -0,0 +1,27 @@
param (
[string]$ServiceName = '',
[string]$TmpFilePath = ''
);
Use-Icinga -Minimal;
[bool]$Success = $TRUE;
[string]$ErrMsg = "";
[string]$Status = '';
try {
Restart-Service "$ServiceName" -ErrorAction Stop;
$Status = [string](Get-Service "$ServiceName").Status;
} catch {
$Success = $FALSE;
$ErrMsg = [string]::Format('Failed to restart service "{0}": {1}', $ServiceName, $_.Exception.Message);
}
Write-IcingaFileSecure -File "$TmpFilePath" -Value (
@{
'Success' = $Success;
'Message' = [string]::Format('Service "{0}" successfully restarted', $ServiceName);
'ErrMsg' = $ErrMsg;
'Status' = $Status;
} | ConvertTo-Json -Depth 100
);

View file

@ -0,0 +1,27 @@
param (
[string]$ServiceName = '',
[string]$TmpFilePath = ''
);
Use-Icinga -Minimal;
[bool]$Success = $TRUE;
[string]$ErrMsg = "";
[string]$Status = '';
try {
Start-Service "$ServiceName" -ErrorAction Stop;
$Status = [string](Get-Service "$ServiceName").Status;
} catch {
$Success = $FALSE;
$ErrMsg = [string]::Format('Failed to start service "{0}": {1}', $ServiceName, $_.Exception.Message);
}
Write-IcingaFileSecure -File "$TmpFilePath" -Value (
@{
'Success' = $Success;
'Message' = [string]::Format('Service "{0}" successfully started', $ServiceName);
'ErrMsg' = $ErrMsg;
'Status' = $Status;
} | ConvertTo-Json -Depth 100
);

View file

@ -0,0 +1,27 @@
param (
[string]$ServiceName = '',
[string]$TmpFilePath = ''
);
Use-Icinga -Minimal;
[bool]$Success = $TRUE;
[string]$ErrMsg = "";
[string]$Status = '';
try {
Stop-Service "$ServiceName" -ErrorAction Stop;
$Status = [string](Get-Service "$ServiceName").Status;
} catch {
$Success = $FALSE;
$ErrMsg = [string]::Format('Failed to stop service "{0}": {1}', $ServiceName, $_.Exception.Message);
}
Write-IcingaFileSecure -File "$TmpFilePath" -Value (
@{
'Success' = $Success;
'Message' = [string]::Format('Service "{0}" successfully stopped', $ServiceName);
'ErrMsg' = $ErrMsg;
'Status' = $Status;
} | ConvertTo-Json -Depth 100
);

View file

@ -89,4 +89,13 @@ function Invoke-IcingaForWindowsMigration()
Set-IcingaForWindowsMigration -MigrationVersion (New-IcingaVersionObject -Version '1.10.1');
}
if (Test-IcingaForWindowsMigration -MigrationVersion (New-IcingaVersionObject -Version '1.12.0')) {
Write-IcingaConsoleNotice 'Applying pending migrations required for Icinga for Windows v1.12.0';
# Add a new scheduled task to automatically renew the Icinga for Windows certificate
Register-IcingaWindowsScheduledTaskRenewCertificate -Force;
Set-IcingaForWindowsMigration -MigrationVersion (New-IcingaVersionObject -Version '1.12.0');
}
}

View file

@ -52,6 +52,8 @@ function Uninstall-IcingaForWindows()
Uninstall-IcingaSecurity -IcingaUser $IcingaUser;
Write-IcingaConsoleNotice 'Uninstalling Icinga Agent';
Uninstall-IcingaAgent -RemoveDataFolder | Out-Null;
Write-IcingaConsoleNotice 'Uninstalling Certificate Renewal Task';
Unregister-IcingaWindowsScheduledTaskRenewCertificate;
if ($ComponentsOnly -eq $FALSE) {
Write-IcingaConsoleNotice 'Uninstalling Icinga for Windows EventLog';
Unregister-IcingaEventLog;

View file

@ -282,6 +282,9 @@ function Start-IcingaForWindowsInstallation()
};
}
# Ensure we add the scheduled task to renew the certificates for Icinga for Windows on a daily basis
Register-IcingaWindowsScheduledTaskRenewCertificate -Force;
switch ($InstallJEAProfile) {
'0' {
Install-IcingaJEAProfile;

View file

@ -0,0 +1,93 @@
function Invoke-IcingaWindowsScheduledTask()
{
param (
[ValidateSet('UninstallAgent', 'UpgradeAgent', 'ReadMSIPackage', 'InstallJEA', 'StartWindowsService', 'StopWindowsService', 'RestartWindowsService', 'GetWindowsService')]
[string]$JobType = '',
[string]$FilePath = '',
[string]$TargetPath = '',
[string]$ObjectName = ''
);
if ((Test-AdministrativeShell) -eq $FALSE) {
Write-IcingaConsoleError 'You require to run this shell in administrative mode for the action "{0}" and object "{1}"' -Objects $JobType, $ObjectName;
return $null;
}
[string]$TaskName = 'Management Task';
[string]$TaskPath = '\Icinga\Icinga for Windows\';
$TaskData = $null;
$TmpFile = New-IcingaTemporaryFile;
if (Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue) {
Unregister-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Confirm:$FALSE -ErrorAction SilentlyContinue | Out-Null;
}
switch ($JobType) {
'StartWindowsService' {
$TaskData = Invoke-IcingaWindowsServiceHandlerTask -ScriptPath 'jobs\StartWindowsService.ps1' -ServiceName $ObjectName -TmpFile $TmpFile.FullName -TaskName $TaskName -TaskPath $TaskPath;
};
'StopWindowsService' {
$TaskData = Invoke-IcingaWindowsServiceHandlerTask -ScriptPath 'jobs\StopWindowsService.ps1' -ServiceName $ObjectName -TmpFile $TmpFile.FullName -TaskName $TaskName -TaskPath $TaskPath;
};
'RestartWindowsService' {
$TaskData = Invoke-IcingaWindowsServiceHandlerTask -ScriptPath 'jobs\RestartWindowsService.ps1' -ServiceName $ObjectName -TmpFile $TmpFile.FullName -TaskName $TaskName -TaskPath $TaskPath;
};
'GetWindowsService' {
$TaskData = Invoke-IcingaWindowsServiceHandlerTask -ScriptPath 'jobs\GetWindowsService.ps1' -ServiceName $ObjectName -TmpFile $TmpFile.FullName -TaskName $TaskName -TaskPath $TaskPath;
};
'UninstallAgent' {
$WinAction = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument ([string]::Format('-WindowStyle Hidden -Command &{{ Use-Icinga -Minimal; Write-IcingaFileSecure -File {0}{1}{0} -Value (Start-IcingaProcess -Executable {0}MsiExec.exe{0} -Arguments {0}"{2}" /q{0} -FlushNewLines | ConvertTo-Json -Depth 100); }}', "'", $TmpFile.FullName, $FilePath, $TargetPath))
Register-ScheduledTask -TaskName $TaskName -Action $WinAction -RunLevel Highest -TaskPath $TaskPath | Out-Null;
Start-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath;
Wait-IcingaWindowsScheduledTask;
# Wait some time before continuing to ensure the service is properly removed
Start-Sleep -Seconds 2;
[string]$TaskOutput = Read-IcingaFileSecure -File $TmpFile.FullName;
$TaskData = ConvertFrom-Json $TaskOutput;
};
'UpgradeAgent' {
};
'ReadMSIPackage' {
if (Test-Path $FilePath) {
$WinAction = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument ([string]::Format('-WindowStyle Hidden -Command &{{ Use-Icinga -Minimal; Write-IcingaFileSecure -File {0}{1}{0} -Value (Read-IcingaMSIMetadata -File {0}{2}{0} | ConvertTo-Json -Depth 100); }}', "'", $TmpFile.FullName, $FilePath))
Register-ScheduledTask -TaskName $TaskName -Action $WinAction -RunLevel Highest -TaskPath $TaskPath | Out-Null;
Start-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath;
Wait-IcingaWindowsScheduledTask;
[string]$TaskOutput = Read-IcingaFileSecure -File $TmpFile.FullName;
$TaskData = ConvertFrom-Json $TaskOutput;
} else {
Write-IcingaConsoleError 'Unable to execute Job Type {0} because the specified file "{1}" does not exist' -Objects $JobType, $FilePath;
}
};
'InstallJEA' {
$WinAction = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument ([string]::Format('-Command &{{ Use-Icinga -Minimal; Install-IcingaJEAProfile; Restart-IcingaWindowsService; }}', "'", $TmpFile.FullName, $FilePath))
Register-ScheduledTask -TaskName $TaskName -Action $WinAction -RunLevel Highest -TaskPath $TaskPath | Out-Null;
Start-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath;
Wait-IcingaWindowsScheduledTask;
# No output data required for this task
};
Default {
Write-IcingaConsoleError 'Unable to execute Job Type {0}. Undefined operation' -Objects $JobType;
};
};
if (Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue) {
Unregister-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Confirm:$FALSE -ErrorAction SilentlyContinue | Out-Null;
}
if (Test-Path $TmpFile) {
Remove-Item -Path $TmpFile -Force;
}
return $TaskData;
}

View file

@ -0,0 +1,33 @@
function Invoke-IcingaWindowsServiceHandlerTask()
{
param (
[string]$ScriptPath = '',
[string]$ServiceName = '',
[string]$TmpFile = '',
[string]$TaskName = '',
[string]$TaskPath = ''
);
if ([string]::IsNullOrEmpty($ScriptPath)) {
return $null;
}
$ScriptPath = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath $ScriptPath;
if ((Test-Path $ScriptPath) -eq $FALSE) {
Write-IcingaConsoleError 'Unable to execute Job. The provided script path "{0}" does not exist' -Objects $ScriptPath;
return $null;
}
$WinAction = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument ([string]::Format("-WindowStyle Hidden -Command &{{ & '{0}' -ServiceName '{1}' -TmpFilePath '{2}' }}", $ScriptPath, $ServiceName, $TmpFile));
Register-ScheduledTask -TaskName $TaskName -Action $WinAction -RunLevel Highest -TaskPath $TaskPath | Out-Null;
Start-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath;
Wait-IcingaWindowsScheduledTask;
[string]$TaskOutput = Read-IcingaFileSecure -File $TmpFile;
$TaskData = ConvertFrom-Json $TaskOutput;
return $TaskData;
}

View file

@ -0,0 +1,26 @@
function Wait-IcingaWindowsScheduledTask()
{
param (
[string]$TaskName = 'Management Task',
[string]$TaskPath = '\Icinga\Icinga for Windows\',
[int]$Timeout = 180
);
[int]$TimeoutTicks = $Timeout * 1000;
while ($TimeoutTicks -gt 0) {
$TaskStatus = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath;
if ($TaskStatus.State -eq 'Ready') {
break;
}
Start-Sleep -Milliseconds 500;
$TimeoutTicks -= 500;
}
if ($TimeoutTicks -le 0) {
Stop-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath | Out-Null;
Write-IcingaConsoleError 'The scheduled task "{0}" at path "{1}" could not be executed within {2} seconds and run into a timeout' -Objects $TaskName, $TaskPath, $Timeout;
}
}

View file

@ -0,0 +1,26 @@
function Register-IcingaWindowsScheduledTaskRenewCertificate()
{
param (
[switch]$Force = $FALSE
);
[string]$TaskName = 'Renew Certificate';
[string]$TaskPath = '\Icinga\Icinga for Windows\';
$RenewCertificateTask = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue;
if ($null -ne $RenewCertificateTask -And $Force -eq $FALSE) {
Write-IcingaConsoleWarning -Message 'The {0} task is already present. User -Force to enforce the re-creation' -Objects $TaskName;
return;
}
$ScriptPath = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath '\jobs\RenewCertificate.ps1';
$TaskTrigger = New-ScheduledTaskTrigger -Daily -DaysInterval 1 -At '1am';
$TaskAction = New-ScheduledTaskAction -Execute 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -Argument ([string]::Format("-WindowStyle Hidden -Command &{{ & '{0}' }}", $ScriptPath));
$TaskPrincipal = New-ScheduledTaskPrincipal -GroupId 'S-1-5-32-544' -RunLevel 'Highest';
$TaskSettings = New-ScheduledTaskSettingsSet -DontStopIfGoingOnBatteries -AllowStartIfOnBatteries -StartWhenAvailable;
Register-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Force -Principal $TaskPrincipal -Action $TaskAction -Trigger $TaskTrigger -Settings $TaskSettings;
Write-IcingaConsoleWarning -Message 'The task "{0}" has been successfully registered at location "{1}".' -Objects $TaskName, $TaskPath;
}

View file

@ -0,0 +1,14 @@
function Start-IcingaWindowsScheduledTaskRenewCertificate()
{
[string]$TaskName = 'Renew Certificate';
[string]$TaskPath = '\Icinga\Icinga for Windows\';
$RenewCertificateTask = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue;
if ($null -eq $RenewCertificateTask) {
Write-IcingaConsoleNotice -Message 'The "{0}" task is not present on this system.' -Objects $TaskName;
return;
}
Start-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath;
}

View file

@ -0,0 +1,16 @@
function Unregister-IcingaWindowsScheduledTaskRenewCertificate()
{
[string]$TaskName = 'Renew Certificate';
[string]$TaskPath = '\Icinga\Icinga for Windows\';
$RenewCertificateTask = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue;
if ($null -eq $RenewCertificateTask) {
Write-IcingaConsoleNotice -Message 'The "{0}" task is not present on this system.' -Objects $TaskName;
return;
}
Stop-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath | Out-Null;
Unregister-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Confirm:$FALSE -ErrorAction SilentlyContinue | Out-Null;
Write-IcingaConsoleNotice -Message 'The "{0}" task was removed from the system.' -Objects $TaskName;
}