NewRelicPS.Configuration.Dashboard.psm1

Using module .\NewRelicPS.Dashboards.Common.psm1

<#
.Synopsis
  Syncs dashboard configurations between accounts
.Description
  Syncs a dashboard configuration from a source account to one or more destination accounts
.Example
  Sync-NRDashboardConfiguration -APIKey $PersonalAPIKey -DefinedDashboards $MyDashboards
  Uses New Relic GraphQL APIs to sync an entire dashboard, including any subpages, to another New Relic account.
  Existing dashboards in destination accounts with names matching the source dashboard have their configuration updated to match the source dashboard.
.Parameter APIKey
  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 DefinedDashboards
  An array of dashboard objects which define the source and destination parameters for syncing the configuration
#>

Function Sync-NRDashboardConfiguration {
  [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 = $true)]
    [array] $DefinedDashboards
  )

  Write-Verbose "There are currently $($DefinedDashboards.count) source dashboards configured for sync."
  Foreach ($dashboard in $DefinedDashboards) {
    $sourceConfig = Get-NRDashboard -APIKey $APIKey -DashboardId $dashboard.sourceDashboardId -ErrorAction Stop
    $sourceConfigJSON = $sourceConfig | ConvertTo-JSON -Depth 50

    # Multiple destination accounts can be defined in the configuration
    Foreach ($account in $dashboard.destinationAccounts) {
      $compareConfig = $sourceConfigJSON | ConvertFrom-Json -Depth 50 # Create copy to not alter the original for comparison with each destination account
      $destinationId = $null
      $destinationConfigJSON = $null
      $shouldCopy = $false

      # Find Dashboard in destination if available
      $destinationId = (Get-NRDashboardList -APIKey $APIKey | Where-Object { $_.name -eq $compareConfig.name -and $_.accountId -eq $account }).guid

      # If a single matching dashboard is found, compare configs between source and destination
      If ($destinationId.count -eq 1) {
        Write-Verbose "Found existing dashboard with name $($compareConfig.name) in account $account, comparing configs"
        $destinationConfig = Get-NRDashboard -APIKey $APIKey -DashboardId $destinationId
        $destinationConfigJSON = $destinationConfig | ConvertTo-Json -Depth 50

        # Update AccountID's before comparison
        If ($dashboard.UpdateWidgetAccountIds) {
          $compareConfig = Update-NRDashboardWidgetAccountID -AccountId $account -DashboardConfig $compareConfig
        }

        $compareConfig = Update-NRDashboardLinkedEntityIDs -SourceConfig $compareConfig -DestinationConfig $destinationConfig
        $compareConfigJSON = $compareConfig | ConvertTo-Json -Depth 50

        # Compare source and destination configs
        If ($compareConfigJSON -ne $destinationConfigJSON) {
          Write-Verbose "Configuration differs for dashboard $($compareConfig.name) between source and destination accounts"
          $shouldCopy = $true
        }
      }
      ElseIf ($destinationId.count -lt 1) {
        Write-Verbose "No matching dashboard found in account $account for dashboard $($compareConfig.name)"
        $shouldCopy = $true
      }
      Else {
        Throw "More than one dashboard with name $($compareConfig.name) found in account $account. Multiple dashboards with the same name in the same account is unsupported."
      }

      # Only copy if required
      If ($shouldCopy) {

        # Make a backup of the destination before overwriting if a destination backup path is specified
        If ($destinationConfigJson -and $dashboard.destinationBackupPath) {
          Backup-NRDashboardConfig -AccountId $account -DashboardName $destinationConfig.name -DashboardConfigJSON $destinationConfigJSON -DestinationPath $dashboard.destinationBackupPath
        }

        Write-Verbose "Updating dashboard $($compareConfig.name) in account $account"
        Copy-NRDashboard -APIKey $APIKey -DashboardId $dashboard.sourceDashboardId -DestinationAccountId $account -UpdateWidgetAccountIds:$dashboard.updateWidgetAccountIds -Whatif:$WhatIfPreference | Out-Null
      }
    }
  }
}

#####################################
# Internal Functions
#####################################
Function Backup-NRDashboardConfig {
  Param(
    [Parameter (Mandatory = $true)]
    [string] $AccountId,
    [Parameter (Mandatory = $true)]
    [string] $DashboardConfigJSON,
    [Parameter (Mandatory = $true)]
    [string] $DashboardName,
    [Parameter (Mandatory = $true)]
    [string] $DestinationPath
  )
  # Create dashboard directory if it doesn't exist
  If (! (Test-Path $DestinationPath)) {
    Write-Verbose "Creating backups directory"
    New-Item -ItemType Directory -Name $DestinationPath -Force | Out-Null
  }

  # Clean up the dashboard name for saving
  $cleanedDashboardName = $DashboardName -replace(' / ','-')
  $filename = "$AccountId-$cleanedDashboardName.json"

  # Save backup
  Write-Verbose "Saving backup of $($DashboardName) to $DestinationPath\$filename"
  $DashboardConfigJSON | Out-File "$DestinationPath\$filename"
}