Skip to content

Commit

Permalink
Merge pull request #20 from krowlandson/main
Browse files Browse the repository at this point in the history
Update for release 0.0.7-preview
  • Loading branch information
Kevin Rowlandson authored Nov 9, 2020
2 parents e4a0609 + f6a3689 commit 6bab697
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 40 deletions.
162 changes: 125 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,22 @@ This module provides an opinionated approach for delivering the core platform ca
Specifically, this module provides a consistent approach for deploying the following core platform components:

- Management Group and Subscription organisation
- Create the Management Group hierarchy
- Create the Management Group resource hierarchy
- Assign Subscriptions to Management Groups
- Create custom Policy Definitions, Policy Set Definitions (Initiatives), and Policy Assignments (Enterprise-scale policies and customer-defined policies)
- Create custom Policy Assignments, Policy Definitions and Policy Set Definitions (Initiatives)<br>(Enterprise-scale policies and customer-defined policies)
- Identity and access management
- Create custom Role Definitions, and Role Assignments (Enterprise-scale policies and customer-defined roles)
- Create custom Role Assignments and Role Definitions<br>(Enterprise-scale roles and customer-defined roles)

The following resource types are deployed and managed by this module:

| Azure Resource | Terraform Resource |
| -------------- | ------------------ |
| [Management Groups][arm_management_group] | [`azurerm_management_group`][azurerm_management_group] |
| [Policy Assignments][arm_policy_assignment] | [`azurerm_policy_assignment`][azurerm_policy_assignment] |
| [Policy Definitions][arm_policy_definition] | [`azurerm_policy_definition`][azurerm_policy_definition] |
| [Policy Set Definitions][arm_policy_set_definition] | [`azurerm_policy_set_definition`][azurerm_policy_set_definition] |
| [Role Assignments][arm_role_assignment] | [`azurerm_role_assignment`][azurerm_role_assignment] |
| [Role Definitions][arm_role_definition] | [`azurerm_role_definition`][azurerm_role_definition] |

The concept of ***archetypes*** is used to define configuration settings for each ***Management Group*** using a template-driven approach. This approach is designed to make reading the high-level configuration settings easier, simplify the process of managing configuration and versioning, reduce code duplication (DRY), and to improve consistency in complex environments. An archetype is defined using a simple JSON structure (as below) and should define which policy and role settings should be deployed. This is associated to the scope (i.e. Management Group) as part of the ***Landing Zone*** definitions and is what fundamentally transforms ***Management Groups*** into ***Landing Zones***.

**Example archetype definition structure**
```json
{
"archetype_id": {
"policy_assignments": [],
"policy_definitions": [],
"policy_set_definitions": [],
"role_definitions": [],
"archetype_config": {
"parameters": {},
"access_control": {}
}
}
}
```

The module contains a default library of templates for the official Enterprise-scale Archetype Definitions, Policy Assignments, Policy Definitions, Policy Set Definitions, Role Assignments, and Role Definitions. These can be overridden using a custom library, details of which are provided under the usage section of this README (below).
| | Azure Resource | Terraform Resource |
| --- | -------------- | ------------------ |
| Management Groups | [`Microsoft.Management/managementGroups`][arm_management_group] | [`azurerm_management_group`][azurerm_management_group] |
| Policy Assignments | [`Microsoft.Authorization/policyAssignments`][arm_policy_assignment] | [`azurerm_policy_assignment`][azurerm_policy_assignment] |
| Policy Definitions | [`Microsoft.Authorization/policyDefinitions`][arm_policy_definition] | [`azurerm_policy_definition`][azurerm_policy_definition] |
| Policy Set Definitions | [`Microsoft.Authorization/policySetDefinitions`][arm_policy_set_definition] | [`azurerm_policy_set_definition`][azurerm_policy_set_definition] |
| Role Assignments | [`Microsoft.Authorization/roleAssignments`][arm_role_assignment] | [`azurerm_role_assignment`][azurerm_role_assignment] |
| Role Definitions | [`Microsoft.Authorization/roleDefinitions`][arm_role_definition] | [`azurerm_role_definition`][azurerm_role_definition] |

## Usage in Terraform 0.13

Expand All @@ -58,6 +38,8 @@ To use this module with all default settings, please include the following in yo
>
> 3. If using the `azurerm_subscription` data source to provide a `tenant_id` value from the current context for `root_parent_id`, you are likely to get a warning that Terraform cannot determine the number of resources to create during the `plan` stage. To avoid the need to use `terraform apply -target=resource` or putting such values in source code, we recommend providing the `root_parent_id` value explicitly via the command-line using `-var 'root_parent_id={{ tenant_id }}'` or your preferred method of injecting variable values at runtime.
### Simple Example

```hcl
provider "azurerm" {
version = ">= 2.31.1"
Expand All @@ -71,13 +53,15 @@ variable "tenant_id" {
module "enterprise_scale" {
source = "Azure/caf-enterprise-scale/azurerm"
version = "0.0.6-preview"
version = "0.0.7-preview"
root_parent_id = var.tenant_id
}
```

### Advanced Example

To customise the module, you can add any of the following optional variables:

```hcl
Expand All @@ -93,7 +77,7 @@ variable "tenant_id" {
module "enterprise_scale" {
source = "Azure/caf-enterprise-scale/azurerm"
version = "0.0.6-preview"
version = "0.0.7-preview"
# Mandatory Variables
root_parent_id = var.tenant_id
Expand Down Expand Up @@ -193,7 +177,7 @@ module "enterprise_scale" {
# parameters = {}
# access_control = {}
# }
# landing_zones = {
# landing-zones = {
# archetype_id = "es_landing_zones"
# parameters = {}
# access_control = {}
Expand All @@ -218,17 +202,17 @@ module "enterprise_scale" {
# parameters = {}
# access_control = {}
# }
# demo_corp = {
# demo-corp = {
# archetype_id = "es_demo_corp"
# parameters = {}
# access_control = {}
# }
# demo_online = {
# demo-online = {
# archetype_id = "es_demo_online"
# parameters = {}
# access_control = {}
# }
# demo_sap = {
# demo-sap = {
# archetype_id = "es_demo_sap"
# parameters = {}
# access_control = {}
Expand Down Expand Up @@ -293,6 +277,109 @@ module "enterprise_scale" {
```

## Guide to further customisation

This module contains a default library containing templates for the default Enterprise-scale Archetype Definitions, Policy Assignments, Policy Definitions, Policy Set Definitions (Initiatives), Role Assignments and Role Definitions. These can be added to and overridden by creating a custom library within your root module. Further details of this are provided in the following sections.

### What is an archetype?

Archetypes are used in the Enterprise-scale architecture to describe the Landing Zone configuration using a template-driven approach. The archetype is what fundamentally transforms ***Management Groups*** and ***Subscriptions*** into ***Landing Zones***.

An archetype defines which Azure Policy and Access control (IAM) settings are needed to secure and configure the Landing Zones with everything needed for safe handover to the Landing Zone owner. This covers critical platform controls and configuration items, such as:

- Consistent role-based access control (RBAC) settings
- Guardrails for security settings
- Guardrails for common workload configurations (e.g. SAP, AKS, WVD, etc.)
- Automate provisioning of critical platform resources such as monitoring and networking solutions in each Landing Zone

This approach provides improved autonomy for application teams, whilst ensuring security policies and standards are enforced.

### Working with archetype definitions and the custom library

The `archetype_definition` is a simple template file written in JSON or YAML. The default archetype definitions can be found in the [built-in module library][TFAES-Library], but custom archetype definitions can also be added to a custom library in the root module. The archetype definition is associated to the scope (i.e. Management Group) by specifying the `archetype_id` within the ***Landing Zone*** configuration object.

Both the built-in and custom libraries are also used to store ARM based templates for the Policy Assignments, Policy Definitions, Policy Set Definitions (Initiatives) and Role Definitions. Role Assignments are an exception as these are defined as part of the `archetype_config` instead.

To use a custom library, simply create a folder in your root module (e.g. `/lib`) and tell the module about it using the `library_path` variable (e.g. `library_path = "${path.root}/lib"`). Save your custom templates in the custom library location and as long as they are valid templates for the resource type and match the following naming conventions, the module will automatically import and use them:

| Resource Type | File Name Pattern |
| ------------- | ----------------- |
| Archetype Definitions | `archetype_definition_*.{json,yml,yaml}` |
| Policy Assignments | `policy_assignment_*.{json,yml,yaml}` |
| Policy Definitions | `policy_definition_*.{json,yml,yaml}` |
| Policy Set Definitions | `policy_set_definition_*.{json,yml,yaml}` |
| Role Definitions | `role_definition_*.{json,yml,yaml}` |

> The decision to store Policy Assignments, Policy Definitions, Policy Set Definitions (Initiatives) and Role Definitions as native ARM was based on a couple of factors:
>
>- Policies in Terraform require you to understand how to write significant sections of the resource configuration in the native ARM format, and then convert this to a JSON string within Terraform resource.
>- This makes copying these items between ARM templates and Terraform much easier.
>- Terraform doesn't support importing data objects from native Terraform file formats (`.hcl`, `.tf` or `.tfvar`) so we had to use an alternative to be able to support the custom library model for extensibility and customisation.<br>**PRO TIP:** The module also supports YAML for these files as long as they match the ARM schema, although we don't know why you would want to do that!
>
This template driven approach is designed to simplify the process of defining an archetype and forms the foundations for how the module is able to provide feature-rich defaults, whilst also allowing a great degree of extensibility and customisation through the input variables instead of having to fork and modify the module.

The `archetype_definition` template contains lists of the Policy Assignments, Policy Definitions, Policy Set Definitions (Initiatives) and Role Definitions you want to create when assigning the archetype to a Management Group. It also includes the ability to set default values for parameters associated with Policy Assignments, and set default Role Assignments.

To keep the `archetype_definition` template as lean as possible, we simply declare the value of the `name` field from the resource templates (by type). The exception is Role Definitions which must have a GUID for the `name` field, so we use the `roleName` value from `properties` instead.

As long as you follows these patterns, you can create your own archetype definitions to start advanced customisation of your Enterprise-scale deployment.

This template-based approach was chosen to make the desired-state easier to understand, simplify the process of managing configuration and versioning, reduce code duplication (DRY), and to improve consistency in complex environments.

#### Example archetype definition

```json
{
"archetype_id": {
"policy_assignments": [
// list of Policy Assignment names
],
"policy_definitions": [
// list of Policy Definition names
],
"policy_set_definitions": [
// list of Policy Set Definition names
],
"role_definitions": [
// list of Role Definition names
],
"archetype_config": {
"parameters": {
// map of parameter objects, grouped by Policy Assignment name
},
"access_control": {
// map of Role Assignments to create, grouped by Role Definition name
}
}
}
}
```

#### Using the `default_empty` archetype definition

The default library includes a `default_empty` archetype definition which is useful when defining Management Groups which only require Role Assignments, or are being used for logical segregation of Landing Zones under a parent arcehtype. You can assign this to any Landing Zone definition, using the `archetype_config` > `archetype_id` value as per the following `custom_landing_zones` example:

```hcl
custom_landing_zones = {
example-landing-zone-id = {
display_name = "Example Landing Zone"
parent_management_group_id = "tf-landing-zones"
subscription_ids = []
archetype_config = {
archetype_id = "default_empty"
parameters = {}
access_control = {}
}
}
}
```

This is equivalent to creating a standard Management Group without creating any custom Policy Assignments, Policy Definitions, Policy Set Definitions (Initiatives) or Role Definitions.

Role Assignments can be created using the `archetype_config` > `access_control` object within the `custom_landing_zones` instance.

> Note that you still need to provide a full and valid Landing Zone object as per the example above.
## License

Expand Down Expand Up @@ -341,3 +428,4 @@ contact [[email protected]](mailto:[email protected]) with any additio
[azurerm_role_definition]: https://www.terraform.io/docs/providers/azurerm/r/role_definition.html

[TFAES-LICENSE]: https://github.com/Azure/terraform-azurerm-enterprise-scale/blob/main/LICENSE
[TFAES-Library]: https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/terraform-azurerm-caf-enterprise-scale-archetypes/lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"default_empty": {
"policy_assignments": [],
"policy_definitions": [],
"policy_set_definitions": [],
"role_definitions": [],
"archetype_config": {
"parameters": {},
"access_control": {}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ locals {
)
}

# Generate a map of parameters from the archetype definition and merge
# with the parameters provided using var.parameters.
# Used to determine the parameter values for Policy Assignments.
locals {
parameters_at_scope = merge(
local.archetype_definition.archetype_config.parameters,
local.parameters,
)
}

# Extract the desired Policy Assignment from archetype_policy_assignments_map.
locals {
archetype_policy_assignments_output = [
Expand All @@ -85,8 +95,8 @@ locals {
# variable. These come from the archetype_config object in
# the enterprise_scale module and are merged with the Policy
# Assignment template values to provide overrides.
parameters = contains(keys(local.parameters), policy_assignment) ? {
for parameter_key, parameter_value in local.parameters[policy_assignment] :
parameters = contains(keys(local.parameters_at_scope), policy_assignment) ? {
for parameter_key, parameter_value in local.parameters_at_scope[policy_assignment] :
parameter_key => {
value = parameter_value
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ locals {
for role_definition_name, members in local.archetype_role_assignments_map : [
for member in members : [
{
resource_id = "${local.provider_path.role_assignment}${uuidv5(uuidv5("url", role_definition_name), member)}"
resource_id = "${local.provider_path.role_assignment}${uuidv5(uuidv5(uuidv5("url", role_definition_name), local.scope_id), member)}"
scope_id = local.scope_id
principal_id = member
role_definition_name = role_definition_name
Expand Down

0 comments on commit 6bab697

Please sign in to comment.