Scenario
- We’re creating playbooks in a managed Microsoft Sentinel-workspace.
- It needs to have the
Microsoft Sentinel Responder
role. - We want to be able to assign that role using access granted by Azure Lighthouse.
- We should do as much of this as possible in a Azure DevOps pipeline.
Setup
- Create the Lighthouse-configuration and onboard it.
- Deploy playbooks with managed identities enabled.
- Create ARM-template for assigning rights (or use Azure CLI, API).
- Deploy ARM-template.
1. Lighthouse-configuration
Create a Lighthouse-configuration and grant the User Access Administrator to a principal (User, Group, SPN):
"authorizations": {
"type": "array",
"metadata": {
"description": "Specify an array of objects, containing tuples of Azure Active Directory principalId, a Azure roleDefinitionId, and an optional principalIdDisplayName. The roleDefinition specified is granted to the principalId in the provider's Active Directory and the principalIdDisplayName is visible to customers."
},
"defaultValue": [
{
//Grants user access administrator to the SPN
"principalId": "3kl47fff-1337-1337-b726-2cf02b05c7c4",
"principalIdDisplayName": "Test-SPN",
"roleDefinitionId": "18d7d88d-d35e-4fb5-a5c3-7773c20a72d9",
//Allows the SPN to assign the following roles
"delegatedRoleDefinitionIds": [
//Microsoft Sentinel Responder
"3e150937-b8fe-4cfb-8069-0eaf05ecd056"
]
}
]
}
The same principal also needs to have write-permission on the object the managed identity resides within - for a playbook with a managed identity we should be fine with Microsoft Sentinel Contributor.
{
//Grants contributor to the SPN
"principalId": "3kl47fff-5655-4779-b726-2cf02b05c7c4",
"principalIdDisplayName": "Test-SPN",
"roleDefinitionId": "ab8e14d6-4a74-4a29-9ba8-549422addade"
}
At this point we can onboard the template.
Note: to read more about onboarding using Azure Lighthouse, click here.
2. Deploy playbooks with managed identities enabled
Using the Sentinel-as-Code project (https://github.com/javiersoriano/sentinelascode) as inspiration, we can deploy playbooks with a simple push-pipeline:
Assuming a simple Azure DevOps setup similar to the one mentioned above with the SPN from the above Lighthouse-configuration defined as the service-connection
or the subscription-object in the Azure Powershell-task.
Example pipeline
Simple pipeline that downloads the local repository and runs the CreatePlaybooks.ps1
-script with inputs.
name: build and deploy Playbooks
trigger:
paths:
include:
- Playbooks/*
stages:
- stage: deploy_playbooks
jobs:
- job: AgentJob
pool:
name: Azure Pipelines
vmImage: 'windows-latest'
variables:
- group: DeployGroup
- name: RepositoryLocation
value: '$(Build.Repository.LocalPath)/SentinelAsCode'
steps:
- checkout: self
- task: AzurePowerShell@5
displayName: 'Create and Update Playbooks'
inputs:
azureSubscription: $(subscription)
ScriptPath: '$(RepositoryLocation)/Scripts/CreatePlaybooks.ps1'
ScriptArguments: '-resourceGroup $(ResourceGroup) -PlaybooksFolder $(RepositoryLocation)/Playbooks'
azurePowerShellVersion: LatestVersion
pwsh: true
Example deployment script
Script that takes a path as input and loops through all files and tries to deploy .json
files to Azure as ARM-templates.
param(
[Parameter(Mandatory=$true)]$resourceGroup,
[Parameter(Mandatory=$true)]$PlaybooksFolder
)
Write-Host "Folder is: $($PlaybooksFolder)"
$armTemplateFiles = Get-ChildItem -Path $PlaybooksFolder -Filter *.json
Write-Host "Files are: " $armTemplateFiles
foreach ($armTemplate in $armTemplateFiles) {
try {
New-AzResourceGroupDeployment -ResourceGroupName $resourceGroup -TemplateFile $armTemplate
}
catch {
$ErrorMessage = $_.Exception.Message
Write-Error "Playbook deployment failed with message: $ErrorMessage"
}
}
Note: script courtesy of Javier Soriano.
Example playbook template
Sample playbook for changing incident severity can be found here.
Note: template courtesy of Yaniv Shasha
In order to enable managed identites the following block must be configured under the "type": "Microsoft.Logic/workflows"
-resource:
"identity": {
"type": "SystemAssigned"
}
If we add the template-file to the Playbook-folder this should trigger the pipeline and deploy the Playbook to Azure.
3. Create the ARM-template
In order to assign roles using the SPN mentioned above we need to create an ARM-template for the roleAssignment:
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
//Generates a new random guid to be used as assignmnent ID
"rbacGuid": {
"type": "string",
"defaultValue": "[newGuid()]"
}
},
"variables": {
//The GUID refering to the Microsoft Sentinel Responder
"responder": "3e150937-b8fe-4cfb-8069-0eaf05ecd056"
},
"resources": [
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2020-03-01-preview",
"name": "[parameters('rbacGuid')]",
"dependsOn": [
],
"properties": {
"roleDefinitionId": "[concat(subscription().id, '/resourceGroups/###resourceGroup###/providers/Microsoft.Authorization/roleDefinitions/', variables('responder'))]",
// The principalType property will tell Microsoft.Authorization not to perform the check for existence on your principal ID during roleAssignment creation
"principalType": "ServicePrincipal",
"delegatedManagedIdentityResourceId": "###delegatedresourceid###",
"principalId": "###principalId###"
}
}
]
}
Save as a .json file.
4. Deploy ARM-template
Create a script for parsing the roleAssignment-template
You grab the content of the template in Powershell:
$armTemplate = Get-Content "$templatePath/API-Connectors/ManagedIdentityAccess.json"
You then grab the resourceId of the playbook(s) you’re trying to change - just change the matching of the name to grab more of them:
$Playbook = Get-AzResource -ResourceType Microsoft.Logic/workflows | Where-Object {$_.Name -match "PlaybookName-*"}
We can then do some simple parsing of the template, grab the principalId of the managedIdentity and insert:
$ID = $Playbook.Identity.PrincipalId
$parsedTemplate = $armTemplate
$parsedTemplate = $parsedTemplate -replace ("###resourceGroup###",$resourceGroup)
$parsedTemplate = $parsedTemplate -replace ("###principalId###",$ID)
$parsedTemplate = $parsedTemplate -replace ("###delegatedresourceid###",$Playbook.ResourceId)
$parsedTemplate | Set-Content "RoleAssignment.json"
After this we have a ready to deploy template, which can be pushed using the following line of code:
New-AzResourceGroupDeployment -ResourceGroupName $resourcegroup -Name GrantRightsSPN -TemplateFile "RoleAssignment.json"
This all results in the following script:
Assign-Roles.ps1
PARAM (
$templatePath,
$resourceGroup
)
$armTemplate = Get-Content $templatePath
$Playbook = Get-AzResource -ResourceType Microsoft.Logic/workflows | Where-Object {$_.Name -match "PlaybookName-*"}
foreach($p in $playbook) {
$ID = $p.Identity.PrincipalId
$parsedTemplate = $armTemplate
$parsedTemplate = $parsedTemplate -replace ("###resourceGroup###",$resourceGroup)
$parsedTemplate = $parsedTemplate -replace ("###principalId###",$ID)
$parsedTemplate = $parsedTemplate -replace ("###delegatedresourceid###",$p.ResourceId)
$parsedTemplate | Set-Content "RoleAssignment.json"
New-AzResourceGroupDeployment -ResourceGroupName $resourcegroup -Name GrantRightsSPN -TemplateFile "RoleAssignment.json"
}
Deploy the roleAssignment-template using a pipeline
Using the script from above, we can create a simple pipeline similar to the one we used for playbooks:
name: assignRoles using Lighthouse
trigger:
paths:
include:
- Playbooks/*
stages:
- stage: assign_roles
jobs:
- job: AgentJob
pool:
name: Azure Pipelines
vmImage: 'windows-latest'
variables:
- group: DeployGroup
- name: RepositoryLocation
value: '$(Build.Repository.LocalPath)/SentinelAsCode'
steps:
- checkout: self
- task: AzurePowerShell@5
displayName: 'Assign Roles'
inputs:
azureSubscription: $(subscription)
ScriptPath: '$(RepositoryLocation)/Scripts/Assign-Roles.ps1'
ScriptArguments: '-templatePath "$(RepositoryLocation)/API-Connectors/ManagedIdentityAccess.json" -resouceGroup $(resourceGroup)'
azurePowerShellVersion: LatestVersion
pwsh: true
This should assign the Sentinel Responder-role to all managed identities for all playbooks in the filter Where-Object {$_.Name -match "PlaybookName-*"}
- you can also set the assignRoles-pipeline to trigger on finished runs by the Create-Playbook pipeline by adding it as a resource:
resources:
pipelines:
- pipeline: Create Playbooks # Name of the pipeline resource.
source: create-playbooks # The name of the pipeline referenced by this pipeline resource.
#project: SentinelAsCode # Required only if the source pipeline is in another project
trigger: true # Run pipeline when any run of assignRoles completes