From 05150201c4aadfde485fb21deb43eef123f65c07 Mon Sep 17 00:00:00 2001 From: Henry Dettmer Date: Mon, 4 Mar 2024 09:35:00 +0100 Subject: [PATCH] feat: workload identity federation Allow metering and replicator service principals to use workload identity federation instead of passwords. --- CHANGELOG.md | 2 ++ main.tf | 12 ++++++++ .../module.tf | 19 +++++++++++- .../outputs.tf | 4 +-- .../variables.tf | 11 +++++++ .../auth.tf | 30 +++++++++++++++++++ .../module.tf | 15 +--------- .../outputs.tf | 4 +-- .../variables.tf | 11 +++++++ outputs.tf | 1 - variables.tf | 12 ++++++++ 11 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 modules/meshcloud-replicator-service-principal/auth.tf diff --git a/CHANGELOG.md b/CHANGELOG.md index 64e4801..3a53b47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added privilege escalation prevention policy for replicator +- New configuration option `workload_identity_federation` to create federated credentials. Unset by default. +- New configuration option `create_passwords` to enable/disable creation of password credentials. Defaults to `true`. ## [v0.2.0] diff --git a/main.tf b/main.tf index ceed66e..40d33c2 100644 --- a/main.tf +++ b/main.tf @@ -47,6 +47,12 @@ module "replicator_service_principal" { additional_required_resource_accesses = var.additional_required_resource_accesses additional_permissions = var.additional_permissions + + create_password = var.create_passwords + workload_identity_federation = var.workload_identity_federation == null ? null : { + issuer = var.workload_identity_federation.issuer, + subject = var.workload_identity_federation.replicator_subject + } } module "metering_service_principal" { @@ -55,6 +61,12 @@ module "metering_service_principal" { service_principal_name = var.metering_service_principal_name assignment_scopes = local.metering_assignment_scopes + + create_password = var.create_passwords + workload_identity_federation = var.workload_identity_federation == null ? null : { + issuer = var.workload_identity_federation.issuer, + subject = var.workload_identity_federation.kraken_subject + } } module "sso_service_principal" { diff --git a/modules/meshcloud-metering-service-principal/module.tf b/modules/meshcloud-metering-service-principal/module.tf index fcf499e..dcb16b5 100644 --- a/modules/meshcloud-metering-service-principal/module.tf +++ b/modules/meshcloud-metering-service-principal/module.tf @@ -59,13 +59,17 @@ resource "azuread_service_principal" "meshcloud_metering" { // Create a password for the Enterprise application //--------------------------------------------------------------------------- resource "time_rotating" "replicator_secret_rotation" { + count = var.create_password ? 1 : 0 + rotation_days = 365 } resource "azuread_application_password" "application_pw" { + count = var.create_password ? 1 : 0 + application_id = azuread_application.meshcloud_metering.id rotate_when_changed = { - rotation = time_rotating.replicator_secret_rotation.id + rotation = time_rotating.replicator_secret_rotation[0].id } } @@ -83,3 +87,16 @@ moved { from = azuread_service_principal.meshcloud_kraken to = azuread_service_principal.meshcloud_metering } + +//--------------------------------------------------------------------------- +// Create federated identity credentials +//--------------------------------------------------------------------------- +resource "azuread_application_federated_identity_credential" "meshcloud_replicator" { + count = var.workload_identity_federation == null ? 0 : 1 + + application_id = azuread_application.meshcloud_metering.id + display_name = var.service_principal_name + audiences = ["api://AzureADTokenExchange"] + issuer = var.workload_identity_federation.issuer + subject = var.workload_identity_federation.subject +} diff --git a/modules/meshcloud-metering-service-principal/outputs.tf b/modules/meshcloud-metering-service-principal/outputs.tf index 1ae9bd5..156d32b 100644 --- a/modules/meshcloud-metering-service-principal/outputs.tf +++ b/modules/meshcloud-metering-service-principal/outputs.tf @@ -3,12 +3,12 @@ output "credentials" { value = { Enterprise_Application_Object_ID = azuread_service_principal.meshcloud_metering.id Application_Client_ID = azuread_application.meshcloud_metering.client_id - Client_Secret = "Execute `terraform output metering_client_secret` to see the password" + Client_Secret = var.workload_identity_federation == null ? "Execute `terraform output metering_service_principal_password` to see the password" : "Not applicable when using workload identity federation" } } output "application_client_secret" { description = "Client Secret Of the Application." - value = azuread_application_password.application_pw.value + value = var.workload_identity_federation == null ? azuread_application_password.application_pw[0].value : null sensitive = true } diff --git a/modules/meshcloud-metering-service-principal/variables.tf b/modules/meshcloud-metering-service-principal/variables.tf index bfd20c0..b84dfd2 100644 --- a/modules/meshcloud-metering-service-principal/variables.tf +++ b/modules/meshcloud-metering-service-principal/variables.tf @@ -7,3 +7,14 @@ variable "assignment_scopes" { type = list(string) description = "The scopes to which Service Principal permissions should be assigned to. Usually this is the management group id of form `/providers/Microsoft.Management/managementGroups/` that sits atop the subscriptions." } + +variable "create_password" { + type = bool + description = "Create a password for the enterprise application." +} + +variable "workload_identity_federation" { + default = null + description = "Enable workload identity federation instead of using a password by providing these additional settings. Usually you should receive the required settings when attempting to configure a platform with workload identity federation in meshStack." + type = object({ issuer = string, subject = string }) +} diff --git a/modules/meshcloud-replicator-service-principal/auth.tf b/modules/meshcloud-replicator-service-principal/auth.tf new file mode 100644 index 0000000..92e0a71 --- /dev/null +++ b/modules/meshcloud-replicator-service-principal/auth.tf @@ -0,0 +1,30 @@ +//--------------------------------------------------------------------------- +// Create new client secret and associate it with the application +//--------------------------------------------------------------------------- +resource "time_rotating" "replicator_secret_rotation" { + count = var.create_password ? 1 : 0 + + rotation_days = 365 +} + +resource "azuread_application_password" "application_pw" { + count = var.create_password ? 1 : 0 + + application_id = azuread_application.meshcloud_replicator.id + rotate_when_changed = { + rotation = time_rotating.replicator_secret_rotation[0].id + } +} + +//--------------------------------------------------------------------------- +// Create federated identity credentials +//--------------------------------------------------------------------------- +resource "azuread_application_federated_identity_credential" "meshcloud_replicator" { + count = var.workload_identity_federation == null ? 0 : 1 + + application_id = azuread_application.meshcloud_replicator.id + display_name = var.service_principal_name + audiences = ["api://AzureADTokenExchange"] + issuer = var.workload_identity_federation.issuer + subject = var.workload_identity_federation.subject +} diff --git a/modules/meshcloud-replicator-service-principal/module.tf b/modules/meshcloud-replicator-service-principal/module.tf index cbe4121..4c044d6 100644 --- a/modules/meshcloud-replicator-service-principal/module.tf +++ b/modules/meshcloud-replicator-service-principal/module.tf @@ -132,20 +132,7 @@ resource "azuread_application" "meshcloud_replicator" { } //--------------------------------------------------------------------------- -// Create new client secret and associate it with the previous application -//--------------------------------------------------------------------------- -resource "time_rotating" "replicator_secret_rotation" { - rotation_days = 365 -} -resource "azuread_application_password" "application_pw" { - application_id = azuread_application.meshcloud_replicator.id - rotate_when_changed = { - rotation = time_rotating.replicator_secret_rotation.id - } -} - -//--------------------------------------------------------------------------- -// Create new Enterprise Application and associate it with the previous application +// Create new Enterprise Application and associate it with the application //--------------------------------------------------------------------------- resource "azuread_service_principal" "meshcloud_replicator" { client_id = azuread_application.meshcloud_replicator.client_id diff --git a/modules/meshcloud-replicator-service-principal/outputs.tf b/modules/meshcloud-replicator-service-principal/outputs.tf index 665afdb..066d254 100644 --- a/modules/meshcloud-replicator-service-principal/outputs.tf +++ b/modules/meshcloud-replicator-service-principal/outputs.tf @@ -3,12 +3,12 @@ output "credentials" { value = { Enterprise_Application_Object_ID = azuread_service_principal.meshcloud_replicator.id Application_Client_ID = azuread_application.meshcloud_replicator.client_id - Client_Secret = "Execute `terraform output replicator_client_secret` to see the password" + Client_Secret = var.workload_identity_federation == null ? "Execute `terraform output replicator_service_principal_password` to see the password" : "Not applicable when using workload identity federation" } } output "application_client_secret" { description = "Client Secret Of the Application." - value = azuread_application_password.application_pw.value + value = var.workload_identity_federation == null ? azuread_application_password.application_pw[0].value : null sensitive = true } diff --git a/modules/meshcloud-replicator-service-principal/variables.tf b/modules/meshcloud-replicator-service-principal/variables.tf index b347097..7428816 100644 --- a/modules/meshcloud-replicator-service-principal/variables.tf +++ b/modules/meshcloud-replicator-service-principal/variables.tf @@ -30,3 +30,14 @@ variable "replicator_rg_enabled" { default = false description = "Whether the created replicator Service Principal should be usable for Azure Resource Group based replication." } + +variable "create_password" { + type = bool + description = "Create a password for the enterprise application." +} + +variable "workload_identity_federation" { + default = null + description = "Enable workload identity federation instead of using a password by providing these additional settings. Usually you should receive the required settings when attempting to configure a platform with workload identity federation in meshStack." + type = object({ issuer = string, subject = string }) +} diff --git a/outputs.tf b/outputs.tf index 77faafb..5331ed5 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,4 +1,3 @@ - output "replicator_service_principal" { description = "Replicator Service Principal." value = length(module.replicator_service_principal) > 0 ? module.replicator_service_principal[0].credentials : null diff --git a/variables.tf b/variables.tf index 9f423cd..97c5860 100644 --- a/variables.tf +++ b/variables.tf @@ -80,3 +80,15 @@ variable "additional_permissions" { default = [] description = "Additional Subscription-Level Permissions the Service Principal needs." } + +variable "create_passwords" { + type = bool + default = true + description = "Create passwords for service principals." +} + +variable "workload_identity_federation" { + default = null + description = "Enable workload identity federation instead of using a password by providing these additional settings. Usually you should receive the required settings when attempting to configure a platform with workload identity federation in meshStack." + type = object({ issuer = string, replicator_subject = string, kraken_subject = string }) +}