CheckMK.psm1
|
<#
.SYNOPSIS Funktionssammlung für CheckMK .DESCRIPTION Dieses Modul soll mit der Zeit wachsen. Es sollen nicht unbedingt alle Funktionen der CheckMK API abgebildet werden, allerdings die am häufigsten verwendeten. Wenn jemand einen Schnittstelle der API anspricht, welche das Modul noch nicht abdeckt, kann er das Modul gerne ergänzen. -Verbose hilft Fehler zu finden. HTTP Error Codes lassen sich so anzeigen. In der Dokumentation zur API ist je Endpunkt aufgelistet, was welcher Code bedeutet. Lassen sich Fehler nicht erklären, kann die interaktive Dokumentation genutzt werden. Diese enthält bei falscher Syntax recht genaue Fehlerbeschreibungen. .LINK Dokumentation https://<CheckMK-Host>/<sitename>/check_mk/openapi/ .LINK Interaktive Dokumentation https://<CheckMK-Host>/<sitename>/check_mk/api/1.0/ui/ #> function Set-CertificateValidationPolicy { # Alternative zu invoke-webRequest -SkipCertificateCheck, welches es nur in PowerShell 7 gibt # Die Änderung soll nur in PS5 erfolgen. Ab PS7 bitte den Schalter an Invoke-Webrequest nutzen If ($PSVersionTable.PSVersion -like '5.*') { If ([System.Net.ServicePointManager]::CertificatePolicy.GetType().Name -eq 'DefaultCertPolicy') { class TrustAllCertsPolicy : System.Net.ICertificatePolicy { [bool] CheckValidationResult ( [System.Net.ServicePoint]$srvPoint, [System.Security.Cryptography.X509Certificates.X509Certificate]$certificate, [System.Net.WebRequest]$request, [int]$certificateProblem ) { return $true } } [System.Net.ServicePointManager]::CertificatePolicy = New-Object -TypeName TrustAllCertsPolicy } } } function EscapeNonAscii([Parameter(Mandatory,ValueFromPipeline)][string] $s) { # Ab Powershell 7 soll stattdessen an ConvertTo-Json der Parameter -EscapeHandling EscapeNonAscii verwendet werden. Process { $sb = New-Object System.Text.StringBuilder for ([int] $i = 0; $i -lt $s.Length; $i++) { $c = $s[$i] if ($c -gt 127) { $sb = $sb.Append("\u").Append(([int] $c).ToString("X").PadLeft(4, "0")) } else { $sb = $sb.Append($c) } } $sb.ToString() } } function Get-CMKHeader { [CmdletBinding()] param ( [parameter(Mandatory, HelpMessage = 'DNS-Name des CheckMK-Servers')] [ValidateNotNullOrEmpty()] [string] $Hostname, [parameter(Mandatory, HelpMessage = 'Instanz auf dem CheckMK-Server')] [ValidateNotNullOrEmpty()] [string] $Sitename, [parameter(Mandatory, HelpMessage = 'Benutzer mit genügend API-Rechten in CheckMK.')] [ValidateNotNullOrEmpty()] [string] $Username, [parameter(Mandatory, HelpMessage = 'Passwort zum Zugriff auf die CheckMK API.')] [ValidateNotNullOrEmpty()] [SecureString] $Secret, [parameter(HelpMessage = 'Wenn bestehende Objekte bearbeitet werden sollen, muss das ETag des Objektes zuvor abgerufen und bei der Änderungsanfrage in den Header eingefügt werden.')] [ValidateNotNullOrEmpty()] [string] $IfMatch ) # Ab PS7 wird ConvertFrom-SecureString möglich $password = [System.Net.NetworkCredential]::new("", $Secret).Password $header = New-Object -TypeName 'System.Collections.Generic.Dictionary[[string],[string]]' $header.Add('Authorization', "Bearer $username $password") $header.Add('Accept', 'application/json') $header.Add('Content-Type', 'application/json') if ($IfMatch) { $header.Add('If-Match', $IfMatch) } return $header } function Invoke-CustomWebRequest { [CmdletBinding()] param ( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestMethod] $Method, [parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Uri, [parameter(Mandatory)] [Object] $Headers, [parameter()] [object] $Body ) # Diese Funktion ist notwendig, da Invoke-WebRequest bei Statuscodes -ne 200 einen Fehler wirft. # Mit Powershell 7 erhält Invoke-Webrequest einen neuen Parameter: -SkipHttpErrorCheck. Damit wäre das hier vermutlich überflüssig. Set-CertificateValidationPolicy $PSBoundParameters.Add('UseBasicParsing', $true) $BaseResponse = try { $PrimaryResponse = Invoke-WebRequest @PSBoundParameters $PrimaryResponse.BaseResponse } catch [System.Net.WebException] { $ErrMessage = $_.ErrorDetails.Message; Write-Verbose "An exception was caught: $($_.Exception.Message)" $ResponseErrorObj = $_.Exception.Response # Nur BaseResponse bei Exceptions möglich Add-Member -InputObject $ResponseErrorObj -NotePropertyName ErrorMessage -NotePropertyValue $ErrMessage #add catched error message to $BaseResponse object $ResponseErrorObj } $ResponseHash = @{ BaseResponse = $BaseResponse Response = $PrimaryResponse } $ResponseObject = New-Object -TypeName psobject -Property $ResponseHash return $ResponseObject } function Invoke-CMKApiCall { [CmdletBinding()] param ( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestMethod] $Method, [parameter(Mandatory, HelpMessage = 'Sub-URI der API Funktion (mit / ab der Versionsangabe)')] [ValidateNotNullOrEmpty()] [string] $Uri, [parameter(Mandatory)] [object] $Connection, [parameter()] [object] $Body, [Parameter()] [switch] $EndpointReturnsList ) # Wandelt das Ergebnis einer CustomWebRequest zu einem Objekt. $ConnectionCheckIntervalMinutes = 60 If ($Global:CMKLastSuccessfulConnect -and (($Global:CMKLastSuccessfulConnect | Get-Date) -gt (Get-Date).AddMinutes(-$ConnectionCheckIntervalMinutes))){ # Recently checked Write-Verbose ":$($MyInvocation.MyCommand): Last connection check at $($Global:CMKLastSuccessfulConnect)." } else { # New check required Write-Verbose ":$($MyInvocation.MyCommand): Connection check..." If (-not (Test-NetConnection -ComputerName $Connection.Hostname -Port 443 -WarningAction SilentlyContinue).TcpTestSucceeded) { Write-Verbose "$($Connection.Hostname) ist nicht erreichbar" throw [System.Net.WebException] } else { $Global:CMKLastSuccessfulConnect = Get-Date -Format 'o' } } $PSBoundParameters.Headers = $Connection.Header $PSBoundParameters.Uri = "$($Connection.APIUrl)$($Uri)" $PSBoundParameters.Remove('Connection') | Out-Null $PSBoundParameters.Remove('EndpointReturnsList') | Out-Null Write-Verbose "$Method $($PSBoundParameters.Uri) --- Body: $($PSBoundParameters.Body)" $Response = Invoke-CustomWebRequest @PSBoundParameters Write-Verbose "$([int]($Response.BaseResponse.StatusCode)) $($Response.BaseResponse.StatusDescription)" if ([int]($Response.BaseResponse.StatusCode) -eq 200) { # 200 Ok $CheckMKObject = ($Response.Response.Content | ConvertFrom-Json) $CheckMKObject | Add-Member -MemberType NoteProperty -Name ETag -Value $Response.Response.Headers.ETag if ($EndpointReturnsList.IsPresent) { return $CheckMKObject.Value } else { return $CheckMKObject } } elseif ((@('Post', 'Delete') -contains $Method) -and ([int]($Response.BaseResponse.StatusCode) -eq 204)) { # 204 No Content } else { # Nicht OK. Error Code lässt sich mit -verbose anzeigen. throw "StatusCode: $([int]($Response.BaseResponse.StatusCode)) StatusDescription: $($Response.BaseResponse.StatusDescription)`r`nMessage: `r`n$($Response.BaseResponse.ErrorMessage)" } } #region Connection function Get-CMKConnection { [CmdletBinding()] param ( [parameter(Mandatory, HelpMessage = 'DNS-Name des CheckMK-Servers')] [ValidateNotNullOrEmpty()] [string] $Hostname, [parameter(Mandatory, HelpMessage = 'Instanz auf dem CheckMK-Server')] [ValidateNotNullOrEmpty()] [string] $Sitename, [parameter(HelpMessage = 'Benutzer mit genügend Rechten in CheckMK. Per Standard wird der Skriptausführende Benutzer gewählt.')] [string] $Username, [parameter(Mandatory, HelpMessage = 'Passwort zum Zugriff auf die CheckMK API.')] [SecureString] $Secret, [parameter(HelpMessage = 'Wenn bestehende Objekte bearbeitet werden sollen, muss das ETag des Objektes zuvor abgerufen und bei der Änderungsanfrage in den Header eingefügt werden.')] [ValidateNotNullOrEmpty()] [string] $IfMatch ) If (-not $PSBoundParameters.ContainsKey('Username')) { $PSBoundParameters.Username = [System.Environment]::UserName } $Connection = @{ hostname = $Hostname sitename = $Sitename username = $PSBoundParameters.Username APIUrl = "https://$hostname/$sitename/check_mk/api/1.0" Header = Get-CMKHeader @PSBoundParameters } return $Connection } #endregion Connection #region Main function Get-CMKServerInfo { [CmdletBinding()] param( [parameter(Mandatory)] [object] $Connection ) return Invoke-CMKApiCall -Method Get -Uri '/version' -Connection $Connection } function Get-CMKPendingChanges { [CmdletBinding()] param ( [Parameter(Mandatory)] [object] $Connection ) return Invoke-CMKApiCall -Method Get -Uri '/domain-types/activation_run/collections/pending_changes' -Connection $Connection } function Invoke-CMKChangeActivation { [CmdletBinding()] param( [Parameter(Mandatory, HelpMessage = 'Abgerufen mit Get-CMKPendingChanges')] [object] $PendingChanges, [parameter(HelpMessage = 'Sollen durch andere Nutzer durchgeführte Änderungen mit Aktiviert werden? Pflicht, wenn es welche gibt.')] [switch] $ForceForeignChanges, [parameter(Mandatory)] [object] $Connection ) $activateChanges = @{ force_foreign_changes = $ForceForeignChanges.IsPresent redirect = $false sites = [array]$Connection.sitename } | ConvertTo-Json | EscapeNonAscii $ConnSecret = $Connection.Header.Authorization.Split(' ')[2] | ConvertTo-SecureString -AsPlainText -Force $oneTimeConnection = Get-CMKConnection -Hostname $Connection.hostname -Sitename $Connection.sitename -Username $Connection.username -Secret $ConnSecret -IfMatch $PendingChanges.Etag try { $CheckMKActivationObject = Invoke-CMKApiCall -Method Post -Uri '/domain-types/activation_run/actions/activate-changes/invoke' -Body $activateChanges -Connection $oneTimeConnection } catch { if ($($_.Exception.Message) -match "Currently there are no changes to activate.") { Write-Warning "Currently there are no changes to activate." return $true } else { Write-Error "Changes could not be activated. Error message: $($_.Exception.Message)" } } if (-not $CheckMKActivationObject) { return $false } $AttemptForCompletion = 0 $maximumAttemptsForCompletion = 14 # Den Wert ggf. noch anpassen. Vielleicht dauern Aktivierungen ja regelmäßig länger. do { Start-Sleep -Seconds 3 $AttemptForCompletion++ $activationStatus = Invoke-CMKApiCall -Method Get -Uri "/objects/activation_run/$($CheckMKActivationObject.id)" -Connection $Connection $result = [string]($activationStatus.title).split(' ')[-1].replace('.', '') } until (([bool]($activationStatus.extensions.is_running) -eq $false) -or ($AttemptForCompletion -gt $maximumAttemptsForCompletion)) If (($result -ne 'complete')) { Write-Verbose "Die Aktivierung der Änderungen konnte nicht innerhalb von $maximumAttemptsForCompletion abgeschlossen werden. Result: $Result" return $false } } #endregion Main #region Hosts function Get-CMKHost { [CmdletBinding()] param ( [Parameter(Mandatory, ParameterSetName = 'Spezifisch')] [ValidateNotNullOrEmpty()] [string] $HostName, [Parameter(Mandatory, ParameterSetName = 'Spezifisch')] [Parameter(Mandatory, ParameterSetName = 'Liste')] $Connection ) If ($PSCmdlet.ParameterSetName -eq 'Spezifisch') { return Invoke-CMKApiCall -Method Get -Uri "/objects/host_config/$($HostName)" -Connection $Connection } elseif ($PSCmdlet.ParameterSetName -eq 'Liste') { return Invoke-CMKApiCall -Method Get -Uri '/domain-types/host_config/collections/all' -Connection $Connection -EndpointReturnsList } } function Get-CMKHostInventory { [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $HostName, [Parameter(Mandatory)] $Connection, [Parameter()] [ValidateSet('json', 'xml')] [string] $OutputFormat='json', [Parameter()] [switch] $AsPlainText ) $Method = 'Get' $Uri = "https://$($Connection.Hostname)/$($Connection.Sitename)/check_mk/host_inv_api.py?host=$HostName&output_format=$OutputFormat" Write-Verbose "$Method $Uri" $Response = Invoke-CustomWebRequest -Method $Method -Uri $Uri -Headers $Connection.Header if ([int]($Response.BaseResponse.StatusCode) -eq 200) { If ($AsPlainText) { return $Response.Response.Content } else { switch ($OutputFormat) { 'xml' { return [xml]($Response.Response.Content) } 'json' { return ($Response.Response.Content | ConvertFrom-Json) } } } } else { # Not OK. throw "StatusCode: $([int]($Response.BaseResponse.StatusCode)) StatusDescription: $($Response.BaseResponse.StatusDescription)`r`nMessage: `r`n$($Response.BaseResponse.ErrorMessage)" } } function New-CMKHost { [CmdletBinding()] param( [Parameter(Mandatory)] [string] $HostName, [Parameter(Mandatory, HelpMessage = 'Pfad zum Ordner. Anstelle von Slash bitte Tilde ~ benutzen. Case-Sensitive. Entspricht dem Attribut id im Objekt von Get-CheckMKFolder.')] [string] $FolderPath, [parameter(Mandatory)] [object] $Connection ) $newHost = @{ folder = "$FolderPath" host_name = "$($HostName)" } | ConvertTo-Json | EscapeNonAscii return Invoke-CMKApiCall -Method Post -Uri '/domain-types/host_config/collections/all' -Body $newHost -Connection $Connection } function New-CMKClusterHost { <# .SYNOPSIS Add cluster to checkmk .DESCRIPTION Add cluster to checkmk .PARAMETER FolderPath The path name of the folder in WATO. case sensitive. corresponds to "id" attribute in Get-CheckMKFolder. example: "~servers/linux" .PARAMETER Nodes an array of nodes .PARAMETER Attributes define attributes like alias, tags, custom variables. example: @{ alias = "PLUTO" tag_criticality = "test" } .EXAMPLE $ClusterAttributes = @{ alias = "MYCLUSTER" tag_criticality = "test" } New-CMKClusterHost -Connection $CMKConn -Hostname mycluster.example -FolderPath "~clusters" -Nodes 'node1','node2' -Attributes $ClusterAttributes #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $HostName, [Parameter(Mandatory, HelpMessage = 'Pfad zum Ordner. Anstelle von Slash bitte Tilde ~ benutzen. Case-Sensitive. Entspricht dem Attribut id im Objekt von Get-CheckMKFolder.')] [string] $FolderPath, [Parameter(Mandatory=$true)] [string[]] $Nodes, [parameter(Mandatory)] [object] $Connection, [Parameter(HelpMessage = 'Hashtable @{attribute = "value"; attr2 = "value"} siehe https://<CheckMK-Host>/<sitename>/check_mk/api/1.0/ui/#/Hosts/cmk.gui.plugins.openapi.endpoints.host_config.create_host')] $Attributes = @{} ) $newCluster = @{ folder = "$FolderPath" host_name = "$($HostName)" nodes = $Nodes attributes = $Attributes } | ConvertTo-Json | EscapeNonAscii try { return Invoke-CMKApiCall -Method Post -Uri '/domain-types/host_config/collections/clusters' -Body $newCluster -Connection $Connection } catch { if ($($_.Exception.Message) -match ".*Host .* already exists.") { Write-Warning "Cluster Host already exists. `r`nFull error message:`r`n$($_.Exception.Message)" } else { Write-Error "Cluster host could not be created in checkmk. `r`nError message:`r`n$($_.Exception.Message)" } } } function Rename-CMKHost { [CmdletBinding()] param( [parameter(Mandatory, HelpMessage = 'Mit Get-CMKHost abgerufen')] [object] $HostObject, [parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $newHostName, [parameter(Mandatory)] [object] $Connection ) # Ist langsam. Behindert den Betrieb von CheckMK (Server steht während der Zeit). Dauer: ca 30 Sekunden # Im Anschluss: Invoke-CMKChangeActivation $ConnSecret = $Connection.Header.Authorization.Split(' ')[2] | ConvertTo-SecureString -AsPlainText -Force $oneTimeConnection = Get-CMKConnection -Hostname $Connection.hostname -Sitename $Connection.sitename -Username $Connection.username -IfMatch $HostObject.Etag -Secret $ConnSecret $newName = @{ new_name = $newHostName } | ConvertTo-Json | EscapeNonAscii return Invoke-CMKApiCall -Method Put -Uri "/objects/host_config/$($HostObject.id)/actions/rename/invoke" -Body $newName -Connection $oneTimeConnection } function Update-CMKHost { [CmdletBinding()] param( [parameter(Mandatory, HelpMessage = 'Mit Get-CMKHost abgerufen')] [object] $HostObject, [parameter(Mandatory, HelpMessage = 'Lies die Doku! https://<CheckMK-Host>/<sitename>/check_mk/api/1.0/ui/#/Hosts/cmk.gui.plugins.openapi.endpoints.host_config.update_host')] $Changeset, [parameter(Mandatory)] [object] $Connection ) # https://<CheckMK-Host>/<sitename>/check_mk/api/1.0/ui/#/Hosts/cmk.gui.plugins.openapi.endpoints.host_config.update_host $ConnSecret = $Connection.Header.Authorization.Split(' ')[2] | ConvertTo-SecureString -AsPlainText -Force $oneTimeConnection = Get-CMKConnection -Hostname $Connection.hostname -Sitename $Connection.sitename -Username $Connection.username -IfMatch $HostObject.Etag -Secret $ConnSecret return Invoke-CMKApiCall -Method Put -Uri "/objects/host_config/$($HostObject.id)" -Body $Changeset -Connection $oneTimeConnection } function Remove-CMKHost { [CmdletBinding()] param( [parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $HostName, [parameter(Mandatory)] [object] $Connection ) return Invoke-CMKApiCall -Method Delete -Uri "/objects/host_config/$HostName" -Connection $Connection } #endregion Hosts #region Hosts Hilfsfunktionen function Set-CMKHostAttribute { [CmdletBinding()] param( [parameter(Mandatory, HelpMessage = 'Mit Get-CMKHost abgerufen', ParameterSetName = 'Update')] [parameter(Mandatory, HelpMessage = 'Mit Get-CMKHost abgerufen', ParameterSetName = 'Remove')] [object] $HostObject, [parameter(Mandatory, ParameterSetName = 'Update')] [Alias('SetAttribute')] [string] $UpdateAttribute, [parameter(Mandatory, ParameterSetName = 'Update')] $Value, [parameter(Mandatory, ParameterSetName = 'Remove')] [string] $RemoveAttribute, [parameter(Mandatory, ParameterSetName = 'Update')] [parameter(Mandatory, ParameterSetName = 'Remove')] [object] $Connection ) #Hinweis zu Custom Host Attributes: Diese lassen sich anlegen und bearbeiten, aber nicht löschen. Da ist die API noch fehlerhaft. $Changeset = @{} If ($PSCmdlet.ParameterSetName -eq 'Update') { $Changeset.update_attributes = @{ $UpdateAttribute = $Value } } elseif ($PSCmdlet.ParameterSetName -eq 'Remove') { $Changeset.remove_attributes = [array]("$RemoveAttribute") } $Changeset = $Changeset | ConvertTo-Json | EscapeNonAscii return Update-CMKHost -HostObject $HostObject -Changeset $Changeset -Connection $Connection } function Add-CMKHostLabel { [CmdletBinding()] param( [parameter(Mandatory)] [object] $HostObject, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Key, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Value, [Parameter(Mandatory)] [object] $Connection ) $Labels = @{} If ($HostObject.extensions.attributes.labels) { Foreach ($Pair in ($HostObject.extensions.attributes.labels.PSObject.Members | Where-Object -FilterScript { $_.MemberType -eq 'NoteProperty' })) { $Labels.add($Pair.Name, $Pair.Value) } } If ($Labels.$Key) { Write-Verbose "Der Schlüssel $Key ist auf $($HostObject.id) bereits vorhanden" return $false } else { $Labels.Add($Key, $Value) return Set-CMKHostAttribute -HostObject $HostObject -UpdateAttribute 'labels' -Value $Labels -Connection $Connection } } function Remove-CMKHostLabel { [CmdletBinding()] param( [parameter(Mandatory)] [object] $HostObject, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Key, [Parameter(Mandatory)] [object] $Connection ) If ($HostObject.extensions.attributes.labels) { $Labels = @{} Foreach ($Pair in ($HostObject.extensions.attributes.labels.PSObject.Members | Where-Object -FilterScript { $_.MemberType -eq 'NoteProperty' })) { $Labels.add($Pair.Name, $Pair.Value) } $Labels.Remove($Key) If ($Labels.Count -gt 0) { return Set-CMKHostAttribute -HostObject $HostObject -UpdateAttribute 'labels' -Value $Labels -Connection $Connection } else { return Set-CMKHostAttribute -HostObject $HostObject -RemoveAttribute 'labels' -Connection $Connection } } else { Write-Verbose "Auf Host $($HostObject.id) sind keine Labels vorhanden" return $false } } #endregion Hosts Hilfsfunktionen #region Folders function Get-CMKFolder { [CmdletBinding()] param ( [Parameter(Mandatory, HelpMessage = 'Pfad zum Ordner. Anstelle von Slash bitte Tilde ~ benutzen. Case-Sensitive. Entspricht dem Attribut id im zurückerhaltenen Objekt.', ParameterSetName = 'Spezifisch')] [ValidateNotNullOrEmpty()] [ValidateScript({ If ( ($_ -notmatch '^~.*$') -or ($_.ToCharArray() -contains @('/', '\')) ) { throw 'Der Ordnerpfad ist nicht wohlgeformt.' } $true })] [string] $FolderPath, [parameter(HelpMessage = 'Liste der Hosts im Ordner einschließen', ParameterSetName = 'Spezifisch')] [switch] $ShowHosts, [parameter(Mandatory, ParameterSetName = 'Spezifisch')] [parameter(Mandatory, ParameterSetName = 'Liste')] $Connection ) If ($PSCmdlet.ParameterSetName -eq 'Spezifisch') { If ($ShowHosts.IsPresent) { $ShowHosts_bool = 'true' } else { $ShowHosts_bool = 'false' } return Invoke-CMKApiCall -Method Get -Uri "/objects/folder_config/$($FolderPath)?show_hosts=$($ShowHosts_bool)" -Connection $Connection } elseif ($PSCmdlet.ParameterSetName -eq 'Liste') { return Invoke-CMKApiCall -Method Get -Uri '/domain-types/folder_config/collections/all?recursive=true&show_hosts=false' -Connection $Connection -EndpointReturnsList } } #endregion Folders #region Downtimes function Get-CMKDowntime { [CmdletBinding()] param( [parameter(HelpMessage = 'Downtimes nur dieses Hosts abfragen')] [string] $HostName, <#[parameter(HelpMessage = 'Downtimes nur dieses Service abfragen. Case-Sensitive')] [string] $ServiceDescription,#> [parameter(Mandatory)] [object] $Connection ) $QueryExtension = '' If ($HostName -or $ServiceDescription) { $QueryExtension += '?' } <#If ($ServiceDescription) { $QueryExtension += "service_description=$($ServiceDescription)" } If ($HostName -and $ServiceDescription) { $QueryExtension += '&' }#> If ($HostName) { $QueryExtension += "host_name=$($HostName)" } return Invoke-CMKApiCall -Method Get -Uri "/domain-types/downtime/collections/all$($QueryExtension)" -Connection $Connection -EndpointReturnsList } function New-CMKDowntime { [CmdletBinding()] param( [parameter(Mandatory, ParameterSetName = 'onHost', HelpMessage = 'Die Downtime wird für den genannten Host gesetzt')] [parameter(Mandatory, ParameterSetName = 'onService', HelpMessage = 'Die Downtime wird für die genannten Services dieses Hosts gesetzt')] [ValidateNotNullOrEmpty()] [string] $HostName, [parameter(Mandatory, ParameterSetName = 'onService', HelpMessage = 'Die Downtime wird nur für angegebene Services gesetzt (Case Sensitive)' )] [ValidateNotNullOrEmpty()] [string[]] $ServiceDescriptions, [parameter(Mandatory = $false, ParameterSetName = 'onHost', HelpMessage = 'Startzeitpunkt ist optional. Wenn nicht befüllt wird die aktuelle Zeit als Start definiert.')] [parameter(Mandatory = $false, ParameterSetName = 'onService', HelpMessage = 'Startzeitpunkt ist optional. Wenn nicht befüllt wird die aktuelle Zeit als Start definiert.')] [datetime] $StartTime = (Get-Date), # EndTime muss zwingend nach StartDate liegen. Ist das nicht der Fall wird kein Fehler gemeldet, CMK legt ohne Fehlermeldung keine Downtime an. [parameter(Mandatory = $true, ParameterSetName = 'onHost', HelpMessage = 'Endzeitpunkt ist nicht optional.')] [parameter(Mandatory = $true, ParameterSetName = 'onService', HelpMessage = 'Endzeitpunkt ist nicht optional.')] [ValidateScript({ if ($_ -gt (Get-Date) -and $_ -gt $StartTime) { $true }else { throw "$_ ist kein valider Wert. Endzeitpunkt muss nach dem Startdatum und in der Zukunft liegen." # Geht nur mit PS6+ # ErrorMessage = "{0} ist kein valider Wert. Endzeitpunkt muss nach dem Startdatum und in der Zukunft liegen." } })] [datetime] $EndTime, [parameter(ParameterSetName = 'onHost')] [parameter(ParameterSetName = 'onService')] [ValidateNotNullOrEmpty()] [string] $Comment, [Parameter(Mandatory = $false, ParameterSetName = 'onHost', HelpMessage = 'Dauer in Minuten. Downtime beginnt erst mit Statuswechsel und gilt für die angegebene Duration. Default ist 0.')] [Parameter(Mandatory = $false, ParameterSetName = 'onService', HelpMessage = 'Dauer in Minuten. Downtime beginnt erst mit Statuswechsel und gilt für die angegebene Duration. Default ist 0.')] [ValidateRange(0,[int]::MaxValue)] [int] $Duration, [parameter(Mandatory, ParameterSetName = 'onHost')] [parameter(Mandatory, ParameterSetName = 'onService')] [object] $Connection ) $Downtime = @{ start_time = ($StartTime | Get-Date -Format 'yyyy-MM-ddTHH:mm:sszzz') #Format ISO 8601 für CheckMK erforderlich end_time = ($EndTime | Get-Date -Format 'yyyy-MM-ddTHH:mm:sszzz') host_name = "$($HostName)" } If ($Comment) { $Downtime.comment = $Comment } if ($Duration) { $Downtime.duration = $Duration } If ($PSCmdlet.ParameterSetName -eq 'onHost') { $Downtime.downtime_type = 'host' $Downtime = $Downtime | ConvertTo-Json | EscapeNonAscii $URI = '/domain-types/downtime/collections/host' } elseif ($PSCmdlet.ParameterSetName -eq 'onService') { $Downtime.downtime_type = 'service' $Downtime.service_descriptions = [array]$ServiceDescriptions $Downtime = $Downtime | ConvertTo-Json | EscapeNonAscii $URI = '/domain-types/downtime/collections/service' } Write-Verbose -Message $Downtime return Invoke-CMKApiCall -Method Post -Uri $URI -Body $Downtime -Connection $Connection } function Remove-CMKDowntime { [CmdletBinding()] param( [parameter(Mandatory, ParameterSetName = 'byID')] [int] $ID, [parameter(Mandatory, ParameterSetName = 'byHostName')] [parameter(Mandatory, ParameterSetName = 'byHostNameAndServiceDescriptions')] [string] $HostName, [parameter(Mandatory, ParameterSetName = 'byHostNameAndServiceDescriptions')] [string[]] $ServiceDescriptions, [parameter(Mandatory, ParameterSetName = 'byHostName')] [parameter(Mandatory, ParameterSetName = 'byID')] [parameter(Mandatory, ParameterSetName = 'byHostNameAndServiceDescriptions')] [object] $Connection ) $Delete = @{} If ($PSCmdlet.ParameterSetName -eq 'byID') { $Delete.delete_type = 'by_id' $Delete.downtime_id = "$ID" $Delete.site_id = "$($Connection.sitename)" } elseif ($PSCmdlet.ParameterSetName -eq 'byHostName') { $Delete.delete_type = 'params' $Delete.host_name = "$($HostName)" } elseif ($PSCmdlet.ParameterSetName -eq 'byHostNameAndServiceDescriptions') { $Delete.delete_type = 'params' $Delete.host_name = "$($HostName)" $Delete.service_descriptions = [array]$ServiceDescriptions } $Delete = $Delete | ConvertTo-Json | EscapeNonAscii return Invoke-CMKApiCall -Method Post -Uri '/domain-types/downtime/actions/delete/invoke' -Body $Delete -Connection $Connection } #endregion Downtimes #region Services function Get-CMKService { <# .SYNOPSIS Retrieve status of services .DESCRIPTION retrieve status of services. Filter by host name, state and/or regular expression on service description using parameter -DescriptionRegExp. .PARAMETER DescriptionRegExp filter on service description by regular expression .PARAMETER State filter on service state (CRIT, WARN, OK, UNKNOWN) multiple choices are possible .PARAMETER Columns control which fields should be returned .PARAMETER HostName control services of which host should be returned .EXAMPLE Get-CMKService -HostName myhost.domain.example -Connection $Connection List all services of one host. .EXAMPLE Get-CMKService -DescriptionRegExp "^Filesystem(.)+" -Columns host_name, description, state -Connection $Connection List all services of all hosts beginning with "Filesystem" and output host_name, description and state .EXAMPLE Get-CMKService -DescriptionRegExp "^Filesystem(.)+" -State CRIT, WARN -Columns host_name, description, state -Connection $Connection List all services beginning with "Filesystem", having state CRIT or WARN and output host_name, description and state .EXAMPLE Get-CMKService -State CRIT -Connection $Connection List all services having a critical state. Output default columns: host_name and description .EXAMPLE Get-CMKService -HostGroup MariaDB, OracleDB -State CRIT -Connection $Connection List all services from host_groups "MariaDB" OR "OracleDB" having a critical state. .LINK https://<CheckMK-Host>/<sitename>/check_mk/openapi/#operation/cmk.gui.plugins.openapi.endpoints.service._list_all_services #> [CmdletBinding()] param ( [Parameter(Mandatory, ParameterSetName = 'byHostName', HelpMessage = 'Zeige Services nur eines Hosts')] $HostName, [Parameter(ParameterSetName = 'byHostName', HelpMessage = 'Filter-Ausdruck für service description als regular expression. Beispiel: "^Filesystem(.)+" (listet alle Services auf, die mit "Filesystem" beginnen)')] [Parameter(ParameterSetName = 'All', HelpMessage = 'Filter-Ausdruck für service description als regular expression. Beispiel: "^Filesystem(.)+" (listet alle Services auf, die mit "Filesystem" beginnen)')] [ValidateNotNullOrEmpty()] $DescriptionRegExp, [Parameter(ParameterSetName = 'byHostName', HelpMessage = 'Filter auf Service state (OK, WARN, CRIT, UNKNOWN)')] [Parameter(ParameterSetName = 'All', HelpMessage = 'Filter auf Service state (OK, WARN, CRIT, UNKNOWN)')] [ValidateSet('', 'OK', 'WARN', 'CRIT', 'UNKNOWN')] [string[]]$State, [Parameter(ParameterSetName = 'byHostName', HelpMessage = 'Filter host_groups, multiple values accepted (link using logical OR), case-insensitive equality')] [Parameter(ParameterSetName = 'All', HelpMessage = 'Filter host_groups, multiple values accepted (link using logical OR), case-insensitive equality')] [string[]]$HostGroup, [Parameter(ParameterSetName = 'byHostName', HelpMessage = 'auszugebende Felder')] [Parameter(ParameterSetName = 'All', HelpMessage = 'auszugebende Felder')] [ValidateSet('host_name', 'description', 'state', 'plugin_output', 'host_groups')] $Columns = @('host_name', 'description'), [Parameter(Mandatory, ParameterSetName = 'byHostName')] [Parameter(Mandatory, ParameterSetName = 'All')] [object] $Connection ) $QueryExtension = '' [string[]]$QueryExprArray = @() If ($DescriptionRegExp) { $QueryExprArray += "{""op"": ""~"", ""left"": ""description"", ""right"": ""$DescriptionRegExp""}" } If ($State) { $StateExprArray = @() #map service state names to numeric state and add to list foreach ($i in $State) { $MapState = "" switch ($i) { 'OK' { $MapState = "0" } 'WARN' { $MapState = "1" } 'CRIT' { $MapState = "2" } 'UNKNOWN' { $MapState = "3" } Default { Write-Error "state could not be mapped." } } $StateExprArray += "{""op"": ""="", ""left"": ""state"", ""right"": ""$MapState""}" } #build query expression $StateExprList = $StateExprArray -join "," If ($StateExprArray.Count -gt 1) { $StateExpr += "{""op"": ""or"", ""expr"": [$StateExprList]}" } else { $StateExpr += "$StateExprList" } $QueryExprArray += $StateExpr } If ($HostGroup) { $HostGroupExprArray = @() #map service state names to numeric state and add to list foreach ($i in $HostGroup) { $HostGroupExprArray += "{""op"": ""<="", ""left"": ""host_groups"", ""right"": ""$i""}" } #build query expression $HostGroupExprList = $HostGroupExprArray -join "," If ($HostGroupExprArray.Count -gt 1) { $HostGroupExpr += "{""op"": ""or"", ""expr"": [$HostGroupExprList]}" } else { $HostGroupExpr += "$HostGroupExprList" } $QueryExprArray += $HostGroupExpr } If ($QueryExprArray.Count -gt 0 -or $Columns) { $QueryExtension += '?' } $QueryExprList = $QueryExprArray -join "," #if more than one query expressions are defined, combine with 'and' operator, else use expression directly If ($QueryExprArray.Count -gt 1) { $QueryExtension += "query={""op"": ""and"", ""expr"": [$QueryExprList]}" } else { #do we have a query? If ($QueryExprArray.Count -gt 0) { $QueryExtension += "query=$QueryExprList" } } If ($Columns) { foreach ($col in $Columns) { $QueryExtension += "&columns=$col" } } Write-Verbose $QueryExtension If ($PSCmdlet.ParameterSetName -eq 'byHostName') { return Invoke-CMKApiCall -Method Get -Uri "/objects/host/$($HostName)/collections/services$($QueryExtension)" -Connection $Connection } elseif ($PSCmdlet.ParameterSetName -eq 'All') { return Invoke-CMKApiCall -Method Get -Uri "/domain-types/service/collections/all$($QueryExtension)" -Connection $Connection -EndpointReturnsList } } function Get-CMKServiceMetric { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $true, ParameterSetName="Single_Metric")] [string] $HostName, [Parameter(Mandatory = $true, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $true, ParameterSetName="Single_Metric")] [object] $Connection, [Parameter(Mandatory = $true, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $true, ParameterSetName="Single_Metric")] [datetime] $StartTime, [Parameter(Mandatory = $true, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $true, ParameterSetName="Single_Metric")] [datetime] $EndTime, [Parameter(Mandatory = $true, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $true, ParameterSetName="Single_Metric")] [string] $ServiceDescription, [Parameter(Mandatory = $true, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $true, ParameterSetName="Single_Metric")] [ValidateSet('predefined_graph','single_metric')] [string] $Type, [Parameter(Mandatory = $true, ParameterSetName="Predefined_Graph", HelpMessage = 'Internal GraphID e.g. "if_errors" for interface error graph.')] [string] $GraphID, [Parameter(Mandatory = $true, ParameterSetName="Single_Metric", HelpMessage = 'Internal MetricID e.g. "if_in_errors" for the single metric of input errors on an interface')] [string] $MetricID, [Parameter(Mandatory = $false, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $false, ParameterSetName="Single_Metric")] [ValidateSet('min','max','average')] [string] $Reduce, [Parameter(Mandatory = $false, ParameterSetName="Predefined_Graph")] [Parameter(Mandatory = $false, ParameterSetName="Single_Metric")] [string] $Site ) $Body = @{ time_range = @{ start = $($StartTime.ToString("yyyy-MM-dd HH:mm:ss")) end = $($EndTime.ToString("yyyy-MM-dd HH:mm:ss")) } host_name = $HostName service_description = $ServiceDescription type = $Type } if ($PSBoundParameters.ContainsKey('Reduce')) { $Body.Add('reduce',"$Reduce") } if ($PSBoundParameters.ContainsKey('Site')) { $Body.Add('site',"$Site") } If ($PSCmdlet.ParameterSetName -eq 'Predefined_Graph') { $Body.Add('graph_id',"$GraphID") } elseif ($PSCmdlet.ParameterSetName -eq 'Single_Metric') { $Body.Add('metric_id',"$MetricID") } $Body = $Body | ConvertTo-Json | EscapeNonAscii return Invoke-CMKApiCall -Method Post -Uri '/domain-types/metric/actions/get/invoke' -Body $Body -Connection $Connection } function Invoke-CMKServiceDiscovery { [CmdletBinding()] param ( [parameter(Mandatory = $true, HelpMessage = 'Mit Get-CMKHost abgerufen')] [string] $HostName, [Parameter(Mandatory = $false)] [ValidateSet('new','remove','fix_all','tabula_rasa','refresh','only_host_labels')] [string] $Mode = 'fix_all', [Parameter(Mandatory = $true)] [object] $Connection ) $Body = @{ host_name = $HostName mode = $Mode } | ConvertTo-Json | EscapeNonAscii return Invoke-CMKApiCall -Method Post -Uri '/domain-types/service_discovery_run/actions/start/invoke' -Body $Body -Connection $Connection } #endregion Services #region Users function Get-CMKUser { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Spezifisch')] [ValidateNotNullOrEmpty()] [string] $Username, [Parameter(Mandatory = $true, ParameterSetName = 'Spezifisch')] [Parameter(Mandatory = $true, ParameterSetName = 'Liste')] [object] $Connection ) if ($PSCmdlet.ParameterSetName -eq 'Spezifisch') { return Invoke-CMKApiCall -Method Get -Uri "/objects/user_config/$($Username)" -Connection $Connection }elseif ($PSCmdlet.ParameterSetName -eq 'Liste') { return Invoke-CMKApiCall -Method Get -Uri "/domain-types/user_config/collections/all" -Connection $Connection -EndpointReturnsList } } function Update-CMKUser { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = 'Mit Get-CMK-User abgerufenes User Objekt')] [object] $UserObject, [Parameter(Mandatory = $true)] $Changeset, [Parameter(Mandatory = $true)] [object] $Connection ) Write-Verbose -Message $UserObject Write-Verbose -Message $Changeset $ConnSecret = $Connection.Header.Authorization.Split(' ')[2] | ConvertTo-SecureString -AsPlainText -Force $oneTimeConnection = Get-CMKConnection -Hostname $Connection.hostname -Sitename $Connection.sitename -Username $Connection.username -Secret $ConnSecret -IfMatch $UserObject.Etag return Invoke-CMKApiCall -Method Put -Uri "/objects/user_config/$($UserObject.Id)" -Body $Changeset -Connection $oneTimeConnection } function Set-CMKUserAttribute { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = 'Mit Get-CMK-User abgerufenes User Objekt')] [object] $UserObject, [Parameter(Mandatory = $true)] [string] $UpdateAttribute, [Parameter(Mandatory = $true)] $Value, [Parameter(Mandatory = $true)] [object] $Connection ) $Changeset = @{ $UpdateAttribute = $Value } $Changeset = $Changeset | ConvertTo-Json | EscapeNonAscii return Update-CMKUser -UserObject $UserObject -Changeset $Changeset -Connection $Connection -Verbose } #endregion Export-ModuleMember -Function @('Get-CMKConnection', 'Get-CMKServerInfo', 'Get-CMKPendingChanges', 'Invoke-CMKChangeActivation', 'Get-CMKHost', 'Get-CMKHostInventory', 'New-CMKHost', 'New-CMKClusterHost', 'Rename-CMKHost', 'Update-CMKHost', 'Remove-CMKHost', 'Set-CMKHostAttribute', 'Add-CMKHostLabel', 'Remove-CMKHostLabel', 'Get-CMKFolder', 'Get-CMKDowntime', 'New-CMKDowntime', 'Remove-CMKDowntime', 'Get-CMKService', 'Get-CMKServiceMetric', 'Invoke-CMKServiceDiscovery', 'Get-CMKUser', 'Update-CMKUser', 'Set-CMKUserAttribute') |