Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .gitleaksignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Gitleaks allowlist for false positives in git history
# Gitleaks allowlist for false positives in git history and working tree
#
# ARM template contentVersion in WAC policy JSON (matches IPv4 rule)
infrastructure/modules/arc-infra/policies/deploy-wac-extension-arc-windows.json:ipv4:56
#
# These are DICOM UIDs that match the IPv4 pattern in old commits.
# Current files use inline `# gitleaks:allow` comments instead.
Expand Down
17 changes: 17 additions & 0 deletions infrastructure/modules/arc-infra/arc.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ data "azuread_service_principal" "arc_onboarding" {
display_name = "spn-azure-arc-onboarding-screening-${var.env_config}"
}

# Look up the Entra ID group that manages this environment
data "azuread_group" "screening" {
count = var.enable_arc_servers ? 1 : 0
display_name = "screening_${var.app_short_name}_${var.env_config}"
}

# Assign "Azure Connected Machine Onboarding" role to allow Arc server enrollment
module "arc_onboarding_role" {
count = var.enable_arc_servers ? 1 : 0
Expand All @@ -22,3 +28,14 @@ module "arc_onboarding_role" {
role_definition_name = "Azure Connected Machine Onboarding"
principal_id = data.azuread_service_principal.arc_onboarding[0].object_id
}

# Assign "Windows Admin Center Administrator Login" to allow the group to connect via WAC
module "arc_wac_admin_login_role" {
count = var.enable_arc_servers ? 1 : 0

source = "../dtos-devops-templates/infrastructure/modules/rbac-assignment"

scope = data.azurerm_resource_group.arc_enabled_servers[0].id
role_definition_name = "Windows Admin Center Administrator Login"
principal_id = data.azuread_group.screening[0].object_id
}
72 changes: 66 additions & 6 deletions infrastructure/modules/arc-infra/azure_monitor.tf
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,83 @@ resource "azurerm_resource_group_policy_assignment" "dcr_association" {
})
}

# ---------------------------------------------------------------------------
# Custom Policy Definition + Assignment for Windows Admin Center
# Replicates the built-in "Configure Windows Arc-enabled machines to enable
# Windows Admin Center Extension" (0e4b8929), which is not available in this tenant.
# Policy rule is defined in policies/deploy-wac-extension-arc-windows.json.
# ---------------------------------------------------------------------------
resource "azurerm_policy_definition" "wac" {
count = var.enable_arc_servers ? 1 : 0

name = "deploy-wac-extension-arc-windows-${var.env_config}"
policy_type = "Custom"
mode = "Indexed"
display_name = "Configure Windows Arc-enabled machines to enable Windows Admin Center Extension"
description = "Deploys the AdminCenter extension to Windows Arc-enabled machines to enable browser-based management via the Azure portal."

parameters = jsonencode({
effect = {
type = "String"
metadata = { displayName = "Effect", description = "Enable or disable the execution of the policy" }
allowedValues = ["DeployIfNotExists", "Disabled"]
defaultValue = "DeployIfNotExists"
}
port = {
type = "Integer"
metadata = { displayName = "Port", description = "The port number to use for Windows Admin Center" }
defaultValue = 6516
}
proxyURL = {
type = "String"
metadata = { displayName = "Proxy URL", description = "Optional proxy URL for Windows Admin Center" }
defaultValue = ""
}
})

policy_rule = file("${path.module}/policies/deploy-wac-extension-arc-windows.json")
}

resource "azurerm_resource_group_policy_assignment" "wac" {
count = var.enable_arc_servers ? 1 : 0

name = "wac-arc-${var.env_config}"
resource_group_id = data.azurerm_resource_group.arc_enabled_servers[0].id
policy_definition_id = azurerm_policy_definition.wac[0].id
location = var.region

identity {
type = "UserAssigned"
identity_ids = [module.arc_monitor_policy_identity[0].id]
}
}

# Remediation tasks — re-evaluate compliance and deploy AMA/DCR association on
# any non-compliant Arc machines each time Terraform applies. Combined with the
# DeployIfNotExists effect, this makes the monitoring setup self-healing.
resource "azurerm_resource_group_policy_remediation" "ama_install" {
count = var.enable_arc_servers ? 1 : 0

name = "remediation-ama-install-${var.env_config}"
resource_group_id = data.azurerm_resource_group.arc_enabled_servers[0].id
policy_assignment_id = azurerm_resource_group_policy_assignment.ama_install[0].id
name = "remediation-ama-install-${var.env_config}"
resource_group_id = data.azurerm_resource_group.arc_enabled_servers[0].id
policy_assignment_id = azurerm_resource_group_policy_assignment.ama_install[0].id
resource_discovery_mode = "ReEvaluateCompliance"
}

resource "azurerm_resource_group_policy_remediation" "dcr_association" {
count = var.enable_arc_servers ? 1 : 0

name = "remediation-dcr-assoc-${var.env_config}"
resource_group_id = data.azurerm_resource_group.arc_enabled_servers[0].id
policy_assignment_id = azurerm_resource_group_policy_assignment.dcr_association[0].id
name = "remediation-dcr-assoc-${var.env_config}"
resource_group_id = data.azurerm_resource_group.arc_enabled_servers[0].id
policy_assignment_id = azurerm_resource_group_policy_assignment.dcr_association[0].id
resource_discovery_mode = "ReEvaluateCompliance"
}

resource "azurerm_resource_group_policy_remediation" "wac" {
count = var.enable_arc_servers ? 1 : 0

name = "remediation-wac-${var.env_config}"
resource_group_id = data.azurerm_resource_group.arc_enabled_servers[0].id
policy_assignment_id = azurerm_resource_group_policy_assignment.wac[0].id
resource_discovery_mode = "ReEvaluateCompliance"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.HybridCompute/machines"
},
{
"field": "Microsoft.HybridCompute/machines/osName",
"equals": "Windows"
}
]
},
"then": {
"effect": "[parameters('effect')]",
"details": {
"type": "Microsoft.HybridCompute/machines/extensions",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302"
],
"existenceCondition": {
"allOf": [
{
"field": "Microsoft.HybridCompute/machines/extensions/type",
"equals": "AdminCenter"
},
{
"field": "Microsoft.HybridCompute/machines/extensions/publisher",
"equals": "Microsoft.AdminCenter"
},
{
"field": "Microsoft.HybridCompute/machines/extensions/provisioningState",
"equals": "Succeeded"
}
]
},
"deployment": {
"properties": {
"mode": "incremental",
"parameters": {
"vmName": {
"value": "[field('name')]"
},
"location": {
"value": "[field('location')]"
},
"proxyURL": {
"value": "[parameters('proxyURL')]"
},
"port": {
"value": "[parameters('port')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vmName": {
"type": "string"
},
"location": {
"type": "string"
},
"proxyURL": {
"type": "string"
},
"port": {
"type": "int"
}
},
"variables": {
"extensionName": "AdminCenter",
"extensionPublisher": "Microsoft.AdminCenter",
"extensionType": "AdminCenter"
},
"resources": [
{
"condition": "[empty(parameters('proxyURL'))]",
"name": "[concat(parameters('vmName'), '/', variables('extensionName'))]",
"type": "Microsoft.HybridCompute/machines/extensions",
"location": "[parameters('location')]",
"apiVersion": "2019-12-12",
"properties": {
"publisher": "[variables('extensionPublisher')]",
"type": "[variables('extensionType')]",
"autoUpgradeMinorVersion": true,
"enableAutomaticUpgrade": true,
"settings": {
"port": "[parameters('port')]",
"proxy": {
"mode": "none"
}
}
}
},
{
"condition": "[not(empty(parameters('proxyURL')))]",
"name": "[concat(parameters('vmName'), '/', variables('extensionName'))]",
"type": "Microsoft.HybridCompute/machines/extensions",
"location": "[parameters('location')]",
"apiVersion": "2019-12-12",
"properties": {
"publisher": "[variables('extensionPublisher')]",
"type": "[variables('extensionType')]",
"autoUpgradeMinorVersion": true,
"enableAutomaticUpgrade": true,
"settings": {
"port": "[parameters('port')]",
"proxy": {
"address": "[toLower(parameters('proxyURL'))]",
"mode": "application"
}
}
}
}
]
}
}
}
}
}
}
9 changes: 5 additions & 4 deletions infrastructure/terraform/resource_group_init/core.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var roleID = {
logAnalyticsContributor: '92aaf0da-9dab-42b6-94a3-d43ce8d16293'
resourcePolicyContributor: '36243c78-bf99-498c-9df9-86d9f8d28608'
virtualMachineAdministratorLogin: '1c0163c0-47e6-4577-8991-ea5c82e286e4'
windowsAdminCenterAdministratorLogin: 'a6333a3e-0164-44c3-b281-7a577aff287f'
}

// Define role assignments for managed identity
Expand All @@ -57,10 +58,10 @@ var miRoleAssignments = [
{
roleName: 'rbacAdmin'
roleId: roleID.rbacAdmin
description: 'RBAC Administrator. Restricted to only assign/remove: Storage Blob Data Contributor, Storage Queue Data Contributor, Azure Connected Machine Onboarding, Azure Connected Machine Resource Administrator, Log Analytics Contributor, and Virtual Machine Administrator Login.'
description: 'RBAC Administrator. Restricted to only assign/remove: Storage Blob Data Contributor, Storage Queue Data Contributor, Azure Connected Machine Onboarding, Azure Connected Machine Resource Administrator, Log Analytics Contributor, Virtual Machine Administrator Login, and Windows Admin Center Administrator Login.'
// Delegated RBAC: This condition restricts the RBAC Administrator to only manage specific roles.
// This is a security best practice that prevents the identity from granting itself or others sensitive roles like 'Owner' or 'User Access Administrator'.
condition: '((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/write\'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}})) AND ((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/delete\'})) OR (@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}}))'
condition: '((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/write\'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}, ${roleID.windowsAdminCenterAdministratorLogin}})) AND ((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/delete\'})) OR (@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}, ${roleID.windowsAdminCenterAdministratorLogin}}))'
conditionVersion: '2.0'
}
]
Expand All @@ -80,10 +81,10 @@ var groupRoleAssignments = [
{
roleName: 'rbacAdmin'
roleId: roleID.rbacAdmin
description: 'RBAC Administrator. Restricted to only assign/remove: Storage Blob Data Contributor, Storage Queue Data Contributor, Azure Connected Machine Onboarding, Azure Connected Machine Resource Administrator, Log Analytics Contributor, and Virtual Machine Administrator Login.'
description: 'RBAC Administrator. Restricted to only assign/remove: Storage Blob Data Contributor, Storage Queue Data Contributor, Azure Connected Machine Onboarding, Azure Connected Machine Resource Administrator, Log Analytics Contributor, Virtual Machine Administrator Login, and Windows Admin Center Administrator Login.'
// Delegated RBAC: This condition restricts the RBAC Administrator to only manage specific roles.
// This is a security best practice that prevents the identity from granting itself or others sensitive roles like 'Owner' or 'User Access Administrator'.
condition: '((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/write\'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}})) AND ((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/delete\'})) OR (@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}}))'
condition: '((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/write\'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}, ${roleID.windowsAdminCenterAdministratorLogin}})) AND ((!(ActionMatches{\'Microsoft.Authorization/roleAssignments/delete\'})) OR (@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${roleID.storageBlobDataContributor}, ${roleID.storageQueueDataContributor}, ${roleID.AzureConnectedMachineOnboarding}, ${roleID.AzureConnectedMachineResourceAdministrator}, ${roleID.logAnalyticsContributor}, ${roleID.virtualMachineAdministratorLogin}, ${roleID.windowsAdminCenterAdministratorLogin}}))'
conditionVersion: '2.0'
}
]
Expand Down
Loading