This project automates the ingestion of Locksmith2 JSON reports into a custom table in an existing Log Analytics workspace.
main.bicep- Creates the following resources in a resource group:
- Custom table
Locksmith2_CL - Data Collection Endpoint (DCE)
- Data Collection Rule (DCR)
- Role assignment
Monitoring Metrics Publisheron the DCR scope (from explicit principal ID, or auto-resolved from an Arc/VM identity source)
- Custom table
- Routes the
Custom-Locksmith2Streamstream intoLocksmith2_CL.
- Creates the following resources in a resource group:
locksmith2Report.ps1- Reads
*-locksmith2.json - Validates records
- Injects
TimeGeneratedin UTC (ISO 8601) - Authenticates via Managed Identity (native Azure VM IMDS at
169.254.169.254, or the Azure Arc HIMDS endpoint atlocalhost:40342for Arc-connected servers) - Sends data through the Log Ingestion API to DCE/DCR
- Archives successfully processed files
- Writes local logs to
logs
- Reads
install.ps1- Complete installation including:
- Azure CLI check/installation
- Permission check before deployment
- Bicep deployment
- Automatic managed identity principal resolution (client ID / Arc token claim)
- Automatic Arc/VM identity source detection (machine name + resource group) when principal ID is not provided
- Bicep-driven RBAC assignment
Monitoring Metrics Publisheron DCR scope config.jsoncreation- Download/extract Locksmith2
- Create/update scheduled task
- Can also run without deployment by using
-SkipDeployment(uses existingconfig.json)
- Complete installation including:
- Windows Server with access to Azure
- Permissions on the target resource group (for example
OwnerorContributor) - Existing Log Analytics workspace
- Managed Identity on the server
- No secrets in scripts or config
- Authentication exclusively via Managed Identity
- Recommended: user-assigned Managed Identity (lifecycle decoupled from server)
- RBAC for ingestion: role
Monitoring Metrics Publisheron DCR scope
Example command:
.\install.ps1 `
-SubscriptionId "<sub-id>" `
-ResourceGroupName "<rg-name>" `
-WorkspaceName "<law-name>" `
-Location "westeurope" `
-ManagedIdentityClientId "<optional-uami-client-id>" `
-ManagedIdentityPrincipalId "<optional-mi-object-id>" `
-ManagedIdentityMachineName "<optional-arc-or-vm-name>" `
-ManagedIdentityResourceGroupName "<optional-identity-rg>"If -ManagedIdentityPrincipalId is omitted, the installer now falls back to machine-based auto-resolution:
- resolves machine name from
-ManagedIdentityMachineNameor local hostname - detects Arc machine (
Microsoft.HybridCompute/machines) or Azure VM (Microsoft.Compute/virtualMachines) - passes that identity source into Bicep, which resolves
identity.principalIdand assignsMonitoring Metrics Publisheron the DCR
Notes:
- Without
-NonInteractive, the script asks for the scheduled task interval interactively. - With
-SkipDeployment, Azure login, Bicep deployment, and RBAC assignment are skipped. - When using
-SkipDeployment, a validconfig.jsonmust already exist in the project directory. - For unattended execution, you can optionally use:
Full installation one-liner (deployment + non-interactive task settings):
.\install.ps1 -SubscriptionId "<sub-id>" -ResourceGroupName "<rg-name>" -WorkspaceName "<law-name>" -Location "westeurope" -TaskSchedule DAILY -TaskModifier 1 -NonInteractive.\install.ps1 `
-SubscriptionId "<sub-id>" `
-ResourceGroupName "<rg-name>" `
-WorkspaceName "<law-name>" `
-Location "westeurope" `
-TaskSchedule DAILY `
-TaskModifier 1 `
-NonInteractiveInstallation without deployment (for example, when Bicep was already deployed from another machine):
.\install.ps1 `
-SkipDeployment `
-TaskSchedule DAILY `
-TaskModifier 1 `
-NonInteractiveThe scheduled task starts locksmith2Report.ps1 on a regular interval.
Each run processes only the newest file matching SourcePattern (default: *-locksmith2.json) in SourceDirectory.
Files in the archive directory are explicitly excluded from processing.
Successfully ingested files are moved to archive.
The config.json file is created by install.ps1.
An example is available in config.json.example.
Important fields:
DceUriDcrImmutableIdStreamNameManagedIdentityClientId(optional; empty for system-assigned MI)
If you deploy main.bicep from another machine, run install.ps1 on the target server with -SkipDeployment.
In this mode, the installer skips Azure login, Bicep deployment, and RBAC assignment, and uses the existing config.json.
You can extract the required values from your deployment outputs:
$outputs = az deployment group show `
--resource-group "<rg-name>" `
--name "<deployment-name>" `
--query "properties.outputs" `
--output json | ConvertFrom-Json
$outputs.dataCollectionEndpointUri.value
$outputs.dataCollectionRuleImmutableId.value
$outputs.streamNameOut.valueMap them to config.json as follows:
DceUri->dataCollectionEndpointUri.valueDcrImmutableId->dataCollectionRuleImmutableId.valueStreamName->streamNameOut.value
Then run:
.\install.ps1 -SkipDeployment -TaskSchedule DAILY -TaskModifier 1 -NonInteractiveLocksmith2_CL
| where TimeGenerated > ago(24h)
| summarize count() by Technique
| order by count_ descA special thanks to these fantastic supporters and Microsoft MVP Fellows:
- Nicola Suter (@nicolonsky) – for optimizing and testing the script
- Jake Hildreth (@jakehildreth) – for his awesome work on Locksmith2 and for backing my solution