Wir bei Experts Inside verwenden für viele automatisierte Aufgaben den Automation Account und Runbooks. Dabei hat sich das zuverlässige Logging oft als Problem herausgestellt. Deshalb werden bei uns wichtige Operationen in einen Log-Analytics-Workspace geschrieben.
Voraussetzungen für dieses Beispiel in diesem Blog sind ein Automation Account und ein Log Analytics workspace.
Aber es kann im Prinzip auch jede ARC-enabled Maschine verwendet werden.
Erstellen eines neuen Data collection Endpoints:
Erstellen einer neuen custom log Tabelle (DCR based)
Erstellen einer neuen data collection rule:
schemasample.json
Managed identity des Automation Account einschalten oder die Managed identity einer Maschine die Arc-enabled ist verwenden.
$endpoint_uri = "https://xi-expertsinside-t-euw-dce-01-b1j8.westeurope-1.ingest.monitor.azure.com" #Get-AutomationVariable -Name 'LogAnalyticsWorkspaceID'
$ImmutableId = "dcr-d16763b3b5b14d00aa83827563515dc4" #Get-AutomationVariable -Name 'LogAnalyticsWorkspaceKey'
$PSDefaultParameterValues = @{
"Write-XiLog:LogsIngestionEndpoint" = $endpoint_uri
"Write-XiLog:ImmutableId" = $ImmutableId
}
function Write-XiLog {
<#
.SYNOPSIS
This function writes log output for automation accounts
.DESCRIPTION
Logging in automation account is crap, so we send everything to log analytics.
The log ingestion api requires authentication, we need to grant the Monitoring Metrics Publisher role
to any identiy that uses this function. We need to call Connect-AzAccount -Identity and retrieve an access token.
.NOTES
log everything
.EXAMPLE
Write-XiLog -Message "this is what's happening"
This just writes a message to the output of the runbook
.EXAMPLE
Write-XiLog -Message "this is what's happening" -Scriptname RUN-ExchangeMigration
This writes a message to the output of the runbook AND sends the message to log analytics
.EXAMPLE
Write-XiLog -Message "this is what's happening" -Scriptname RUN-ExchangeMigration -Type Warning
This writes a warning to the output of the runbook AND sends the message to log analytics
#>
[CmdletBinding()]
param(
[string]$Message,
[string]$ScriptName,
[ValidatePattern('.ingest.monitor.azure.com$')]
[string]$LogsIngestionEndpoint,
[ValidatePattern('^dcr-')]
[string]$ImmutableId,
[ValidatePattern('^Custom-')]
[string]$StreamName = 'Custom-AutomationEngine',
[ValidateSet("output", "warning", "error")]
$Type = "output"
)
begin {
$azContext = Get-AzContext
if (-not($azContext)) {
Connect-AzAccount -identity -TenantID 'a6k4k3k2-b751-46b9-85e0-78e7c873hd61' -SubscriptionId '027463n9-a84b-4b53-b972-c9f7473hf685' -ErrorAction Stop -WarningAction SilentlyContinue
}
Get-AzContext
$Token = (Get-AzAccessToken -ResourceUrl 'https://monitor.azure.com' -ErrorAction Stop -WarningAction SilentlyContinue).Token
$secureToken = ConvertTo-SecureString -String $Token -AsPlainText -Force
$accesToken = [System.Net.NetworkCredential]::new(0,$secureToken).Password
if (-not($accesToken)) {
Write-Error "Could not retrieve access token."
}
}
process {
$time = Get-Date ([datetime]::Now) -Format O
$logEvent = @{
TimeGenerated = $time
EventType = $type
Message = $message
Script = $scriptname
}
$uri = "$LogsIngestionEndpoint/dataCollectionRules/$ImmutableId/streams/$($StreamName)?api-version=2023-01-01"
$headers = @{"Authorization" = "Bearer $accesToken" }
$body = ConvertTo-Json -InputObject @($logevent) -Compress
#$body = $logEvent | ConvertTo-Json -AsArray -Compress
Write-Verbose "Invoking [$uri] with body [$body]"
$null = Invoke-RestMethod -Uri $uri -Method "Post" -Body $body -Headers $headers -ContentType 'application/json'
switch ($type) {
"output" { Write-Output "$time`: $message" }
"warning" { Write-Warning "$time`: $message" }
"error" { Write-Error "$time`: $message" }
}
}
}
Write-XiLog -Message "Testcomment" -Scriptname RUN-ExchangeMigration
Tenant-ID
Subscription-ID
Data collection endpoint:
Data collection rule Immutable ID:
StreamName Parameter:
Jetzt kann das Script ausgeführt werden:
Jetzt heisst es sich in Geduld zu üben bis der Eintrag in Log Analytics erscheint.
Die KQL Abfrage ist einfach der Tabellenname.
Code-Archiv:
schemasample.json
[
{
"TimeGenerated": "2024-12-02T14:45:32Z",
"EventType": "SampleEvent",
"Script": "SampleScript",
"Message": "This is a sample message."
}
]
RUN-WriteToLogAnalytics.ps1
$endpoint_uri = "https://xi-expertsinside-t-euw-dce-01-b1j8.westeurope-1.ingest.monitor.azure.com" #Get-AutomationVariable -Name 'LogAnalyticsWorkspaceID'
$ImmutableId = "dcr-d16763b3b5b14d00aa83827563515dc4" #Get-AutomationVariable -Name 'LogAnalyticsWorkspaceKey'
$PSDefaultParameterValues = @{
"Write-XiLog:LogsIngestionEndpoint" = $endpoint_uri
"Write-XiLog:ImmutableId" = $ImmutableId
}
function Write-XiLog {
<#
.SYNOPSIS
This function writes log output for automation accounts
.DESCRIPTION
Logging in automation account is crap, so we send everything to log analytics.
The log ingestion api requires authentication, we need to grant the Monitoring Metrics Publisher role
to any identiy that uses this function. We need to call Connect-AzAccount -Identity and retrieve an access token.
.NOTES
log everything
.EXAMPLE
Write-XiLog -Message "this is what's happening"
This just writes a message to the output of the runbook
.EXAMPLE
Write-XiLog -Message "this is what's happening" -Scriptname RUN-ExchangeMigration
This writes a message to the output of the runbook AND sends the message to log analytics
.EXAMPLE
Write-XiLog -Message "this is what's happening" -Scriptname RUN-ExchangeMigration -Type Warning
This writes a warning to the output of the runbook AND sends the message to log analytics
#>
[CmdletBinding()]
param(
[string]$Message,
[string]$ScriptName,
[ValidatePattern('.ingest.monitor.azure.com$')]
[string]$LogsIngestionEndpoint,
[ValidatePattern('^dcr-')]
[string]$ImmutableId,
[ValidatePattern('^Custom-')]
[string]$StreamName = 'Custom-AutomationEngine',
[ValidateSet("output", "warning", "error")]
$Type = "output"
)
begin {
$azContext = Get-AzContext
if (-not($azContext)) {
Connect-AzAccount -identity -TenantID 'a6k4k3k2-b751-46b9-85e0-78e7c873hd61' -SubscriptionId '027463n9-a84b-4b53-b972-c9f7473hf685' -ErrorAction Stop -WarningAction SilentlyContinue
}
Get-AzContext
$Token = (Get-AzAccessToken -ResourceUrl 'https://monitor.azure.com' -ErrorAction Stop -WarningAction SilentlyContinue).Token
$secureToken = ConvertTo-SecureString -String $Token -AsPlainText -Force
$accesToken = [System.Net.NetworkCredential]::new(0,$secureToken).Password
if (-not($accesToken)) {
Write-Error "Could not retrieve access token."
}
}
process {
$time = Get-Date ([datetime]::Now) -Format O
$logEvent = @{
TimeGenerated = $time
EventType = $type
Message = $message
Script = $scriptname
}
$uri = "$LogsIngestionEndpoint/dataCollectionRules/$ImmutableId/streams/$($StreamName)?api-version=2023-01-01"
$headers = @{"Authorization" = "Bearer $accesToken" }
$body = ConvertTo-Json -InputObject @($logevent) -Compress
#$body = $logEvent | ConvertTo-Json -AsArray -Compress
Write-Verbose "Invoking [$uri] with body [$body]"
$null = Invoke-RestMethod -Uri $uri -Method "Post" -Body $body -Headers $headers -ContentType 'application/json'
switch ($type) {
"output" { Write-Output "$time`: $message" }
"warning" { Write-Warning "$time`: $message" }
"error" { Write-Error "$time`: $message" }
}
}
}
Write-XiLog -Message "Testcomment" -Scriptname RUN-ExchangeMigration