From a6f6b49bb45fed21fd21a9353c56332bee0e88ad Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Thu, 12 Jun 2025 16:28:42 +0530 Subject: [PATCH 01/17] Adding code for azure deploy. --- azure-deploy/README.md | 83 +++++ azure-deploy/mainTemplate.bicep | 142 ++++++++ azure-deploy/modules/containerIdentity.bicep | 28 ++ azure-deploy/modules/containerInstance.bicep | 77 ++++ azure-deploy/modules/deploymentIdentity.bicep | 11 + azure-deploy/modules/graphPermissions.bicep | 123 +++++++ azure-deploy/scripts/create-container-umi.ps1 | 196 ++++++++++ .../create-deployment-umi-with-perms.ps1 | 221 ++++++++++++ .../scripts/single-script-full-deployment.ps1 | 338 ++++++++++++++++++ 9 files changed, 1219 insertions(+) create mode 100644 azure-deploy/README.md create mode 100644 azure-deploy/mainTemplate.bicep create mode 100644 azure-deploy/modules/containerIdentity.bicep create mode 100644 azure-deploy/modules/containerInstance.bicep create mode 100644 azure-deploy/modules/deploymentIdentity.bicep create mode 100644 azure-deploy/modules/graphPermissions.bicep create mode 100644 azure-deploy/scripts/create-container-umi.ps1 create mode 100644 azure-deploy/scripts/create-deployment-umi-with-perms.ps1 create mode 100644 azure-deploy/scripts/single-script-full-deployment.ps1 diff --git a/azure-deploy/README.md b/azure-deploy/README.md new file mode 100644 index 00000000..c4b0acc3 --- /dev/null +++ b/azure-deploy/README.md @@ -0,0 +1,83 @@ +# AzureHoundDeploy + +## Overview + +AzureHound now supports Managed Identity authentication. This allows AzureHound to be run in an Azure Container Instance. The Container Instance must be associated with a Managed Identity that has the required RBAC Roles and Graph Permissions that AzureHound requires. + +This repository containes the Azure Resource Manager (ARM) Template along with supporting scripts that +allow a user to conveniently deploy and configure AzureHound to an Azure Container Instance. Specifically this ARM template provides the following functionallity. + +1) Deploy an AzureHound Instance that runs in an Azure Container Instance +2) Creates or uses an existing Container Instance +3) Creates or uses an existing Managed Identity for the Container that provides Azure permissions +4) Provides a wizard that configures the AzureHound Instance + +## Process + + +## Prerequisites + +In order for this ARM Template to create the container's User Managed Identity, the ARM Template requires an existing User Managed Identity with the following permissions: + + - Application.ReadWrite.All + - Managed Identity Contributor + - User Access Administrator + +This repository contains a `create-deployment-umi-with-perms.ps1` script that can be used to create deployment's user managed identity. Alternatively you can create the User Managed Identity in the Azure portal. + +## AzureHound Required Permissions + +AzureHound requires the following Azure permissions + + - Directory.Read.All + - Reader + +The ARM Template will create the Container Instance along with a User Managed Identity that provides this permissions to the AzureHound Instance. + +## Supporting Scripts + +The ARM Template is designed to create the Container Instance along with a User Managed Identity that will provide AzureHound with all the permissions it needs to run. However, it + +- Create/Fix Managed Identity For The ARM Template + `create-deployment-umi-with-perms.ps1` +- Create/Fix Managed Identity For the Container + `create-container-umi.ps1` +- Full end to end script. + `single-script-full-deployment.ps1` + +## Notes About Approach +ManagedIdentities can be assigned permissions just like App Registration (Enterprise Applications), however you are assigning the permissions to +the managed identity's application object id. After creation of a Managed Identity it takes some amount of time before the application id is associated with the managed identity. Therefore we add retry logic. + +## Permissions DeploymentScript requires +The `managed-identity-permissions.sh` script will require +the following permissions to be assigned to a managed identity. + +# Issues to document + +## `single-script-full-deployment` + +- it requires tenant-id, but this can be retrieved with `(Get-AzTenant).Id` after logging in with `Connect-AzAccount` +- does it require an existing container registry. + - Maybe use `$registry = New-AzContainerRegistry -ResourceGroupName "myResourceGroup" -Name "mycontainerregistry" -EnableAdminUser -Sku Basic -Location EastUS` + - but permissions required may be a problem. + + **maybe create a separate script** + +## The docker image must be loaded into the container registry before hand. +To do this this is the following procedure: + + 1) Have docker installed and running + 2) An image registry in Azure must exist or be created + 3) Have the authentication information for the existing azure image registry + 4) At the command line authenticate with the azure registry + - + 5) pull the latest AzureHound image + `docker pull ghcr.io/bloodhoundad/azurehound:` Note: Use the tag 'latest' unless you want a specific version of azurehound. + 6) tag the image to associate it with an the azure registry + `docker tag ghcr.io/owner/repository:tag .azurecr.io/:tag Note: Best to use latest unless you need to maintain multiple versions in Azure's registry. + 7) docker push .azurecr.io/:tag + + + + diff --git a/azure-deploy/mainTemplate.bicep b/azure-deploy/mainTemplate.bicep new file mode 100644 index 00000000..d0f30ba3 --- /dev/null +++ b/azure-deploy/mainTemplate.bicep @@ -0,0 +1,142 @@ +// mainTemplate.bicep +targetScope = 'subscription' + +@description('Name of the resource group to deploy into') +param resourceGroupName string + +@description('Location for all resources') +param location string + +@description('Name of the container user managed identity') +param containerUMIName string + +@description('Resource group for the container UMI (defaults to main resource group if not specified)') +param containerUMIResourceGroupName string = resourceGroupName + +@description('Name of the deployment user managed identity') +param deploymentUMIName string + +@description('Resource group containing the deployment user managed identity') +param deploymentUMIResourceGroupName string + +@description('Azure tenant ID to analyze') +param azureTenantId string + +@description('Bloodhound instance domain') +param bloodhoundInstanceDomain string + +@description('Bloodhound token ID') +@secure() +param bloodhoundTokenId string + +@description('Bloodhound token') +@secure() +param bloodhoundToken string + +var containerName = '${resourceGroupName}-container-group' +var imageName = 'azurehounddeploy.azurecr.io/azurehound:latest' + +// Create main resource group if it doesn't exist +resource mainRG 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: location +} + +// Create container UMI resource group if different from main and doesn't exist +resource containerUMIRG 'Microsoft.Resources/resourceGroups@2023-07-01' = if (containerUMIResourceGroupName != resourceGroupName) { + name: containerUMIResourceGroupName + location: location +} + +// Create deployment UMI resource group if different from main +resource deploymentUMIRG 'Microsoft.Resources/resourceGroups@2023-07-01' = if (deploymentUMIResourceGroupName != resourceGroupName) { + name: deploymentUMIResourceGroupName + location: location +} + + +// Deploy or reference the container UMI +module containerIdentity './modules/containerIdentity.bicep' = { + name: 'containerIdentity-deployment' + scope: resourceGroup(containerUMIResourceGroupName) + params: { + location: location + containerUMIName: containerUMIName + } + dependsOn: [ + mainRG + containerUMIRG + ] +} + +// Call module to create deployment UMI in correct RG +module deploymentIdentity 'modules/deploymentIdentity.bicep' = { + name: 'deploymentIdentity-deployment' + scope: resourceGroup(deploymentUMIResourceGroupName) + params: { + location: location + deploymentUMIName: deploymentUMIName + } + dependsOn: [ + mainRG + deploymentUMIRG + ] +} + +// Assign Reader role at subscription scope +resource readerRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().subscriptionId, containerUMIName, 'Reader') + scope: subscription() + properties: { + principalId: containerIdentity.outputs.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') // Reader role + principalType: 'ServicePrincipal' + } + dependsOn: [ + containerIdentity + ] +} + +// Deploy script to configure Graph API permissions +module graphPermissions './modules/graphPermissions.bicep' = { + name: 'graph-permissions-deployment' + scope: resourceGroup(resourceGroupName) + params: { + location: location + deploymentUMIName: deploymentUMIName + deploymentUMIResourceGroupName: deploymentUMIResourceGroupName + containerUMIPrincipalId: containerIdentity.outputs.principalId + } + dependsOn: [ + mainRG + containerIdentity + ] +} + +// Deploy the container instance +module containerInstance './modules/containerInstance.bicep' = { + name: 'container-instance-deployment' + scope: resourceGroup(resourceGroupName) + params: { + location: location + containerGroupName: containerName + containerUMIResourceId: containerIdentity.outputs.resourceId + imageName: imageName + bloodhoundInstanceDomain: bloodhoundInstanceDomain + azureTenantId: azureTenantId + bloodhoundTokenId: bloodhoundTokenId + bloodhoundToken: bloodhoundToken + } + dependsOn: [ + mainRG + graphPermissions + ] +} + +output containerUMIResourceId string = containerIdentity.outputs.resourceId +output containerUMIPrincipalId string = containerIdentity.outputs.principalId +output permissionSetupRequired bool = graphPermissions.outputs.needsManualSetup +output permissionStatus string = graphPermissions.outputs.statusMessage +output assignedPermissions array = graphPermissions.outputs.assignedPermissions +output deploymentUMIPrincipalId string = deploymentIdentity.outputs.principalId +output deploymentUMIId string = deploymentIdentity.outputs.resourceId \ No newline at end of file diff --git a/azure-deploy/modules/containerIdentity.bicep b/azure-deploy/modules/containerIdentity.bicep new file mode 100644 index 00000000..54e6d110 --- /dev/null +++ b/azure-deploy/modules/containerIdentity.bicep @@ -0,0 +1,28 @@ +// modules/containerIdentity.bicep +param location string +param containerUMIName string + +// Reference existing identity if it exists, create if it doesn't +resource containerUMI 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: containerUMIName + location: location +} + +// Check if Reader role is already assigned +resource existingReaderRole 'Microsoft.Authorization/roleAssignments@2022-04-01' existing = { + scope: subscription() + name: guid(subscription().id, containerUMI.id, 'Reader') +} + +// Assign Reader role at subscription scope if not already assigned +resource readerRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, containerUMI.id, 'Reader') + properties: { + principalId: containerUMI.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') // Reader role + principalType: 'ServicePrincipal' + } +} + +output resourceId string = containerUMI.id +output principalId string = containerUMI.properties.principalId diff --git a/azure-deploy/modules/containerInstance.bicep b/azure-deploy/modules/containerInstance.bicep new file mode 100644 index 00000000..03579716 --- /dev/null +++ b/azure-deploy/modules/containerInstance.bicep @@ -0,0 +1,77 @@ +param location string +param containerGroupName string +param containerUMIResourceId string +param imageName string +param bloodhoundInstanceDomain string +param azureTenantId string + +@secure() +param bloodhoundTokenId string + +@secure() +param bloodhoundToken string + +var config = { + app: '' + auth: '' + batchsize: 100 + config: '/home/nonroot/.config/azurehound/config.json' + instance: 'https://${bloodhoundInstanceDomain}/' + json: false + 'managed-identity': true + maxconnsperhost: 20 + maxidleconnsperhost: 20 + region: 'cloud' + streamcount: 25 + tenant: azureTenantId + token: bloodhoundToken + tokenid: bloodhoundTokenId + verbosity: 0 +} + +resource containerGroup 'Microsoft.ContainerInstance/containerGroups@2023-05-01' = { + name: containerGroupName + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${containerUMIResourceId}': {} + } + } + properties: { + containers: [ + { + name: 'azurehound' + properties: { + image: imageName + command: [ + '/azurehound' + 'start' + ] + volumeMounts: [ + { + name: 'config-volume' + mountPath: '/home/nonroot/.config/azurehound' + } + ] + resources: { + requests: { + cpu: '1' + memoryInGB: '1' + } + } + } + } + ] + volumes: [ + { + name: 'config-volume' + secret: { + 'config.json': base64(string(config)) + } + } + ] + osType: 'Linux' + restartPolicy: 'Never' + } +} diff --git a/azure-deploy/modules/deploymentIdentity.bicep b/azure-deploy/modules/deploymentIdentity.bicep new file mode 100644 index 00000000..e7e96cf8 --- /dev/null +++ b/azure-deploy/modules/deploymentIdentity.bicep @@ -0,0 +1,11 @@ +// modules/deploymentIdentity.bicep +param location string +param deploymentUMIName string + +resource deploymentUMI 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: deploymentUMIName + location: location +} + +output resourceId string = deploymentUMI.id +output principalId string = deploymentUMI.properties.principalId diff --git a/azure-deploy/modules/graphPermissions.bicep b/azure-deploy/modules/graphPermissions.bicep new file mode 100644 index 00000000..8a823bb3 --- /dev/null +++ b/azure-deploy/modules/graphPermissions.bicep @@ -0,0 +1,123 @@ +// modules/graphPermissions.bicep +param location string +param containerUMIPrincipalId string +param deploymentUMIResourceGroupName string +param deploymentUMIName string + +// Reference the deployment UMI to ensure it exists before using it +resource deploymentUMI 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = { + name: deploymentUMIName + scope: resourceGroup(deploymentUMIResourceGroupName) +} + +resource graphPermissionScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = { + name: 'graph-permissions-script' + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${deploymentUMI.id}': {} + } + } + properties: { + azPowerShellVersion: '9.7' + retentionInterval: 'P1D' + timeout: 'PT30M' + cleanupPreference: 'Always' + scriptContent: ''' + $ErrorActionPreference = "Continue" + + # Initialize arrays for tracking + $warningsList = @() + $successList = @() + $needManualSetup = $false + + try { + $token = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/").Token + $graphAppId = "00000003-0000-0000-c000-000000000000" + + $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId + if (-not $graphSp) { + $needManualSetup = $true + } + + if ($graphSp) { + $headers = @{ + 'Authorization' = "Bearer $token" + 'Content-Type' = 'application/json' + } + + try { + $apiUrl = "https://graph.microsoft.com/v1.0/servicePrincipals/$($env:ContainerUMIPrincipalId)/appRoleAssignments" + $existingAssignments = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Get + + $directoryReadAllId = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" + $existingAssignment = $existingAssignments.value | Where-Object { + $_.appRoleId -eq $directoryReadAllId -and + $_.resourceId -eq $graphSp.Id + } + + if (-not $existingAssignment) { + try { + $body = @{ + principalId = $env:ContainerUMIPrincipalId + resourceId = $graphSp.Id + appRoleId = $directoryReadAllId + } | ConvertTo-Json + + $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body + $successList += "Directory.Read.All" + } + catch { + $needManualSetup = $true + } + } + else { + $successList += "Directory.Read.All" + } + } + catch { + $needManualSetup = $true + } + } + } + catch { + $needManualSetup = $true + } + + # Create a structured permission status message + $statusMessage = if ($needManualSetup) { + @" +MANUAL PERMISSION SETUP REQUIRED +------------------------------ +The container's managed identity requires the following Microsoft Graph permission: +- Directory.Read.All + +Please run the provided setup script to configure these permissions: +./setup-container-permissions.ps1 -PrincipalId $($env:ContainerUMIPrincipalId) +"@ + } else { + "All required permissions have been configured successfully." + } + + # Output the results + $DeploymentScriptOutputs = @{ + needsManualSetup = $needManualSetup + statusMessage = $statusMessage + assignedPermissions = $successList + } + ''' + environmentVariables: [ + { + name: 'ContainerUMIPrincipalId' + value: containerUMIPrincipalId + } + ] + } +} + +// Output these values so they can be captured by the main template +output needsManualSetup bool = graphPermissionScript.properties.outputs.needsManualSetup +output statusMessage string = graphPermissionScript.properties.outputs.statusMessage +output assignedPermissions array = graphPermissionScript.properties.outputs.assignedPermissions diff --git a/azure-deploy/scripts/create-container-umi.ps1 b/azure-deploy/scripts/create-container-umi.ps1 new file mode 100644 index 00000000..27b58164 --- /dev/null +++ b/azure-deploy/scripts/create-container-umi.ps1 @@ -0,0 +1,196 @@ +param( + [Parameter(Mandatory=$true)] + [string]$ResourceGroupName, + [Parameter(Mandatory=$true)] + [string]$Location, + [Parameter(Mandatory=$true)] + [string]$IdentityName +) + +function New-ManagedIdentity { + param( + [Parameter(Mandatory=$true)] + [PSObject]$ResourceGroup, + [Parameter(Mandatory=$true)] + [string]$IdentityName + ) + + $identity = New-AzUserAssignedIdentity -ResourceGroupName $ResourceGroup.ResourceGroupName -Name $IdentityName -Location $ResourceGroup.Location + if (-not $identity) { + throw "Failed to create managed identity" + } + + # Wait for identity to propagate to AAD + Write-Host "Waiting for identity $IdentityName to propagate to Azure AD..." + $timeout = (Get-Date).AddMinutes(2) + $servicePrincipal = $null + + while ((Get-Date) -lt $timeout) { + $servicePrincipal = Get-AzADServicePrincipal -ObjectId $identity.PrincipalId -ErrorAction SilentlyContinue + if ($servicePrincipal) { + Write-Host "Identity propagation confirmed" + break + } + Write-Host "Waiting for identity propagation..." + Start-Sleep -Seconds 10 + } + + if (-not $servicePrincipal) { + throw "Timeout waiting for identity to propagate to Azure AD" + } + + return $identity +} + + +function Add-RoleAssignment { + param( + [Parameter(Mandatory=$true)] + [string]$PrincipalId, + [Parameter(Mandatory=$true)] + [string]$RoleDefinitionName, + [Parameter(Mandatory=$true)] + [string]$Scope + ) + + try { + New-AzRoleAssignment -ObjectId $PrincipalId ` + -RoleDefinitionName $RoleDefinitionName ` + -Scope $Scope + } catch { + Write-Host "Failed to add role assignment skipping" + } +} + +function Test-RoleAssignment { + param( + [string]$PrincipalId, + [string]$RoleDefinitionName, + [string]$Scope + ) + + $assignment = Get-AzRoleAssignment ` + -ObjectId $PrincipalId ` + -RoleDefinitionName $RoleDefinitionName ` + -Scope $Scope ` + -ErrorAction SilentlyContinue + + return $null -ne $assignment +} + +# +#Add-RoleAssignment ` +# -PrincipalId $containerUMI.PrincipalId ` +# -RoleDefinitionName "Reader" ` +# -Scope $subscriptionScope +# + +function Add-GraphApiPermissionWithPropogationTest { + param( + [Parameter(Mandatory=$true)] + [string]$PrincipalId, + [Parameter(Mandatory=$true)] + [string]$PermissionName, + [int]$TimeoutInMinutes = 2 + ) + + $token = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/").Token + $graphAppId = "00000003-0000-0000-c000-000000000000" + $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId + + # Define permission IDs + $permissionIds = @{ + "Directory.Read.All" = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" + "User.Read" = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" + } + + if (-not $permissionIds.ContainsKey($PermissionName)) { + throw "Unknown permission: $PermissionName. Supported permissions are: $($permissionIds.Keys -join ', ')" + } + + $headers = @{ + 'Authorization' = "Bearer $token" + 'Content-Type' = 'application/json' + } + + # Function to check if permission exists + function Test-PermissionAssignment { + $existingAssignments = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$PrincipalId/appRoleAssignments" -Headers $headers -Method Get + return $existingAssignments.value | Where-Object { $_.appRoleId -eq $permissionIds[$PermissionName] } + } + + # Check if permission is already assigned + $existingAssignment = Test-PermissionAssignment + if ($existingAssignment) { + Write-Host "Permission $PermissionName is already assigned" + return + } + + $body = @{ + principalId = $PrincipalId + resourceId = $graphSp.Id + appRoleId = $permissionIds[$PermissionName] + } | ConvertTo-Json + + try { + $apiUrl = "https://graph.microsoft.com/v1.0/servicePrincipals/$PrincipalId/appRoleAssignments" + $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body + Write-Host "Permission assignment initiated for $PermissionName" + + # Wait for permission to propagate + Write-Host "Waiting for permission $PermissionName to propagate..." + $timeout = (Get-Date).AddMinutes($TimeoutInMinutes) + $permissionConfirmed = $false + + while ((Get-Date) -lt $timeout) { + if (Test-PermissionAssignment) { + $permissionConfirmed = $true + Write-Host "Permission $PermissionName successfully propagated" + break + } + Write-Host "Waiting for permission to propagate..." + Start-Sleep -Seconds 10 + } + + if (-not $permissionConfirmed) { + throw "Timeout waiting for permission $PermissionName to propagate" + } + } + catch { + $errorMessage = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue + Write-Warning "Failed to assign Graph API permission: $PermissionName" + Write-Warning "Error: $($errorMessage.error.message)" + throw + } +} + +try { + # Create the managed identity + if (-not ($resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue)) { + Write-Host "Creating resource group: $ResourceGroupName" + $resourceGroup = New-AzResourceGroup -Name $ResourceGroupName -Location $Location + } + + if (-not ($containerUMI = Get-AzUserAssignedIdentity -ResourceGroupName $resourceGroup.ResourceGroupName -Name $IdentityName -ErrorAction SilentlyContinue)){ + $containerUMI = New-ManagedIdentity -ResourceGroup $resourceGroup -IdentityName $IdentityName + } + + # Add permissions to the container UMI + $subscriptionScope = "/subscriptions/$((Get-AzContext).Subscription.Id)" + Add-RoleAssignment ` + -PrincipalId $containerUMI.PrincipalId ` + -RoleDefinitionName "Reader" ` + -Scope $subscriptionScope + + # Then add Graph API permission + Add-GraphApiPermissionWithPropogationTest -PrincipalId $containerUMI.PrincipalId -PermissionName "Directory.Read.All" + + # Output the identity details needed for deployment + Write-Host "`nSetup complete! Use these values in your deployment:" -ForegroundColor Green + Write-Host "Identity Resource ID: $($containerUMI.Id)" + Write-Host "Principal ID: $($containerUMI.PrincipalId)" +} catch { + Write-Error "Error: $($_.Exception.Message)" +} + + diff --git a/azure-deploy/scripts/create-deployment-umi-with-perms.ps1 b/azure-deploy/scripts/create-deployment-umi-with-perms.ps1 new file mode 100644 index 00000000..6e040dab --- /dev/null +++ b/azure-deploy/scripts/create-deployment-umi-with-perms.ps1 @@ -0,0 +1,221 @@ +param( + [Parameter(Mandatory=$true)] + [string]$ResourceGroupName, + [Parameter(Mandatory=$true)] + [string]$Location, + [Parameter(Mandatory=$true)] + [string]$IdentityName +) + +# Ensure we're connected to Azure +if (-not (Get-AzContext)) { + Write-Error "Not connected to Azure. Please run Connect-AzAccount first." + exit 1 +} + +function New-DeploymentManagedIdentity { + param( + [Parameter(Mandatory=$true)] + [string]$ResourceGroupName, + [Parameter(Mandatory=$true)] + [string]$Location, + [Parameter(Mandatory=$true)] + [string]$IdentityName + ) + + # Create Resource Group if it doesn't exist + if (-not (Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue)) { + Write-Host "Creating resource group: $ResourceGroupName" + New-AzResourceGroup -Name $ResourceGroupName -Location $Location + } + + # Create the managed identity + Write-Host "Creating managed identity: $IdentityName" + $identity = New-AzUserAssignedIdentity -ResourceGroupName $ResourceGroupName -Name $IdentityName -Location $Location + + # Wait for identity to propagate to AAD + Write-Host "Waiting for identity to propagate to Azure AD..." + $timeout = (Get-Date).AddMinutes(2) + $servicePrincipal = $null + + while ((Get-Date) -lt $timeout) { + $servicePrincipal = Get-AzADServicePrincipal -ObjectId $identity.PrincipalId -ErrorAction SilentlyContinue + if ($servicePrincipal) { + Write-Host "Identity propagation confirmed" + break + } + Write-Host "Waiting for identity propagation..." + Start-Sleep -Seconds 10 + } + + if (-not $servicePrincipal) { + throw "Timeout waiting for identity to propagate to Azure AD" + } + + return $identity +} + +function Add-GraphPermissions { + param( + [Parameter(Mandatory=$true)] + [string]$PrincipalId, + [Parameter(Mandatory=$true)] + [hashtable]$RequiredPermissions + ) + + $token = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/").Token + $graphAppId = "00000003-0000-0000-c000-000000000000" + $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId + + $assignedPermissions = @{} + + foreach($permission in $RequiredPermissions.Keys) { + $assignedPermissions[$permission] = $false + $permissionId = $RequiredPermissions[$permission] + + $headers = @{ + 'Authorization' = "Bearer $token" + 'Content-Type' = 'application/json' + } + + # Check if permission exists + $existingAssignments = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$PrincipalId/appRoleAssignments" -Headers $headers -Method Get + $existingAssignment = $existingAssignments.value | Where-Object { $_.appRoleId -eq $permissionId } + + if ($existingAssignment) { + Write-Host "$permission permission is already assigned" + $assignedPermissions[$permission] = $true + continue + } + + $body = @{ + principalId = $PrincipalId + resourceId = $graphSp.Id + appRoleId = $permissionId + } | ConvertTo-Json + + try { + $apiUrl = "https://graph.microsoft.com/v1.0/servicePrincipals/$PrincipalId/appRoleAssignments" + $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body + Write-Host "Successfully assigned $permission permission" + $assignedPermissions[$permission] = $true + } + catch { + Write-Error "Failed to assign $permission permission: $_" + continue + } + } + return $assignedPermissions +} + +function Add-SubscriptionRoleAssignments { + param( + [Parameter(Mandatory=$true)] + [string]$PrincipalId, + [Parameter(Mandatory=$true)] + [hashtable]$RequiredRoles, + [Parameter(Mandatory=$false)] + [string]$SubscriptionId = (Get-AzContext).Subscription.Id + ) + + $assignedRoles = @{} + + $subscriptionScope = "/subscriptions/$SubscriptionId" + + foreach ($role in $RequiredRoles.Keys) { + $assignedRoles[$role] = $false + $roleDefinitionId = $RequiredRoles[$role] + + # Check if role assignment exists + $existingAssignment = Get-AzRoleAssignment ` + -ObjectId $PrincipalId ` + -RoleDefinitionId $roleDefinitionId ` + -Scope $subscriptionScope ` + -ErrorAction SilentlyContinue + + if ($existingAssignment) { + Write-Host "$role is already assigned" + $assignedRoles[$role] = $true + continue + } + + try { + Write-Host "Assigning $role..." + $assigned = $false + $role_assignment = New-AzRoleAssignment ` + -ObjectId $PrincipalId ` + -RoleDefinitionId $roleDefinitionId ` + -Scope $subscriptionScope + + Write-Host "Did role assignment $($info)" + if (-not $role_assignment) { + Write-Host "Failed to assign role $role to $PrincipalId skipping this role assignment" + continue + } + + # Wait for role assignment to propagate + $timeout = (Get-Date).AddMinutes(2) + + while ((Get-Date) -lt $timeout -and -not $assigned) { + Write-Host "Waiting for $role assignment to propagate..." + Start-Sleep -Seconds 10 + + $assigned = Get-AzRoleAssignment ` + -ObjectId $PrincipalId ` + -RoleDefinitionId $roleDefinitionId ` + -Scope $subscriptionScope ` + -ErrorAction SilentlyContinue + } + + if ($assigned) { + Write-Host "$role assignment confirmed" + $assignedRoles[$role] = $true + } + } + catch { + Write-Error "Failed to assign $role. skipping this role assignment" + } + } + return $assignedRoles +} + +try { + # Create the deployment managed identity + $identity = New-DeploymentManagedIdentity -ResourceGroupName $ResourceGroupName -Location $Location -IdentityName $IdentityName + + $graphPermissionsRequired = @{ + "Application.ReadWrite.All" = "1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9" + } + $graphPermissions = Add-GraphPermissions -PrincipalId $identity.PrincipalId -RequiredPermissions $graphPermissionsRequired + + $roleDefinitionsRequired = @{ + 'Managed Identity Contributor' = 'f1a07417-d97a-45cb-824c-7a7467783830' + 'User Access Administrator' = '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + } + $roles = Add-SubscriptionRoleAssignments -PrincipalId $identity.PrincipalId -RequiredRoles $roleDefinitionsRequired + + # Output the identity details needed for deployment + Write-Host "`nDeployment identity created! Use these values in your ARM template:" -ForegroundColor Green + Write-Host "Identity Resource ID: $($identity.Id)" + Write-Host "Principal ID: $($identity.PrincipalId)" + Write-Host "Client ID: $($identity.ClientId)" + + Write-Host "`nIMPORTANT: Ensure that all permissions have been assigned!" -ForegroundColor Yellow + Write-Host "Please have an administrator assign the following permissions to the managed identity:" + Write-Host "1. Azure RBAC Roles (at subscription scope):" + foreach ($role in $roleDefinitionsRequired.Keys) { + if (-not $roles[$role]) { + Write-Host "Have administrator assign $role $($roleDefinitionsRequired[$role])" + } + } + Write-Host "2. Graph API Permissions:" + foreach ($graphRole in $graphPermissionsRequired.Keys) { + if (-not $graphPermissions[$graphRole]) { + Write-Host "Have administrator assign $graphRole $($graphPermissionsRequired[$graphRole])" + } + } +} catch { + Write-Error "Error: $($_.Exception.Message)" + Write-Error "Stack Trace: $($_.ScriptStackTrace)" + throw +} \ No newline at end of file diff --git a/azure-deploy/scripts/single-script-full-deployment.ps1 b/azure-deploy/scripts/single-script-full-deployment.ps1 new file mode 100644 index 00000000..3fcf3bb7 --- /dev/null +++ b/azure-deploy/scripts/single-script-full-deployment.ps1 @@ -0,0 +1,338 @@ +param( + [Parameter(Mandatory=$true)] + [string]$ResourceGroupName, + [Parameter(Mandatory=$true)] + [string]$Location, + [Parameter(Mandatory=$true)] + [string]$ContainerUMIName, + [Parameter(Mandatory=$true)] + [string]$AzureTenantId, + [Parameter(Mandatory=$true)] + [string]$BloodhoundInstanceDomain, + [Parameter(Mandatory=$true)] + [string]$BloodhoundTokenId, + [Parameter(Mandatory=$true)] + [string]$BloodhoundToken, + [Parameter(Mandatory=$true)] + [string]$RegistryPassword +) + +# Ensure we're connected to Azure +if (-not (Get-AzContext)) { + Write-Error "Not connected to Azure. Please run Connect-AzAccount first." + exit 1 +} + +function New-ManagedIdentity { + param( + [Parameter(Mandatory=$true)] + [PSObject]$ResourceGroup, + [Parameter(Mandatory=$true)] + [string]$IdentityName + ) + + $identity = New-AzUserAssignedIdentity -ResourceGroupName $ResourceGroup.ResourceGroupName -Name $IdentityName -Location $ResourceGroup.Location + if (-not $identity) { + throw "Failed to create managed identity" + } + + # Wait for identity to propagate to AAD + Write-Host "Waiting for identity $IdentityName to propagate to Azure AD..." + $timeout = (Get-Date).AddMinutes(2) + $servicePrincipal = $null + + while ((Get-Date) -lt $timeout) { + $servicePrincipal = Get-AzADServicePrincipal -ObjectId $identity.PrincipalId -ErrorAction SilentlyContinue + if ($servicePrincipal) { + Write-Host "Identity propagation confirmed" + break + } + Write-Host "Waiting for identity propagation..." + Start-Sleep -Seconds 10 + } + + if (-not $servicePrincipal) { + throw "Timeout waiting for identity to propagate to Azure AD" + } + + return $identity +} + + +function Add-RoleAssignment { + param( + [Parameter(Mandatory=$true)] + [string]$PrincipalId, + [Parameter(Mandatory=$true)] + [string]$RoleDefinitionName, + [Parameter(Mandatory=$true)] + [string]$Scope + ) + + New-AzRoleAssignment -ObjectId $PrincipalId ` + -RoleDefinitionName $RoleDefinitionName ` + -Scope $Scope +} + +function Test-RoleAssignment { + param( + [string]$PrincipalId, + [string]$RoleDefinitionName, + [string]$Scope + ) + + $assignment = Get-AzRoleAssignment ` + -ObjectId $PrincipalId ` + -RoleDefinitionName $RoleDefinitionName ` + -Scope $Scope ` + -ErrorAction SilentlyContinue + + return $null -ne $assignment +} + +# +#Add-RoleAssignment ` +# -PrincipalId $containerUMI.PrincipalId ` +# -RoleDefinitionName "Reader" ` +# -Scope $subscriptionScope +# + +function Add-GraphApiPermissionWithPropogationTest { + param( + [Parameter(Mandatory=$true)] + [string]$PrincipalId, + [Parameter(Mandatory=$true)] + [string]$PermissionName, + [int]$TimeoutInMinutes = 2 + ) + + $token = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/").Token + $graphAppId = "00000003-0000-0000-c000-000000000000" + $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId + + # Define permission IDs + $permissionIds = @{ + "Directory.Read.All" = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" + "User.Read" = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" + } + + if (-not $permissionIds.ContainsKey($PermissionName)) { + throw "Unknown permission: $PermissionName. Supported permissions are: $($permissionIds.Keys -join ', ')" + } + + $headers = @{ + 'Authorization' = "Bearer $token" + 'Content-Type' = 'application/json' + } + + # Function to check if permission exists + function Test-PermissionAssignment { + $existingAssignments = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$PrincipalId/appRoleAssignments" -Headers $headers -Method Get + return $existingAssignments.value | Where-Object { $_.appRoleId -eq $permissionIds[$PermissionName] } + } + + # Check if permission is already assigned + $existingAssignment = Test-PermissionAssignment + if ($existingAssignment) { + Write-Host "Permission $PermissionName is already assigned" + return + } + + $body = @{ + principalId = $PrincipalId + resourceId = $graphSp.Id + appRoleId = $permissionIds[$PermissionName] + } | ConvertTo-Json + + try { + $apiUrl = "https://graph.microsoft.com/v1.0/servicePrincipals/$PrincipalId/appRoleAssignments" + $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body + Write-Host "Permission assignment initiated for $PermissionName" + + # Wait for permission to propagate + Write-Host "Waiting for permission $PermissionName to propagate..." + $timeout = (Get-Date).AddMinutes($TimeoutInMinutes) + $permissionConfirmed = $false + + while ((Get-Date) -lt $timeout) { + if (Test-PermissionAssignment) { + $permissionConfirmed = $true + Write-Host "Permission $PermissionName successfully propagated" + break + } + Write-Host "Waiting for permission to propagate..." + Start-Sleep -Seconds 10 + } + + if (-not $permissionConfirmed) { + throw "Timeout waiting for permission $PermissionName to propagate" + } + } + catch { + $errorMessage = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue + Write-Warning "Failed to assign Graph API permission: $PermissionName" + Write-Warning "Error: $($errorMessage.error.message)" + throw + } +} + + +# Create User Assigned Managed Identity if it doesn't exist +function New-AzureHoundContainerGroup { + param( + [Parameter(Mandatory=$true)] + [PSObject]$ResourceGroup, + [Parameter(Mandatory=$true)] + [string]$ContainerGroupName, + [Parameter(Mandatory=$true)] + [string]$ContainerInstanceName, + [Parameter(Mandatory=$true)] + [PSObject]$ContainerRegistry, + [Parameter(Mandatory=$true)] + [string]$ContainerImage, + [Parameter(Mandatory=$true)] + [string[]]$ContainerEntrypoint, + [Parameter(Mandatory=$true)] + [string]$ConfigJson, + [Parameter(Mandatory=$true)] + [PSObject]$ContainerUMI + ) + + # Convert config.json to base64 + $configJsonBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($configJson)) + + # Create container instance + $containerInstance = New-AzContainerInstanceObject ` + -Name $ContainerGroupName ` + -Image $ContainerImage ` + -VolumeMount @( + @{ + Name = "config-volume" + MountPath = "/home/nonroot/.config/azurehound" + } + ) ` + -Command $ContainerEntrypoint + + + # Create container group + $containerGroup = New-AzContainerGroup ` + -ResourceGroupName $ResourceGroup.ResourceGroupName ` + -Name $ContainerGroupName ` + -Location $ResourceGroup.Location ` + -IdentityType "UserAssigned" ` + -IdentityUserAssignedIdentity @{ + $ContainerUMI.Id = @{} + } ` + -Volume @{ + Name = "config-volume" + Secret = @{ + "config.json" = $configJsonBase64 + } + } ` + -ImageRegistryCredential @( + @{ + Server = $ContainerRegistry.LoginServer + Username = "ditkinreg" + Password = $RegistryPassword + } + ) ` + -Container $containerInstance ` + -OsType Linux ` + -RestartPolicy Never ` + + + return $containerGroup +} + +try { + # Create Resource Group if it doesn't exist assign existing or new resource group to variable rg + if (-not ($resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue)) { + Write-Host "Creating resource group: $ResourceGroupName" + $resourceGroup = New-AzResourceGroup -Name $ResourceGroupName -Location $Location + } else { + Write-Host "Resource group $ResourceGroupName already exists" + } + + # Identity name derived from the resource group name + if (-not $ContainerUMIName) { + $containerUMIName = "$ResourceGroupName-container-umi" + Write-Host "Container UMI name not provided, using default: $containerUMIName" + } else { + Write-Host "Using provided container UMI name: $ContainerUMIName" + $containerUMIName = $ContainerUMIName + } + $containerName = "$ResourceGroupName-container-group" + + # Get the container registry + $containerRegistryName = "ditkinreg" + $containerRegistryResourceGroup = "ditkin-test-registry" + + # TODO: This should be parameterized + $imageName = "ditkin-test-image:latest" + + # Authenticate with ContainerRegistry + Connect-AzContainerRegistry -Name $containerRegistryName + + # Authenticate with ContainerRegistry + $acr = Get-AzContainerRegistry -ResourceGroupName $containerRegistryResourceGroup -Name $containerRegistryName + + if (-not ($containerUMI = Get-AzUserAssignedIdentity -ResourceGroupName $resourceGroup.ResourceGroupName -Name $containerUMIName -ErrorAction SilentlyContinue)){ + Write-Host "Creating managed identity: $containerUMIName" + $containerUMI = New-ManagedIdentity -ResourceGroup $resourceGroup -IdentityName $containerUMIName + } else { + Write-Host "Managed identity $containerUMIName already exists" + } + + # json config for azurehound + $config = @{ + app = "appValue" + auth = "" + batchsize = 100 + config = "/home/nonroot/.config/azurehound/config.json" + instance = "https://${BloodhoundInstanceDomain}/" + json = $false + 'managed-identity' = $true + maxconnsperhost = 20 + maxidleconnsperhost = 20 + region = "cloud" + streamcount = 25 + tenant = "${AzureTenantId}" + token = "${BloodhoundToken}" + tokenid = "${BloodhoundTokenId}" + verbosity = 0 + } + + $configJson = $config | ConvertTo-Json + + if (-not ($containerGroup = Get-AzContainerGroup -ResourceGroupName $ResourceGroupName -Name $containerName -ErrorAction SilentlyContinue)) { + $containerGroup = New-AzureHoundContainerGroup ` + -ResourceGroup $resourceGroup ` + -ContainerGroupName $containerName ` + -ContainerInstanceName "azurehound" ` + -ContainerRegistry $acr ` + -ContainerImage "$($acr.LoginServer)/$($imageName)" ` + -ContainerEntrypoint @("sleep", "infinity") ` + -ConfigJson $configJson ` + -ContainerUMI $containerUMI + } + + # Add permissions to the container UMI + # TODO: I think the scope needs to be based on the tenant we are analyzing + $subscriptionScope = "/subscriptions/$((Get-AzContext).Subscription.Id)" + Add-RoleAssignment ` + -PrincipalId $containerUMI.PrincipalId ` + -RoleDefinitionName "Reader" ` + -Scope $subscriptionScope + + # Then add Graph API permission as before + Add-GraphApiPermissionWithPropogationTest -PrincipalId $containerUMI.PrincipalId -PermissionName "Directory.Read.All" + + # Output the identity details needed for deployment + Write-Host "`nSetup complete! Use these values in your deployment:" -ForegroundColor Green + Write-Host "Identity Resource ID: $($containerUMI.Id)" + Write-Host "Principal ID: $($containerUMI.PrincipalId)" + +} catch { + Write-Error "Error: $($_.Exception.Message)" + Write-Error "Stack Trace: $($_.ScriptStackTrace)" +} From 4792d4cb611bed70deef52e3ba71c13f7fac769c Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Thu, 12 Jun 2025 17:18:48 +0530 Subject: [PATCH 02/17] Adding code for azure deploy. --- azure-deploy/azurehound.parameters.json | 16 + azure-deploy/createUiDefinition.json | 157 +++++++ azure-deploy/mainTemplate.json | 522 ++++++++++++++++++++++++ 3 files changed, 695 insertions(+) create mode 100644 azure-deploy/azurehound.parameters.json create mode 100644 azure-deploy/createUiDefinition.json create mode 100644 azure-deploy/mainTemplate.json diff --git a/azure-deploy/azurehound.parameters.json b/azure-deploy/azurehound.parameters.json new file mode 100644 index 00000000..e2f18148 --- /dev/null +++ b/azure-deploy/azurehound.parameters.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceGroupName": { "value": "rg-azurehound-dev2" }, + "location": { "value": "eastus" }, + "containerUMIName": { "value": "azurehound-container-umi2" }, + "containerUMIResourceGroupName": { "value": "rg-azurehound-dev2" }, + "deploymentUMIName": { "value": "azurehound-deploy-umi2" }, + "deploymentUMIResourceGroupName": { "value": "rg-azurehound-dev2" }, + "azureTenantId": { "value": "6c12b0b0-b2cc-4a73-8252-0b94bfca2145" }, + "bloodhoundInstanceDomain": { "value": "https://maplesyrup.bloodhoundenterprise.io/" }, + "bloodhoundTokenId": { "value": "501ca8f5-2dd2-40b6-a16d-378d8ce60db7" }, + "bloodhoundToken": { "value": "" } + } +} \ No newline at end of file diff --git a/azure-deploy/createUiDefinition.json b/azure-deploy/createUiDefinition.json new file mode 100644 index 00000000..f33a1b6b --- /dev/null +++ b/azure-deploy/createUiDefinition.json @@ -0,0 +1,157 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [], + "steps": [ + { + "name": "azureHoundConfig", + "label": "AzureHound Config Params", + "elements": [ + { + "name": "resourceGroupName", + "type": "Microsoft.Common.TextBox", + "label": "Resource Group Name", + "defaultValue": "", + "toolTip": "Enter the name of the resource group.", + "placeholder": "Enter the name of the resource group.", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", + "validationMessage": "Enter a valid resource group name (alphanumeric, dashes, dots, underscores, max 90 characters)." + } + }, + { + "name": "location", + "type": "Microsoft.Common.TextBox", + "label": "Location", + "defaultValue": "", + "toolTip": "Azure region (e.g., eastus, westus2)", + "placeholder": "Azure region (e.g., eastus, westus2)", + "constraints": { + "required": true, + "regex": "^[a-z0-9]+$", + "validationMessage": "Location must be lowercase and contain no spaces." + } + }, + { + "name": "containerUMIName", + "type": "Microsoft.Common.TextBox", + "label": "Container UMI Name", + "defaultValue": "", + "toolTip": "Name of the Container User-Assigned Managed Identity", + "placeholder": "Name of the Container User-Assigned Managed Identity", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_]{1,90}$", + "validationMessage": "Enter a valid user-assigned identity name." + } + }, + { + "name": "containerUMIResourceGroupName", + "type": "Microsoft.Common.TextBox", + "label": "Container UMI Resource Group Name", + "defaultValue": "", + "toolTip": "Resource group name for the Container UMI", + "placeholder": "Resource group name for the Container UMI", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", + "validationMessage": "Enter a valid resource group name." + } + }, + { + "name": "deploymentUMIName", + "type": "Microsoft.Common.TextBox", + "label": "Deployment UMI Name", + "defaultValue": "", + "toolTip": "Name of the Deployment User-Assigned Managed Identity", + "placeholder": "Name of the Deployment User-Assigned Managed Identity", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_]{1,90}$", + "validationMessage": "Enter a valid identity name." + } + }, + { + "name": "deploymentUMIResourceGroupName", + "type": "Microsoft.Common.TextBox", + "label": "Deployment UMI Resource Group Name", + "defaultValue": "", + "toolTip": "Resource group name for the Deployment UMI", + "placeholder": "Resource group name for the Deployment UMI", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", + "validationMessage": "Enter a valid resource group name." + } + }, + { + "name": "azureTenantId", + "type": "Microsoft.Common.TextBox", + "label": "Azure Tenant ID", + "defaultValue": "", + "toolTip": "Azure Tenant ID", + "placeholder": "Azure Tenant ID", + "constraints": { + "required": true, + "regex": "^[0-9a-fA-F-]{36}$", + "validationMessage": "Must be a valid GUID" + } + }, + { + "name": "bloodhoundInstanceDomain", + "type": "Microsoft.Common.TextBox", + "label": "BloodHound Instance Domain", + "defaultValue": "", + "toolTip": "Domain name of the BloodHound instance", + "placeholder": "Domain name of the BloodHound instance", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9.-]+$", + "validationMessage": "Enter a valid domain (e.g., example.com )." + } + }, + { + "name": "bloodhoundTokenId", + "type": "Microsoft.Common.TextBox", + "label": "BloodHound Token ID", + "defaultValue": "", + "toolTip": "The BloodHound Token Id", + "placeholder": "The BloodHound Token Id", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_]+$", + "validationMessage": "Token ID must be alphanumeric." + } + }, + { + "name": "bloodhoundToken", + "type": "Microsoft.Common.TextBox", + "label": "BloodHound Token Secret", + "toolTip": "The secret Key of the BloodHound token", + "placeholder": "The BloodHound Token Key", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_]+$", + "validationMessage": "Token must be alphanumeric." + } + } + ] + } + ], + "outputs": { + "resourceGroupName": "[steps('azureHoundConfig').resourceGroupName]", + "location": "[steps('azureHoundConfig').location()]", + "containerUMIName": "[steps('azureHoundConfig').containerUMIName]", + "containerUMIResourceGroupName": "[steps('azureHoundConfig').containerUMIResourceGroupName]", + "deploymentUMIName": "[steps('azureHoundConfig').deploymentUMIName]", + "deploymentUMIResourceGroupName": "[steps('azureHoundConfig').deploymentUMIResourceGroupName]", + "azureTenantId": "[steps('azureHoundConfig').azureTenantId]", + "bloodhoundInstanceDomain": "[steps('azureHoundConfig').bloodhoundInstanceDomain]", + "bloodhoundTokenId": "[steps('azureHoundConfig').bloodhoundTokenId]", + "bloodhoundToken": "[steps('azureHoundConfig').bloodhoundToken]" + } + } +} diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json new file mode 100644 index 00000000..3ba2c3be --- /dev/null +++ b/azure-deploy/mainTemplate.json @@ -0,0 +1,522 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "12410386356341231464" + } + }, + "parameters": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "Name of the resource group to deploy into" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location for all resources" + } + }, + "containerUMIName": { + "type": "string", + "metadata": { + "description": "Name of the container user managed identity" + } + }, + "containerUMIResourceGroupName": { + "type": "string", + "defaultValue": "[parameters('resourceGroupName')]", + "metadata": { + "description": "Resource group for the container UMI (defaults to main resource group if not specified)" + } + }, + "deploymentUMIName": { + "type": "string", + "metadata": { + "description": "Name of the deployment user managed identity" + } + }, + "deploymentUMIResourceGroupName": { + "type": "string", + "metadata": { + "description": "Resource group containing the deployment user managed identity" + } + }, + "azureTenantId": { + "type": "string", + "metadata": { + "description": "Azure tenant ID to analyze" + } + }, + "bloodhoundInstanceDomain": { + "type": "string", + "metadata": { + "description": "Bloodhound instance domain" + } + }, + "bloodhoundTokenId": { + "type": "securestring", + "metadata": { + "description": "Bloodhound token ID" + } + }, + "bloodhoundToken": { + "type": "securestring", + "metadata": { + "description": "Bloodhound token" + } + } + }, + "variables": { + "containerName": "[format('{0}-container-group', parameters('resourceGroupName'))]", + "imageName": "azurehounddeploy.azurecr.io/azurehound:latest" + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2023-07-01", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('location')]" + }, + { + "condition": "[not(equals(parameters('containerUMIResourceGroupName'), parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2023-07-01", + "name": "[parameters('containerUMIResourceGroupName')]", + "location": "[parameters('location')]" + }, + { + "condition": "[not(equals(parameters('deploymentUMIResourceGroupName'), parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2023-07-01", + "name": "[parameters('deploymentUMIResourceGroupName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().subscriptionId, parameters('containerUMIName'), 'Reader')]", + "properties": { + "principalId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.principalId.value]", + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "containerIdentity-deployment", + "resourceGroup": "[parameters('containerUMIResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "containerUMIName": { + "value": "[parameters('containerUMIName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "7723817674947761621" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "containerUMIName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('containerUMIName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), 'Reader')]", + "properties": { + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), '2023-01-31').principalId]", + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName'))]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName'))]" + }, + "principalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), '2023-01-31').principalId]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('containerUMIResourceGroupName'))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "deploymentIdentity-deployment", + "resourceGroup": "[parameters('deploymentUMIResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "deploymentUMIName": { + "value": "[parameters('deploymentUMIName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "11305625534337036173" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "deploymentUMIName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('deploymentUMIName')]", + "location": "[parameters('location')]" + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentUMIName'))]" + }, + "principalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentUMIName')), '2023-01-31').principalId]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('deploymentUMIResourceGroupName'))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "graph-permissions-deployment", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "deploymentUMIName": { + "value": "[parameters('deploymentUMIName')]" + }, + "deploymentUMIResourceGroupName": { + "value": "[parameters('deploymentUMIResourceGroupName')]" + }, + "containerUMIPrincipalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.principalId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "2556770267153897272" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "containerUMIPrincipalId": { + "type": "string" + }, + "deploymentUMIResourceGroupName": { + "type": "string" + }, + "deploymentUMIName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "graph-permissions-script", + "location": "[parameters('location')]", + "kind": "AzurePowerShell", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('deploymentUMIResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentUMIName')))]": {} + } + }, + "properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "timeout": "PT30M", + "cleanupPreference": "Always", + "scriptContent": " $ErrorActionPreference = \"Continue\"\n \n # Initialize arrays for tracking\n $warningsList = @()\n $successList = @()\n $needManualSetup = $false\n \n try {\n $token = (Get-AzAccessToken -ResourceUrl \"https://graph.microsoft.com/\").Token\n $graphAppId = \"00000003-0000-0000-c000-000000000000\"\n \n $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId\n if (-not $graphSp) {\n $needManualSetup = $true\n }\n \n if ($graphSp) {\n $headers = @{\n 'Authorization' = \"Bearer $token\"\n 'Content-Type' = 'application/json'\n }\n\n try {\n $apiUrl = \"https://graph.microsoft.com/v1.0/servicePrincipals/$($env:ContainerUMIPrincipalId)/appRoleAssignments\"\n $existingAssignments = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Get\n \n $directoryReadAllId = \"7ab1d382-f21e-4acd-a863-ba3e13f7da61\"\n $existingAssignment = $existingAssignments.value | Where-Object { \n $_.appRoleId -eq $directoryReadAllId -and \n $_.resourceId -eq $graphSp.Id\n }\n\n if (-not $existingAssignment) {\n try {\n $body = @{\n principalId = $env:ContainerUMIPrincipalId\n resourceId = $graphSp.Id\n appRoleId = $directoryReadAllId\n } | ConvertTo-Json\n\n $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body\n $successList += \"Directory.Read.All\"\n }\n catch {\n $needManualSetup = $true\n }\n }\n else {\n $successList += \"Directory.Read.All\"\n }\n }\n catch {\n $needManualSetup = $true\n }\n }\n }\n catch {\n $needManualSetup = $true\n }\n\n # Create a structured permission status message\n $statusMessage = if ($needManualSetup) {\n @\"\nMANUAL PERMISSION SETUP REQUIRED\n------------------------------\nThe container's managed identity requires the following Microsoft Graph permission:\n- Directory.Read.All\n\nPlease run the provided setup script to configure these permissions:\n./setup-container-permissions.ps1 -PrincipalId $($env:ContainerUMIPrincipalId)\n\"@\n } else {\n \"All required permissions have been configured successfully.\"\n }\n \n # Output the results\n $DeploymentScriptOutputs = @{\n needsManualSetup = $needManualSetup\n statusMessage = $statusMessage\n assignedPermissions = $successList\n }\n ", + "environmentVariables": [ + { + "name": "ContainerUMIPrincipalId", + "value": "[parameters('containerUMIPrincipalId')]" + } + ] + } + } + ], + "outputs": { + "needsManualSetup": { + "type": "bool", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.needsManualSetup]" + }, + "statusMessage": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.statusMessage]" + }, + "assignedPermissions": { + "type": "array", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.assignedPermissions]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "container-instance-deployment", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "containerGroupName": { + "value": "[variables('containerName')]" + }, + "containerUMIResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.resourceId.value]" + }, + "imageName": { + "value": "[variables('imageName')]" + }, + "bloodhoundInstanceDomain": { + "value": "[parameters('bloodhoundInstanceDomain')]" + }, + "azureTenantId": { + "value": "[parameters('azureTenantId')]" + }, + "bloodhoundTokenId": { + "value": "[parameters('bloodhoundTokenId')]" + }, + "bloodhoundToken": { + "value": "[parameters('bloodhoundToken')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "14470760509824127242" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "containerGroupName": { + "type": "string" + }, + "containerUMIResourceId": { + "type": "string" + }, + "imageName": { + "type": "string" + }, + "bloodhoundInstanceDomain": { + "type": "string" + }, + "azureTenantId": { + "type": "string" + }, + "bloodhoundTokenId": { + "type": "securestring" + }, + "bloodhoundToken": { + "type": "securestring" + } + }, + "variables": { + "config": { + "batchsize": 100, + "config": "/home/nonroot/.config/azurehound/config.json", + "instance": "[format('https://{0}/', parameters('bloodhoundInstanceDomain'))]", + "json": false, + "managed-identity": true, + "maxconnsperhost": 20, + "maxidleconnsperhost": 20, + "region": "cloud", + "streamcount": 25, + "tenant": "[parameters('azureTenantId')]", + "token": "[parameters('bloodhoundToken')]", + "tokenid": "[parameters('bloodhoundTokenId')]", + "verbosity": 0 + } + }, + "resources": [ + { + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2023-05-01", + "name": "[parameters('containerGroupName')]", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', parameters('containerUMIResourceId'))]": {} + } + }, + "properties": { + "containers": [ + { + "name": "azurehound", + "properties": { + "image": "[parameters('imageName')]", + "command": [ + "/azurehound", + "start" + ], + "volumeMounts": [ + { + "name": "config-volume", + "mountPath": "/home/nonroot/.config/azurehound" + } + ], + "resources": { + "requests": { + "cpu": 1, + "memoryInGB": 1 + } + } + } + } + ], + "volumes": [ + { + "name": "config-volume", + "secret": { + "config.json": "[base64(string(variables('config')))]" + } + } + ], + "osType": "Linux", + "restartPolicy": "Never" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment')]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + } + ], + "outputs": { + "containerUMIResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.resourceId.value]" + }, + "containerUMIPrincipalId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.principalId.value]" + }, + "permissionSetupRequired": { + "type": "bool", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment'), '2022-09-01').outputs.needsManualSetup.value]" + }, + "permissionStatus": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment'), '2022-09-01').outputs.statusMessage.value]" + }, + "assignedPermissions": { + "type": "array", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment'), '2022-09-01').outputs.assignedPermissions.value]" + }, + "deploymentUMIPrincipalId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('deploymentUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'deploymentIdentity-deployment'), '2022-09-01').outputs.principalId.value]" + }, + "deploymentUMIId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('deploymentUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'deploymentIdentity-deployment'), '2022-09-01').outputs.resourceId.value]" + } + } +} From d2179960fc8f7661a5e5d7b754284277ba928157 Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Fri, 13 Jun 2025 13:42:40 +0530 Subject: [PATCH 03/17] updating regex --- azure-deploy/createUiDefinition.json | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/azure-deploy/createUiDefinition.json b/azure-deploy/createUiDefinition.json index f33a1b6b..b0326c01 100644 --- a/azure-deploy/createUiDefinition.json +++ b/azure-deploy/createUiDefinition.json @@ -9,19 +9,6 @@ "name": "azureHoundConfig", "label": "AzureHound Config Params", "elements": [ - { - "name": "resourceGroupName", - "type": "Microsoft.Common.TextBox", - "label": "Resource Group Name", - "defaultValue": "", - "toolTip": "Enter the name of the resource group.", - "placeholder": "Enter the name of the resource group.", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", - "validationMessage": "Enter a valid resource group name (alphanumeric, dashes, dots, underscores, max 90 characters)." - } - }, { "name": "location", "type": "Microsoft.Common.TextBox", @@ -109,8 +96,8 @@ "placeholder": "Domain name of the BloodHound instance", "constraints": { "required": true, - "regex": "^[a-zA-Z0-9.-]+$", - "validationMessage": "Enter a valid domain (e.g., example.com )." + "regex": "^.+$", + "validationMessage": "Enter a valid domain (e.g, example.com )." } }, { @@ -134,7 +121,7 @@ "placeholder": "The BloodHound Token Key", "constraints": { "required": true, - "regex": "^[a-zA-Z0-9-_]+$", + "regex": "^.+$", "validationMessage": "Token must be alphanumeric." } } @@ -142,8 +129,7 @@ } ], "outputs": { - "resourceGroupName": "[steps('azureHoundConfig').resourceGroupName]", - "location": "[steps('azureHoundConfig').location()]", + "location": "[steps('azureHoundConfig').location]", "containerUMIName": "[steps('azureHoundConfig').containerUMIName]", "containerUMIResourceGroupName": "[steps('azureHoundConfig').containerUMIResourceGroupName]", "deploymentUMIName": "[steps('azureHoundConfig').deploymentUMIName]", From 88b00d9a6e985fb02ac17eb5911f363a573213ed Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Fri, 13 Jun 2025 13:50:31 +0530 Subject: [PATCH 04/17] updating createuidefinition --- azure-deploy/createUiDefinition.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/azure-deploy/createUiDefinition.json b/azure-deploy/createUiDefinition.json index b0326c01..4f5e69d2 100644 --- a/azure-deploy/createUiDefinition.json +++ b/azure-deploy/createUiDefinition.json @@ -9,6 +9,19 @@ "name": "azureHoundConfig", "label": "AzureHound Config Params", "elements": [ + { + "name": "resourceGroupName", + "type": "Microsoft.Common.TextBox", + "label": "Resource Group Name", + "defaultValue": "", + "toolTip": "Enter the name of the resource group.", + "placeholder": "Enter the name of the resource group.", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", + "validationMessage": "Enter a valid resource group name (alphanumeric, dashes, dots, underscores, max 90 characters)." + } + }, { "name": "location", "type": "Microsoft.Common.TextBox", @@ -129,7 +142,8 @@ } ], "outputs": { - "location": "[steps('azureHoundConfig').location]", + "resourceGroupName": "[steps('azureHoundConfig').resourceGroupName]", + "location": "[steps('azureHoundConfig').location()]", "containerUMIName": "[steps('azureHoundConfig').containerUMIName]", "containerUMIResourceGroupName": "[steps('azureHoundConfig').containerUMIResourceGroupName]", "deploymentUMIName": "[steps('azureHoundConfig').deploymentUMIName]", From 3ccd65bbe3d212357e933624ce74c7e838af7ab4 Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Fri, 13 Jun 2025 13:51:25 +0530 Subject: [PATCH 05/17] updating createuidefinition --- azure-deploy/createUiDefinition.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-deploy/createUiDefinition.json b/azure-deploy/createUiDefinition.json index 4f5e69d2..b26c17b8 100644 --- a/azure-deploy/createUiDefinition.json +++ b/azure-deploy/createUiDefinition.json @@ -143,7 +143,7 @@ ], "outputs": { "resourceGroupName": "[steps('azureHoundConfig').resourceGroupName]", - "location": "[steps('azureHoundConfig').location()]", + "location": "[steps('azureHoundConfig').location]", "containerUMIName": "[steps('azureHoundConfig').containerUMIName]", "containerUMIResourceGroupName": "[steps('azureHoundConfig').containerUMIResourceGroupName]", "deploymentUMIName": "[steps('azureHoundConfig').deploymentUMIName]", From d023303c630a705d36e131979b5e62de18b39cf9 Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Fri, 20 Jun 2025 17:06:41 +0530 Subject: [PATCH 06/17] Starting to create the ARM templates from scratch --- azure-deploy/createUiDefinition.json | 169 +------ azure-deploy/mainTemplate.json | 710 ++++++++++++++------------- 2 files changed, 380 insertions(+), 499 deletions(-) diff --git a/azure-deploy/createUiDefinition.json b/azure-deploy/createUiDefinition.json index b26c17b8..9aed97b1 100644 --- a/azure-deploy/createUiDefinition.json +++ b/azure-deploy/createUiDefinition.json @@ -1,157 +1,16 @@ { - "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", - "handler": "Microsoft.Azure.CreateUIDef", - "version": "0.1.2-preview", - "parameters": { - "basics": [], - "steps": [ - { - "name": "azureHoundConfig", - "label": "AzureHound Config Params", - "elements": [ - { - "name": "resourceGroupName", - "type": "Microsoft.Common.TextBox", - "label": "Resource Group Name", - "defaultValue": "", - "toolTip": "Enter the name of the resource group.", - "placeholder": "Enter the name of the resource group.", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", - "validationMessage": "Enter a valid resource group name (alphanumeric, dashes, dots, underscores, max 90 characters)." - } - }, - { - "name": "location", - "type": "Microsoft.Common.TextBox", - "label": "Location", - "defaultValue": "", - "toolTip": "Azure region (e.g., eastus, westus2)", - "placeholder": "Azure region (e.g., eastus, westus2)", - "constraints": { - "required": true, - "regex": "^[a-z0-9]+$", - "validationMessage": "Location must be lowercase and contain no spaces." - } - }, - { - "name": "containerUMIName", - "type": "Microsoft.Common.TextBox", - "label": "Container UMI Name", - "defaultValue": "", - "toolTip": "Name of the Container User-Assigned Managed Identity", - "placeholder": "Name of the Container User-Assigned Managed Identity", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9-_]{1,90}$", - "validationMessage": "Enter a valid user-assigned identity name." - } - }, - { - "name": "containerUMIResourceGroupName", - "type": "Microsoft.Common.TextBox", - "label": "Container UMI Resource Group Name", - "defaultValue": "", - "toolTip": "Resource group name for the Container UMI", - "placeholder": "Resource group name for the Container UMI", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", - "validationMessage": "Enter a valid resource group name." - } - }, - { - "name": "deploymentUMIName", - "type": "Microsoft.Common.TextBox", - "label": "Deployment UMI Name", - "defaultValue": "", - "toolTip": "Name of the Deployment User-Assigned Managed Identity", - "placeholder": "Name of the Deployment User-Assigned Managed Identity", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9-_]{1,90}$", - "validationMessage": "Enter a valid identity name." - } - }, - { - "name": "deploymentUMIResourceGroupName", - "type": "Microsoft.Common.TextBox", - "label": "Deployment UMI Resource Group Name", - "defaultValue": "", - "toolTip": "Resource group name for the Deployment UMI", - "placeholder": "Resource group name for the Deployment UMI", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9-_\\.]{1,90}$", - "validationMessage": "Enter a valid resource group name." - } - }, - { - "name": "azureTenantId", - "type": "Microsoft.Common.TextBox", - "label": "Azure Tenant ID", - "defaultValue": "", - "toolTip": "Azure Tenant ID", - "placeholder": "Azure Tenant ID", - "constraints": { - "required": true, - "regex": "^[0-9a-fA-F-]{36}$", - "validationMessage": "Must be a valid GUID" - } - }, - { - "name": "bloodhoundInstanceDomain", - "type": "Microsoft.Common.TextBox", - "label": "BloodHound Instance Domain", - "defaultValue": "", - "toolTip": "Domain name of the BloodHound instance", - "placeholder": "Domain name of the BloodHound instance", - "constraints": { - "required": true, - "regex": "^.+$", - "validationMessage": "Enter a valid domain (e.g, example.com )." - } - }, - { - "name": "bloodhoundTokenId", - "type": "Microsoft.Common.TextBox", - "label": "BloodHound Token ID", - "defaultValue": "", - "toolTip": "The BloodHound Token Id", - "placeholder": "The BloodHound Token Id", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9-_]+$", - "validationMessage": "Token ID must be alphanumeric." - } - }, - { - "name": "bloodhoundToken", - "type": "Microsoft.Common.TextBox", - "label": "BloodHound Token Secret", - "toolTip": "The secret Key of the BloodHound token", - "placeholder": "The BloodHound Token Key", - "constraints": { - "required": true, - "regex": "^.+$", - "validationMessage": "Token must be alphanumeric." - } - } - ] - } - ], - "outputs": { - "resourceGroupName": "[steps('azureHoundConfig').resourceGroupName]", - "location": "[steps('azureHoundConfig').location]", - "containerUMIName": "[steps('azureHoundConfig').containerUMIName]", - "containerUMIResourceGroupName": "[steps('azureHoundConfig').containerUMIResourceGroupName]", - "deploymentUMIName": "[steps('azureHoundConfig').deploymentUMIName]", - "deploymentUMIResourceGroupName": "[steps('azureHoundConfig').deploymentUMIResourceGroupName]", - "azureTenantId": "[steps('azureHoundConfig').azureTenantId]", - "bloodhoundInstanceDomain": "[steps('azureHoundConfig').bloodhoundInstanceDomain]", - "bloodhoundTokenId": "[steps('azureHoundConfig').bloodhoundTokenId]", - "bloodhoundToken": "[steps('azureHoundConfig').bloodhoundToken]" + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + {} + ], + "steps": [ + + ], + "outputs": { + "location": "[location()]" + } } - } -} +} \ No newline at end of file diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index 3ba2c3be..c13f1178 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -1,254 +1,259 @@ { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "12410386356341231464" + "version": "0.11.1.770", + "templateHash": "4536988653306547200" } }, "parameters": { - "resourceGroupName": { + "azureTenantId": { "type": "string", "metadata": { - "description": "Name of the resource group to deploy into" - } + "description": "Azure tenant ID to analyze" + }, + "defaultValue": "12345" }, - "location": { + "bloodhoundInstanceDomain": { "type": "string", "metadata": { - "description": "Location for all resources" - } + "description": "Bloodhound instance domain" + }, + "defaultValue": "12345" }, - "containerUMIName": { - "type": "string", + "bloodhoundTokenId": { + "type": "securestring", "metadata": { - "description": "Name of the container user managed identity" - } + "description": "Bloodhound token ID" + }, + "defaultValue": "12345" }, - "containerUMIResourceGroupName": { + "bloodhoundToken": { + "type": "securestring", + "metadata": { + "description": "Bloodhound token" + }, + "defaultValue": "12345" + }, + "containerAppName": { "type": "string", - "defaultValue": "[parameters('resourceGroupName')]", + "defaultValue": "[format('app-{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "Resource group for the container UMI (defaults to main resource group if not specified)" + "description": "Specifies the name of the container app." } }, - "deploymentUMIName": { + "containerAppEnvName": { "type": "string", + "defaultValue": "[format('env-{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "Name of the deployment user managed identity" + "description": "Specifies the name of the container app environment." } }, - "deploymentUMIResourceGroupName": { + "containerAppLogAnalyticsName": { "type": "string", + "defaultValue": "[format('containerapp-log-{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "Resource group containing the deployment user managed identity" + "description": "Specifies the name of the log analytics workspace." } }, - "azureTenantId": { + "containerRegistryName": { "type": "string", + "defaultValue": "[format('cr{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "Azure tenant ID to analyze" + "description": "Specifies the name of the container app environment." } }, - "bloodhoundInstanceDomain": { + "location": { "type": "string", + "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Bloodhound instance domain" + "description": "Specifies the location for all resources." } }, - "bloodhoundTokenId": { - "type": "securestring", + "minReplica": { + "type": "int", + "defaultValue": 1, + "maxValue": 25, + "minValue": 0, "metadata": { - "description": "Bloodhound token ID" + "description": "Minimum number of replicas that will be deployed" } }, - "bloodhoundToken": { - "type": "securestring", + "maxReplica": { + "type": "int", + "defaultValue": 3, + "maxValue": 25, + "minValue": 0, "metadata": { - "description": "Bloodhound token" + "description": "Maximum number of replicas that will be deployed" } } }, "variables": { - "containerName": "[format('{0}-container-group', parameters('resourceGroupName'))]", - "imageName": "azurehounddeploy.azurecr.io/azurehound:latest" - }, + "acrPullRole": "[resourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "config": { + "batchsize": 100, + "config": "/home/nonroot/.config/azurehound/config.json", + "instance": "[format('https://{0}/', parameters('bloodhoundInstanceDomain'))]", + "json": false, + "managed-identity": true, + "maxconnsperhost": 20, + "maxidleconnsperhost": 20, + "region": "cloud", + "streamcount": 25, + "tenant": "[parameters('azureTenantId')]", + "token": "[parameters('bloodhoundToken')]", + "tokenid": "[parameters('bloodhoundTokenId')]", + "verbosity": 0 + } + }, "resources": [ { - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2023-07-01", - "name": "[parameters('resourceGroupName')]", - "location": "[parameters('location')]" + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-06-01", + "name": "[parameters('containerAppLogAnalyticsName')]", + "location": "[parameters('location')]", + "properties": { + "sku": { + "name": "PerGB2018" + } + } }, { - "condition": "[not(equals(parameters('containerUMIResourceGroupName'), parameters('resourceGroupName')))]", - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2023-07-01", - "name": "[parameters('containerUMIResourceGroupName')]", - "location": "[parameters('location')]" + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2022-06-01-preview", + "name": "[parameters('containerAppEnvName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Consumption" + }, + "properties": { + "appLogsConfiguration": { + "destination": "log-analytics", + "logAnalyticsConfiguration": { + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('containerAppLogAnalyticsName'))).customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('containerAppLogAnalyticsName')), '2021-06-01').primarySharedKey]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('containerAppLogAnalyticsName'))]" + ] }, { - "condition": "[not(equals(parameters('deploymentUMIResourceGroupName'), parameters('resourceGroupName')))]", - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2023-07-01", - "name": "[parameters('deploymentUMIResourceGroupName')]", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2022-01-31-preview", + "name": "[format('id-{0}', parameters('containerAppName'))]", "location": "[parameters('location')]" }, { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "name": "[guid(subscription().subscriptionId, parameters('containerUMIName'), 'Reader')]", + "name": "[guid(resourceGroup().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName'))), variables('acrPullRole'))]", "properties": { - "principalId": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.principalId.value]", - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "roleDefinitionId": "[variables('acrPullRole')]", + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))).principalId]", "principalType": "ServicePrincipal" }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment')]" - ] + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]" + ], + "metadata": { + "description": "This allows the managed identity of the container app to access the registry, note scope is applied to the wider ResourceGroup not the ACR" + } }, { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "containerIdentity-deployment", - "resourceGroup": "[parameters('containerUMIResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "containerUMIName": { - "value": "[parameters('containerUMIName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "7723817674947761621" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "containerUMIName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[parameters('containerUMIName')]", - "location": "[parameters('location')]" - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[guid(subscription().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), 'Reader')]", - "properties": { - "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), '2023-01-31').principalId]", - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName'))]" - ] - } - ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName'))]" - }, - "principalId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), '2023-01-31').principalId]" - } - } + "type": "Microsoft.App/containerApps", + "apiVersion": "2022-06-01-preview", + "name": "[parameters('containerAppName')]", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName'))))]": {} } }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('containerUMIResourceGroupName'))]", - "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "deploymentIdentity-deployment", - "resourceGroup": "[parameters('deploymentUMIResourceGroupName')]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", + "configuration": { + "ingress": { + "external": true, + "targetPort": 80, + "allowInsecure": false, + "traffic": [ + { + "latestRevision": true, + "weight": 100 + } + ] }, - "deploymentUMIName": { - "value": "[parameters('deploymentUMIName')]" - } + "registries": [ + { + "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]", + "server": "[reference(resourceId('Microsoft.Resources/deployments', 'acr')).outputs.loginServer.value]" + } + ] }, "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "11305625534337036173" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "deploymentUMIName": { - "type": "string" - } - }, - "resources": [ + "revisionSuffix": "firstrevision", + "containers": [ { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[parameters('deploymentUMIName')]", - "location": "[parameters('location')]" + "name": "[parameters('containerAppName')]", + "image": "[reference(resourceId('Microsoft.Resources/deployments', 'importContainerImage')).outputs.importedImages.value[0].acrHostedImage]", + "command": [ + "/azurehound", + "start" + ], + "volumeMounts": [ + { + "name": "config-volume", + "mountPath": "/home/nonroot/.config/azurehound" + } + ], + "resources": { + "cpu": "[json('.25')]", + "memory": ".5Gi" + } } ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentUMIName'))]" - }, - "principalId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentUMIName')), '2023-01-31').principalId]" + "volumes": [ + { + "name": "config-volume", + "storageType": "Secret", + "secret": { + "config.json": "[base64(string(variables('config')))]" + } } + ], + "scale": { + "minReplicas": "[parameters('minReplica')]", + "maxReplicas": "[parameters('maxReplica')]", + "rules": [ + { + "name": "http-requests", + "http": { + "metadata": { + "concurrentRequests": "10" + } + } + } + ] } } }, "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('deploymentUMIResourceGroupName'))]", - "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + "[resourceId('Microsoft.Resources/deployments', 'acr')]", + "[resourceId('Microsoft.Resources/deployments', 'importContainerImage')]", + "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]" ] }, { "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "graph-permissions-deployment", - "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2020-10-01", + "name": "acr", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -258,14 +263,8 @@ "location": { "value": "[parameters('location')]" }, - "deploymentUMIName": { - "value": "[parameters('deploymentUMIName')]" - }, - "deploymentUMIResourceGroupName": { - "value": "[parameters('deploymentUMIResourceGroupName')]" - }, - "containerUMIPrincipalId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.principalId.value]" + "containerRegistryName": { + "value": "[parameters('containerRegistryName')]" } }, "template": { @@ -274,107 +273,70 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "2556770267153897272" + "version": "0.11.1.770", + "templateHash": "5294368001789442670" } }, "parameters": { "location": { - "type": "string" - }, - "containerUMIPrincipalId": { - "type": "string" - }, - "deploymentUMIResourceGroupName": { - "type": "string" + "type": "string", + "metadata": { + "description": "Specifies the location for all resources." + } }, - "deploymentUMIName": { + "containerRegistryName": { "type": "string" } }, "resources": [ { - "type": "Microsoft.Resources/deploymentScripts", - "apiVersion": "2023-08-01", - "name": "graph-permissions-script", + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2022-02-01-preview", + "name": "[parameters('containerRegistryName')]", "location": "[parameters('location')]", - "kind": "AzurePowerShell", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('deploymentUMIResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentUMIName')))]": {} - } + "sku": { + "name": "Standard" }, "properties": { - "azPowerShellVersion": "9.7", - "retentionInterval": "P1D", - "timeout": "PT30M", - "cleanupPreference": "Always", - "scriptContent": " $ErrorActionPreference = \"Continue\"\n \n # Initialize arrays for tracking\n $warningsList = @()\n $successList = @()\n $needManualSetup = $false\n \n try {\n $token = (Get-AzAccessToken -ResourceUrl \"https://graph.microsoft.com/\").Token\n $graphAppId = \"00000003-0000-0000-c000-000000000000\"\n \n $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId\n if (-not $graphSp) {\n $needManualSetup = $true\n }\n \n if ($graphSp) {\n $headers = @{\n 'Authorization' = \"Bearer $token\"\n 'Content-Type' = 'application/json'\n }\n\n try {\n $apiUrl = \"https://graph.microsoft.com/v1.0/servicePrincipals/$($env:ContainerUMIPrincipalId)/appRoleAssignments\"\n $existingAssignments = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Get\n \n $directoryReadAllId = \"7ab1d382-f21e-4acd-a863-ba3e13f7da61\"\n $existingAssignment = $existingAssignments.value | Where-Object { \n $_.appRoleId -eq $directoryReadAllId -and \n $_.resourceId -eq $graphSp.Id\n }\n\n if (-not $existingAssignment) {\n try {\n $body = @{\n principalId = $env:ContainerUMIPrincipalId\n resourceId = $graphSp.Id\n appRoleId = $directoryReadAllId\n } | ConvertTo-Json\n\n $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body\n $successList += \"Directory.Read.All\"\n }\n catch {\n $needManualSetup = $true\n }\n }\n else {\n $successList += \"Directory.Read.All\"\n }\n }\n catch {\n $needManualSetup = $true\n }\n }\n }\n catch {\n $needManualSetup = $true\n }\n\n # Create a structured permission status message\n $statusMessage = if ($needManualSetup) {\n @\"\nMANUAL PERMISSION SETUP REQUIRED\n------------------------------\nThe container's managed identity requires the following Microsoft Graph permission:\n- Directory.Read.All\n\nPlease run the provided setup script to configure these permissions:\n./setup-container-permissions.ps1 -PrincipalId $($env:ContainerUMIPrincipalId)\n\"@\n } else {\n \"All required permissions have been configured successfully.\"\n }\n \n # Output the results\n $DeploymentScriptOutputs = @{\n needsManualSetup = $needManualSetup\n statusMessage = $statusMessage\n assignedPermissions = $successList\n }\n ", - "environmentVariables": [ - { - "name": "ContainerUMIPrincipalId", - "value": "[parameters('containerUMIPrincipalId')]" - } - ] + "adminUserEnabled": true } } ], "outputs": { - "needsManualSetup": { - "type": "bool", - "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.needsManualSetup]" + "id": { + "type": "string", + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('containerRegistryName'))]" }, - "statusMessage": { + "name": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.statusMessage]" + "value": "[parameters('containerRegistryName')]" }, - "assignedPermissions": { - "type": "array", - "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.assignedPermissions]" + "loginServer": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('containerRegistryName'))).loginServer]" } } } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment')]", - "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" - ] + } }, { "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "container-instance-deployment", - "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2020-10-01", + "name": "importContainerImage", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { + "acrName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'acr')).outputs.name.value]" + }, "location": { "value": "[parameters('location')]" }, - "containerGroupName": { - "value": "[variables('containerName')]" - }, - "containerUMIResourceId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.resourceId.value]" - }, - "imageName": { - "value": "[variables('imageName')]" - }, - "bloodhoundInstanceDomain": { - "value": "[parameters('bloodhoundInstanceDomain')]" - }, - "azureTenantId": { - "value": "[parameters('azureTenantId')]" - }, - "bloodhoundTokenId": { - "value": "[parameters('bloodhoundTokenId')]" - }, - "bloodhoundToken": { - "value": "[parameters('bloodhoundToken')]" + "images": { + "value": "[array('azurehounddeploy.azurecr.io/azurehound:latest')]" } }, "template": { @@ -383,140 +345,200 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.1.42791", - "templateHash": "14470760509824127242" + "version": "0.11.1.770", + "templateHash": "8432140579349303037" } }, "parameters": { + "acrName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Container Registry" + } + }, "location": { - "type": "string" + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location to deploy the resources to" + } }, - "containerGroupName": { - "type": "string" + "forceUpdateTag": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "How the deployment script should be forced to execute" + } }, - "containerUMIResourceId": { - "type": "string" + "rbacRoleNeeded": { + "type": "string", + "defaultValue": "b24988ac-6180-42a0-ab88-20f7382dd24c", + "metadata": { + "description": "Azure RoleId that are required for the DeploymentScript resource to import images" + } }, - "imageName": { - "type": "string" + "useExistingManagedIdentity": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Does the Managed Identity already exists, or should be created" + } }, - "bloodhoundInstanceDomain": { - "type": "string" + "managedIdentityName": { + "type": "string", + "defaultValue": "id-ContainerRegistryImport", + "metadata": { + "description": "Name of the Managed Identity resource" + } }, - "azureTenantId": { - "type": "string" + "existingManagedIdentitySubId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "For an existing Managed Identity, the Subscription Id it is located in" + } }, - "bloodhoundTokenId": { - "type": "securestring" + "existingManagedIdentityResourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "For an existing Managed Identity, the Resource Group it is located in" + } }, - "bloodhoundToken": { - "type": "securestring" - } - }, - "variables": { - "config": { - "batchsize": 100, - "config": "/home/nonroot/.config/azurehound/config.json", - "instance": "[format('https://{0}/', parameters('bloodhoundInstanceDomain'))]", - "json": false, - "managed-identity": true, - "maxconnsperhost": 20, - "maxidleconnsperhost": 20, - "region": "cloud", - "streamcount": 25, - "tenant": "[parameters('azureTenantId')]", - "token": "[parameters('bloodhoundToken')]", - "tokenid": "[parameters('bloodhoundTokenId')]", - "verbosity": 0 + "images": { + "type": "array", + "metadata": { + "description": "An array of fully qualified images names to import" + } + }, + "initialScriptDelay": { + "type": "string", + "defaultValue": "30s", + "metadata": { + "description": "A delay before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate" + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "OnSuccess", + "metadata": { + "description": "When the script resource is cleaned up" + }, + "allowedValues": [ + "OnSuccess", + "OnExpiration", + "Always" + ] } }, "resources": [ { - "type": "Microsoft.ContainerInstance/containerGroups", - "apiVersion": "2023-05-01", - "name": "[parameters('containerGroupName')]", + "condition": "[not(parameters('useExistingManagedIdentity'))]", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2018-11-30", + "name": "[parameters('managedIdentityName')]", + "location": "[parameters('location')]" + }, + { + "condition": "[not(empty(parameters('rbacRoleNeeded')))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-08-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('acrName'))]", + "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), parameters('rbacRoleNeeded'), if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('rbacRoleNeeded'))]", + "principalId": "[if(parameters('useExistingManagedIdentity'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId)]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]" + ] + }, + { + "copy": { + "name": "createImportImage", + "count": "[length(parameters('images'))]" + }, + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('ACR-Import-{0}-{1}', parameters('acrName'), last(split(replace(parameters('images')[copyIndex()], ':', ''), '/')))]", "location": "[parameters('location')]", "identity": { "type": "UserAssigned", "userAssignedIdentities": { - "[format('{0}', parameters('containerUMIResourceId'))]": {} + "[format('{0}', if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]": {} } }, + "kind": "AzureCLI", "properties": { - "containers": [ + "forceUpdateTag": "[parameters('forceUpdateTag')]", + "azCliVersion": "2.30.0", + "timeout": "PT30M", + "retentionInterval": "P1D", + "environmentVariables": [ { - "name": "azurehound", - "properties": { - "image": "[parameters('imageName')]", - "command": [ - "/azurehound", - "start" - ], - "volumeMounts": [ - { - "name": "config-volume", - "mountPath": "/home/nonroot/.config/azurehound" - } - ], - "resources": { - "requests": { - "cpu": 1, - "memoryInGB": 1 - } - } - } - } - ], - "volumes": [ + "name": "acrName", + "value": "[parameters('acrName')]" + }, { - "name": "config-volume", - "secret": { - "config.json": "[base64(string(variables('config')))]" - } + "name": "imageName", + "value": "[parameters('images')[copyIndex()]]" + }, + { + "name": "initialDelay", + "value": "[parameters('initialScriptDelay')]" + }, + { + "name": "retryMax", + "value": "2" + }, + { + "name": "retrySleep", + "value": "5s" } ], - "osType": "Linux", - "restartPolicy": "Never" + "scriptContent": " #!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\"\n sleep $initialDelay\n \n #Retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image: $imageName into ACR: $acrName\"\n az acr import -n $acrName --source $imageName --force \\\n && break\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n ", + "cleanupPreference": "[parameters('cleanupPreference')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]", + "[extensionResourceId(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), parameters('rbacRoleNeeded'), if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')))))]" + ] + } + ], + "outputs": { + "importedImages": { + "type": "array", + "copy": { + "count": "[length(parameters('images'))]", + "input": { + "originalImage": "[parameters('images')[copyIndex()]]", + "acrHostedImage": "[format('{0}{1}', reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), '2021-12-01-preview').loginServer, string(skip(parameters('images')[copyIndex()], indexOf(parameters('images')[copyIndex()], '/'))))]" + } + }, + "metadata": { + "description": "An array of the imported images" } } - ] + } } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment')]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment')]", - "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" - ] + "[resourceId('Microsoft.Resources/deployments', 'acr')]" + ], + "metadata": { + "description": "This module seeds the ACR with the public version of the app" + } } ], "outputs": { - "containerUMIResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.resourceId.value]" - }, - "containerUMIPrincipalId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('containerUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.principalId.value]" - }, - "permissionSetupRequired": { - "type": "bool", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment'), '2022-09-01').outputs.needsManualSetup.value]" - }, - "permissionStatus": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment'), '2022-09-01').outputs.statusMessage.value]" - }, - "assignedPermissions": { - "type": "array", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'graph-permissions-deployment'), '2022-09-01').outputs.assignedPermissions.value]" - }, - "deploymentUMIPrincipalId": { + "containerAppFQDN": { "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('deploymentUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'deploymentIdentity-deployment'), '2022-09-01').outputs.principalId.value]" + "value": "[reference(resourceId('Microsoft.App/containerApps', parameters('containerAppName'))).configuration.ingress.fqdn]" }, - "deploymentUMIId": { + "containerImage": { "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('deploymentUMIResourceGroupName')), 'Microsoft.Resources/deployments', 'deploymentIdentity-deployment'), '2022-09-01').outputs.resourceId.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'importContainerImage')).outputs.importedImages.value[0].acrHostedImage]" } } -} +} \ No newline at end of file From b27d43a406302bfe8d0c52f9196e29339a2d7def Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Fri, 20 Jun 2025 17:36:31 +0530 Subject: [PATCH 07/17] Mounted the config.json --- azure-deploy/mainTemplate.json | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index c13f1178..6031c915 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -189,6 +189,12 @@ } ] }, + "secrets": [ + { + "name": "configjson", + "value": "[base64(string(variables('config')))]" + } + ], "registries": [ { "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]", @@ -208,7 +214,7 @@ ], "volumeMounts": [ { - "name": "config-volume", + "volumeName": "config-volume", "mountPath": "/home/nonroot/.config/azurehound" } ], @@ -222,9 +228,12 @@ { "name": "config-volume", "storageType": "Secret", - "secret": { - "config.json": "[base64(string(variables('config')))]" - } + "secrets": [ + { + "secretRef": "configjson", + "path": "config.json" + } + ] } ], "scale": { From 1a2a093a0154d015205309e7425d0964aab79cfe Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Tue, 24 Jun 2025 13:58:40 +0530 Subject: [PATCH 08/17] Used Azure File Storage for creating config.json --- azure-deploy/mainTemplate.json | 82 ++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index 6031c915..bc121123 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -93,9 +93,9 @@ }, "variables": { "acrPullRole": "[resourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", - "config": { + "configcontent": { "batchsize": 100, - "config": "/home/nonroot/.config/azurehound/config.json", + "config": "/mnt/config/config.json", "instance": "[format('https://{0}/', parameters('bloodhoundInstanceDomain'))]", "json": false, "managed-identity": true, @@ -107,7 +107,11 @@ "token": "[parameters('bloodhoundToken')]", "tokenid": "[parameters('bloodhoundTokenId')]", "verbosity": 0 - } + }, + "fileShareVolumeName": "config-volume", // Internal name for the volume + "storageMountName": "azure-files-config-storage", // Name of the storage link in the environment + "storageAccountName": "azstorageaccname", + "fileShareName": "azfilesharename" }, "resources": [ { @@ -164,6 +168,45 @@ "description": "This allows the managed identity of the container app to access the registry, note scope is applied to the wider ResourceGroup not the ACR" } }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-01-01", + "name": "[variables('storageAccountName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "StorageV2", + "properties": {} + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2023-01-01", + "name": "[concat(variables('storageAccountName'), '/default/', variables('fileShareName'))]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" + ], + "properties": { + "shareQuota": 8 // 8MB, adjust as needed + } + }, + { + "type": "Microsoft.App/managedEnvironments/storages", + "apiVersion": "2024-03-01", + "name": "[concat(parameters('containerAppEnvName'), '/', variables('storageMountName'))]", + "properties": { + "azureFile": { + "accountName": "[variables('storageAccountName')]", + "accountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2023-01-01').keys[0].value]", + "shareName": "[variables('fileShareName')]", + "accessMode": "ReadWrite" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", + "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', variables('storageAccountName'), 'default', variables('fileShareName'))]" + ] + }, { "type": "Microsoft.App/containerApps", "apiVersion": "2022-06-01-preview", @@ -189,12 +232,6 @@ } ] }, - "secrets": [ - { - "name": "configjson", - "value": "[base64(string(variables('config')))]" - } - ], "registries": [ { "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]", @@ -203,19 +240,22 @@ ] }, "template": { - "revisionSuffix": "firstrevision", + "revisionSuffix": "revision240612", "containers": [ { "name": "[parameters('containerAppName')]", "image": "[reference(resourceId('Microsoft.Resources/deployments', 'importContainerImage')).outputs.importedImages.value[0].acrHostedImage]", "command": [ - "/azurehound", - "start" + "/bin/sh"], // Or "/bin/sh" if bash is not available + "args": [ + "-c", + "[concat('echo ', base64(string(variables('configcontent'))), ' > /mnt/config/config.json && azurehound start -c /mnt/config/config.json && tail -f /dev/null')]" +// "[concat('echo ', base64(string(variables('configcontent'))), ' > /mnt/config/config.json && tail -f /dev/null')]" //Use this to Debug the container ], "volumeMounts": [ { - "volumeName": "config-volume", - "mountPath": "/home/nonroot/.config/azurehound" + "volumeName": "[variables('fileShareVolumeName')]", + "mountPath": "/mnt/config" } ], "resources": { @@ -226,14 +266,9 @@ ], "volumes": [ { - "name": "config-volume", - "storageType": "Secret", - "secrets": [ - { - "secretRef": "configjson", - "path": "config.json" - } - ] + "name": "[variables('fileShareVolumeName')]", + "storageType": "AzureFile", + "storageName": "[variables('storageMountName')]" } ], "scale": { @@ -256,7 +291,8 @@ "[resourceId('Microsoft.Resources/deployments', 'acr')]", "[resourceId('Microsoft.Resources/deployments', 'importContainerImage')]", "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]" + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]", + "[resourceId('Microsoft.App/managedEnvironments/storages', parameters('containerAppEnvName'), variables('storageMountName'))]" ] }, { From 48f48b7cb517685b6738761e3a7102d0c00c4246 Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Tue, 24 Jun 2025 19:21:26 +0530 Subject: [PATCH 09/17] Create config.json using initContainer --- azure-deploy/mainTemplate.json | 49 +++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index bc121123..758c5136 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -93,21 +93,9 @@ }, "variables": { "acrPullRole": "[resourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", - "configcontent": { - "batchsize": 100, - "config": "/mnt/config/config.json", - "instance": "[format('https://{0}/', parameters('bloodhoundInstanceDomain'))]", - "json": false, - "managed-identity": true, - "maxconnsperhost": 20, - "maxidleconnsperhost": 20, - "region": "cloud", - "streamcount": 25, - "tenant": "[parameters('azureTenantId')]", - "token": "[parameters('bloodhoundToken')]", - "tokenid": "[parameters('bloodhoundTokenId')]", - "verbosity": 0 - }, + "configcontent": "[concat('{\\\"batchsize\\\": 100,\\\"config\\\": \\\"/mnt/config/config.json\\\",\\\"instance\\\": \\\"https://', parameters('bloodhoundInstanceDomain'), '\\\",\\\"json\\\": false,\\\"managed-identity\\\": true,\\\"maxconnsperhost\\\": 20,\\\"maxidleconnsperhost\\\": 20,\\\"region\\\": \\\"cloud\\\",\\\"streamcount\\\": 25,\\\"tenant\\\": \\\"', parameters('azureTenantId'),'\\\",\\\"token\\\": \\\"', parameters('bloodhoundToken'),'\\\",\\\"tokenid\\\": \\\"', parameters('bloodhoundTokenId'),'\\\",\\\"verbosity\\\": 2}' + )]", + "fileShareVolumeName": "config-volume", // Internal name for the volume "storageMountName": "azure-files-config-storage", // Name of the storage link in the environment "storageAccountName": "azstorageaccname", @@ -240,17 +228,40 @@ ] }, "template": { - "revisionSuffix": "revision240612", + "revisionSuffix": "revision240622", + "initContainers": [ + { + "name": "config-writer", + "image": "mcr.microsoft.com/azure-cli:latest", // A lightweight image with bash for scripting + "command": [ + "bash", + "-c" + ], + "args": [ + // Construct the JSON using environment variables populated from Container variable + "[concat('echo ', variables('configcontent'), ' > /mnt/config/config.json')]", + // Add a confirmation message to logs + "echo 'config.json created successfully in /mnt/config/'" + ], + "volumeMounts": [ + { + "mountPath": "/mnt/config", // Mount the volume here for the init container to write to + "volumeName": "config-volume" + } + ] + } + ], "containers": [ { "name": "[parameters('containerAppName')]", "image": "[reference(resourceId('Microsoft.Resources/deployments', 'importContainerImage')).outputs.importedImages.value[0].acrHostedImage]", "command": [ - "/bin/sh"], // Or "/bin/sh" if bash is not available + "/azurehound", + "start" + ], "args": [ "-c", - "[concat('echo ', base64(string(variables('configcontent'))), ' > /mnt/config/config.json && azurehound start -c /mnt/config/config.json && tail -f /dev/null')]" -// "[concat('echo ', base64(string(variables('configcontent'))), ' > /mnt/config/config.json && tail -f /dev/null')]" //Use this to Debug the container + "/mnt/config/config.json" ], "volumeMounts": [ { From 9d5da205a3f68bd1a4d97519fec2b462b75e0983 Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Tue, 24 Jun 2025 20:23:09 +0530 Subject: [PATCH 10/17] Removed unused resources --- azure-deploy/mainTemplate.json | 150 +++++++++------------------------ 1 file changed, 38 insertions(+), 112 deletions(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index 758c5136..af94363b 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -14,7 +14,9 @@ "metadata": { "description": "Azure tenant ID to analyze" }, - "defaultValue": "12345" + "defaultValue": "12345", + "minLength": 36, // GUIDs are 36 characters long (32 hex digits + 4 hyphens) + "maxLength": 36 }, "bloodhoundInstanceDomain": { "type": "string", @@ -37,41 +39,6 @@ }, "defaultValue": "12345" }, - "containerAppName": { - "type": "string", - "defaultValue": "[format('app-{0}', uniqueString(resourceGroup().id))]", - "metadata": { - "description": "Specifies the name of the container app." - } - }, - "containerAppEnvName": { - "type": "string", - "defaultValue": "[format('env-{0}', uniqueString(resourceGroup().id))]", - "metadata": { - "description": "Specifies the name of the container app environment." - } - }, - "containerAppLogAnalyticsName": { - "type": "string", - "defaultValue": "[format('containerapp-log-{0}', uniqueString(resourceGroup().id))]", - "metadata": { - "description": "Specifies the name of the log analytics workspace." - } - }, - "containerRegistryName": { - "type": "string", - "defaultValue": "[format('cr{0}', uniqueString(resourceGroup().id))]", - "metadata": { - "description": "Specifies the name of the container app environment." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Specifies the location for all resources." - } - }, "minReplica": { "type": "int", "defaultValue": 1, @@ -93,20 +60,20 @@ }, "variables": { "acrPullRole": "[resourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", - "configcontent": "[concat('{\\\"batchsize\\\": 100,\\\"config\\\": \\\"/mnt/config/config.json\\\",\\\"instance\\\": \\\"https://', parameters('bloodhoundInstanceDomain'), '\\\",\\\"json\\\": false,\\\"managed-identity\\\": true,\\\"maxconnsperhost\\\": 20,\\\"maxidleconnsperhost\\\": 20,\\\"region\\\": \\\"cloud\\\",\\\"streamcount\\\": 25,\\\"tenant\\\": \\\"', parameters('azureTenantId'),'\\\",\\\"token\\\": \\\"', parameters('bloodhoundToken'),'\\\",\\\"tokenid\\\": \\\"', parameters('bloodhoundTokenId'),'\\\",\\\"verbosity\\\": 2}' - )]", - - "fileShareVolumeName": "config-volume", // Internal name for the volume - "storageMountName": "azure-files-config-storage", // Name of the storage link in the environment - "storageAccountName": "azstorageaccname", - "fileShareName": "azfilesharename" - }, + "configcontent": "[concat('{\\\"batchsize\\\": 100,\\\"config\\\": \\\"/mnt/config/config.json\\\",\\\"instance\\\": \\\"https://', parameters('bloodhoundInstanceDomain'), '\\\",\\\"json\\\": false,\\\"managed-identity\\\": true,\\\"maxconnsperhost\\\": 20,\\\"maxidleconnsperhost\\\": 20,\\\"region\\\": \\\"cloud\\\",\\\"streamcount\\\": 25,\\\"tenant\\\": \\\"', parameters('azureTenantId'),'\\\",\\\"token\\\": \\\"', parameters('bloodhoundToken'),'\\\",\\\"tokenid\\\": \\\"', parameters('bloodhoundTokenId'),'\\\",\\\"verbosity\\\": 2}')]", + "fileShareVolumeName": "config-volume", + "containerAppName": "[format('app-{0}', uniqueString(resourceGroup().id))]", + "containerAppEnvName": "[format('env-{0}', uniqueString(resourceGroup().id))]", + "containerAppLogAnalyticsName": "[format('containerapp-log-{0}', uniqueString(resourceGroup().id))]", + "containerRegistryName": "[format('cr{0}', uniqueString(resourceGroup().id))]", + "location": "[resourceGroup().location]" + }, "resources": [ { "type": "Microsoft.OperationalInsights/workspaces", "apiVersion": "2021-06-01", - "name": "[parameters('containerAppLogAnalyticsName')]", - "location": "[parameters('location')]", + "name": "[variables('containerAppLogAnalyticsName')]", + "location": "[variables('location')]", "properties": { "sku": { "name": "PerGB2018" @@ -116,8 +83,8 @@ { "type": "Microsoft.App/managedEnvironments", "apiVersion": "2022-06-01-preview", - "name": "[parameters('containerAppEnvName')]", - "location": "[parameters('location')]", + "name": "[variables('containerAppEnvName')]", + "location": "[variables('location')]", "sku": { "name": "Consumption" }, @@ -125,89 +92,50 @@ "appLogsConfiguration": { "destination": "log-analytics", "logAnalyticsConfiguration": { - "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('containerAppLogAnalyticsName'))).customerId]", - "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('containerAppLogAnalyticsName')), '2021-06-01').primarySharedKey]" + "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', variables('containerAppLogAnalyticsName'))).customerId]", + "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', variables('containerAppLogAnalyticsName')), '2021-06-01').primarySharedKey]" } } }, "dependsOn": [ - "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('containerAppLogAnalyticsName'))]" + "[resourceId('Microsoft.OperationalInsights/workspaces', variables('containerAppLogAnalyticsName'))]" ] }, { "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2022-01-31-preview", - "name": "[format('id-{0}', parameters('containerAppName'))]", - "location": "[parameters('location')]" + "name": "[format('id-{0}', variables('containerAppName'))]", + "location": "[variables('location')]" }, { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "name": "[guid(resourceGroup().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName'))), variables('acrPullRole'))]", + "name": "[guid(resourceGroup().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName'))), variables('acrPullRole'))]", "properties": { "roleDefinitionId": "[variables('acrPullRole')]", - "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))).principalId]", + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))).principalId]", "principalType": "ServicePrincipal" }, "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]" + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))]" ], "metadata": { "description": "This allows the managed identity of the container app to access the registry, note scope is applied to the wider ResourceGroup not the ACR" } }, - { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-01-01", - "name": "[variables('storageAccountName')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard_LRS" - }, - "kind": "StorageV2", - "properties": {} - }, - { - "type": "Microsoft.Storage/storageAccounts/fileServices/shares", - "apiVersion": "2023-01-01", - "name": "[concat(variables('storageAccountName'), '/default/', variables('fileShareName'))]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" - ], - "properties": { - "shareQuota": 8 // 8MB, adjust as needed - } - }, - { - "type": "Microsoft.App/managedEnvironments/storages", - "apiVersion": "2024-03-01", - "name": "[concat(parameters('containerAppEnvName'), '/', variables('storageMountName'))]", - "properties": { - "azureFile": { - "accountName": "[variables('storageAccountName')]", - "accountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2023-01-01').keys[0].value]", - "shareName": "[variables('fileShareName')]", - "accessMode": "ReadWrite" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", - "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', variables('storageAccountName'), 'default', variables('fileShareName'))]" - ] - }, { "type": "Microsoft.App/containerApps", "apiVersion": "2022-06-01-preview", - "name": "[parameters('containerAppName')]", - "location": "[parameters('location')]", + "name": "[variables('containerAppName')]", + "location": "[variables('location')]", "identity": { "type": "UserAssigned", "userAssignedIdentities": { - "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName'))))]": {} + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName'))))]": {} } }, "properties": { - "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", + "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', variables('containerAppEnvName'))]", "configuration": { "ingress": { "external": true, @@ -222,13 +150,13 @@ }, "registries": [ { - "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]", + "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))]", "server": "[reference(resourceId('Microsoft.Resources/deployments', 'acr')).outputs.loginServer.value]" } ] }, "template": { - "revisionSuffix": "revision240622", + "revisionSuffix": "revision240624", "initContainers": [ { "name": "config-writer", @@ -246,14 +174,14 @@ "volumeMounts": [ { "mountPath": "/mnt/config", // Mount the volume here for the init container to write to - "volumeName": "config-volume" + "volumeName": "[variables('fileShareVolumeName')]" } ] } ], "containers": [ { - "name": "[parameters('containerAppName')]", + "name": "[variables('containerAppName')]", "image": "[reference(resourceId('Microsoft.Resources/deployments', 'importContainerImage')).outputs.importedImages.value[0].acrHostedImage]", "command": [ "/azurehound", @@ -278,8 +206,7 @@ "volumes": [ { "name": "[variables('fileShareVolumeName')]", - "storageType": "AzureFile", - "storageName": "[variables('storageMountName')]" + "emptyDir": {} // An ephemeral volume that exists for the lifetime of the pod } ], "scale": { @@ -301,9 +228,8 @@ "dependsOn": [ "[resourceId('Microsoft.Resources/deployments', 'acr')]", "[resourceId('Microsoft.Resources/deployments', 'importContainerImage')]", - "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', parameters('containerAppName')))]", - "[resourceId('Microsoft.App/managedEnvironments/storages', parameters('containerAppEnvName'), variables('storageMountName'))]" + "[resourceId('Microsoft.App/managedEnvironments', variables('containerAppEnvName'))]", + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))]" ] }, { @@ -317,10 +243,10 @@ "mode": "Incremental", "parameters": { "location": { - "value": "[parameters('location')]" + "value": "[variables('location')]" }, "containerRegistryName": { - "value": "[parameters('containerRegistryName')]" + "value": "[variables('containerRegistryName')]" } }, "template": { @@ -389,7 +315,7 @@ "value": "[reference(resourceId('Microsoft.Resources/deployments', 'acr')).outputs.name.value]" }, "location": { - "value": "[parameters('location')]" + "value": "[variables('location')]" }, "images": { "value": "[array('azurehounddeploy.azurecr.io/azurehound:latest')]" @@ -590,7 +516,7 @@ "outputs": { "containerAppFQDN": { "type": "string", - "value": "[reference(resourceId('Microsoft.App/containerApps', parameters('containerAppName'))).configuration.ingress.fqdn]" + "value": "[reference(resourceId('Microsoft.App/containerApps', variables('containerAppName'))).configuration.ingress.fqdn]" }, "containerImage": { "type": "string", From 53b63366a7303d5407e3a11165ed371af5826927 Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Tue, 24 Jun 2025 22:40:22 +0530 Subject: [PATCH 11/17] Deployment script to assign readall role --- azure-deploy/mainTemplate.json | 171 ++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 2 deletions(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index af94363b..4604ac98 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -14,7 +14,7 @@ "metadata": { "description": "Azure tenant ID to analyze" }, - "defaultValue": "12345", + "defaultValue": "7d174b6e-ae92-4bbe-9b67-04b9e1523d74", "minLength": 36, // GUIDs are 36 characters long (32 hex digits + 4 hyphens) "maxLength": 36 }, @@ -66,7 +66,8 @@ "containerAppEnvName": "[format('env-{0}', uniqueString(resourceGroup().id))]", "containerAppLogAnalyticsName": "[format('containerapp-log-{0}', uniqueString(resourceGroup().id))]", "containerRegistryName": "[format('cr{0}', uniqueString(resourceGroup().id))]", - "location": "[resourceGroup().location]" + "location": "[resourceGroup().location]", + "containerUMIName": "[format('umi-{0}', uniqueString(resourceGroup().id))]" }, "resources": [ { @@ -511,6 +512,172 @@ "metadata": { "description": "This module seeds the ACR with the public version of the app" } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "graph-permissions-deployment", + "resourceGroup": "[resourceGroup().name]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[variables('location')]" + }, + "deploymentUMIName": { + "value": "[variables('containerUMIName')]" + }, + "deploymentUMIResourceGroupName": { + "value": "[resourceGroup().name]" + }, + "containerUMIPrincipalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, resourceGroup().name), 'Microsoft.Resources/deployments', 'containerIdentity-deployment'), '2022-09-01').outputs.principalId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "2556770267153897272" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "containerUMIPrincipalId": { + "type": "string" + }, + "deploymentUMIResourceGroupName": { + "type": "string" + }, + "deploymentUMIName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "graph-permissions-script", + "location": "[parameters('location')]", + "kind": "AzurePowerShell", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('deploymentUMIResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentUMIName')))]": {} + } + }, + "properties": { + "azPowerShellVersion": "9.7", + "retentionInterval": "P1D", + "timeout": "PT30M", + "cleanupPreference": "Always", + "scriptContent": " $ErrorActionPreference = \"Continue\"\n \n # Initialize arrays for tracking\n $warningsList = @()\n $successList = @()\n $needManualSetup = $false\n \n try {\n $token = (Get-AzAccessToken -ResourceUrl \"https://graph.microsoft.com/\").Token\n $graphAppId = \"00000003-0000-0000-c000-000000000000\"\n \n $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId\n if (-not $graphSp) {\n $needManualSetup = $true\n }\n \n if ($graphSp) {\n $headers = @{\n 'Authorization' = \"Bearer $token\"\n 'Content-Type' = 'application/json'\n }\n\n try {\n $apiUrl = \"https://graph.microsoft.com/v1.0/servicePrincipals/$($env:ContainerUMIPrincipalId)/appRoleAssignments\"\n $existingAssignments = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Get\n \n $directoryReadAllId = \"7ab1d382-f21e-4acd-a863-ba3e13f7da61\"\n $existingAssignment = $existingAssignments.value | Where-Object { \n $_.appRoleId -eq $directoryReadAllId -and \n $_.resourceId -eq $graphSp.Id\n }\n\n if (-not $existingAssignment) {\n try {\n $body = @{\n principalId = $env:ContainerUMIPrincipalId\n resourceId = $graphSp.Id\n appRoleId = $directoryReadAllId\n } | ConvertTo-Json\n\n $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body\n $successList += \"Directory.Read.All\"\n }\n catch {\n $needManualSetup = $true\n }\n }\n else {\n $successList += \"Directory.Read.All\"\n }\n }\n catch {\n $needManualSetup = $true\n }\n }\n }\n catch {\n $needManualSetup = $true\n }\n\n # Create a structured permission status message\n $statusMessage = if ($needManualSetup) {\n @\"\nMANUAL PERMISSION SETUP REQUIRED\n------------------------------\nThe container's managed identity requires the following Microsoft Graph permission:\n- Directory.Read.All\n\nPlease run the provided setup script to configure these permissions:\n./setup-container-permissions.ps1 -PrincipalId $($env:ContainerUMIPrincipalId)\n\"@\n } else {\n \"All required permissions have been configured successfully.\"\n }\n \n # Output the results\n $DeploymentScriptOutputs = @{\n needsManualSetup = $needManualSetup\n statusMessage = $statusMessage\n assignedPermissions = $successList\n }\n ", + "environmentVariables": [ + { + "name": "ContainerUMIPrincipalId", + "value": "[parameters('containerUMIPrincipalId')]" + } + ] + } + } + ], + "outputs": { + "needsManualSetup": { + "type": "bool", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.needsManualSetup]" + }, + "statusMessage": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.statusMessage]" + }, + "assignedPermissions": { + "type": "array", + "value": "[reference(resourceId('Microsoft.Resources/deploymentScripts', 'graph-permissions-script'), '2023-08-01').outputs.assignedPermissions]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, resourceGroup().name), 'Microsoft.Resources/deployments', 'containerIdentity-deployment')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "containerIdentity-deployment", + "resourceGroup": "[resourceGroup().name]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[variables('location')]" + }, + "containerUMIName": { + "value": "[variables('containerUMIName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "7723817674947761621" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "containerUMIName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('containerUMIName')]", + "location": "[parameters('location')]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), 'Reader')]", + "properties": { + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), '2023-01-31').principalId]", + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName'))]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName'))]" + }, + "principalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('containerUMIName')), '2023-01-31').principalId]" + } + } + } + } } ], "outputs": { From 4b01065a66493a1f4bc8e49c63304a1501ed4ed3 Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Wed, 25 Jun 2025 12:07:13 +0530 Subject: [PATCH 12/17] Reuse existing umi --- azure-deploy/mainTemplate.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index 4604ac98..5960f457 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -67,7 +67,7 @@ "containerAppLogAnalyticsName": "[format('containerapp-log-{0}', uniqueString(resourceGroup().id))]", "containerRegistryName": "[format('cr{0}', uniqueString(resourceGroup().id))]", "location": "[resourceGroup().location]", - "containerUMIName": "[format('umi-{0}', uniqueString(resourceGroup().id))]" + "containerUMIName": "[format('id-app-{0}', uniqueString(resourceGroup().id))]" }, "resources": [ { @@ -105,20 +105,20 @@ { "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2022-01-31-preview", - "name": "[format('id-{0}', variables('containerAppName'))]", + "name": "[variables('containerUMIName')]", "location": "[variables('location')]" }, { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "name": "[guid(resourceGroup().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName'))), variables('acrPullRole'))]", + "name": "[guid(resourceGroup().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName')), variables('acrPullRole'))]", "properties": { "roleDefinitionId": "[variables('acrPullRole')]", - "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))).principalId]", + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))).principalId]", "principalType": "ServicePrincipal" }, "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))]" + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]" ], "metadata": { "description": "This allows the managed identity of the container app to access the registry, note scope is applied to the wider ResourceGroup not the ACR" @@ -132,7 +132,7 @@ "identity": { "type": "UserAssigned", "userAssignedIdentities": { - "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName'))))]": {} + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName')))]": {} } }, "properties": { @@ -151,13 +151,13 @@ }, "registries": [ { - "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))]", + "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]", "server": "[reference(resourceId('Microsoft.Resources/deployments', 'acr')).outputs.loginServer.value]" } ] }, "template": { - "revisionSuffix": "revision240624", + "revisionSuffix": "revision25062", "initContainers": [ { "name": "config-writer", @@ -230,7 +230,7 @@ "[resourceId('Microsoft.Resources/deployments', 'acr')]", "[resourceId('Microsoft.Resources/deployments', 'importContainerImage')]", "[resourceId('Microsoft.App/managedEnvironments', variables('containerAppEnvName'))]", - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('id-{0}', variables('containerAppName')))]" + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]" ] }, { @@ -578,7 +578,7 @@ "azPowerShellVersion": "9.7", "retentionInterval": "P1D", "timeout": "PT30M", - "cleanupPreference": "Always", + "cleanupPreference": "OnSuccess", "scriptContent": " $ErrorActionPreference = \"Continue\"\n \n # Initialize arrays for tracking\n $warningsList = @()\n $successList = @()\n $needManualSetup = $false\n \n try {\n $token = (Get-AzAccessToken -ResourceUrl \"https://graph.microsoft.com/\").Token\n $graphAppId = \"00000003-0000-0000-c000-000000000000\"\n \n $graphSp = Get-AzADServicePrincipal -ApplicationId $graphAppId\n if (-not $graphSp) {\n $needManualSetup = $true\n }\n \n if ($graphSp) {\n $headers = @{\n 'Authorization' = \"Bearer $token\"\n 'Content-Type' = 'application/json'\n }\n\n try {\n $apiUrl = \"https://graph.microsoft.com/v1.0/servicePrincipals/$($env:ContainerUMIPrincipalId)/appRoleAssignments\"\n $existingAssignments = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Get\n \n $directoryReadAllId = \"7ab1d382-f21e-4acd-a863-ba3e13f7da61\"\n $existingAssignment = $existingAssignments.value | Where-Object { \n $_.appRoleId -eq $directoryReadAllId -and \n $_.resourceId -eq $graphSp.Id\n }\n\n if (-not $existingAssignment) {\n try {\n $body = @{\n principalId = $env:ContainerUMIPrincipalId\n resourceId = $graphSp.Id\n appRoleId = $directoryReadAllId\n } | ConvertTo-Json\n\n $result = Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method Post -Body $body\n $successList += \"Directory.Read.All\"\n }\n catch {\n $needManualSetup = $true\n }\n }\n else {\n $successList += \"Directory.Read.All\"\n }\n }\n catch {\n $needManualSetup = $true\n }\n }\n }\n catch {\n $needManualSetup = $true\n }\n\n # Create a structured permission status message\n $statusMessage = if ($needManualSetup) {\n @\"\nMANUAL PERMISSION SETUP REQUIRED\n------------------------------\nThe container's managed identity requires the following Microsoft Graph permission:\n- Directory.Read.All\n\nPlease run the provided setup script to configure these permissions:\n./setup-container-permissions.ps1 -PrincipalId $($env:ContainerUMIPrincipalId)\n\"@\n } else {\n \"All required permissions have been configured successfully.\"\n }\n \n # Output the results\n $DeploymentScriptOutputs = @{\n needsManualSetup = $needManualSetup\n statusMessage = $statusMessage\n assignedPermissions = $successList\n }\n ", "environmentVariables": [ { From d70504fbc694147a2ee0ff5bf6219d2674861cb6 Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Wed, 25 Jun 2025 19:27:52 +0530 Subject: [PATCH 13/17] Add dependency on the graph-permission-script --- azure-deploy/mainTemplate.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index 5960f457..34dab37e 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -157,7 +157,7 @@ ] }, "template": { - "revisionSuffix": "revision25062", + "revisionSuffix": "revision25065", "initContainers": [ { "name": "config-writer", @@ -230,7 +230,8 @@ "[resourceId('Microsoft.Resources/deployments', 'acr')]", "[resourceId('Microsoft.Resources/deployments', 'importContainerImage')]", "[resourceId('Microsoft.App/managedEnvironments', variables('containerAppEnvName'))]", - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]" + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]", + "[resourceId('Microsoft.Resources/deployments', 'graph-permissions-deployment')]" ] }, { From 4ab6f836faa96fad4eccd1b1178e27443e2f7d21 Mon Sep 17 00:00:00 2001 From: Kapil Bisen Date: Thu, 26 Jun 2025 10:59:43 +0530 Subject: [PATCH 14/17] Do not use managed identity --- azure-deploy/mainTemplate.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index 34dab37e..d9adaf7b 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -18,6 +18,20 @@ "minLength": 36, // GUIDs are 36 characters long (32 hex digits + 4 hyphens) "maxLength": 36 }, + "azureSecret": { + "type": "string", + "metadata": { + "description": "Azure Secret" + }, + "defaultValue": "" + }, + "azureAppID": { + "type": "string", + "metadata": { + "description": "Azure App ID" + }, + "defaultValue": "" + }, "bloodhoundInstanceDomain": { "type": "string", "metadata": { @@ -60,7 +74,7 @@ }, "variables": { "acrPullRole": "[resourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", - "configcontent": "[concat('{\\\"batchsize\\\": 100,\\\"config\\\": \\\"/mnt/config/config.json\\\",\\\"instance\\\": \\\"https://', parameters('bloodhoundInstanceDomain'), '\\\",\\\"json\\\": false,\\\"managed-identity\\\": true,\\\"maxconnsperhost\\\": 20,\\\"maxidleconnsperhost\\\": 20,\\\"region\\\": \\\"cloud\\\",\\\"streamcount\\\": 25,\\\"tenant\\\": \\\"', parameters('azureTenantId'),'\\\",\\\"token\\\": \\\"', parameters('bloodhoundToken'),'\\\",\\\"tokenid\\\": \\\"', parameters('bloodhoundTokenId'),'\\\",\\\"verbosity\\\": 2}')]", + "configcontent": "[concat('{\\\"batchsize\\\": 100,\\\"config\\\": \\\"/mnt/config/config.json\\\",\\\"instance\\\": \\\"https://', parameters('bloodhoundInstanceDomain'), '\\\",\\\"json\\\": false,\\\"managed-identity\\\": false,\\\"maxconnsperhost\\\": 20,\\\"maxidleconnsperhost\\\": 20,\\\"region\\\": \\\"cloud\\\",\\\"streamcount\\\": 25,\\\"tenant\\\": \\\"', parameters('azureTenantId'),'\\\",\\\"token\\\": \\\"', parameters('bloodhoundToken'),'\\\",\\\"tokenid\\\": \\\"', parameters('bloodhoundTokenId'),'\\\",\\\"secret\\\": \\\"', parameters('azureSecret'),'\\\",\\\"app\\\": \\\"', parameters('azureAppID'),'\\\",\\\"verbosity\\\": 2}')]", "fileShareVolumeName": "config-volume", "containerAppName": "[format('app-{0}', uniqueString(resourceGroup().id))]", "containerAppEnvName": "[format('env-{0}', uniqueString(resourceGroup().id))]", From 0c7bd013e286f7be93f84412d5664754062d3f50 Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Mon, 30 Jun 2025 10:45:53 +0530 Subject: [PATCH 15/17] Updated createUIDefinition with app id and secret. Also cadding azurehoundPackage.zip file that works as expected. --- azure-deploy/azurehound.parameters.json | 12 ++-- azure-deploy/azurehoundPackage.zip | Bin 0 -> 6734 bytes azure-deploy/createUiDefinition.json | 91 +++++++++++++++++++++++- azure-deploy/mainTemplate.json | 22 +++--- 4 files changed, 101 insertions(+), 24 deletions(-) create mode 100644 azure-deploy/azurehoundPackage.zip diff --git a/azure-deploy/azurehound.parameters.json b/azure-deploy/azurehound.parameters.json index e2f18148..a02bec42 100644 --- a/azure-deploy/azurehound.parameters.json +++ b/azure-deploy/azurehound.parameters.json @@ -2,15 +2,11 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - "resourceGroupName": { "value": "rg-azurehound-dev2" }, - "location": { "value": "eastus" }, - "containerUMIName": { "value": "azurehound-container-umi2" }, - "containerUMIResourceGroupName": { "value": "rg-azurehound-dev2" }, - "deploymentUMIName": { "value": "azurehound-deploy-umi2" }, - "deploymentUMIResourceGroupName": { "value": "rg-azurehound-dev2" }, "azureTenantId": { "value": "6c12b0b0-b2cc-4a73-8252-0b94bfca2145" }, - "bloodhoundInstanceDomain": { "value": "https://maplesyrup.bloodhoundenterprise.io/" }, - "bloodhoundTokenId": { "value": "501ca8f5-2dd2-40b6-a16d-378d8ce60db7" }, + "azureAppID": {"value": ""}, + "azureSecret": {"value": ""}, + "bloodhoundInstanceDomain": { "value": "maplesyrup.bloodhoundenterprise.io/" }, + "bloodhoundTokenId": { "value": "" }, "bloodhoundToken": { "value": "" } } } \ No newline at end of file diff --git a/azure-deploy/azurehoundPackage.zip b/azure-deploy/azurehoundPackage.zip new file mode 100644 index 0000000000000000000000000000000000000000..d3c278b3015fcfa78f28d67d22790a25cc96253d GIT binary patch literal 6734 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" + } }, "azureAppID": { "type": "string", "metadata": { "description": "Azure App ID" - }, - "defaultValue": "" + } }, "bloodhoundInstanceDomain": { "type": "string", "metadata": { "description": "Bloodhound instance domain" - }, - "defaultValue": "12345" + } }, "bloodhoundTokenId": { - "type": "securestring", + "type": "string", "metadata": { "description": "Bloodhound token ID" - }, - "defaultValue": "12345" + } }, "bloodhoundToken": { "type": "securestring", "metadata": { "description": "Bloodhound token" - }, - "defaultValue": "12345" + } }, "minReplica": { "type": "int", @@ -334,7 +328,7 @@ "value": "[variables('location')]" }, "images": { - "value": "[array('azurehounddeploy.azurecr.io/azurehound:latest')]" + "value": "[array('azurehoundregistry.azurecr.io/ishika-azurehound-v1:latest')]" } }, "template": { From be7661c668cf7c46f7bc83b31601ecb9697cb2f2 Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Wed, 16 Jul 2025 16:42:37 +0530 Subject: [PATCH 16/17] updating maintemplate removing container registry from managed resource group and adding viewtemplate --- azure-deploy/mainTemplate.json | 312 +------------------------------ azure-deploy/viewDefinition.json | 12 ++ 2 files changed, 16 insertions(+), 308 deletions(-) create mode 100644 azure-deploy/viewDefinition.json diff --git a/azure-deploy/mainTemplate.json b/azure-deploy/mainTemplate.json index 9343d18a..be3370ac 100644 --- a/azure-deploy/mainTemplate.json +++ b/azure-deploy/mainTemplate.json @@ -67,13 +67,13 @@ } }, "variables": { - "acrPullRole": "[resourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "acrLoginServer": "azurehoundregistry.azurecr.io", + "acrImageName": "[format('{0}/ishika-azurehound-v1:latest', variables('acrLoginServer'))]", "configcontent": "[concat('{\\\"batchsize\\\": 100,\\\"config\\\": \\\"/mnt/config/config.json\\\",\\\"instance\\\": \\\"https://', parameters('bloodhoundInstanceDomain'), '\\\",\\\"json\\\": false,\\\"managed-identity\\\": false,\\\"maxconnsperhost\\\": 20,\\\"maxidleconnsperhost\\\": 20,\\\"region\\\": \\\"cloud\\\",\\\"streamcount\\\": 25,\\\"tenant\\\": \\\"', parameters('azureTenantId'),'\\\",\\\"token\\\": \\\"', parameters('bloodhoundToken'),'\\\",\\\"tokenid\\\": \\\"', parameters('bloodhoundTokenId'),'\\\",\\\"secret\\\": \\\"', parameters('azureSecret'),'\\\",\\\"app\\\": \\\"', parameters('azureAppID'),'\\\",\\\"verbosity\\\": 2}')]", "fileShareVolumeName": "config-volume", "containerAppName": "[format('app-{0}', uniqueString(resourceGroup().id))]", "containerAppEnvName": "[format('env-{0}', uniqueString(resourceGroup().id))]", "containerAppLogAnalyticsName": "[format('containerapp-log-{0}', uniqueString(resourceGroup().id))]", - "containerRegistryName": "[format('cr{0}', uniqueString(resourceGroup().id))]", "location": "[resourceGroup().location]", "containerUMIName": "[format('id-app-{0}', uniqueString(resourceGroup().id))]" }, @@ -116,22 +116,6 @@ "name": "[variables('containerUMIName')]", "location": "[variables('location')]" }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[guid(resourceGroup().id, resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName')), variables('acrPullRole'))]", - "properties": { - "roleDefinitionId": "[variables('acrPullRole')]", - "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))).principalId]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]" - ], - "metadata": { - "description": "This allows the managed identity of the container app to access the registry, note scope is applied to the wider ResourceGroup not the ACR" - } - }, { "type": "Microsoft.App/containerApps", "apiVersion": "2022-06-01-preview", @@ -156,13 +140,7 @@ "weight": 100 } ] - }, - "registries": [ - { - "identity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]", - "server": "[reference(resourceId('Microsoft.Resources/deployments', 'acr')).outputs.loginServer.value]" } - ] }, "template": { "revisionSuffix": "revision25065", @@ -191,7 +169,7 @@ "containers": [ { "name": "[variables('containerAppName')]", - "image": "[reference(resourceId('Microsoft.Resources/deployments', 'importContainerImage')).outputs.importedImages.value[0].acrHostedImage]", + "image": "[variables('acrImageName')]", "command": [ "/azurehound", "start" @@ -235,293 +213,11 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'acr')]", - "[resourceId('Microsoft.Resources/deployments', 'importContainerImage')]", "[resourceId('Microsoft.App/managedEnvironments', variables('containerAppEnvName'))]", "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('containerUMIName'))]", "[resourceId('Microsoft.Resources/deployments', 'graph-permissions-deployment')]" ] }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2020-10-01", - "name": "acr", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[variables('location')]" - }, - "containerRegistryName": { - "value": "[variables('containerRegistryName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.11.1.770", - "templateHash": "5294368001789442670" - } - }, - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Specifies the location for all resources." - } - }, - "containerRegistryName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.ContainerRegistry/registries", - "apiVersion": "2022-02-01-preview", - "name": "[parameters('containerRegistryName')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard" - }, - "properties": { - "adminUserEnabled": true - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('containerRegistryName'))]" - }, - "name": { - "type": "string", - "value": "[parameters('containerRegistryName')]" - }, - "loginServer": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('containerRegistryName'))).loginServer]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2020-10-01", - "name": "importContainerImage", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "acrName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'acr')).outputs.name.value]" - }, - "location": { - "value": "[variables('location')]" - }, - "images": { - "value": "[array('azurehoundregistry.azurecr.io/ishika-azurehound-v1:latest')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.11.1.770", - "templateHash": "8432140579349303037" - } - }, - "parameters": { - "acrName": { - "type": "string", - "metadata": { - "description": "The name of the Azure Container Registry" - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "The location to deploy the resources to" - } - }, - "forceUpdateTag": { - "type": "string", - "defaultValue": "[utcNow()]", - "metadata": { - "description": "How the deployment script should be forced to execute" - } - }, - "rbacRoleNeeded": { - "type": "string", - "defaultValue": "b24988ac-6180-42a0-ab88-20f7382dd24c", - "metadata": { - "description": "Azure RoleId that are required for the DeploymentScript resource to import images" - } - }, - "useExistingManagedIdentity": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Does the Managed Identity already exists, or should be created" - } - }, - "managedIdentityName": { - "type": "string", - "defaultValue": "id-ContainerRegistryImport", - "metadata": { - "description": "Name of the Managed Identity resource" - } - }, - "existingManagedIdentitySubId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "For an existing Managed Identity, the Subscription Id it is located in" - } - }, - "existingManagedIdentityResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "For an existing Managed Identity, the Resource Group it is located in" - } - }, - "images": { - "type": "array", - "metadata": { - "description": "An array of fully qualified images names to import" - } - }, - "initialScriptDelay": { - "type": "string", - "defaultValue": "30s", - "metadata": { - "description": "A delay before the script import operation starts. Primarily to allow Azure AAD Role Assignments to propagate" - } - }, - "cleanupPreference": { - "type": "string", - "defaultValue": "OnSuccess", - "metadata": { - "description": "When the script resource is cleaned up" - }, - "allowedValues": [ - "OnSuccess", - "OnExpiration", - "Always" - ] - } - }, - "resources": [ - { - "condition": "[not(parameters('useExistingManagedIdentity'))]", - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2018-11-30", - "name": "[parameters('managedIdentityName')]", - "location": "[parameters('location')]" - }, - { - "condition": "[not(empty(parameters('rbacRoleNeeded')))]", - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-08-01-preview", - "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('acrName'))]", - "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), parameters('rbacRoleNeeded'), if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('rbacRoleNeeded'))]", - "principalId": "[if(parameters('useExistingManagedIdentity'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), '2018-11-30').principalId)]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]" - ] - }, - { - "copy": { - "name": "createImportImage", - "count": "[length(parameters('images'))]" - }, - "type": "Microsoft.Resources/deploymentScripts", - "apiVersion": "2020-10-01", - "name": "[format('ACR-Import-{0}-{1}', parameters('acrName'), last(split(replace(parameters('images')[copyIndex()], ':', ''), '/')))]", - "location": "[parameters('location')]", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))))]": {} - } - }, - "kind": "AzureCLI", - "properties": { - "forceUpdateTag": "[parameters('forceUpdateTag')]", - "azCliVersion": "2.30.0", - "timeout": "PT30M", - "retentionInterval": "P1D", - "environmentVariables": [ - { - "name": "acrName", - "value": "[parameters('acrName')]" - }, - { - "name": "imageName", - "value": "[parameters('images')[copyIndex()]]" - }, - { - "name": "initialDelay", - "value": "[parameters('initialScriptDelay')]" - }, - { - "name": "retryMax", - "value": "2" - }, - { - "name": "retrySleep", - "value": "5s" - } - ], - "scriptContent": " #!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\"\n sleep $initialDelay\n \n #Retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image: $imageName into ACR: $acrName\"\n az acr import -n $acrName --source $imageName --force \\\n && break\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n ", - "cleanupPreference": "[parameters('cleanupPreference')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName'))]", - "[extensionResourceId(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), parameters('rbacRoleNeeded'), if(parameters('useExistingManagedIdentity'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('existingManagedIdentitySubId'), parameters('existingManagedIdentityResourceGroupName')), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdentityName')))))]" - ] - } - ], - "outputs": { - "importedImages": { - "type": "array", - "copy": { - "count": "[length(parameters('images'))]", - "input": { - "originalImage": "[parameters('images')[copyIndex()]]", - "acrHostedImage": "[format('{0}{1}', reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), '2021-12-01-preview').loginServer, string(skip(parameters('images')[copyIndex()], indexOf(parameters('images')[copyIndex()], '/'))))]" - } - }, - "metadata": { - "description": "An array of the imported images" - } - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'acr')]" - ], - "metadata": { - "description": "This module seeds the ACR with the public version of the app" - } - }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -696,7 +392,7 @@ }, "containerImage": { "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'importContainerImage')).outputs.importedImages.value[0].acrHostedImage]" + "value": "[variables('acrImageName')]" } } } \ No newline at end of file diff --git a/azure-deploy/viewDefinition.json b/azure-deploy/viewDefinition.json new file mode 100644 index 00000000..937f6cbc --- /dev/null +++ b/azure-deploy/viewDefinition.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/viewdefinition/0.0.1-preview/ViewDefinition.json#", + "views": [ + { + "kind": "Overview", + "properties": { + "header": "🚀 AzureHound Application", + "description": "Follow these steps to configure your Azurehound integration:\n\n **Azure Tenant ID:** Enter your Azure Tenant ID.\n **Azure Application ID:** Register an application in Microsoft Entra ID. Grant it the following API permissions: Directory.Read.All, RoleManagement.Read.All, and provide admin consent.\n **Azure Secret ID:** Create a Client Secret for the registered app, and enter the secret value (not the ID).\n **BloodHound Instance Domain:** Enter your BloodHound instance domain name (exclude http:// or https://).\n **BloodHound Token ID:** Enter the Managed Client Token ID.\n **BloodHound Token Secret:** Enter the Managed Client Token Secret.\n Click **Next**, then **Review + Create**.\n After validation, click **Create** to begin deployment." + } + } + ] +} From 1e52f29a85d6a3798dd6dab99952a489c1604e1c Mon Sep 17 00:00:00 2001 From: ishikap-metron Date: Thu, 24 Jul 2025 13:51:33 +0530 Subject: [PATCH 17/17] Finalizing AzureHound Package including Azurefunction Code and upadated ACR --- azure-deploy/azurehound.parameters.json | 4 +- azure-deploy/azurehoundPackage.zip | Bin 6734 -> 0 bytes azure-deploy/mainTemplate.json | 114 +++++++++++++++++++++++- azure-deploy/viewDefinition.json | 2 +- 4 files changed, 114 insertions(+), 6 deletions(-) delete mode 100644 azure-deploy/azurehoundPackage.zip diff --git a/azure-deploy/azurehound.parameters.json b/azure-deploy/azurehound.parameters.json index a02bec42..3ae3d640 100644 --- a/azure-deploy/azurehound.parameters.json +++ b/azure-deploy/azurehound.parameters.json @@ -2,10 +2,10 @@ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - "azureTenantId": { "value": "6c12b0b0-b2cc-4a73-8252-0b94bfca2145" }, + "azureTenantId": { "value": "" }, "azureAppID": {"value": ""}, "azureSecret": {"value": ""}, - "bloodhoundInstanceDomain": { "value": "maplesyrup.bloodhoundenterprise.io/" }, + "bloodhoundInstanceDomain": { "value": "" }, "bloodhoundTokenId": { "value": "" }, "bloodhoundToken": { "value": "" } } diff --git a/azure-deploy/azurehoundPackage.zip b/azure-deploy/azurehoundPackage.zip deleted file mode 100644 index d3c278b3015fcfa78f28d67d22790a25cc96253d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6734 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