Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions azure-deploy/README.md
Original file line number Diff line number Diff line change
@@ -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:<tag>` 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 <AzureRegistryName>.azurecr.io/<AzureRepositoryName>:tag Note: Best to use latest unless you need to maintain multiple versions in Azure's registry.
7) docker push <AzureRegistryName>.azurecr.io/<AzureRepositoryName>:tag




12 changes: 12 additions & 0 deletions azure-deploy/azurehound.parameters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"azureTenantId": { "value": "" },
"azureAppID": {"value": ""},
"azureSecret": {"value": ""},
"bloodhoundInstanceDomain": { "value": "" },
"bloodhoundTokenId": { "value": "" },
"bloodhoundToken": { "value": "" }
}
}
103 changes: 103 additions & 0 deletions azure-deploy/createUiDefinition.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
{
"$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": "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]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
"validationMessage": "Must be a valid GUID"
}
},
{
"name": "azureAppID",
"type": "Microsoft.Common.TextBox",
"label": "Azure Application ID",
"defaultValue": "",
"toolTip": "Azure App ID",
"placeholder": "Azure App ID",
"constraints": {
"required": true,
"regex": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
"validationMessage": "Must be a valid GUID"
}
},
{
"name": "azureSecret",
"type": "Microsoft.Common.TextBox",
"label": "Azure Secret ID",
"defaultValue": "",
"toolTip": "Azure Secret ID",
"placeholder": "Azure Secret ID",
"constraints": {
"required": true,
"regex": "^.+$",
"validationMessage": "Token must be alphanumeric."
}
},
{
"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]([-a-zA-Z0-9]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$",
"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": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
"validationMessage": "Token ID must be a valid GUID."
}
},
{
"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": {
"azureTenantId": "[steps('azureHoundConfig').azureTenantId]",
"azureAppID": "[steps('azureHoundConfig').azureAppID]",
"azureSecret": "[steps('azureHoundConfig').azureSecret]",
"bloodhoundInstanceDomain": "[steps('azureHoundConfig').bloodhoundInstanceDomain]",
"bloodhoundTokenId": "[steps('azureHoundConfig').bloodhoundTokenId]",
"bloodhoundToken": "[steps('azureHoundConfig').bloodhoundToken]"
}
}
}
142 changes: 142 additions & 0 deletions azure-deploy/mainTemplate.bicep
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading