Public/Import-LMDashboard.ps1

<#
.SYNOPSIS
Imports LogicMonitor dashboards from various sources.

.DESCRIPTION
The Import-LMDashboard function allows you to import LogicMonitor dashboards from different sources, such as local files, GitHub repositories, or LogicMonitor dashboard groups. It supports importing dashboards in JSON format.

.PARAMETER FilePath
Specifies the path to a local file or directory containing the JSON dashboard files to import.

.PARAMETER File
Specifies a single JSON dashboard file to import.

.PARAMETER GithubUserRepo
Specifies the GitHub repository (in the format "username/repo") from which to import JSON dashboard files.

.PARAMETER GithubAccessToken
Specifies the GitHub access token for authenticated requests. Required for large repositories due to API rate limits.

.PARAMETER ParentGroupId
The ID of the parent dashboard group where imported dashboards will be placed.

.PARAMETER ParentGroupName
The name of the parent dashboard group where imported dashboards will be placed.

.PARAMETER ReplaceAPITokensOnImport
Switch to replace API tokens in imported dashboards with a dynamically generated token.

.PARAMETER APIToken
The API token to use when replacing tokens in imported dashboards.

.PARAMETER PrivateUserName
The username of dashboard owner when creating dashboard as private.

.EXAMPLE
#Import dashboards from a directory
Import-LMDashboard -FilePath "C:\Dashboards" -ParentGroupId 12345 -ReplaceAPITokensOnImport -APIToken $apiToken

.EXAMPLE
#Import dashboards from GitHub
Import-LMDashboard -GithubUserRepo "username/repo" -ParentGroupName "MyDashboards" -ReplaceAPITokensOnImport -APIToken $apiToken

.NOTES
You must run Connect-LMAccount before running this command.

.INPUTS
None. You cannot pipe objects to this command.

.OUTPUTS
Returns imported dashboard objects.
#>

Function Import-LMDashboard {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory, ParameterSetName = 'FilePath-GroupId')]
        [Parameter(Mandatory, ParameterSetName = 'FilePath-GroupName')]
        [String]$FilePath,

        [Parameter(Mandatory, ParameterSetName = 'File-GroupId')]
        [Parameter(Mandatory, ParameterSetName = 'File-GroupName')]
        [String]$File,

        [Parameter(Mandatory, ParameterSetName = 'Repo-GroupId')]
        [Parameter(Mandatory, ParameterSetName = 'Repo-GroupName')]
        [String]$GithubUserRepo, # format "username/repo"

        [Parameter(ParameterSetName = 'Repo-GroupId')]
        [Parameter(ParameterSetName = 'Repo-GroupName')]
        [String]$GithubAccessToken, #Required for large repos, github api is limited to 60 requests per hour when unauthenticated

        [Parameter(Mandatory, ParameterSetName = 'FilePath-GroupId')]
        [Parameter(Mandatory, ParameterSetName = 'File-GroupId')]
        [Parameter(Mandatory, ParameterSetName = 'Repo-GroupId')]
        [String]$ParentGroupId,

        [Parameter(Mandatory, ParameterSetName = 'FilePath-GroupName')]
        [Parameter(Mandatory, ParameterSetName = 'File-GroupName')]
        [Parameter(Mandatory, ParameterSetName = 'Repo-GroupName')]
        [String]$ParentGroupName,

        [Switch]$ReplaceAPITokensOnImport,

        $APIToken,

        [String]$PrivateUserName = ""
    )

    #Check if we are logged in and have valid api creds
    Begin {}
    Process {
        If ($Script:LMAuth.Valid) {
            $Results = @()
            $DashboardList = @()

            If ($ParentGroupName) {
                $ParentGroupId = (Get-LMDashboardGroup -Name $ParentGroupName | Select-Object -First 1 ).Id
            }
            If ($ParentGroupId) {
                $ParentGroupName = (Get-LMDashboardGroup -Id $ParentGroupId | Select-Object -First 1 ).Name
            }

            If ($FilePath) {
                If ((Get-Item $FilePath) -is [System.IO.DirectoryInfo]) {
                    $FullPath = (Resolve-Path $FilePath).Path
                    $Files = Get-ChildItem $FullPath -Recurse | Where-Object { ([IO.Path]::GetExtension($_.Name) -eq '.json') }
                    Foreach ($F in $Files) {
                        #Convert from json into object
                        $RawFile = Get-Content $F.FullName -Raw | ConvertFrom-Json
                        $DashboardList += @{
                            file       = $RawFile
                            path       = $($F.DirectoryName -split $FullPath)[1]
                            parentid   = $ParentGroupId
                            parentname = $ParentGroupName
                        }
                    }
                }
                Else {
                    If (!(Test-Path -Path $FilePath) -and (!([IO.Path]::GetExtension($FilePath) -eq '.json'))) {
                        Write-Error "File not found or is not a valid json file, check file path and try again"
                        Return
                    }

                    #Convert from json into object
                    $RawFile = Get-Content $FilePath -Raw | ConvertFrom-Json
                    $DashboardList += @{
                        file       = $RawFile
                        path       = ""
                        parentid   = $ParentGroupId
                        parentname = $ParentGroupName
                    }
                }
            }

            If ($File) {
                $DashboardList += @{
                    file       = $File | ConvertFrom-Json
                    path       = ""
                    parentid   = $ParentGroupId
                    parentname = $ParentGroupName
                }
            }

            If ($GithubUserRepo) {
                $Headers = @{}
                If ($GithubAccessToken) {
                    $Headers = @{"Authorization" = "token $GithubAccessToken" }
                }
                $Uri = "https://api.github.com/repos/$GithubUserRepo/git/trees/master?recursive=1"
                $RepoData = (Invoke-RestMethod -Uri $Uri -Headers $Headers[0] -WebSession $Headers[1]).tree | Where-Object { $_.Path -like "*.json" -and $_.Path -notlike "Packages/LogicMonitor_Dashboards*" } | Select-Object path, url
                If ($RepoData) {
                    $TotalItems = ($RepoData | Measure-Object).Count
                    Write-Information "[INFO]: Found $TotalItems JSON files from Github repo ($GithubUserRepo)"
                    Foreach ($Item in $RepoData) {
                        $EncodedDash = (Invoke-RestMethod -Uri $Item.url -Headers $Headers[0] -WebSession $Headers[1]).content
                        $DashboardList += @{
                            file       = [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($EncodedDash)) | ConvertFrom-Json
                            path       = [System.IO.Path]::GetDirectoryName($Item.path)
                            parentid   = $ParentGroupId
                            parentname = $ParentGroupName
                        }
                        
                        Write-Information "[INFO]: Successfully downloaded dashboard ($($Item.path)) from Github repo ($GithubUserRepo)"
                    }
                }
            }
    
            If ($ReplaceAPITokensOnImport -and !($APIToken)) {
                $DashboardAPIRoleName = "lm-dynamic-dashboards"
                $DashboardAPIUserName = "lm_dynamic_dashboards"
                $DashboardAPIRole = Get-LMRole -Name $DashboardAPIRoleName
                $DashboardAPIUser = Get-LMUser -Name $DashboardAPIUserName
                If (!$DashboardAPIRole) {
                    $DashboardAPIRole = New-LMRole -Name $DashboardAPIRoleName -ResourcePermission view -DashboardsPermission manage -Description "Auto provisioned for use with dynamic dashboards"
                    Write-Information "[INFO]: Successfully generated required API role ($DashboardAPIRoleName) for dynamic dashboards"
                }
                If (!$DashboardAPIUser) {
                    $DashboardAPIUser = New-LMAPIUser -Username "$DashboardAPIUserName" -note "Auto provisioned for use with dynamic dashboards" -RoleNames @($DashboardAPIRoleName)
                    Write-Information "[INFO]: Successfully generated required API user ($DashboardAPIUserName) for dynamic dashboards"
                }
                If ($DashboardAPIRole -and $DashboardAPIUser) {
                    $APIToken = New-LMAPIToken -Username $DashboardAPIUserName -Note "Auto provisioned for use with dynamic dashboards"
                    If ($APIToken) {
                        Write-Information "[INFO]: Successfully generated required API token for dynamic dashboards for user: $DashboardAPIUserName"
                    }
                }
                Else {
                    Write-Warning "[WARN]: Unable to generate required API token for dynamic dashboards, manually update the required tokens to use dynamic dashboards" 
                }
            }

            Foreach ($Dashboard in $DashboardList) {
                #Swap apiKeys for dynamic dashboards
                If ($ReplaceAPITokensOnImport) {
                    If ($APIToken) {
                        If ($Dashboard.file.widgetTokens.name -contains "apiKey") {
                            $KeyIndex = $Dashboard.file.widgetTokens.name.toLower().IndexOf("apikey")
                            $Dashboard.file.widgetTokens[$KeyIndex].value = $APIToken.accessKey
                        }
                        If ($Dashboard.file.widgetTokens.name -contains "apiID") {
                            $IdIndex = $Dashboard.file.widgetTokens.name.toLower().IndexOf("apiid")
                            $Dashboard.file.widgetTokens[$IdIndex].value = $APIToken.accessId
                        }
                    }
                }

                #Check if a path has been provided and check if folder exists in selected root folder, if not create
                If ($Dashboard.path) {
                    [Array]$SubFolders = $Dashboard.path -split "\\|/" | Where-Object { $_ }

                    For ($Index = 0; $Index -lt $($SubFolders | Measure-Object).Count; $Index++) {

                        If ($Index -eq 0) {
                            $DashboardGroup = Get-LMDashboardGroup -ParentGroupId $ParentGroupId | Where-Object { $_.Name -eq $SubFolders[$Index] }

                            If (!$DashboardGroup) {
                                Write-Information "[INFO]: Existing dashboard group not found for $($Subfolders[$Index]) creating new resource group under root group ($ParentGroupName)"
                                $NewDashboardGroup = New-LMDashboardGroup -Name $SubFolders[$Index] -ParentGroupId $ParentGroupId
                                $Dashboard.parentid = $NewDashboardGroup.id
                                $Dashboard.parentname = $NewDashboardGroup.name

                            }
                            Else {
                                $Dashboard.parentid = $DashboardGroup.id
                                $Dashboard.parentname = $DashboardGroup.name
                            }
                        }
                        Else {
                            $DashboardGroup = Get-LMDashboardGroup -Name $Subfolders[$Index] | Where-Object { $_.fullPath -like "$($Subfolders[0])*$($Subfolders[$Index])" }
                            
                            If (!$DashboardGroup) {

                                $NewDashboardParentGroup = Get-LMDashboardGroup -Name $Subfolders[$Index - 1] | Where-Object { $_.fullPath -like "$ParentGroupName*" -or $_.fullPath -eq $Subfolders[$Index - 1] }
                                Write-Information "[INFO]: Existing dashboard group not found for $($Subfolders[$Index]) creating new resource group under group ($($NewDashboardParentGroup.Name))"
                                $NewDashboardGroup = New-LMDashboardGroup -Name $SubFolders[$Index] -ParentGroupId $NewDashboardParentGroup.id

                                $Dashboard.parentid = $NewDashboardGroup.id
                                $Dashboard.parentname = $NewDashboardGroup.name

                            }
                            Else {
                                $Dashboard.parentid = $DashboardGroup.id
                                $Dashboard.parentname = $DashboardGroup.name
                            }
                        }
                    }
                }
    
                #Construct our object for import
                $Data = @{
                    description          = $Dashboard.file.description
                    groupId              = [int]$Dashboard.parentid
                    groupName            = $Dashboard.parentname
                    name                 = $Dashboard.file.name
                    sharable             = If ($PrivateUserName) { $False } Else { $True }
                    owner                = $PrivateUserName
                    template             = $Dashboard.file | Select-Object -ExcludeProperty group
                    widgetTokens         = $Dashboard.file.widgetTokens
                    widgetsConfigVersion = $Dashboard.file.widgetsConfigVersion
                }
    
                #Build header and uri
                $ResourcePath = "/dashboard/dashboards"
                
                Try {
                    $Data = ($Data | ConvertTo-Json -Depth 10)

                    $Headers = New-LMHeader -Auth $Script:LMAuth -Method "POST" -ResourcePath $ResourcePath -Data $Data
                    $Uri = "https://$($Script:LMAuth.Portal).logicmonitor.com/santaba/rest" + $ResourcePath
        
                    Resolve-LMDebugInfo -Url $Uri -Headers $Headers[0] -Command $MyInvocation -Payload $Data

                    #Issue request
                    $Response = Invoke-RestMethod -Uri $Uri -Method "POST" -Headers $Headers[0] -WebSession $Headers[1] -Body $Data
                    Write-Output "Successfully imported dashboard: $($Dashboard.file.name)"
    
                    $Results += (Add-ObjectTypeInfo -InputObject $Response -TypeName "LogicMonitor.Dashboard" )
    
                }
                Catch [Exception] {
                    Write-Output "Failed to import dashboard: $($Dashboard.file.name)"
                    $Proceed = Resolve-LMException -LMException $PSItem
                    If (!$Proceed) {
                        # Return
                    }
                }
            }
        }
        Else {
            Write-Error "Please ensure you are logged in before running any commands, use Connect-LMAccount to login and try again."
        }
    }
    End {
        $Results
    }
}