diff --git a/README.md b/README.md index 37ecf4c..57cb4cd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,59 @@ Terraform module for creation Azure <> ## Usage +## Requirements +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [azuredevops](#requirement\_azuredevops) | =0.11.0 | +| [azurerm](#requirement\_azurerm) | >= 3.40.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azuredevops](#provider\_azuredevops) | =0.11.0 | +| [azurerm](#provider\_azurerm) | >= 3.40.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azuredevops_serviceendpoint_azurerm.this](https://registry.terraform.io/providers/microsoft/azuredevops/0.11.0/docs/resources/serviceendpoint_azurerm) | resource | +| [azurerm_federated_identity_credential.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/federated_identity_credential) | resource | +| [azurerm_key_vault_access_policy.assigned_identity](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | +| [azurerm_role_assignment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | +| [azurerm_user_assigned_identity.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/user_assigned_identity) | resource | +| [azuredevops_project.this](https://registry.terraform.io/providers/microsoft/azuredevops/0.11.0/docs/data-sources/project) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [ado\_project\_name](#input\_ado\_project\_name) | ADO Project Name | `string` | n/a | yes | +| [ado\_workload\_identity\_federation\_enabled](#input\_ado\_workload\_identity\_federation\_enabled) | Workload Identity Federation enable | `bool` | `true` | no | +| [custom\_federated\_identity\_credential\_name](#input\_custom\_federated\_identity\_credential\_name) | Specifies the name of the Federated Identity Credential | `string` | `""` | no | +| [custom\_serviceendpoint\_name](#input\_custom\_serviceendpoint\_name) | Specifies the name of the ADO Service Connection | `string` | `""` | no | +| [key\_vault\_policy\_config](#input\_key\_vault\_policy\_config) | List of object with parameters to create Key Vault Access Policy |
list(object({
key_vault_name = string
key_vault_id = string
tenant_id = string
key_permissions = optional(list(string), ["Get", "List", "Encrypt", "Decrypt"])
secret_permissions = optional(list(string), ["Get", "List"])
}))
| `[]` | no | +| [location](#input\_location) | Azure Region | `string` | n/a | yes | +| [resource\_group](#input\_resource\_group) | The name of the resource group | `string` | n/a | yes | +| [role\_assignment\_scope](#input\_role\_assignment\_scope) | ADO Service Connection target Subscription Id | `string` | n/a | yes | +| [role\_assignments\_allowed](#input\_role\_assignments\_allowed) | This variable determines whether Service Principal used by Terraform can assign Roles to Azure resources | `bool` | `true` | no | +| [subscription\_id](#input\_subscription\_id) | ADO Service Connection target Subscription Id | `string` | n/a | yes | +| [tenant\_id](#input\_tenant\_id) | ADO Service Connection target Tenant Id | `string` | n/a | yes | +| [user\_assigned\_identity\_name](#input\_user\_assigned\_identity\_name) | Specifies the name of the User Assigned Identity | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [azurerm\_user\_assigned\_identity\_name](#output\_azurerm\_user\_assigned\_identity\_name) | Built name of single User Assigned Identity | +| [user\_assigned\_identity\_principal\_id](#output\_user\_assigned\_identity\_principal\_id) | Built principal id of single User Assigned Identity | ## License diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..0f1731b --- /dev/null +++ b/main.tf @@ -0,0 +1,58 @@ +locals { + key_vault_policy_config_mapped = { for object in var.key_vault_policy_config : object.key_vault_name => object } +} + +data "azuredevops_project" "this" { + name = var.ado_project_name +} + +resource "azurerm_user_assigned_identity" "this" { + name = var.user_assigned_identity_name + resource_group_name = var.resource_group + location = var.location +} + +resource "azuredevops_serviceendpoint_azurerm" "this" { + count = var.ado_workload_identity_federation_enabled ? 1 : 0 + + project_id = data.azuredevops_project.this.project_id + service_endpoint_name = coalesce(var.custom_serviceendpoint_name, "federated-(${var.subscription_id})-${var.user_assigned_identity_name}") + description = "Managed by Terraform" + service_endpoint_authentication_scheme = "WorkloadIdentityFederation" + credentials { + serviceprincipalid = azurerm_user_assigned_identity.this.client_id + } + azurerm_spn_tenantid = var.tenant_id + azurerm_subscription_id = var.subscription_id + azurerm_subscription_name = "Example Subscription Name" +} + +resource "azurerm_federated_identity_credential" "this" { + count = var.ado_workload_identity_federation_enabled ? 1 : 0 + + name = coalesce(var.custom_federated_identity_credential_name, "federated-${var.user_assigned_identity_name}") + resource_group_name = var.resource_group + parent_id = azurerm_user_assigned_identity.this.id + audience = ["api://AzureADTokenExchange"] + issuer = azuredevops_serviceendpoint_azurerm.this[0].workload_identity_federation_issuer + subject = azuredevops_serviceendpoint_azurerm.this[0].workload_identity_federation_subject +} + + +resource "azurerm_role_assignment" "this" { + count = alltrue([var.ado_workload_identity_federation_enabled, var.role_assignments_allowed]) ? 1 : 0 + + principal_id = azurerm_user_assigned_identity.this.principal_id + scope = var.role_assignment_scope + role_definition_name = "Reader" +} + +resource "azurerm_key_vault_access_policy" "assigned_identity" { + for_each = var.ado_workload_identity_federation_enabled ? local.key_vault_policy_config_mapped : {} + + object_id = azurerm_user_assigned_identity.this.principal_id + key_vault_id = each.value.key_vault_id + tenant_id = each.value.tenant_id + key_permissions = each.value.key_permissions + secret_permissions = each.value.secret_permissions +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..a4aedf0 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,9 @@ +output "azurerm_user_assigned_identity_name" { + description = "Built name of single User Assigned Identity" + value = try(azurerm_user_assigned_identity.this.name, null) +} + +output "user_assigned_identity_principal_id" { + description = "Built principal id of single User Assigned Identity" + value = try(azurerm_user_assigned_identity.this.principal_id, null) +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..d402da4 --- /dev/null +++ b/variables.tf @@ -0,0 +1,68 @@ +variable "location" { + type = string + description = "Azure Region" +} + +variable "resource_group" { + description = "The name of the resource group" + type = string +} + +variable "ado_workload_identity_federation_enabled" { + description = "Workload Identity Federation enable" + default = true +} + +variable "role_assignments_allowed" { + description = "This variable determines whether Service Principal used by Terraform can assign Roles to Azure resources" + default = true +} + +variable "user_assigned_identity_name" { + type = string + description = "Specifies the name of the User Assigned Identity" +} + +variable "custom_serviceendpoint_name" { + type = string + description = "Specifies the name of the ADO Service Connection" + default = "" +} + +variable "custom_federated_identity_credential_name" { + type = string + description = "Specifies the name of the Federated Identity Credential" + default = "" +} + +variable "ado_project_name" { + type = string + description = "ADO Project Name" +} + +variable "role_assignment_scope" { + type = string + description = "ADO Service Connection target Subscription Id" +} + +variable "subscription_id" { + type = string + description = "ADO Service Connection target Subscription Id" +} + +variable "tenant_id" { + type = string + description = "ADO Service Connection target Tenant Id" +} + +variable "key_vault_policy_config" { + description = "List of object with parameters to create Key Vault Access Policy" + type = list(object({ + key_vault_name = string + key_vault_id = string + tenant_id = string + key_permissions = optional(list(string), ["Get", "List", "Encrypt", "Decrypt"]) + secret_permissions = optional(list(string), ["Get", "List"]) + })) + default = [] +} diff --git a/versions.tf b/versions.tf new file mode 100644 index 0000000..111bee7 --- /dev/null +++ b/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.40.0" + } + azuredevops = { + source = "microsoft/azuredevops" + version = "=0.11.0" + } + } +}