Adds support for check_by_icingaforwindows.ps1

This commit is contained in:
Lord Hepipud 2022-01-28 23:24:50 +01:00
parent ca1eab8b00
commit e0fc3e52e2
4 changed files with 138 additions and 25 deletions

View file

@ -47,6 +47,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
* [#445](https://github.com/Icinga/icinga-powershell-framework/pull/445) Adds command `Repair-IcingaService` to repair Icinga Agent service in case it was broken during upgrades, mostly caused by `The specified service has been marked for deletion`
* [#448](https://github.com/Icinga/icinga-powershell-framework/pull/448) Adds support to sort arrays without ScriptBlocks
* [#450](https://github.com/Icinga/icinga-powershell-framework/pull/450) Improves show command `Show-IcingaRegisteredServiceChecks`, adds new command `Show-IcingaRegisteredBackgroundDaemons` and extends `Show-Icinga` by both commands and adds debug and api forwarder features to environment list
* [#455](https://github.com/Icinga/icinga-powershell-framework/pull/455) Adds support for remote execution plugin [check_by_icingaforwindows](https://github.com/LordHepipud/check_by_icingaforwindows)
## 1.7.1 (2021-11-11)

View file

@ -2,31 +2,32 @@ function Invoke-IcingaInternalServiceCall()
{
param (
[string]$Command = '',
[array]$Arguments = @()
[array]$Arguments = @(),
[switch]$NoExit = $FALSE
);
# If our Framework is running as daemon, never call our api
if ($Global:Icinga.Protected.RunAsDaemon) {
return;
return $NULL;
}
# If the API forward feature is disabled, do nothing
if ((Get-IcingaFrameworkApiChecks) -eq $FALSE) {
return;
return $NULL;
}
# Test our Icinga for Windows service. If the service is not installed or not running, execute the plugin locally
$IcingaForWindowsService = (Get-Service 'icingapowershell' -ErrorAction SilentlyContinue);
if ($null -eq $IcingaForWindowsService -Or $IcingaForWindowsService.Status -ne 'Running') {
return;
return $NULL;
}
# In case the REST-Api module ist not configured, do nothing
$BackgroundDaemons = Get-IcingaBackgroundDaemons;
if ($null -eq $BackgroundDaemons -Or $BackgroundDaemons.ContainsKey('Start-IcingaWindowsRESTApi') -eq $FALSE) {
return;
return $NULL;
}
$RestApiPort = 5668;
@ -57,6 +58,11 @@ function Invoke-IcingaInternalServiceCall()
[string]$Argument = [string]$Value;
$ArgumentValue = $null;
if ($Argument -eq '-IcingaForWindowsRemoteExecution' -Or $Argument -eq '-IcingaForWindowsJEARemoteExecution') {
$ArgumentIndex += 1;
continue;
}
if ($Value[0] -eq '-') {
if (($ArgumentIndex + 1) -lt $Arguments.Count) {
[string]$NextValue = $Arguments[$ArgumentIndex + 1];
@ -85,7 +91,7 @@ function Invoke-IcingaInternalServiceCall()
} catch {
# Fallback to execute plugin locally
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -ExceptionObject $_ -Objects $Command, $CommandArguments;
return;
return $NULL;
}
# Resolve our result from the API
@ -95,12 +101,12 @@ function Invoke-IcingaInternalServiceCall()
# In case we didn't receive a check result, fallback to local execution
if ([string]::IsNullOrEmpty($IcingaResult.$Command.checkresult)) {
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $CommandArguments;
return;
return $NULL;
}
if ([string]::IsNullOrEmpty($IcingaResult.$Command.exitcode)) {
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $CommandArguments;
return;
return $NULL;
}
$IcingaCR = ($IcingaResult.$Command.checkresult.Replace("`r`n", "`n"));
@ -112,6 +118,12 @@ function Invoke-IcingaInternalServiceCall()
}
}
if ($NoExit) {
Set-IcingaInternalPluginExitCode -ExitCode $IcingaResult.$Command.exitcode;
return $IcingaCR;
}
# Print our response and exit with the provide exit code
Write-IcingaConsolePlain $IcingaCR;
exit $IcingaResult.$Command.exitcode;

View file

@ -4,34 +4,103 @@ function Exit-IcingaExecutePlugin()
[string]$Command = ''
);
$JEAProfile = Get-IcingaJEAContext;
[string]$JEAProfile = Get-IcingaJEAContext;
[bool]$CheckByIcingaForWindows = $FALSE;
[bool]$CheckByJEAShell = $FALSE;
if ($args -Contains '-IcingaForWindowsRemoteExecution') {
$CheckByIcingaForWindows = $TRUE;
}
if ($args -Contains '-IcingaForWindowsJEARemoteExecution') {
$CheckByJEAShell = $TRUE;
}
# We use the plugin check_by_icingaforwindows.ps1 to execute
# checks from a Linux/Windows remote source
if ($CheckByIcingaForWindows) {
# First try to queue the check over the REST-Api
$CheckResult = Invoke-IcingaInternalServiceCall -Command $Command -Arguments $args -NoExit;
if ($null -ne $CheckResult) {
# Seems we got a result
Write-IcingaConsolePlain -Message $CheckResult;
# Do not close the session, we need to read the ExitCode from Get-IcingaInternalPluginExitCode
# The plugin itself will terminate the session
return;
}
# We couldn't use our Rest-Api and Api-Checks feature, then lets execute the plugin locally
# Set daemon true, because this will change internal handling for errors and plugin output
$Global:Icinga.Protected.RunAsDaemon = $TRUE;
try {
# Execute our plugin
(& $Command @args) | Out-Null;
} catch {
# Handle errors within our plugins
# If anything goes wrong handle the error very detailed
$Global:Icinga.Protected.RunAsDaemon = $FALSE;
Write-IcingaExecutePluginException -Command $Command -ErrorObject $_ -Arguments $args;
$args.Clear();
# Do not close the session, we need to read the ExitCode from Get-IcingaInternalPluginExitCode
# The plugin itself will terminate the session
return;
}
# Disable it again - we need to write data to our shell now. Not very intuitive, but it is the easiest
# solution to do it this way
$Global:Icinga.Protected.RunAsDaemon = $FALSE;
# Now print the result to shell
Write-IcingaPluginResult -PluginOutput (Get-IcingaInternalPluginOutput) -PluginPerfData (Get-IcingaCheckSchedulerPerfData);
# Do not close the session, we need to read the ExitCode from Get-IcingaInternalPluginExitCode
# The plugin itself will terminate the session
return;
}
# Regardless of JEA enabled or disabled, forward all checks to the internal API
# and check if we get a result from there
Invoke-IcingaInternalServiceCall -Command $Command -Arguments $args;
try {
# If the plugin is not installed, throw a good exception
Exit-IcingaPluginNotInstalled -Command $Command;
# In case we have JEA enabled on our system, this shell currently open most likely has no
# JEA configuration installed. This is because a JEA shell will not return an exit code and
# Icinga relies on that. Therefor we will try to open a new PowerShell with the JEA configuration
# assigned for Icinga for Windows, execute the plugins there and return the result
if ([string]::IsNullOrEmpty($JEAProfile) -eq $FALSE) {
$ErrorHandler = ''
$JEARun = (
& powershell.exe -ConfigurationName $JEAProfile -NoLogo -NoProfile -Command {
# Load Icinga for Windows
Use-Icinga;
# Enable our JEA context
$Global:Icinga.Protected.JEAContext = $TRUE;
# Parse the arguments our previous shell received
$Command = $args[0];
$Arguments = $args[1];
$Output = '';
try {
# Try executing our checks, store the exit code and plugin output
$ExitCode = (& $Command @Arguments);
$Output = (Get-IcingaInternalPluginOutput);
$ExitCode = (Get-IcingaInternalPluginExitCode);
} catch {
# If we failed for some reason, print a detailed error and use exit code 3 to mark the check as unkown
$Output = [string]::Format('[UNKNOWN] Icinga Exception: Error while executing plugin in JEA context{0}{0}{1}', (New-IcingaNewLine), $_.Exception.Message);
$ExitCode = 3;
}
# Return the result to our main PowerShell
return @{
'Output' = $Output;
'PerfData' = (Get-IcingaCheckSchedulerPerfData)
@ -40,34 +109,27 @@ function Exit-IcingaExecutePlugin()
} -args $Command, $args
) 2>$ErrorHandler;
# If we have an exit code larger or equal 0, the execution inside the JEA shell was successfully and we can share the result
# In case we had an error inside the JEA shell, it will returned here as well
if ($LASTEXITCODE -ge 0) {
Write-IcingaPluginResult -PluginOutput $JEARun.Output -PluginPerfData $JEARun.PerfData;
exit $JEARun.ExitCode;
} else {
# If for some reason the PowerShell could not be started within JEA context, we can throw an exception with exit code 3
# to mark the check as unknown including our error message
Write-IcingaConsolePlain '[UNKNOWN] Icinga Exception: Unable to start the PowerShell.exe with the provided JEA profile "{0}" for CheckCommand: {1}' -Objects $JEAProfile, $Command;
exit 3;
}
} else {
# If we simply run the check without JEA context or from remote, we can just execute the plugin and
# exit with the exit code received from the result
exit (& $Command @args);
}
} catch {
$ExMsg = $_.Exception.Message;
$StackTrace = $_.ScriptStackTrace;
$ExErrorId = $_.FullyQualifiedErrorId;
$ArgName = $_.Exception.ParameterName;
$ListArgs = $args;
if ($ExErrorId -Like "*ParameterArgumentTransformationError*" -And $ExMsg.Contains('System.Security.SecureString')) {
$ExMsg = [string]::Format(
'Cannot bind parameter {0}. Cannot convert the provided value for argument "{0}" of type "System.String" to type "System.Security.SecureString".',
$ArgName
);
# If anything goes wrong handle the error
Write-IcingaExecutePluginException -Command $Command -ErrorObject $_ -Arguments $args;
$args.Clear();
$ListArgs = 'Hidden for security reasons';
}
Write-IcingaConsolePlain '[UNKNOWN] Icinga Exception: {0}{1}{1}CheckCommand: {2}{1}Arguments: {3}{1}{1}StackTrace:{1}{4}' -Objects $ExMsg, (New-IcingaNewLine), $Command, $ListArgs, $StackTrace;
exit 3;
}
}

View file

@ -0,0 +1,38 @@
function Write-IcingaExecutePluginException()
{
param (
$Command = '',
$ErrorObject = $null,
$Arguments = @()
);
if ($null -eq $ErrorObject) {
return;
}
$ExMsg = $ErrorObject.Exception.Message;
$StackTrace = $ErrorObject.ScriptStackTrace;
$ExErrorId = $ErrorObject.FullyQualifiedErrorId;
$ArgName = $ErrorObject.Exception.ParameterName;
$ListArgs = @();
foreach ($entry in $Arguments) {
if ($entry -eq '-IcingaForWindowsRemoteExecution' -Or $entry -eq '-IcingaForWindowsJEARemoteExecution') {
continue;
}
$ListArgs += $entry;
}
if ($ExErrorId -Like "*ParameterArgumentTransformationError*" -And $ExMsg.Contains('System.Security.SecureString')) {
$ExMsg = [string]::Format(
'Cannot bind parameter {0}. Cannot convert the provided value for argument "{0}" of type "System.String" to type "System.Security.SecureString".',
$ArgName
);
$Arguments.Clear();
$ListArgs = 'Hidden for security reasons';
}
Write-IcingaConsolePlain '[UNKNOWN] Icinga Exception: {0}{1}{1}CheckCommand: {2}{1}Arguments: {3}{1}{1}StackTrace:{1}{4}' -Objects $ExMsg, (New-IcingaNewLine), $Command, $ListArgs, $StackTrace;
$Global:Icinga.Private.Scheduler.ExitCode = 3;
}