PowershellModules/net48/Resources/Scripts/log_monitoring_common.ps1

# ---------------- Resource Deployment Functions (per Account) ----------------

function New-Workspace {
    param (
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$WorkspaceName,
        [Parameter(Mandatory)][string]$Location
    )

    $workspace = Get-AzOperationalInsightsWorkspace -ResourceGroupName $ResourceGroupName -Name $WorkspaceName -ErrorAction SilentlyContinue
    if (-not $workspace) {
        Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Creating Log Analytics Workspace $WorkspaceName"
        $workspace = New-AzOperationalInsightsWorkspace -Location $Location `
            -Name $WorkspaceName `
            -ResourceGroupName $ResourceGroupName `
            -Sku "PerGB2018" | Out-Null
    }

    # Wait for provisioning
    $attempt = 0
    $MaxAttempts = 20
    while ($workspace.ProvisioningState -ne "Succeeded" -and $attempt -lt $MaxAttempts) {
        $attempt++
        Start-Sleep -Seconds 15
        $workspace = Get-AzOperationalInsightsWorkspace -ResourceGroupName $ResourceGroupName -Name $WorkspaceName
    }

    if ($workspace.ProvisioningState -ne "Succeeded") {
        throw "Workspace did not become active in time."
    }

    return $workspace
}


function New-Table {
    param (
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$WorkspaceName,
        [Parameter(Mandatory)][string]$TableName
    )
    
    $table = Get-AzOperationalInsightsTable -ResourceGroupName $ResourceGroupName -WorkspaceName $WorkspaceName -TableName $TableName -ErrorAction SilentlyContinue
    if (-not $table) {
        Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Creating custom log table $TableName in workspace $WorkspaceName"
        $table = New-AzOperationalInsightsTable `
            -ResourceGroupName $ResourceGroupName `
            -WorkspaceName $WorkspaceName `
            -TableName $TableName `
            -TotalRetentionInDays 30 `
            -Column @{ 'TimeGenerated' = 'datetime'; 'RawData' = 'string' } | Out-Null
    }
    return $table
}

function New-DCE {
    param (
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$DCEName,
        [Parameter(Mandatory)][string]$Location
    )
    
    $dce = Get-AzDataCollectionEndpoint -ResourceGroupName $ResourceGroupName -Name $DCEName -ErrorAction SilentlyContinue
    if (-not $dce) {
        Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Creating secure Data Collection Endpoint '$DCEName'"
        $dce = New-AzDataCollectionEndpoint -Name $DCEName `
            -ResourceGroupName $ResourceGroupName `
            -Location $Location `
            -NetworkAclsPublicNetworkAccess "Enabled" `
            -Description "DCE for NCache counters" | Out-Null
    }
    return $dce
}

function New-DCR {
    param (
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$SubscriptionId,
        [Parameter(Mandatory)][string]$DCEName,
        [Parameter(Mandatory)][string]$DCRName,
        [Parameter(Mandatory)][string]$WorkspaceName,
        [Parameter(Mandatory)][string]$Location,
        [Parameter(Mandatory)][string]$OS
    )

    $dcrJson = Get-DcrJsonString -ResourceGroupName $ResourceGroupName -SubscriptionId $SubscriptionId -DCEName $DCEName -DCRName $DCRName -WorkspaceName $WorkspaceName -Location $Location -OS $OS
    $dcr = Get-AzDataCollectionRule -ResourceGroupName $ResourceGroupName -Name $DCRName -ErrorAction SilentlyContinue
    if (-not $dcr) {
        Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Creating Data Collection Rule $DCRName"
        $dcr = New-AzDataCollectionRule -ResourceGroupName $ResourceGroupName `
            -Name $DCRName `
            -JsonString $dcrJson | Out-Null
    }
    return $dcr
}

function Get-DcrJsonString {
    param (
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$SubscriptionId,
        [Parameter(Mandatory)][string]$DCEName,
        [Parameter(Mandatory)][string]$DCRName,
        [Parameter(Mandatory)][string]$WorkspaceName,
        [Parameter(Mandatory)][string]$Location,
        [Parameter(Mandatory)][string]$OS
    )

    $laGuid = (New-Guid).ToString()
    $dcrJson = ""
    if ($OS -eq "Linux") {
        $dcrJson = @"
{
  "location": "$Location",
  "kind": "Windows",
  "type": "Microsoft.Insights/dataCollectionRules",
  "properties": {
    "dataCollectionEndpointId": "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Insights/dataCollectionEndpoints/$DCEName",
    "dataSources": {
      "logFiles": [
        {
          "streams": [ "Custom-Text-NCacheLogs_CL" ],
          "filePatterns": [ "/opt/ncache/log-files/*.txt" ],
          "format": "text",
          "name": "Custom-Text-NCacheLogs_CL"
        },
        {
          "streams": [ "Custom-Text-NCacheEventLogs_CL" ],
          "filePatterns": [ "/opt/ncache/log-files/eventlogs/*.txt" ],
          "format": "text",
          "name": "Custom-Text-NCacheEventLogs_CL"
        }
      ]
    },
    "destinations": {
      "logAnalytics": [
        {
          "workspaceResourceId": "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.OperationalInsights/workspaces/$WorkspaceName",
          "name": "$laGuid"
        }
      ]
    },
    "dataFlows": [
      {
        "streams": [ "Custom-Text-NCacheLogs_CL" ],
        "destinations": [ "$laGuid" ],
        "transformKql": "source",
        "outputStream": "Custom-NCacheLogs_CL"
      },
      {
        "streams": [ "Custom-Text-NCacheEventLogs_CL" ],
        "destinations": [ "$laGuid" ],
        "transformKql": "source",
        "outputStream": "Custom-NCacheEventLogs_CL"
      }
    ],
    "streamDeclarations": {
      "Custom-Text-NCacheLogs_CL": {
        "columns": [
          { "name": "TimeGenerated", "type": "datetime" },
          { "name": "RawData", "type": "string" }
        ]
      },
      "Custom-Text-NCacheEventLogs_CL": {
        "columns": [
          { "name": "TimeGenerated", "type": "datetime" },
          { "name": "RawData", "type": "string" }
        ]
      }
    }
  }
}
"@

    }
    else {
        $dcrJson = @"
{
    "location": "$Location",
    "kind": "Windows",
    "type": "Microsoft.Insights/dataCollectionRules",
    "properties": {
        "dataCollectionEndpointId": "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Insights/dataCollectionEndpoints/$DCEName",
        "dataSources": {
            "windowsEventLogs": [
                {
                    "streams": [ "Microsoft-Event" ],
                    "xPathQueries": [ "Application!*[System[Provider[@Name='NBridgeSvc' or @Name='NCache' or @Name='NCache Bridge']]]" ],
                    "name": "eventLogsDataSource"
                }
            ],
            "logFiles": [
                {
                    "streams": [ "Custom-Text-NCacheLogs_CL" ],
                    "filePatterns": [ "C:\\Program Files\\NCache\\log-files\\*.txt" ],
                    "format": "text",
                    "name": "Custom-Text-NCacheLogs_CL"
                }
            ]
        },
        "destinations": {
            "logAnalytics": [
                {
                    "workspaceResourceId": "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.OperationalInsights/workspaces/$WorkspaceName",
                    "name": "$laGuid"
                }
            ]
        },
        "dataFlows": [
            {
                "streams": [ "Microsoft-Event" ],
                "destinations": [ "$laGuid" ],
                "outputStream": "Microsoft-Event"
            },
            {
                "streams": [ "Custom-Text-NCacheLogs_CL" ],
                "destinations": [ "$laGuid" ],
                "transformKql": "source",
                "outputStream": "Custom-NCacheLogs_CL"
            }
        ],
        "streamDeclarations": {
            "Custom-Text-NCacheLogs_CL": {
                "columns": [
                    { "name": "TimeGenerated", "type": "datetime" },
                    { "name": "RawData", "type": "string" }
                ]
            }
        }
    }
}
"@

    }
    
    return $dcrJson
}

function Install-NcAzMonitoringResources {
    param(
        [Parameter(Mandatory)][string]$ResourceGroupName
    )

    $WorkspaceName = "$ResourceGroupName-Workspace"
    $DCEName = "$ResourceGroupName-DCE"
    $DCRName = "$ResourceGroupName-DCR"
    $LogTableName = "NCacheLogs_CL"
    $EventTableName = "NCacheEventLogs_CL"
    $rg = Get-AzResourceGroup -Name $ResourceGroupName
    $Location = $rg.Location
    $osType = $null
    if ($rg.Tags.ContainsKey("OsType")) {
        $osType = $rg.Tags["OsType"]
    }
    $SubscriptionId = (Get-AzContext).Subscription.Id

    if (-not $SubscriptionId) {
        throw "Could not determine subscription ID."
    }

    New-Workspace -ResourceGroupName $ResourceGroupName -WorkspaceName $WorkspaceName -Location $Location | Out-Null
    New-Table -ResourceGroupName $ResourceGroupName -TableName $LogTableName -WorkspaceName $WorkspaceName | Out-Null
    if ($osType -eq "Linux") {
        New-Table -ResourceGroupName $ResourceGroupName -TableName $EventTableName -WorkspaceName $WorkspaceName | Out-Null
    }
    New-DCE -ResourceGroupName $ResourceGroupName -DCEName $DCEName -Location $Location | Out-Null
    New-DCR -ResourceGroupName $ResourceGroupName -SubscriptionId $SubscriptionId -DCEName $DCEName -DCRName $DCRName -WorkspaceName $WorkspaceName -Location $Location -OS $osType | Out-Null

    Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Monitoring resources deployed successfully."
}

# ---------------- VM Preparation Functions (per VM) ----------------
function Install-AMAExtension {
    param(
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$VmName,
        [Parameter(Mandatory)][string]$Location,
        [Parameter(Mandatory)][string]$OS
    )

    Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Installing Azure Monitor Agent (AMA) on VM $VmName"
    Set-AzVMExtension `
        -ResourceGroupName $ResourceGroupName `
        -VMName $VmName `
        -Name "AzureMonitor$($OS)Agent" `
        -Publisher "Microsoft.Azure.Monitor" `
        -ExtensionType "AzureMonitor$($OS)Agent" `
        -TypeHandlerVersion "1.0" `
        -Location $Location `
        -EnableAutomaticUpgrade $true `
        -ForceRerun (New-Guid).ToString() | Out-Null

    # Wait for provisioning
    $maxAttempts = 20
    $attempt = 0
    $ext = Get-AzVMExtension -ResourceGroupName $ResourceGroupName -VMName $VmName -Name "AzureMonitor$($OS)Agent"
    while ($ext.ProvisioningState -ne "Succeeded" -and $attempt -lt $maxAttempts) {
        $attempt++
        Start-Sleep -Seconds 15
        $ext = Get-AzVMExtension -ResourceGroupName $ResourceGroupName -VMName $VmName -Name "AzureMonitor$($OS)Agent"
    }

    if ($ext.ProvisioningState -ne "Succeeded") {
        throw "AMA did not become active in time."
    }
}

function Add-DceAssociation {
    param(
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$SubscriptionId,
        [Parameter(Mandatory)][string]$VmName,
        [Parameter(Mandatory)][string]$DCEName
    )

    Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Linking Data Collection Endpoint to VM '$VmName'"
    New-AzDataCollectionRuleAssociation -AssociationName "configurationAccessEndpoint" `
        -ResourceUri "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Compute/virtualMachines/$VmName" `
        -DataCollectionEndpointId "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Insights/dataCollectionEndpoints/$DCEName" `
        -Description "Associate DCE with VM" | Out-Null

    # Wait for provisioning
    Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Waiting for the endpoint link to complete..."
    $attempt = 0
    $MaxAttempts = 20
    $check = $null
    while (-not $check -and $attempt -lt $MaxAttempts) {
        Start-Sleep -Seconds 15
        $check = Get-AzDataCollectionRuleAssociation -TargetResourceId "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Compute/virtualMachines/$VmName" `
        | Where-Object { $_.Name -eq "configurationAccessEndpoint" }
        $attempt++
    }

    if (-not $check) {
        throw "DCE for VM $VmName did not become active in time."
    }
    return $check
}

function Add-DcrAssociation {
    param(
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$SubscriptionId,
        [Parameter(Mandatory)][string]$VmName,
        [Parameter(Mandatory)][string]$DCRName
    )

    Write-Host "$((Get-Date).ToString('yyyy-MM-dd')) - Linking Data Collection Rule to VM '$VmName'"
    $assoc = New-AzDataCollectionRuleAssociation -AssociationName "$VmName-association" `
        -ResourceUri "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Compute/virtualMachines/$VmName" `
        -DataCollectionRuleId "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Insights/dataCollectionRules/$DCRName" `
        -Description "Associate DCR with VM" | Out-Null

    return $assoc
}

function Install-NcAzMonitoringOnVM {
    param(
        [Parameter(Mandatory)][string]$ResourceGroupName,
        [Parameter(Mandatory)][string]$VmName
    )

    $DCEName = "$ResourceGroupName-DCE"
    $DCRName = "$ResourceGroupName-DCR"
    $rg = Get-AzResourceGroup -Name $ResourceGroupName
    $Location = $rg.Location
    $osType = $null
    if ($rg.Tags.ContainsKey("OsType")) {
        $osType = $rg.Tags["OsType"]
    }
    $SubscriptionId = (Get-AzContext).Subscription.Id

    if (-not $SubscriptionId) {
        throw "Could not determine subscription ID."
    }

    Install-AMAExtension -ResourceGroupName $ResourceGroupName -VmName $VmName -Location $Location -OS $osType
    Add-DceAssociation -ResourceGroupName $ResourceGroupName -SubscriptionId $SubscriptionId -VmName $VmName -DCEName $DCEName | Out-Null
    Add-DcrAssociation -ResourceGroupName $ResourceGroupName -SubscriptionId $SubscriptionId -VmName $VmName -DCRName $DCRName | Out-Null
}