Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for azure AD groups #155 #375

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ The following requirements are needed by this module:

The following Modules are called:

### <a name="module_aadgroup"></a> [aadgroup](#module\_aadgroup)

Source: ./modules/aadgroup

Version:

### <a name="module_budget"></a> [budget](#module\_budget)

Source: ./modules/budget
Expand Down Expand Up @@ -213,6 +219,69 @@ Type: `string`

The following input variables are optional (have default values):

### <a name="input_aad_groups"></a> [aad\_groups](#input\_aad\_groups)

Description: A map defining the configuration for Entra ID (AAD) groups.

- `name` - The display name of the group.

**Optional Parameters:**

- `administrative_unit_ids` - (optional) A list of object IDs of administrative units for group membership.
- `assignable_to_role` - (optional) Whether the group can be assigned to an Azure AD role (default: false).
- `description` - (optional) The description for the group (default: "").
- `ignore_owner_and_member_changes` - (optional) If true, changes to ownership and membership will be ignored (default: false).
- `members` - (optional) A set of members (Users, Groups, or Service Principals).
- `owners` - (optional) A list of object IDs of owners (Users or Service Principals) (default: current user).
- `prevent_duplicate_names` - (optional) If true, throws an error on duplicate names (default: true).
- `add_deployment_user_as_owner` - (optional) If true, adds the current service principal the terraform deployment is running as to the owners, useful if further management by terraform is required (default: false).

- `role_assignments` - (optional) A map defining role assignments for the group.
- `definition` - The name of the role to assign.
- `relative_scope` - The scope of the role assignment relative to the subscription
- `description` - (optional) Description for the role assignment.
- `skip_service_principal_aad_check` - (optional) If true, skips the Azure AD check for service principal (default: false).
- `condition` - (optional) The condition for the role assignment.
- `condition_version` - (optional) The condition version for the role assignment.
- `delegated_managed_identity_resource_id` - (optional) The resource ID of the delegated managed identity.

Type:

```hcl
map(object({
name = string

administrative_unit_ids = optional(list(string), null)
assignable_to_role = optional(bool, false)
description = optional(string, null)
ignore_owner_and_member_changes = optional(bool, false)
members = optional(map(list(string)), null)
owners = optional(map(list(string)), null)
prevent_duplicate_names = optional(bool, true)
add_deployment_user_as_owner = optional(bool, false)
role_assignments = optional(map(object({
definition = string
relative_scope = string
description = optional(string, null)
skip_service_principal_aad_check = optional(bool, false)
condition = optional(string, null)
condition_version = optional(string, null)
delegated_managed_identity_resource_id = optional(string, null)
})), {})
}))
```

Default: `{}`

### <a name="input_aadgroup_enabled"></a> [aadgroup\_enabled](#input\_aadgroup\_enabled)

Description: Whether to create Entra ID (Azure AD) groups.
If enabled, supply the list of aadgroups in `var.aadgroups`.

Type: `bool`

Default: `false`

### <a name="input_budget_enabled"></a> [budget\_enabled](#input\_budget\_enabled)

Description: Whether to create budgets.
Expand Down
13 changes: 13 additions & 0 deletions main.aadgroup.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module "aadgroup" {
source = "./modules/aadgroup"
count = var.aadgroup_enabled ? 1 : 0
depends_on = [
module.resourcegroup_networkwatcherrg,
module.resourcegroup,
module.subscription,
module.usermanagedidentity,
module.virtualnetwork,
]
aad_groups = var.aad_groups
subscription_id = local.subscription_id
}
151 changes: 151 additions & 0 deletions modules/aadgroup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<!-- BEGIN_TF_DOCS -->
# Landing zone Entra ID (AAD) Group submodule

## Overview

Creates groups in Entra ID and role assignments for resources.

## Notes

See [README.md](https://github.com/Azure/terraform-azurerm-lz-vending#readme) in the parent module for more information.

## Example

```terraform
module "aadgroup" {
source = "Azure/lz-vending/azurerm/modules/aadgroup"
version = "<version>" # change this to your desired version, https://www.terraform.io/language/expressions/version-constraints

aad_groups = {
contributor_group = {
name = "my-ad-group-name"

# optional parameters
description = "the description for my ad group"
members = {
object_ids = [
"e64a9602-6a56-4d45-a4b0-7a7fe605f89d",
"8c537ad4-0289-41f5-84b7-3d1450c04643",
]
}
owners = {
object_ids = ["1f32f09d-bae9-4f02-8905-1ae0a5d97d2f"]
}

# optional role assignment
role_assignments = {
rg_contributor = {
definition = "Contributor"
relative_scope = "/resourceGroups/rg-some-resource-group"
}
}

# optionally tell Terraform to ignore changes to owners & members
ignore_owner_and_member_changes = true

# optionally add the deployment user to the owners to allow subsequent membership updates
add_deployment_user_as_owner = true
}
}

subscription_id = "00000000-0000-0000-0000-000000000000"
}
```

## Documentation
<!-- markdownlint-disable MD033 -->

## Requirements

The following requirements are needed by this module:

- <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) (>= 1.3.0)

- <a name="requirement_azuread"></a> [azuread](#requirement\_azuread) (~> 2.47)

## Modules

No modules.

<!-- markdownlint-disable MD013 -->
## Required Inputs

The following input variables are required:

### <a name="input_aad_groups"></a> [aad\_groups](#input\_aad\_groups)

Description: A map defining the configuration for an Entra ID (Azure Active Directory) group.

- `name` - The display name of the group.

**Optional Parameters:**

- `administrative_unit_ids` - (optional) A list of object IDs of administrative units for group membership.
- `assignable_to_role` - (optional) Whether the group can be assigned to an Azure AD role (default: false).
- `description` - (optional) The description for the group (default: "").
- `ignore_owner_and_member_changes` - (optional) If true, changes to ownership and membership will be ignored (default: false).
- `members` - (optional) A set of members (Users, Groups, or Service Principals).
- `owners` - (optional) A list of object IDs of owners (Users or Service Principals) (default: current user).
- `prevent_duplicate_names` - (optional) If true, throws an error on duplicate names (default: true).
- `add_deployment_user_as_owner` - (optional) If true, adds the current service principal the terraform deployment is running as to the owners, useful if further management by terraform is required (default: false).

- `role_assignments` - (optional) A map defining role assignments for the group.
- `definition` - The name of the role to assign.
- `relative_scope` - The scope of the role assignment relative to the subscription
- `description` - (optional) Description for the role assignment.
- `skip_service_principal_aad_check` - (optional) If true, skips the Azure AD check for service principal (default: false).
- `condition` - (optional) The condition for the role assignment.
- `condition_version` - (optional) The condition version for the role assignment.
- `delegated_managed_identity_resource_id` - (optional) The resource ID of the delegated managed identity.

Type:

```hcl
map(object({
name = string

administrative_unit_ids = optional(list(string), null)
assignable_to_role = optional(bool, false)
description = optional(string, null)
ignore_owner_and_member_changes = optional(bool, false)
members = optional(map(list(string)), null)
owners = optional(map(list(string)), null)
prevent_duplicate_names = optional(bool, true)
add_deployment_user_as_owner = optional(bool, false)
role_assignments = optional(map(object({
definition = string
relative_scope = string
description = optional(string, null)
skip_service_principal_aad_check = optional(bool, false)
condition = optional(string, null)
condition_version = optional(string, null)
delegated_managed_identity_resource_id = optional(string, null)
})), {})
}))
```

### <a name="input_subscription_id"></a> [subscription\_id](#input\_subscription\_id)

Description: The subscription ID of the subscriptions where group role assignments are applied.

Type: `string`

## Optional Inputs

No optional inputs.

## Resources

The following resources are used by this module:

- [azuread_group.ignore_owner_and_member_changes](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/group) (resource)
- [azuread_group.this](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/group) (resource)
- [azurerm_role_assignment.groups](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) (resource)
- [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) (data source)

## Outputs

No outputs.

<!-- markdownlint-enable -->
<!-- END_TF_DOCS -->
Empty file added modules/aadgroup/footer.md
Empty file.
52 changes: 52 additions & 0 deletions modules/aadgroup/header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Landing zone Entra ID (AAD) Group submodule

## Overview

Creates groups in Entra ID and role assignments for resources.

## Notes

See [README.md](https://github.com/Azure/terraform-azurerm-lz-vending#readme) in the parent module for more information.

## Example

```terraform
module "aadgroup" {
source = "Azure/lz-vending/azurerm/modules/aadgroup"
version = "<version>" # change this to your desired version, https://www.terraform.io/language/expressions/version-constraints

aad_groups = {
contributor_group = {
name = "my-ad-group-name"

# optional parameters
description = "the description for my ad group"
members = {
object_ids = [
"e64a9602-6a56-4d45-a4b0-7a7fe605f89d",
"8c537ad4-0289-41f5-84b7-3d1450c04643",
]
}
owners = {
object_ids = ["1f32f09d-bae9-4f02-8905-1ae0a5d97d2f"]
}

# optional role assignment
role_assignments = {
rg_contributor = {
definition = "Contributor"
relative_scope = "/resourceGroups/rg-some-resource-group"
}
}

# optionally tell Terraform to ignore changes to owners & members
ignore_owner_and_member_changes = true

# optionally add the deployment user to the owners to allow subsequent membership updates
add_deployment_user_as_owner = true
}
}

subscription_id = "00000000-0000-0000-0000-000000000000"
}
```
14 changes: 14 additions & 0 deletions modules/aadgroup/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
locals {
aad_groups_role_assignments = { for ra in flatten([
for k_group, v_group in var.aad_groups : [
for k_role, v_role in v_group.role_assignments : {
group_key = k_group
ra_key = k_role
role_assignment = v_role
ignore_changes = try(v_group.ignore_owner_and_member_changes, false)
}
]
]) : "${ra.group_key}-${ra.ra_key}" => ra }

role_definition_resource_substring = "/providers/Microsoft.Authorization/roleDefinitions"
}
53 changes: 53 additions & 0 deletions modules/aadgroup/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
data "azurerm_client_config" "current" {}

resource "azuread_group" "this" {
for_each = { for key, value in var.aad_groups : key => value if !value.ignore_owner_and_member_changes }

display_name = each.value.name

administrative_unit_ids = each.value.administrative_unit_ids
assignable_to_role = each.value.assignable_to_role
description = each.value.description

security_enabled = true
members = each.value.members.object_ids
prevent_duplicate_names = each.value.prevent_duplicate_names
owners = try(each.value.add_deployment_user_as_owner, false) ? setunion(each.value.owners.object_ids, [data.azurerm_client_config.current.object_id]) : each.value.owners.object_ids
visibility = "Private"
}

resource "azuread_group" "ignore_owner_and_member_changes" {
for_each = { for key, value in var.aad_groups : key => value if value.ignore_owner_and_member_changes }

display_name = each.value.name

administrative_unit_ids = each.value.administrative_unit_ids
assignable_to_role = each.value.assignable_to_role
description = each.value.description

security_enabled = true
members = each.value.members.object_ids
prevent_duplicate_names = each.value.prevent_duplicate_names
owners = try(each.value.add_deployment_user_as_owner, false) ? setunion(each.value.owners.object_ids, [data.azurerm_client_config.current.object_id]) : each.value.owners.object_ids
visibility = "Private"

lifecycle {
ignore_changes = [
members,
owners
]
}
}

resource "azurerm_role_assignment" "groups" {
for_each = local.aad_groups_role_assignments

principal_id = each.value.ignore_changes ? azuread_group.ignore_owner_and_member_changes[each.value.group_key].object_id : azuread_group.this[each.value.group_key].object_id
scope = "/subscriptions/${var.subscription_id}${each.value.role_assignment.relative_scope}"
condition = each.value.role_assignment.condition
condition_version = each.value.role_assignment.condition_version
delegated_managed_identity_resource_id = each.value.role_assignment.delegated_managed_identity_resource_id
role_definition_id = strcontains(lower(each.value.role_assignment.definition), lower(local.role_definition_resource_substring)) ? each.value.role_assignment.definition : null
role_definition_name = strcontains(lower(each.value.role_assignment.definition), lower(local.role_definition_resource_substring)) ? null : each.value.role_assignment.definition
skip_service_principal_aad_check = each.value.role_assignment.skip_service_principal_aad_check
}
9 changes: 9 additions & 0 deletions modules/aadgroup/terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
azuread = {
source = "hashicorp/azuread"
version = "~> 2.47"
}
}
}
Loading