NewRelicPS.Configuration.NRQLCondition.psm1
<# .Synopsis Idempotently applies New Relic condition configurations .Description Idempotently applies New Relic condition configurations .Example Set-NRQLConditionConfiguration -AdminAPIKey $AdminAPIKey -PersonalAPIKey $PersonalAPIKey -DefinedPolicies $Config.AlertPolicies -AccountId $AccountId Uses New Relic APIs to update alert conditions to match the conditions defined in $Config.AlertPolicies. Any existing conditions that are not defined will be removed. .Parameter AccountId The New Relic account Id where the configuration is being applied. .Parameter AdminAPIAPIKey Must be an admin user's API Key, not an account-level REST API Key. See more here: https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys .Parameter PersonalAPIKey Must be a personal API Key, not an account-level REST API Key. See more here: https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys .Parameter DefinedPolicies An array of policy objects which define the desired configuration state for New Relic alert policies and conditions #> Function Set-NRQLConditionConfiguration { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", '', Justification = "All CMDLets being called have ShouldProcess in place and the preference is passed to them." )] [CMDLetBinding(SupportsShouldProcess = $true)] Param ( [Parameter (Mandatory = $true)] [string] $AdminAPIKey, [Parameter (Mandatory = $true)] [string] $PersonalAPIKey, [Parameter (Mandatory = $true)] [string] $AccountId, [Parameter (Mandatory = $false)] [array] $DefinedPolicies ) # Conditions are described in a policy, but the API only returns the link in the notification channel $existingPolicies = Get-NRAlertPolicy -APIKey $AdminAPIKey $NRQLTypes = @('Static','Baseline','Outlier') Foreach ($policy in $DefinedPolicies) { $existingPolicy = $existingPolicies | Where-Object {$_.name -eq $policy.name} [System.Collections.ArrayList]$existingConditions = @(Get-NRQLCondition -APIKey $PersonalAPIKey -AccountId $AccountId -PolicyId $existingPolicy.id) Write-Verbose "Policy $($policy.name) has $(($policy.conditions | Where-Object {$_.type -in $NRQLTypes}).name.count) defined NRQL conditions and $($existingConditions.count) existing NRQL conditions." # Iterate over each NRQL condition described in the policy Foreach ($condition in ($policy.conditions | Where-Object {$NRQLTypes -contains $_.type})) { [array]$existingConditionConfig = $existingConditions | Where-Object { $_.name -eq $condition.name } # Items are declared by name and are later given an ID by New Relic, therefore resources with the same name will cause issues and are unsupported. If ($existingConditionConfig.count -gt 1) { Write-Error "Multiple conditions found with the name $($existingConditionConfig.name[0])! Ids: $($existingConditionConfig.id -join (',')) This must be resolved manually before continuing." -ErrorAction 'Stop' } # Update existing condition if needed If ($existingConditionConfig) { $conditionRequiresUpdate = Compare-NRConditionState -DefinedCondition $condition -ExistingCondition $existingConditionConfig -Verbose:$PSCMDLet.GetVariableValue('VerbosePreference') If ($conditionRequiresUpdate) { Write-Verbose "Updating condition $($condition.name) on policy $($policy.name)." $params = Get-NRQLConditionParamSet -APIKey $PersonalAPIKEY -Condition $condition -AccountId $AccountId Update-NRQLCondition @params -ConditionId $existingConditionConfig.id -Whatif:$WhatIfPreference | Out-Null } # Any extra existing channels that are not defined will be removed later $existingConditions.Remove($existingConditionConfig[0]) } # Otherwise, create the condition if the policy exists ElseIf ($existingPolicy) { $params = Get-NRQLConditionParamSet -APIKey $PersonalAPIKEY -Condition $condition -AccountId $AccountId Write-Verbose "Creating condition $($condition.name)" New-NRQLCondition @params -PolicyId $existingPolicy.id -Whatif:$WhatIfPreference | Out-Null } } # Check for existing conditions not in the policy's definition and remove them If ($existingConditions) { Write-Verbose "Removing condition(s) $($existingConditions.Name -join (','))" $existingConditions.Id | Remove-NRCondition $PersonalAPIKey -AccountId $AccountId -Whatif:$WhatIfPreference | Out-Null } } } ########################## # Internal Functions ########################## Function Get-NRQLConditionParamSet { [CMDLetBinding()] param ( [Parameter (Mandatory = $true)] $APIKey, [Parameter (Mandatory = $true)] $AccountId, [Parameter (Mandatory = $true)] $Condition ) # Take the data available and create a hash table of CMDLET parameters $params = @{ APIKey = $APIKEY AccountId = $AccountId } # Build up all optional parameters Foreach ($Property in $Condition.Keys) { # There are a few possibilities for nested properties, so address those first $NestedProperties = ('nrql','expiration') If ($NestedProperties -contains $Property) { Foreach ($subProperty in $Condition.$Property.Keys) { $params += @{ $subProperty = $Condition.$Property.$subProperty } } } # Terms require special handling ElseIf ($Property -eq 'terms') { Foreach ($term in $Condition.$Property) { If ($term.priority -eq 'Critical') { Switch ($term.keys) { 'operator' {$params += @{CriticalOperator = $term.operator}} 'threshold' {$params += @{CriticalThreshold = $term.threshold}} 'thresholdDuration' {$params += @{CriticalDuration = $term.thresholdDuration}} 'thresholdOccurrences' {$params += @{CriticalOccurrences = $term.thresholdOccurrences}} } } ElseIf ($term.priority -eq 'Warning') { Switch ($term.keys) { 'operator' {$params += @{WarningOperator = $term.operator}} 'threshold' {$params += @{WarningThreshold = $term.threshold}} 'thresholdDuration' {$params += @{WarningDuration = $term.thresholdDuration}} 'thresholdOccurrences' {$params += @{WarningOccurrences = $term.thresholdOccurrences}} } } Else { Write-Error "$($Condition.name) has terms defined but a priority was not found!" -ErrorAction 'Stop' } } } # Process the top-level properties Else { $params += @{ $Property = $Condition.$Property } } } Return $params } Function Compare-NRConditionState { [CMDLetBinding()] Param ( $DefinedCondition, $ExistingCondition ) $returnValue = $false $NestedProperties = ('nrql','expiration') Foreach ($Property in $DefinedCondition.Keys) { # Terms require special handling since there may be multiple term objects returned in any order If ($Property -eq 'terms') { Foreach ($term in $DefinedCondition.$Property) { # There can only be one Warning term and one Critical term so match the term based on priority $existingTerm = $ExistingCondition.$Property | Where-Object {$_.priority -eq $term.priority} # Iterate over sub items in terms and compare them Foreach ($subProperty in $term.keys) { If ($term.$subProperty -ne $existingTerm.$subProperty) { Write-Verbose "Condition $($DefinedCondition.Name) has subproperty $subProperty defined as $($term.$subProperty) but it is currently set to $($Existingterm.$subProperty)" $returnValue = $true } } } } # There are a few possibilities for nested properties, so address those first ElseIf ($NestedProperties -contains $Property) { Foreach ($subProperty in $DefinedCondition.$Property.Keys) { If ($DefinedCondition.$Property.$subProperty -ne $ExistingCondition.$Property.$subProperty) { Write-Verbose "Condition $($DefinedCondition.Name) has subproperty $subProperty defined as $($DefinedCondition.$Property.$subProperty) but it is currently set to $($ExistingCondition.$Property.$subProperty)" $returnValue = $true } } } ElseIf ($DefinedCondition.$Property -ne $ExistingCondition.$Property) { Write-Verbose "Condition $($DefinedCondition.Name) has property $Property defined as $($DefinedCondition.$Property) but it is currently set to $($ExistingCondition.$Property)" $returnValue = $true } } Return $returnValue } |