NewRelicPS.Configuration.Channel.psm1

<#
.Synopsis
  Idempotently applies New Relic channel configurations
.Description
  Idempotently applies New Relic channel configurations
.Example
  Set-NRChannelConfiguration -APIKey $AdminAPIKey -DefinedChannels $MyChannels -OpsGenieAPIKey $OpsGenieAPIKey
  Uses New Relic APIs to update notification channels to match the channels defined in $MyChannels. Any existing channels that are not defined will be removed.
.Parameter APIKey
  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 DefinedChannels
  An array of channel objects which define the desired configuration state for New Relic notification channels
.Parameter OpsGenieAPIKey
  The APIKey New Relic uses to send OpsGenie alerts
#>

Function Set-NRChannelConfiguration {
  [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] $APIKey,
    [Parameter (Mandatory = $false)]
    [array] $DefinedChannels
  )

  # Get existing notificaiton channels but exclude the built-in user channels which can't be edited
  [System.Collections.ArrayList]$existingChannels = Get-NRNotificationChannel -APIKey $APIKey | Where-Object { $_.type -ne 'user' }
  Write-Verbose "There are currently $($DefinedChannels.count) defined channels and $($existingChannels.count) existing channels."

  Foreach ($channel in $DefinedChannels) {
    $CurrentConfig = $existingChannels | Where-Object { $_.name -eq $channel.name }

    # Update the channel if required
    If ($CurrentConfig) {
      $configurationDiff = Compare-ChannelConfiguration -CurrentConfig $CurrentConfig.configuration -DefinedConfig $channel.configuration

      # The API Key is not returned on the channel and cannot be checked
      If ($configurationDiff -or $channel.type -ne $CurrentConfig.type) {

        # There is no update endpoint for channels, so it must be recreated
        Write-Verbose "Recreating channel $($channel.name)"
        Remove-NRNotificationChannel -APIKey $APIKey -ChannelID $CurrentConfig.id -WhatIf:$WhatIfPreference
        New-NRNotificationChannel -APIKey $APIKey -Name $channel.name -Type $channel.type -Configuration $channel.configuration -WhatIf:$WhatIfPreference | Out-Null
      }

      # Any extra existing channels that are not defined will be removed later
      $existingChannels.Remove($CurrentConfig)
    }

    # Create the channel if it doesn't exist
    Else {
        Write-Verbose "Creating channel $($channel.name)"
        New-NRNotificationChannel -APIKey $APIKey -Name $channel.name -Type $channel.type -Configuration $channel.configuration -WhatIf:$WhatIfPreference | Out-Null
    }
  }

  # Check for existing channels not in the definition and remove them
  If ($existingChannels) {
      Write-Verbose "Removing channel(s) $($existingChannels.name -join(','))"
      $existingChannels.Id | Remove-NRNotificationChannel -APIKey $APIKey -WhatIf:$WhatIfPreference | Out-Null
  }
}

<#
.Synopsis
  Idempotently applies New Relic channel-policy link configurations
.Description
  Idempotently applies New Relic channel-policy link configurations
.Example
  Set-NRChannelPolicyLink -APIKey $AdminAPIKey -DefinedPolicies $Config.AlertPolicies
  Uses New Relic APIs to update policy-channel links to match the channels defined in $Config.AlertPolicies. Any existing links that are not defined will be removed.
.Parameter APIKey
  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 DefinedPolicies
  An array of policy objects which define the desired configuration state for New Relic alert policies
#>

Function Set-NRChannelPolicyLink {
  [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] $APIKey,
    [Parameter (Mandatory = $false)]
    [array] $DefinedPolicies
  )

  $existingPolicies = Get-NRAlertPolicy -APIKey $APIKey
  $existingChannels = Get-NRNotificationChannel -APIKey $APIKey

  # Notification channels are described in a policy, but the API only returns the link in the notification channel
  Foreach ($policy in $DefinedPolicies) {
    $CurrentPolicyConfig = $existingPolicies | Where-Object { $_.name -eq $policy.name }

    # Iterate over each channel in the policy described in the policy
    Foreach ($channelName in $policy.channels) {
      $CurrentChannelConfig = $existingChannels | Where-Object { $_.name -eq $channelName }

      # If the current channel configuration's link does not contain the ID of the policy, add a new link
      If ($CurrentChannelConfig -and $CurrentChannelConfig.links.policy_ids -notcontains $CurrentPolicyConfig.id) {

        # This command doesn't remove any existing links, it only appends the new link
        Write-Verbose "Adding policy-channel link for policy $($policy.name) and channel $channelName"
        Add-NRNotificationChannelToAlertPolicy -APIKey $APIKey -PolicyId $CurrentPolicyConfig.id -ChannelIds $CurrentChannelConfig.id -WhatIf:$WhatIfPreference | Out-Null
      }
    }
  }

  # Remove extra links
  Write-Verbose 'Checking for extra channel/policy links.'
  Foreach ($channel in $existingChannels) {

    # A channel can be linked to multiple policies so check each
    Foreach ($policyId in $channel.links.policy_ids) {
      $policyName = ($existingPolicies | Where-Object { $_.id -eq $policyId }).name
      $definedPolicy = $DefinedPolicies | Where-Object { $_.name -eq $policyName }

      # If the policy declaration doesn't contain the channel name, delete the link.
      If ($definedPolicy.channels -notcontains $channel.name) {
        Write-Verbose "Removing policy-channel link for policy $policyName and channel $($channel.name)"
        Remove-NRNotificationChannelFromAlertPolicy -APIKey $APIKey -PolicyId $policyId -ChannelId $channel.id -WhatIf:$WhatIfPreference
      }
    }
  }
}

############################
# Internal Functions
############################
Function Compare-ChannelConfiguration {
  Param (
    [Parameter (Mandatory = $true)]
    $CurrentConfig,
    [Parameter (Mandatory = $true)]
    $DefinedConfig
  )
  $result = $false
  $diff = $null

  # Some items are not returned from the channel API and cannot be compared
  $itemsToExclude = @('api_Key')

  Foreach ($item in $DefinedConfig.keys) {

    # Tags requires special processing
    If ($item -eq 'tags') {
      $diff = Compare-Object ($CurrentConfig.tags -split (' ')) ($DefinedConfig.tags -split (' '))

      If ($diff) {
        Write-Verbose 'Difference found between defined and current channel configuration tags'
        $result = $true
      }
    }

    # Compare the rest of the non-excluded items
    ElseIf ($item -notin $itemsToExclude) {
      If ($DefinedConfig.$item -ne $CurrentConfig.$item) {
        Write-Verbose "Difference found between defined and current configuration $item"
        $result = $true
      }
    }
  }
  Return $result
}