Adds native Icinga config support, publish cmdlet

Adds native support for writing Icinga 2 configuration for plugins and allows to easy publish new configurations for modules with the new Cmdlet Publish-IcingaPluginConfiguration
This commit is contained in:
Lord Hepipud 2020-11-20 19:52:32 +01:00
parent 0469908999
commit 3c82757f1f
5 changed files with 327 additions and 10 deletions

View file

@ -16,7 +16,8 @@ Configuring Check-Commands
To get started, there are two ways to configure check command objects: To get started, there are two ways to configure check command objects:
* [Automated configuration](icingaintegration/01-Director-Baskets.md) with Baskets * [Automated Icinga Director configuration](icingaintegration/01-Director-Baskets.md) with Baskets
* [Automated Icinga 2 configuration](icingaintegration/04-Icinga-Config.md) with plain Icinga config
* [Manual configuration](icingaintegration/02-Manual-Integration.md) of check commands * [Manual configuration](icingaintegration/02-Manual-Integration.md) of check commands
* [Using PowerShell Arrays in Icinga](icingaintegration/03-PowerShell-Arrays.md) * [Using PowerShell Arrays in Icinga](icingaintegration/03-PowerShell-Arrays.md)
* [Windows Terminal Integration](icingaintegration/50-Windows-Terminal.md) * [Windows Terminal Integration](icingaintegration/50-Windows-Terminal.md)

View file

@ -24,6 +24,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
* [#149](https://github.com/Icinga/icinga-powershell-framework/pull/149) Adds support to add Wmi permissions for a specific user and namespace with `Add-IcingaWmiPermissions`. In addition you can remove users from Wmi namespaces by using `Remove-IcingaWmiPermissions` * [#149](https://github.com/Icinga/icinga-powershell-framework/pull/149) Adds support to add Wmi permissions for a specific user and namespace with `Add-IcingaWmiPermissions`. In addition you can remove users from Wmi namespaces by using `Remove-IcingaWmiPermissions`
* [#155](https://github.com/Icinga/icinga-powershell-framework/pull/155) Adds support to write all objects collected by `Get-IcingaWindowsInformation` into the Windows EventLog in case the debug output for the Icinga PowerShell Framework is enabled. * [#155](https://github.com/Icinga/icinga-powershell-framework/pull/155) Adds support to write all objects collected by `Get-IcingaWindowsInformation` into the Windows EventLog in case the debug output for the Icinga PowerShell Framework is enabled.
* [#162](https://github.com/Icinga/icinga-powershell-framework/pull/162) Adds feature to test the length of plugin custom variables during config generation and throws error in case the total length is bigger than 64 digits, as imports into the Icinga Director by using baskets is not possible otherwise * [#162](https://github.com/Icinga/icinga-powershell-framework/pull/162) Adds feature to test the length of plugin custom variables during config generation and throws error in case the total length is bigger than 64 digits, as imports into the Icinga Director by using baskets is not possible otherwise
* [#163](https://github.com/Icinga/icinga-powershell-framework/pull/163) Adds native support for writing Icinga 2 configuration for plugins and allows to easy publish new configurations for modules with the new Cmdlet `Publish-IcingaPluginConfiguration`
### Bugfixes ### Bugfixes

View file

@ -0,0 +1,56 @@
# Icinga Config Generator
To make the integration as easy as possible, the Framework is shipping with an Icinga 2 configuration generator. Each Check-Plugin available within the Framework is able to auto-generate a Icinga config which can be copied to your Icinga 2 hosts.
## Generating Configuration
To automatically generate the Icinga configuration, open a PowerShell terminal and type in
```powershell
icinga
```
to load the Icinga PowerShell Framework.
Afterwards use the command
```powershell
Get-IcingaCheckCommandConfig -IcingaConfig;
```
to automatically generate the configuration for all found Check-Commands.
If you wish to specify specific commands only, you can filter them as well:
```powershell
Get-IcingaCheckCommandConfig -CheckName Invoke-IcingaCheckBiosSerial, Invoke-IcingaCheckCPU -IcingaConfig;
```
Last but not least you can output the Icinga configuration directly into a file. To do this, simply use the `OutDirectory` argument. You only require to specify a directory here, as the file including a timestamp is generated by the Cmdlet itself
```powershell
Get-IcingaCheckCommandConfig -OutDirectory 'C:\Users\myuser\Documents\'
```
Once the file is exported, you can copy the `.conf` files onto your Icinga 2 hosts to use them.
**Note:** Because of a possible configuration error cased by multiple `PowerShell Base` CheckCommands, it is generated separately. You only require this once on your system and is cross-compatible with every single CheckCommand.
## Custom File Names
You can modify the name of the output `.conf` file by using the `-FileName` argument in combination with the other arguments:
```powershell
Get-IcingaCheckCommandConfig -CheckName Invoke-IcingaCheckBiosSerial, Invoke-IcingaCheckCPU -IcingaConfig -OutDirectory 'C:\Users\myuser\Documents\' -FileName 'IcingaForWindows';
```
This will generate the plugins configuration `C:\Users\myuser\Documents\IcingaForWindows.conf` and `C:\Users\myuser\Documents\PowerShell_Base.conf`
The `.conf` ending is not required, as the Cmdlet will take care of that for you.
## Developer Note
The generated Icinga configuration will benefit from a detailed documentation of the module and each single argument. Descriptions for arguments are parsed into the commands description field, informing users of what the argument actually does. Furthermore arguments are automatically mapped to certain object types. A `switch` argument for example will always be rendered with a `set_if` flag, ensuring you only require to set the corresponding custom variable to true to set this argument.
In addition `array` arguments use the Icinga DSL to properly build PowerShell arrays based on Icinga arrays.
This will increase the entire usability of the module and prevent you from having to document plugins multiple times.

View file

@ -0,0 +1,104 @@
<#
.SYNOPSIS
Fetches plugins within the namespace `Invoke-IcingaCheck*` for a given
component name or the direct path and creates Icinga Director as well as
Icinga 2 configuration files.
The configuration files are printed within a `config` folder of the
specific module and splitted into `director` and `icinga`
.DESCRIPTION
etches plugins within the namespace `Invoke-IcingaCheck*` for a given
component name or the direct path and creates Icinga Director as well as
Icinga 2 configuration files.
The configuration files are printed within a `config` folder of the
specific module and splitted into `director` and `icinga`
.FUNCTIONALITY
Creates Icinga 2 and Icinga Director configuration files for plugins
.EXAMPLE
PS>Publish-IcingaPluginConfiguration -ComponentName 'plugins';
.EXAMPLE
PS>Publish-IcingaPluginConfiguration -ComponentPath 'C:\Program Files\WindowsPowerShell\modules\icinga-powershell-plugins';
.PARAMETER ComponentName
The name of the component to lookup for plugins and write configuration for.
The leading icinga-powershell- is not required and you should simply use the name,
like 'plugins' or 'mssql'
.PARAMETER ComponentPath
The path to the root directory of a PowerShell Plugin repository, like
'C:\Program Files\WindowsPowerShell\modules\icinga-powershell-plugins'
.INPUTS
System.String
.LINK
https://github.com/Icinga/icinga-powershell-framework
#>
function Publish-IcingaPluginConfiguration()
{
param (
[string]$ComponentName,
[string]$ComponentPath
);
if ([string]::IsNullOrEmpty($ComponentName) -And [string]::IsNullOrEmpty($ComponentPath)) {
Write-IcingaConsoleError 'Please specify either a component name like "plugins" or set the component path to the root folder if a component, like "C:\Program Files\WindowsPowerShell\modules\icinga-powershell\plugins".';
return;
}
if ([string]::IsNullOrEmpty($ComponentPath)) {
$ComponentPath = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath ([string]::Format('icinga-powershell-{0}', $ComponentName));
}
if ((Test-Path $ComponentPath) -eq $FALSE) {
Write-IcingaConsoleError 'The path "{0}" for the Icinga for Windows component is not valid' -Objects $ComponentPath;
return;
}
try {
Import-Module $ComponentPath -Global -Force -ErrorAction Stop;
} catch {
[string]$Message = $_.Exception.Message;
Write-IcingaConsoleError 'Failed to import the module on path "{0}". Please verify that this is a valid PowerShell module root folder. Exception: {1}{2}' -Objects $ComponentPath, (New-IcingaNewLine), $Message;
return;
}
$CheckCommands = Get-Command -ListImported -Name 'Invoke-IcingaCheck*' -ErrorAction SilentlyContinue;
if ($null -eq $CheckCommands) {
Write-IcingaConsoleError 'No Icinga CheckCommands were configured for module "{0}". Please verify that this is a valid PowerShell module root folder. Exception: {1}{2}' -Objects $ComponentPath, (New-IcingaNewLine), $Message;
return;
}
[array]$CheckList = @();
[string]$BasketConfigDir = Join-Path -Path $ComponentPath -ChildPath 'config\director';
[string]$IcingaConfigDir = Join-Path -Path $ComponentPath -ChildPath 'config\icinga';
if ((Test-Path $BasketConfigDir)) {
Remove-Item -Path $BasketConfigDir -Recurse -Force | Out-Null;
}
if ((Test-Path $IcingaConfigDir)) {
Remove-Item -Path $IcingaConfigDir -Recurse -Force | Out-Null;
}
if ((Test-Path $BasketConfigDir) -eq $FALSE) {
New-Item -Path $BasketConfigDir -ItemType Directory | Out-Null;
}
if ((Test-Path $IcingaConfigDir) -eq $FALSE) {
New-Item -Path $IcingaConfigDir -ItemType Directory | Out-Null;
}
foreach ($check in $CheckCommands) {
[string]$CheckPath = $check.Module.ModuleBase;
if ($CheckPath.Contains($ComponentPath) -eq $FALSE) {
continue;
}
$CheckList += [string]$check;
Get-IcingaCheckCommandConfig -CheckName $check -OutDirectory $BasketConfigDir -FileName $check;
Get-IcingaCheckCommandConfig -CheckName $check -OutDirectory $IcingaConfigDir -FileName $check -IcingaConfig;
}
Get-IcingaCheckCommandConfig -CheckName $CheckList -OutDirectory $BasketConfigDir -FileName ([string]::Format('{0}_Bundle', (Get-Culture).TextInfo.ToTitleCase($ComponentName)));
Get-IcingaCheckCommandConfig -CheckName $CheckList -OutDirectory $IcingaConfigDir -FileName ([string]::Format('{0}_Bundle', (Get-Culture).TextInfo.ToTitleCase($ComponentName))) -IcingaConfig;
}

View file

@ -10,7 +10,7 @@
.FUNCTIONALITY .FUNCTIONALITY
This module is intended to be used to export one or all PowerShell-Modules with the namespace 'Invoke-IcingaCheck'. This module is intended to be used to export one or all PowerShell-Modules with the namespace 'Invoke-IcingaCheck'.
The JSON-Export, which will be egenerated through this module is structured like an Icinga-Director-JSON-Export, so it can be imported via the Icinga-Director the same way. The JSON-Export, which will be generated through this module is structured like an Icinga-Director-JSON-Export, so it can be imported via the Icinga-Director the same way.
.EXAMPLE .EXAMPLE
PS>Get-IcingaCheckCommandConfig PS>Get-IcingaCheckCommandConfig
@ -47,7 +47,14 @@
.PARAMETER CheckName .PARAMETER CheckName
Used to specify an array of commands which should be exported. Used to specify an array of commands which should be exported.
Seperated with ',' Separated with ','
.PARAMETER FileName
Define a custom file name for the exported `.json`/`.conf` file
.PARAMETER IcingaConfig
Will switch the configuration generator to write plain Icinga 2 `.conf`
files instead of Icinga Director Basket `.json` files
.INPUTS .INPUTS
System.Array System.Array
@ -65,7 +72,9 @@ function Get-IcingaCheckCommandConfig()
{ {
param( param(
[array]$CheckName, [array]$CheckName,
[string]$OutDirectory [string]$OutDirectory = '',
[string]$Filename,
[switch]$IcingaConfig
); );
# Check whether all Checks will be exported or just the ones specified # Check whether all Checks will be exported or just the ones specified
@ -161,7 +170,7 @@ function Get-IcingaCheckCommandConfig()
} }
); );
$Basket.Command[$Data.Name].vars.Add($parameter.Name, $FALSE); $Basket.Command[$Data.Name].vars.Add($IcingaCustomVariable.Replace('$', ''), $FALSE);
} elseif ($parameter.type.name -eq 'Array') { } elseif ($parameter.type.name -eq 'Array') {
# Conditional whether type of parameter is array # Conditional whether type of parameter is array
@ -322,15 +331,26 @@ function Get-IcingaCheckCommandConfig()
} }
} }
# Build Filename with given Timestamp [string]$FileType = '.json';
if ($IcingaConfig) {
$FileType = '.conf';
}
if ([string]::IsNullOrEmpty($Filename)) {
$TimeStamp = (Get-Date -Format "MM-dd-yyyy-HH-mm-ffff"); $TimeStamp = (Get-Date -Format "MM-dd-yyyy-HH-mm-ffff");
$FileName = "PowerShell_CheckCommands_$TimeStamp.json"; $FileName = [string]::Format("PowerShell_CheckCommands_{0}{1}", $TimeStamp, $FileType);
} else {
if ($Filename.Contains($FileType) -eq $FALSE) {
$Filename = [string]::Format('{0}{1}', $Filename, $FileType);
}
}
# Generate JSON Output from Hashtable # Generate JSON Output from Hashtable
$output = ConvertTo-Json -Depth 100 $Basket -Compress; $output = ConvertTo-Json -Depth 100 $Basket -Compress;
# Determine whether json output via powershell or in file (based on param -OutDirectory) # Determine whether json output via powershell or in file (based on param -OutDirectory)
if ([string]::IsNullOrEmpty($OutDirectory) -eq $false) { if ([string]::IsNullOrEmpty($OutDirectory) -eq $false) {
$ConfigDirectory = $OutDirectory;
$OutDirectory = (Join-Path -Path $OutDirectory -ChildPath $FileName); $OutDirectory = (Join-Path -Path $OutDirectory -ChildPath $FileName);
if ((Test-Path($OutDirectory)) -eq $false) { if ((Test-Path($OutDirectory)) -eq $false) {
New-Item -Path $OutDirectory -ItemType File -Force | Out-Null; New-Item -Path $OutDirectory -ItemType File -Force | Out-Null;
@ -340,7 +360,11 @@ function Get-IcingaCheckCommandConfig()
throw 'Failed to create specified directory. Please try again or use a different target location.'; throw 'Failed to create specified directory. Please try again or use a different target location.';
} }
if ($IcingaConfig) {
Write-IcingaPlainConfigurationFiles -Content $Basket -OutDirectory $ConfigDirectory -FileName $FileName;
} else {
Set-Content -Path $OutDirectory -Value $output; Set-Content -Path $OutDirectory -Value $output;
}
# Output-Text # Output-Text
Write-IcingaConsoleNotice "The following commands have been exported:" Write-IcingaConsoleNotice "The following commands have been exported:"
@ -362,6 +386,137 @@ function Get-IcingaCheckCommandConfig()
return $output; return $output;
} }
function Write-IcingaPlainConfigurationFiles()
{
param (
$Content,
$OutDirectory,
$FileName
);
$ConfigDirectory = $OutDirectory;
$OutDirectory = (Join-Path -Path $OutDirectory -ChildPath $FileName);
$IcingaConfig = '';
foreach ($entry in $Content.Command.Keys) {
$CheckCommand = $Content.Command[$entry];
# Skip PowerShell base, this is written at the end in a separate file
if ($CheckCommand.object_name -eq 'PowerShell Base') {
continue;
}
# Create the CheckCommand object
$IcingaConfig += [string]::Format('object CheckCommand "{0}" {{{1}', $CheckCommand.object_name, (New-IcingaNewLine));
# Import all defined import templates
foreach ($import in $CheckCommand.imports) {
$IcingaConfig += [string]::Format(' import "{0}"{1}', $import, (New-IcingaNewLine));
}
$IcingaConfig += New-IcingaNewLine;
if ($CheckCommand.arguments.Count -ne 0) {
# Arguments for the configuration
$IcingaConfig += ' arguments += {'
$IcingaConfig += New-IcingaNewLine;
foreach ($argument in $CheckCommand.arguments.Keys) {
$CheckArgument = $CheckCommand.arguments[$argument];
# Each single argument, like "-Verbosity" = {
$IcingaConfig += [string]::Format(' "{0}" = {{{1}', $argument, (New-IcingaNewLine));
foreach ($argconfig in $CheckArgument.Keys) {
$Value = '';
# Order is numeric -> no "" required
if ($argconfig -eq 'order') {
$StringFormater = ' {0} = {1}{2}';
} else {
# All other entries should be handled as strings and contain ""
$StringFormater =' {0} = "{1}"{2}'
}
# In case it is a hashtable, this is most likely a DSL function
# We have to render it differently to also match the intends
if ($CheckArgument[$argconfig] -is [Hashtable]) {
$Value = $CheckArgument[$argconfig].body;
$DSLArray = $Value.Split("`r`n");
$Value = '';
foreach ($item in $DSLArray) {
if ([string]::IsNullOrEmpty($item)) {
continue;
}
$Value += [string]::Format(' {0}{1}', $item, (New-IcingaNewLine));
}
$Value = $Value.Substring(0, $Value.Length - 2);
$StringFormater =' {0} = {{{{{2}{1}{2} }}}}{2}'
} else {
# All other values besides DSL
$Value = $CheckArgument[$argconfig];
}
# Read description from our variables
if ($argconfig -eq 'value') {
foreach ($item in $Content.DataField.Keys) {
$DataField = $Content.DataField[$item];
if ($Value.Contains($DataField.varname)) {
if ([string]::IsNullOrEmpty($DataField.description)) {
break;
}
$IcingaConfig += [string]::Format(' description = "{0}"{1}', $DataField.description.Replace("`r`n", ''), (New-IcingaNewLine));
break;
}
}
}
# Write the argument to your CheckCommand
$IcingaConfig += [string]::Format($StringFormater, $argconfig, $Value, (New-IcingaNewLine));
}
# Close this specific argument
$IcingaConfig += ' }'
$IcingaConfig += New-IcingaNewLine;
}
# Close all arguments content
$IcingaConfig += New-IcingaNewLine;
$IcingaConfig += ' }'
}
# In case we pre-define custom variables, we should add them here
if ($CheckCommand.vars.Count -ne 0) {
$IcingaConfig += New-IcingaNewLine;
foreach ($var in $CheckCommand.vars.Keys) {
$Value = $CheckCommand.vars[$var];
$IcingaConfig += [string]::Format(' vars.{0} = {1}{2}', $var, $Value, (New-IcingaNewLine));
}
}
# Close the CheckCommand object
$IcingaConfig += '}';
if ($Content.Command.Count -gt 2) {
$IcingaConfig += New-IcingaNewLine;
$IcingaConfig += New-IcingaNewLine;
}
}
# Write the PowerShell Base command to a separate file for Icinga 2 configuration
[string]$PowerShellBase = [string]::Format('object CheckCommand "PowerShell Base" {{{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' import "plugin-check-command"{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' command = [{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' ]{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' timeout = 3m{0}', (New-IcingaNewLine));
$PowerShellBase += '}';
Set-Content -Path (Join-Path -Path $ConfigDirectory -ChildPath 'PowerShell_Base.conf') -Value $PowerShellBase;
Set-Content -Path $OutDirectory -Value $IcingaConfig;
}
function Add-PowerShellDataList() function Add-PowerShellDataList()
{ {
param( param(