Skip to content

Commit 45193ea

Browse files
committed
chore: adding multiple mca scopes and scp's
1 parent 94cb8c9 commit 45193ea

File tree

7 files changed

+70
-50
lines changed

7 files changed

+70
-50
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ Before opening a Pull Request, please do the following:
271271
| <a name="input_can_cancel_subscriptions_in_scopes"></a> [can\_cancel\_subscriptions\_in\_scopes](#input\_can\_cancel\_subscriptions\_in\_scopes) | The scopes to which Service Principal cancel subscription permission is assigned to. List of management group id of form `/providers/Microsoft.Management/managementGroups/<mgmtGroupId>/`. | `list(string)` | `[]` | no |
272272
| <a name="input_can_delete_rgs_in_scopes"></a> [can\_delete\_rgs\_in\_scopes](#input\_can\_delete\_rgs\_in\_scopes) | The scopes to which Service Principal delete resource group permission is assigned to. Only relevant when `replicator_rg_enabled`. List of subscription scopes of form `/subscriptions/<subscriptionId>`. | `list(string)` | `[]` | no |
273273
| <a name="input_create_passwords"></a> [create\_passwords](#input\_create\_passwords) | Create passwords for service principals. | `bool` | `true` | no |
274-
| <a name="input_mca"></a> [mca](#input\_mca) | n/a | <pre>object({<br> service_principal_names = list(string)<br> billing_account_name = string<br> billing_profile_name = string<br> invoice_section_name = string<br> })</pre> | `null` | no |
274+
| <a name="input_mca"></a> [mca](#input\_mca) | n/a | <pre>object({<br> service_principals = map(object({<br> billing_scopes = list(object({<br> billing_account_name = string<br> billing_profile_name = string<br> invoice_section_name = string<br> }))<br> }))<br> })</pre> | `null` | no |
275275
| <a name="input_metering_assignment_scopes"></a> [metering\_assignment\_scopes](#input\_metering\_assignment\_scopes) | Names or UUIDs of the Management Groups that kraken should collect costs for. | `list(string)` | n/a | yes |
276276
| <a name="input_metering_enabled"></a> [metering\_enabled](#input\_metering\_enabled) | Whether to create Metering Service Principal or not. | `bool` | `true` | no |
277277
| <a name="input_metering_service_principal_name"></a> [metering\_service\_principal\_name](#input\_metering\_service\_principal\_name) | Service principal for collecting cost data. Kraken ist the name of the meshStack component. Name must be unique per Entra ID. | `string` | `"kraken"` | no |
@@ -293,7 +293,7 @@ Before opening a Pull Request, please do the following:
293293
|------|-------------|
294294
| <a name="output_azure_ad_tenant_id"></a> [azure\_ad\_tenant\_id](#output\_azure\_ad\_tenant\_id) | The Azure AD tenant id. |
295295
| <a name="output_documentation"></a> [documentation](#output\_documentation) | Complete module documentation in markdown format |
296-
| <a name="output_mca_service_billing_scope"></a> [mca\_service\_billing\_scope](#output\_mca\_service\_billing\_scope) | n/a |
296+
| <a name="output_mca_service_billing_scopes"></a> [mca\_service\_billing\_scopes](#output\_mca\_service\_billing\_scopes) | n/a |
297297
| <a name="output_mca_service_principal"></a> [mca\_service\_principal](#output\_mca\_service\_principal) | MCA Service Principal. |
298298
| <a name="output_mca_service_principal_password"></a> [mca\_service\_principal\_password](#output\_mca\_service\_principal\_password) | Password for MCA Service Principal. |
299299
| <a name="output_metering_service_principal"></a> [metering\_service\_principal](#output\_metering\_service\_principal) | Metering Service Principal. |

main.tf

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,7 @@ module "mca_service_principal" {
8080
count = var.mca != null ? 1 : 0
8181
source = "./modules/meshcloud-mca-service-principal"
8282

83-
service_principal_names = var.mca.service_principal_names
84-
85-
billing_account_name = var.mca.billing_account_name
86-
billing_profile_name = var.mca.billing_profile_name
87-
invoice_section_name = var.mca.invoice_section_name
83+
service_principals = var.mca.service_principals
8884

8985
create_password = var.create_passwords
9086
workload_identity_federation = var.workload_identity_federation == null ? null : {

modules/meshcloud-mca-service-principal/module.tf

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,60 +20,84 @@ terraform {
2020
}
2121
}
2222

23+
locals {
24+
# Flatten all billing scopes from all service principals with unique keys
25+
all_billing_scopes = merge([
26+
for sp_name, sp_config in var.service_principals : {
27+
for idx, scope in sp_config.billing_scopes :
28+
"${sp_name}-${idx}" => scope
29+
}
30+
]...)
31+
}
2332

2433
data "azurerm_billing_mca_account_scope" "mca" {
25-
billing_account_name = var.billing_account_name
26-
billing_profile_name = var.billing_profile_name
27-
invoice_section_name = var.invoice_section_name
34+
for_each = local.all_billing_scopes
35+
36+
billing_account_name = each.value.billing_account_name
37+
billing_profile_name = each.value.billing_profile_name
38+
invoice_section_name = each.value.invoice_section_name
2839
}
2940

3041
resource "azuread_application" "mca" {
31-
for_each = toset(var.service_principal_names)
42+
for_each = var.service_principals
3243
display_name = each.key
3344
owners = var.application_owners
3445
}
3546

3647
resource "azuread_service_principal" "mca" {
37-
for_each = toset(var.service_principal_names)
48+
for_each = var.service_principals
3849
client_id = azuread_application.mca[each.key].client_id
3950
owners = var.application_owners
4051
}
4152

4253
data "azapi_resource_list" "billing_role_definitions" {
54+
for_each = data.azurerm_billing_mca_account_scope.mca
4355
type = "Microsoft.Billing/billingAccounts/billingProfiles/invoiceSections/billingRoleDefinitions@2020-05-01"
44-
parent_id = data.azurerm_billing_mca_account_scope.mca.id
56+
parent_id = each.value.id
4557
response_export_values = ["*"]
4658
}
4759

4860
locals {
49-
azure_subscription_creator_role_id = jsondecode(
50-
data.azapi_resource_list.billing_role_definitions.output).value[
51-
index(jsondecode(data.azapi_resource_list.billing_role_definitions.output).value[*].properties.roleName, "Azure subscription creator")
52-
].id
61+
azure_subscription_creator_role_ids = {
62+
for key, role_def in data.azapi_resource_list.billing_role_definitions : key => role_def.output.value[
63+
index(role_def.output.value[*].properties.roleName, "Azure subscription creator")
64+
].id
65+
}
5366
}
5467

5568
resource "azapi_resource_action" "add_role_assignment_subscription_creator" {
56-
for_each = toset(var.service_principal_names)
69+
for_each = {
70+
for item in flatten([
71+
for sp_name, sp_config in var.service_principals : [
72+
for idx, scope in sp_config.billing_scopes : {
73+
key = "${sp_name}-${idx}"
74+
sp_name = sp_name
75+
scope_id = data.azurerm_billing_mca_account_scope.mca["${sp_name}-${idx}"].id
76+
scope_key = "${sp_name}-${idx}"
77+
}
78+
]
79+
]) : item.key => item
80+
}
5781

5882
type = "Microsoft.Billing/billingAccounts/billingProfiles/invoiceSections@2019-10-01-preview"
59-
resource_id = data.azurerm_billing_mca_account_scope.mca.id
83+
resource_id = each.value.scope_id
6084
action = "createBillingRoleAssignment"
6185
method = "POST"
6286
when = "apply"
6387
response_export_values = ["*"]
64-
body = jsonencode({
88+
body = {
6589
properties = {
66-
principalId = azuread_service_principal.mca[each.key].object_id
67-
roleDefinitionId = local.azure_subscription_creator_role_id
90+
principalId = azuread_service_principal.mca[each.value.sp_name].object_id
91+
roleDefinitionId = local.azure_subscription_creator_role_ids[each.value.scope_key]
6892
}
69-
})
93+
}
7094
}
7195

7296
resource "azapi_resource_action" "remove_role_assignment_subscription_creator" {
73-
for_each = toset(var.service_principal_names)
97+
for_each = azapi_resource_action.add_role_assignment_subscription_creator
7498

7599
type = "Microsoft.Billing/billingAccounts/billingProfiles/invoiceSections/billingRoleAssignments@2019-10-01-preview"
76-
resource_id = jsondecode(azapi_resource_action.add_role_assignment_subscription_creator[each.key].output).id
100+
resource_id = each.value.output.id
77101
method = "DELETE"
78102
when = "destroy"
79103
}
@@ -88,7 +112,7 @@ resource "time_rotating" "mca_secret_rotation" {
88112
}
89113

90114
resource "azuread_application_password" "mca" {
91-
for_each = var.create_password ? toset(var.service_principal_names) : toset([])
115+
for_each = var.create_password ? var.service_principals : {}
92116

93117
application_id = azuread_application.mca[each.key].id
94118
rotate_when_changed = {
@@ -105,7 +129,7 @@ locals {
105129
var.workload_identity_federation.subjects != null
106130
? var.workload_identity_federation.subjects
107131
: var.workload_identity_federation.subject != null
108-
? { for name in var.service_principal_names : name => var.workload_identity_federation.subject }
132+
? { for name in keys(var.service_principals) : name => var.workload_identity_federation.subject }
109133
: {}
110134
)
111135
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
output "billing_scope" {
2-
value = data.azurerm_billing_mca_account_scope.mca.id
1+
output "billing_scopes" {
2+
value = [for scope in data.azurerm_billing_mca_account_scope.mca : scope.id]
33
}
44

55
output "credentials" {
66
description = "Service Principal application id and object id"
77
value = {
8-
for name in var.service_principal_names : name => {
8+
for name in keys(var.service_principals) : name => {
99
Enterprise_Application_Object_ID = azuread_service_principal.mca[name].object_id
1010
Application_Client_ID = azuread_application.mca[name].client_id
1111
Client_Secret = var.create_password ? "Execute `terraform output mca_service_principal_password` to see the password" : "No password was created"
@@ -15,6 +15,6 @@ output "credentials" {
1515

1616
output "application_client_secret" {
1717
description = "Client Secret Of the Application."
18-
value = var.create_password ? { for name in var.service_principal_names : name => azuread_application_password.mca[name].value } : {}
18+
value = var.create_password ? { for name in keys(var.service_principals) : name => azuread_application_password.mca[name].value } : {}
1919
sensitive = true
2020
}

modules/meshcloud-mca-service-principal/variables.tf

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
variable "service_principal_names" {
2-
type = list(string)
3-
}
4-
5-
variable "billing_account_name" {
6-
type = string
7-
}
81

9-
variable "billing_profile_name" {
10-
type = string
11-
}
122

13-
variable "invoice_section_name" {
14-
type = string
3+
variable "service_principals" {
4+
type = map(object({
5+
billing_scopes = list(object({
6+
billing_account_name = string
7+
billing_profile_name = string
8+
invoice_section_name = string
9+
}))
10+
}))
11+
description = "Map of service principal names to their respective billing scopes"
1512
}
1613

1714
variable "application_owners" {

outputs.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ output "mca_service_principal_password" {
2020
sensitive = true
2121
}
2222

23-
output "mca_service_billing_scope" {
24-
value = length(module.mca_service_principal) > 0 ? module.mca_service_principal[0].billing_scope : null
23+
output "mca_service_billing_scopes" {
24+
value = length(module.mca_service_principal) > 0 ? module.mca_service_principal[0].billing_scopes : null
2525
}
2626

2727
output "metering_service_principal" {
@@ -153,7 +153,7 @@ The following outputs are available after deployment:
153153
| replicator_service_principal_password | Replicator service principal password | Yes | ${length(module.replicator_service_principal) > 0 ? "" : ""} |
154154
| mca_service_principal | MCA service principal credentials | No | ${length(module.mca_service_principal) > 0 ? "" : ""} |
155155
| mca_service_principal_password | MCA service principal password | Yes | ${length(module.mca_service_principal) > 0 ? "" : ""} |
156-
| mca_service_billing_scope | MCA billing scope | No | ${length(module.mca_service_principal) > 0 ? "" : ""} |
156+
| mca_service_billing_scopes | MCA billing scopes | No | ${length(module.mca_service_principal) > 0 ? "" : ""} |
157157
| metering_service_principal | Metering service principal credentials | No | ${length(module.metering_service_principal) > 0 ? "" : ""} |
158158
| metering_service_principal_password | Metering service principal password | Yes | ${length(module.metering_service_principal) > 0 ? "" : ""} |
159159
| sso_service_principal_client_id | SSO service principal client ID | No | ${length(module.sso_service_principal) > 0 ? "" : ""} |

variables.tf

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,13 @@ variable "workload_identity_federation" {
141141

142142
variable "mca" {
143143
type = object({
144-
service_principal_names = list(string)
145-
billing_account_name = string
146-
billing_profile_name = string
147-
invoice_section_name = string
144+
service_principals = map(object({
145+
billing_scopes = list(object({
146+
billing_account_name = string
147+
billing_profile_name = string
148+
invoice_section_name = string
149+
}))
150+
}))
148151
})
149152
default = null
150153
}

0 commit comments

Comments
 (0)