Public/Deploy/Core/monitoring/New-CmAzCoreMonitor.ps1

function New-CmAzCoreMonitor {

    <#
        .Synopsis
         Deploys monitoring for core resources.
 
        .Description
         Completes the following:
            * Deploys log analytics, app insights and storage accountp.
            * Deploys management solutions for keyvaults, subscription activity, agent health, updates and VM insights.
             * Deploys action groups and alerts for service health, keyvault admin and resource health for applicable core resources.
 
        .Parameter SettingsFile
         File path for the settings file to be converted into a settings object.
 
        .Parameter SettingsObject
         Object containing the configuration values required to run this cmdlet.
 
        .Parameter TagSettingsFile
         File path for settings containing tags definition.
 
        .Component
         Core
 
        .Example
         New-CmAzCoreMonitor -SettingsFile "c:/directory/settingsFile.yml"
 
        .Example
         New-CmAzCoreMonitor -SettingsObject $settings
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")]
    param(
        [parameter(Mandatory = $true, ParameterSetName = "Settings File")]
        [string]$SettingsFile,
        [parameter(Mandatory = $true, ParameterSetName = "Settings Object")]
        [object]$SettingsObject,
        [AllowEmptyString()]
        [String]$TagSettingsFile
    )

    $ErrorActionPreference = "Stop"

    try {

        Write-CommandStatus -CommandName $MyInvocation.MyCommand.Name

        $SettingsObject = Get-Settings -SettingsFile $SettingsFile -SettingsObject $SettingsObject -CmdletName (Get-CurrentCmdletName -ScriptRoot $PSCommandPath)

        if ($PSCmdlet.ShouldProcess((Get-CmAzSubscriptionName), "Deploy Core Monitoring")) {

            foreach ($key in $SettingsObject.alerts.keys) {

                $alert = $SettingsObject.alerts[$key]

                if (($SettingsObject.actionGroups | Where-Object { $_.name -eq $alert.actionGroupName }).count -eq 0) {
                    Write-Error "Action group $($alert.actionGroupName) not found in settings." -Category InvalidArgument -CategoryTargetName "actionGroupName"
                }
            }

            $workbookService = Get-CmAzService -Service $SettingsObject.service.publish.workbook

            $workbook = @{
                DisplayName    = Get-CmAzResourceName -Resource "Workbook" -Architecture "Core" -Location $SettingsObject.location -Name "workspaceUsage-$($SettingsObject.name)"
                Category       = "workbook"
                Guid           = New-Guid
                exists         = $workbookService ? $true : $false
                serializedJson = (Get-CmAzSettingsFile -path "$PSScriptRoot/WorkspaceUsage.workbook.json") | ConvertTo-Json -Compress -Depth 100
            }

            Write-Verbose "Formatting action group receivers..."
            $receiverTypes = @("armRoles", "emails", "functions", "itsm", "logicApps", "notifications", "runbooks", "sms", "voice", "webhooks")
            $receiverTypesWithCommonSchema = @("armRoles", "emails", "functions", "logicApps", "runbooks", "webhooks")

            $nameKey = "name"

            foreach ($actionGroup in $SettingsObject.actionGroups) {

                if (!$actionGroup.name -or !$actionGroup.shortName) {
                    Write-Error "Please ensure a action group has a name, a shortname and at least one receiver." -Category InvalidArgument -CategoryTargetName "ActionGroups"
                }

                foreach ($receiverType in $receiverTypes) {

                    $receivers = $actionGroup[$receiverType]

                    if (!$receivers) {
                        $actionGroup[$receiverType] = @()
                        continue
                    }

                    for ($j = 0; $j -lt $receivers.count; $j++) {

                        $receiver = $receivers[$j]
                        $receiver[$nameKey] = Get-CmAzResourceName -Resource "ActionGroupReceiver" -Architecture "Core" -Location "Global" -Name "$($actionGroup[$nameKey])$($receiverType)-$($j)"

                        if ($receiverTypesWithCommonSchema -Contains $receiverType) {
                            $receiver.useCommonAlertSchema = $true
                        }
                    }
                }

                $actionGroup[$nameKey] = Get-CmAzResourceName -Resource "ActionGroup" -Architecture "Core" -Location "Global" -Name $actionGroup[$nameKey]

                Set-GlobalServiceValues -GlobalServiceContainer $SettingsObject -ServiceKey "actionGroup" -ResourceServiceContainer $actiongroup
            }

            Write-Verbose "Setting alerts..."
            $keyvaultAdminAlert = @{
                $nameKey          = Get-CmAzResourceName -Resource "Alert" -Architecture "Core" -Location "Global"-Name "$($SettingsObject.name)-KeyvaultAdmin";
                "actionGroupName" = Get-CmAzResourceName -Resource "ActionGroup" -Architecture "Core" -Location "Global" -Name $SettingsObject.alerts.keyvaultAdmin.actionGroupName;
                "enabled"         = $SettingsObject.alerts.keyvaultAdmin.enabled ??= $false;
                "servicePublish"  = $SettingsObject.service.publish.keyvaultAdminAlert;
                "conditions"      = @(
                    @{
                        "field"  = "category";
                        "equals" = "Administrative";
                    },
                    @{
                        "field"  = "resourceType";
                        "equals" = "Microsoft.Keyvault/Vaults";
                    }
                );
            }

            $resourceHealthAlert = @{
                $nameKey          = Get-CmAzResourceName -Resource "Alert" -Architecture "Core" -Location "Global"-Name "$($SettingsObject.name)-ResourceHealth";
                "actionGroupName" = Get-CmAzResourceName -Resource "ActionGroup" -Architecture "Core" -Location "Global" -Name $SettingsObject.alerts.resourceHealth.actionGroupName;
                "enabled"         = $SettingsObject.alerts.resourceHealth.enabled ??= $false;
                "servicePublish"  = $SettingsObject.service.publish.resourceHealthAlert;
                "conditions"      = @(
                    @{
                        "field"  = "category";
                        "equals" = "ResourceHealth";
                    },
                    @{
                        "field"  = "resourceType";
                        "equals" = "Microsoft.Keyvault/Vaults";
                    },
                    @{
                        "field"  = "resourceType";
                        "equals" = "Microsoft.OperationalInsights/Workspaces";
                    },
                    @{
                        "field"  = "resourceType";
                        "equals" = "Microsoft.Storage/StorageAccounts";
                    }
                );
            }

            $serviceHealthAlert = @{
                $nameKey          = Get-CmAzResourceName -Resource "Alert" -Architecture "Core" -Location "Global"-Name "$($SettingsObject.name)-ServiceHealth";
                "actionGroupName" = Get-CmAzResourceName -Resource "ActionGroup" -Architecture "Core" -Location "Global" -Name $SettingsObject.alerts.serviceHealth.actionGroupName;
                "enabled"         = $SettingsObject.alerts.serviceHealth.enabled ??= $false;
                "servicePublish"  = $SettingsObject.service.publish.serviceHealthAlert;
                "conditions"      = @(
                    @{
                        "field"  = "category";
                        "equals" = "ServiceHealth";
                    },
                    @{
                        "field"       = "properties.impactedServices[*].ImpactedRegions[*].RegionName";
                        "containsAny" = $SettingsObject.alerts.serviceHealth.impactedLocations ?? @($SettingsObject.location);
                    }
                );
            }

            $SettingsObject.alerts = @($keyvaultAdminAlert, $resourceHealthAlert, $serviceHealthAlert)

            $SettingsObject.workspaceDataRetentionInDays ??= 90
            $SettingsObject.storageDataRetentionInDays ??= 0

            Write-Verbose "Generating resource names..."
            $resourceGroupName = Get-CmAzResourceName -Resource "ResourceGroup" -Architecture "Core" -Location $SettingsObject.Location -Name $SettingsObject.name

            Write-Verbose "Deploying resource group ($resourceGroupName)..."
            New-AzResourceGroup `
                -Name $resourceGroupName `
                -Location $SettingsObject.location `
                -Tag @{ "cm-service" = $SettingsObject.service.publish.resourceGroup } `
                -Force

            if ($SettingsObject.service.publish.appInsights) {
                $appInsightsName = Get-CmAzResourceName -Resource "ApplicationInsights" -Architecture "Core" -Location $SettingsObject.location -Name $SettingsObject.name
            }
            else {
                Write-Verbose "Service locating existing app insights instance..."
                $appInsightsName = (Get-CmAzService -Service $SettingsObject.service.dependencies.appInsights -ThrowIfUnavailable -ThrowIfMultiple).resourceName
            }

            if ($SettingsObject.service.publish.storage) {

                Write-Verbose "Deploying storage..."
                $storageObject = @{

                    location        = $SettingsObject.location;
                    service         = @{
                        dependencies = @{
                            resourceGroup = $SettingsObject.service.publish.resourceGroup
                        };
                        publish      = @{
                            storage = $SettingsObject.service.publish.storage
                        }
                    }
                    storageAccounts = @(@{
                            storageAccountName = $SettingsObject.name;
                            accountType        = "Standard";
                            blobContainer      = @(
                                @{ name = "insights-logs-addonazurebackuppolicy"; },
                                @{ name = "insights-logs-azurebackupreport"; },
                                @{ name = "insights-logs-coreazurebackup"; },
                                @{ name = "insights-logs-networksecuritygroupflowevent"; }
                            )
                        })
                }

                New-CmAzIaasStorage -SettingsObject $storageObject -OmitTags

                $storageService = $SettingsObject.service.publish.storage
            }
            else {
                Write-Verbose "Service locating existing storage account instance..."
                $storageService = $SettingsObject.service.dependencies.storage
            }

            $storageAccountId = (Get-CmAzService -Service $storageService -Location $SettingsObject.location -ThrowIfUnavailable -ThrowIfMultiple).Id

            if ($SettingsObject.service.publish.workspace) {
                $workspaceName = Get-CmAzResourceName -Resource "LogAnalyticsWorkspace" -Architecture "Core" -Location $SettingsObject.location -Name $SettingsObject.name
            }
            else {
                Write-Verbose "Service locating existing log analytics workspace instance..."
                $workspaceName = (Get-CmAzService -Service $SettingsObject.service.dependencies.workspace -ThrowIfUnavailable -ThrowIfMultiple).resourceName
            }

            Write-Verbose "Deploying monitor resources..."

            $deploymentName = Get-CmAzResourceName -Resource "Deployment" -Architecture "Core" -Location $SettingsObject.Location -Name "New-CmAzCoreMonitor"

            $serviceContainer = @{
                appInsights = $SettingsObject.service.publish.appInsights;
                solution    = $SettingsObject.service.publish.solution;
                workbook    = $SettingsObject.service.publish.workbook;
                workspace   = $SettingsObject.service.publish.workspace;
            }

            New-AzResourceGroupDeployment `
                -Name $deploymentName `
                -ResourceGroupName $resourceGroupName `
                -TemplateFile "$PSScriptRoot/New-CmAzCoreMonitor.json" `
                -TemplateParameterObject @{
                    ActionGroups                 = $SettingsObject.actionGroups
                    Alerts                       = $SettingsObject.alerts
                    AppInsightsName              = $appInsightsName
                    StorageDataRetentionInDays   = $SettingsObject.storageDataRetentionInDays
                    WorkspaceDataRetentionInDays = $SettingsObject.workspaceDataRetentionInDays
                    ServiceContainer             = $serviceContainer
                    StorageAccountId             = $storageAccountId
                    WorkspaceName                = $workspaceName
                    Workbook                     = $workbook
                }

            Write-Verbose "Setting advisor configuration cpu threshold..."
            Set-AzAdvisorConfiguration -LowCpuThreshold $SettingsObject.AdvisorLowCPUThresholdPercentage

            Set-DeployedResourceTags -TagSettingsFile $TagSettingsFile -ResourceGroupIds @($resourceGroupName)

            Write-CommandStatus -CommandName $MyInvocation.MyCommand.Name -Start $false
        }
    }
    catch {
        $PSCmdlet.ThrowTerminatingError($PSItem);
    }
}