Skip to content

Commit

Permalink
feat: add dependency management (#143)
Browse files Browse the repository at this point in the history
* feat: add dependency management

* docs: docs

* docs: add dependencies var to main docs

* chore: update provider minimums

* chore: doc and lint fixes
  • Loading branch information
matt-FFFFFF authored Nov 14, 2024
1 parent 4d0a811 commit 2283fc2
Show file tree
Hide file tree
Showing 13 changed files with 347 additions and 17 deletions.
68 changes: 60 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,29 @@
We use the AzAPI provider to interact with the Azure APIs.
The new features allow us to be more efficient and reliable, with orders of magnitude speed improvements and retry logic for transient errors.

## Unknown Values
## Unknown Values & Depends On

This module uses the ALZ Terraform provider. This uses a data source which **must** be read prior to creating the plan.
If you pass an unknown (known after apply) value into the module, it will not be able to read the data source until the plan is being applied.

The `depends_on` feature is therefore not supported in the ALZ provider.
Please do not add a `depends_on` attribute to the module declaration.

Similarly, if you pass an unknown (known after apply) value into the module, it will not be able to read the data source until the plan is being applied.
This may cause resources to be unnecessarily recreated.

Such unknown values include resource ids. For example, if you are creating a resource and passing the id of the resource group to the module, this will cause the issue.
Instead, use string interpolation or provider functions to pass the values. For example:
To work around this, we have two features.
Firstly we have a `dependencies` variable.
This variable is used to ensure that policies and policy role assignments do not get created until dependent resources are available.

Secondly, for values that are passed into the module, use string interpolation or provider functions to create the required. For example:

### Using `var.dependencies`

### Recommended
This variable is used as a workaround for the lack of support for `depends_on` in the ALZ provider.
Place values into this variable to ensure that policies and policy role assignments do not get created until dependent resources are available.
See the variable documentation and the examples (private DNS and management) for more information.

### Using Provider Functions

Either: Use known values as inputs, or use Terraform Stacks.

Expand Down Expand Up @@ -75,9 +88,16 @@ module "example" {
}
```

### `var.dependencies`

This variable is used as a workaround for the lack of support for `depends_on` in the ALZ provider.
Place values into this variable to ensure that policies and policy role assignments do not get created until dependent resources are available.
See the variable documentation and the examples (private DNS and management) for more information.

### Deferred Actions

We are awaiting the results of the upstream Terraform language experiment *deferred actions*. This will provide a solution to this issue.
We are awaiting the results of the upstream Terraform language experiment *deferred actions*.
This will provide a solution to this issue.
See the release notes [here](https://github.com/hashicorp/terraform/releases/tag/v1.10.0-alpha20241023) for more information.

<!-- markdownlint-disable MD033 -->
Expand All @@ -87,7 +107,7 @@ The following requirements are needed by this module:

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

- <a name="requirement_alz"></a> [alz](#requirement\_alz) (~> 0.16)
- <a name="requirement_alz"></a> [alz](#requirement\_alz) (~> 0.16, >= 0.16.2)

- <a name="requirement_azapi"></a> [azapi](#requirement\_azapi) (~> 2.0, >= 2.0.1)

Expand Down Expand Up @@ -118,6 +138,8 @@ The following resources are used by this module:
- [azapi_update_resource.hierarchy_settings](https://registry.terraform.io/providers/azure/azapi/latest/docs/resources/update_resource) (resource)
- [modtm_telemetry.telemetry](https://registry.terraform.io/providers/azure/modtm/latest/docs/resources/telemetry) (resource)
- [random_uuid.telemetry](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/uuid) (resource)
- [terraform_data.policy_assignments_dependencies](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) (resource)
- [terraform_data.policy_role_assignments_dependencies](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) (resource)
- [time_sleep.after_management_groups](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) (resource)
- [time_sleep.after_policy_definitions](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) (resource)
- [time_sleep.after_policy_set_definitions](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) (resource)
Expand Down Expand Up @@ -184,6 +206,36 @@ object({

Default: `{}`

### <a name="input_dependencies"></a> [dependencies](#input\_dependencies)

Description: Place dependent values into this variable to ensure that resources are created in the correct order.
Ensure that the values placed here are computed/known after apply, e.g. the resource ids.

This is necessary as the unknown values and `depends_on` are not supported by this module as we use the alz provider.
See the "Unknown Values & Depends On" section above for more information.

e.g.

```hcl
dependencies = {
policy_role_assignments = [
module.dependency_example1.output,
module.dependency_example2.output,
]
}
```

Type:

```hcl
object({
policy_role_assignments = optional(any, null)
policy_assignments = optional(any, null)
})
```

Default: `{}`

### <a name="input_enable_telemetry"></a> [enable\_telemetry](#input\_enable\_telemetry)

Description: This variable controls whether or not telemetry is enabled for the module.
Expand Down Expand Up @@ -359,7 +411,7 @@ object({
}), {})
policy_role_assignments = optional(object({
error_message_regex = optional(list(string), [
"RoleAssignmentNotFound" # Added to fix an eventual consistency error with a GET following soon after a PUT
"ResourceNotFound", # If the resource has just been created, retry until it is available.
])
interval_seconds = optional(number, null)
max_interval_seconds = optional(number, null)
Expand Down
32 changes: 26 additions & 6 deletions _header.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,29 @@
We use the AzAPI provider to interact with the Azure APIs.
The new features allow us to be more efficient and reliable, with orders of magnitude speed improvements and retry logic for transient errors.

## Unknown Values
## Unknown Values & Depends On

This module uses the ALZ Terraform provider. This uses a data source which **must** be read prior to creating the plan.
If you pass an unknown (known after apply) value into the module, it will not be able to read the data source until the plan is being applied.

The `depends_on` feature is therefore not supported in the ALZ provider.
Please do not add a `depends_on` attribute to the module declaration.

Similarly, if you pass an unknown (known after apply) value into the module, it will not be able to read the data source until the plan is being applied.
This may cause resources to be unnecessarily recreated.

Such unknown values include resource ids. For example, if you are creating a resource and passing the id of the resource group to the module, this will cause the issue.
Instead, use string interpolation or provider functions to pass the values. For example:
To work around this, we have two features.
Firstly we have a `dependencies` variable.
This variable is used to ensure that policies and policy role assignments do not get created until dependent resources are available.

Secondly, for values that are passed into the module, use string interpolation or provider functions to create the required. For example:

### Using `var.dependencies`

### Recommended
This variable is used as a workaround for the lack of support for `depends_on` in the ALZ provider.
Place values into this variable to ensure that policies and policy role assignments do not get created until dependent resources are available.
See the variable documentation and the examples (private DNS and management) for more information.

### Using Provider Functions

Either: Use known values as inputs, or use Terraform Stacks.

Expand Down Expand Up @@ -74,7 +87,14 @@ module "example" {
}
```

### `var.dependencies`

This variable is used as a workaround for the lack of support for `depends_on` in the ALZ provider.
Place values into this variable to ensure that policies and policy role assignments do not get created until dependent resources are available.
See the variable documentation and the examples (private DNS and management) for more information.

### Deferred Actions

We are awaiting the results of the upstream Terraform language experiment *deferred actions*. This will provide a solution to this issue.
We are awaiting the results of the upstream Terraform language experiment *deferred actions*.
This will provide a solution to this issue.
See the release notes [here](https://github.com/hashicorp/terraform/releases/tag/v1.10.0-alpha20241023) for more information.
7 changes: 7 additions & 0 deletions examples/management/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ module "alz" {
ama_user_assigned_managed_identity_name = jsonencode({ value = local.uami_name })
log_analytics_workspace_id = jsonencode({ value = provider::azapi::resource_group_resource_id(data.azapi_client_config.current.subscription_id, local.resource_group_name, "Microsoft.OperationalInsights/workspaces", [local.log_analytics_workspace_name]) })
}
dependencies = {
policy_assignments = [
module.management.data_collection_rule_ids,
module.management.resource_id,
module.management.user_assigned_identity_ids,
]
}
policy_assignments_to_modify = {
connectivity = {
policy_assignments = {
Expand Down
7 changes: 7 additions & 0 deletions examples/management/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ module "alz" {
ama_user_assigned_managed_identity_name = jsonencode({ value = local.uami_name })
log_analytics_workspace_id = jsonencode({ value = provider::azapi::resource_group_resource_id(data.azapi_client_config.current.subscription_id, local.resource_group_name, "Microsoft.OperationalInsights/workspaces", [local.log_analytics_workspace_name]) })
}
dependencies = {
policy_assignments = [
module.management.data_collection_rule_ids,
module.management.resource_id,
module.management.user_assigned_identity_ids,
]
}
policy_assignments_to_modify = {
connectivity = {
policy_assignments = {
Expand Down
123 changes: 123 additions & 0 deletions examples/privatednszones/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<!-- BEGIN_TF_DOCS -->
# ALZ + Management

This example shows how to deploy the ALZ reference architecture combines with the ALZ management module.

```hcl
provider "alz" {
library_references = [{
path = "platform/alz"
ref = "2024.11.0"
}]
}
provider "azurerm" {
features {}
}
variable "random_suffix" {
type = string
default = "fgcsnm"
description = "Change me to something unique"
}
data "azapi_client_config" "current" {}
locals {
location = "swedencentral"
resource_group_name = "rg-private-dns-${var.random_suffix}"
}
module "private_dns_zones" {
source = "Azure/avm-ptn-network-private-link-private-dns-zones/azurerm"
version = "0.6.0"
location = local.location
resource_group_name = local.resource_group_name
}
module "alz" {
source = "../../"
architecture_name = "alz"
parent_resource_id = data.azapi_client_config.current.tenant_id
location = local.location
policy_default_values = {
private_dns_zone_subscription_id = jsonencode({ value = data.azapi_client_config.current.subscription_id })
private_dns_zone_region = jsonencode({ value = local.location })
private_dns_zone_resource_group_name = jsonencode({ value = local.resource_group_name })
}
dependencies = {
policy_assignments = [
module.private_dns_zones.private_dns_zone_resource_ids,
]
}
policy_assignments_to_modify = {
connectivity = {
policy_assignments = {
# As we don't have a DDOS protection plan, we need to disable this policy
# to prevent a modify action from failing.
Enable-DDoS-VNET = {
enforcement_mode = "DoNotEnforce"
}
}
}
}
}
```

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

The following requirements are needed by this module:

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

- <a name="requirement_alz"></a> [alz](#requirement\_alz) (~> 0.16)

- <a name="requirement_azapi"></a> [azapi](#requirement\_azapi) (~> 2.0, >= 2.0.1)

- <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) (~> 3.100)

## Resources

The following resources are used by this module:

- [azapi_client_config.current](https://registry.terraform.io/providers/Azure/azapi/latest/docs/data-sources/client_config) (data source)

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

No required inputs.

## Optional Inputs

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

### <a name="input_random_suffix"></a> [random\_suffix](#input\_random\_suffix)

Description: Change me to something unique

Type: `string`

Default: `"fgcsnm"`

## Outputs

No outputs.

## Modules

The following Modules are called:

### <a name="module_alz"></a> [alz](#module\_alz)

Source: ../../

Version:

### <a name="module_private_dns_zones"></a> [private\_dns\_zones](#module\_private\_dns\_zones)

Source: Azure/avm-ptn-network-private-link-private-dns-zones/azurerm

Version: 0.6.0

<!-- END_TF_DOCS -->
Empty file.
3 changes: 3 additions & 0 deletions examples/privatednszones/_header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ALZ + Management

This example shows how to deploy the ALZ reference architecture combines with the ALZ management module.
58 changes: 58 additions & 0 deletions examples/privatednszones/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
provider "alz" {
library_references = [{
path = "platform/alz"
ref = "2024.11.0"
}]
}

provider "azurerm" {
features {}
}

variable "random_suffix" {
type = string
default = "fgcsnm"
description = "Change me to something unique"
}

data "azapi_client_config" "current" {}

locals {
location = "swedencentral"
resource_group_name = "rg-private-dns-${var.random_suffix}"
}

module "private_dns_zones" {
source = "Azure/avm-ptn-network-private-link-private-dns-zones/azurerm"
version = "0.6.0"
location = local.location
resource_group_name = local.resource_group_name
}

module "alz" {
source = "../../"
architecture_name = "alz"
parent_resource_id = data.azapi_client_config.current.tenant_id
location = local.location
policy_default_values = {
private_dns_zone_subscription_id = jsonencode({ value = data.azapi_client_config.current.subscription_id })
private_dns_zone_region = jsonencode({ value = local.location })
private_dns_zone_resource_group_name = jsonencode({ value = local.resource_group_name })
}
dependencies = {
policy_assignments = [
module.private_dns_zones.private_dns_zone_resource_ids,
]
}
policy_assignments_to_modify = {
connectivity = {
policy_assignments = {
# As we don't have a DDOS protection plan, we need to disable this policy
# to prevent a modify action from failing.
Enable-DDoS-VNET = {
enforcement_mode = "DoNotEnforce"
}
}
}
}
}
Loading

0 comments on commit 2283fc2

Please sign in to comment.