Important
This repository has moved to schubergphilis-ep/terraform-aws-mcaf-avm.
Please update your references and use the new location for issues, PRs, and contributions.
Terraform module providing an AWS Account Vending Machine (AVM). This module provisions an AWS account using the "AWS Control Tower Account Factory" product in Service Catalog with one or more Terraform Cloud/Enterprise (TFE) workspaces backed by a VCS project.
This module provides three modes of workspace authentication:
- (default) An IAM role using OpenID Connect integration with the AWS account. This works for remote runners or with using self-hosted Terraform Cloud agents (agent version v1.7.0+).
- An IAM role using an external ID to authenticate with the AWS account in combination with using self-hosted Terraform Cloud agents.
- An IAM user per workspace in the provisioned AWS account.
Using one of the first 2 authentication methods is in line with authentication best practices to use IAM roles over IAM users with long-lived tokens.
The IAM roles with OIDC feature creates an IAM role with a trust policy allowing the OIDC provider created as part of this module. The workspace will be configured to use OIDC by feeding the AWS provider with the right environment variables.
Warning
When using using self-hosted Terraform Cloud agents, ensure that your agents use v1.12.0+ when using multiple configurations (e.g. provider aliases).
To use IAM roles for authentication:
- Set
var.tfe_workspace.agent_pool_idor (agent_pool_idif specifying additional workspaces) to the Terraform Cloud agent pool ID. - Set
var.tfe_workspace.auth_methodor (auth_methodif specifying additional workspaces) toiam_role. - Set
var.tfe_workspace.agent_role_arnsor (agent_role_arnsif specifying additional workspaces) to the IAM role assumed by the Terraform Cloud agents in the specified agent pool.
This will create an IAM role in the provisioned AWS account with a randomly generated external ID which can only be assumed by the Terraform Cloud agent role. The created role and external ID value are stored in the new workspace as Terraform variables which can be used to configure your AWS provider. Using the default workspace the created role will be called TPEPipelineRole, role names for additional workspaces will be calculated for you based on the workspace name but you can always set your own via the role_name variable (similarly you can set your own role name in the default workspace via var.tfe_workspace.role_name); but please be aware that each IAM role must have a unique name.
To use the created IAM role, use the following when configuring your AWS provider:
provider "aws" {
assume_role {
role_arn = var.aws_assume_role
external_id = var.aws_assume_role_external_id
session_name = "tfe-agent"
}
}- Set
var.tfe_workspace.auth_methodor (auth_methodif specifying additional workspaces) toiam_user.
This will create an IAM user in the provisioned AWS account with the access key and secret access key added as environmental variables to the workspace.
This module supports TFE project-level authentication by setting var.tfe_project.enabled to true and var.tfe_project.auth.enabled to true. By default, this creates an IAM Role with OIDC and sets the required environment variables on the project. All workspaces within the project will then use this role to authenticate, which is useful for example:
- Workspace creation outside of AVM
- Ephemeral workspaces
The module supports both IAM roles and IAM users as authentication methods.
By default, each workspace gets its own IAM role or user. If you want to rely exclusively on project-level authentication, disable workspace-level authentication:
- Set
var.tfe_workspace.enable_workspace_authenticationtofalse - Set
var.additional_tfe_workspaces[*].enable_workspace_authenticationtofalse(note:var.additional_tfe_workspaces[*].enable_workspace_authenticationdefaults tonull. Ifvar.tfe_workspace.enable_workspace_authenticationis set tofalse, all additional workspaces will automatically inheritfalseunless explicitly overridden)
You can override project-level authentication for specific workspaces when stricter or different permissions are required. For example, to use a stricter policy for a particular workspace:
- Set
auth_methodto a valid value (e.g.,iam_role_oidc) - Provide the required additional variables (e.g.,
policy_arn) - Ensure
enable_workspace_authenticationis set totrue.
This creates an workspace-specific IAM role / user and workspace-specific environment variables that take precedence over project-level variables.
Team access can be configured per workspace using the team_access variable.
As the state is considered sensitive, we recommend the following custom role permissions which is similar to the pre-existing "write" permission but blocks read access to the state (viewing outputs is still allowed):
team_access = {
"MyTeamName" = {
permissions = {
run_tasks = false
runs = "apply"
sentinel_mocks = "read"
state_versions = "read-outputs"
variables = "write"
workspace_locking = true
}
}
}More complete usage information can be found in the underlying terraform-aws-mcaf-workspace module README.
Warning
The team should already exist, this module will not create it for you.
In the account variable, the SSO attributes (sso_email, sso_firstname and sso_lastname) will be used by AWS Service Catalog to provide initial access to the newly created account.
You should use the details from the AWS Control Tower Admin user.
module "aws_account" {
source = "github.com/schubergphilis/terraform-aws-mcaf-avm?ref=VERSION"
name = "my-aws-account"
tags = { Terraform = true }
account = {
email = "my-aws-account@email.com"
environment = "prod"
organizational_unit = "Production"
sso_email = "control-tower-admin@company.com"
}
tfe_workspace = {
default_region = "eu-west-1"
repository_identifier = "myorg/myworkspacerepo"
organization = "myorg"
vcs_oauth_token_id = var.oauth_token_id
}
}module "aws_account" {
source = "github.com/schubergphilis/terraform-aws-mcaf-avm?ref=VERSION"
name = "my-aws-account"
tags = { Terraform = true }
account = {
email = "my-aws-account@email.com"
environment = "prod"
organizational_unit = "Production"
sso_email = "control-tower-admin@company.com"
}
tfe_workspace = {
default_region = "eu-west-1"
repository_identifier = "schubergphilis/terraform-aws-mcaf-avm"
organization = "schubergphilis"
vcs_oauth_token_id = var.oauth_token_id
}
additional_tfe_workspaces = {
baseline-my-aws-account = {
auto_apply = true
repository_identifier = "schubergphilis/terraform-aws-mcaf-account-baseline"
}
}
}module "aws_account" {
source = "github.com/schubergphilis/terraform-aws-mcaf-avm?ref=VERSION"
create_default_workspace = false
name = "my-aws-account"
tags = { Terraform = true }
account = {
email = "my-aws-account@email.com"
environment = "prod"
organizational_unit = "Production"
sso_email = "control-tower-admin@company.com"
}
tfe_workspace = {
default_region = "eu-west-1"
repository_identifier = "schubergphilis/terraform-aws-mcaf-avm"
organization = "schubergphilis"
vcs_oauth_token_id = var.oauth_token_id
}
additional_tfe_workspaces = {
my-aws-account-subsystem1 = {
working_directory = "terraform/subsystem1"
}
my-aws-account-subsystem2 = {
working_directory = "terraform/subsystem2"
}
}
}The module supports setting a Permission Boundary on the workspace iam_user or iam_role by passing down permissions_boundaries.workspace_boundary, which needs to be referencing the path where the permissions boundary is stored in git and the name: permissions_boundaries.workspace_boundary_name. By setting var.tfe_workspace.add_permissions_boundary or var.additional_tfe_workspaces.add_permissions_boundary to true, the permissions boundary will be attached to that specific workspace user/role.
In case you want to reference a permission boundary that needs to be attached to every IAM role/user that will be created by the workspace role/user then you can create this permission boundary by specifying permissions_boundaries.workload_boundary which needs to be referencing the path where the permissions boundary is stored in git and the name: permissions_boundaries.workload_boundary_name.
module "aws_account" {
source = "github.com/schubergphilis/terraform-aws-mcaf-avm?ref=VERSION"
...
permissions_boundaries = {
workspace_boundary = "${path.module}/workspace_boundary.json"
workspace_boundary_name = "workspace_boundary"
workload_boundary = "${path.module}/workload_boundary.json"
workload_boundary_name = "workload_boundary"
}
...
}Tip
The workspace_boundary and workload_boundary can be templated files, account_id will be replaced by AVM by the account ID of the AWS account created.
| Name | Version |
|---|---|
| terraform | >= 1.12.0 |
| aws | >= 4.9.0 |
| mcaf | >= 0.4.2 |
| random | >= 3.0.0 |
| tfe | >= 0.70.0 |
| tls | >= 4.0.4 |
| Name | Version |
|---|---|
| aws.account | >= 4.9.0 |
| tfe | >= 0.70.0 |
| tls | >= 4.0.4 |
| Name | Source | Version |
|---|---|---|
| account | schubergphilis/mcaf-account/aws | ~> 0.5.1 |
| additional_tfe_workspaces | schubergphilis/mcaf-workspace/aws | ~> 4.0.0 |
| tfe_project_auth | schubergphilis/mcaf-workspace/aws//modules/auth | ~> 3.1.0 |
| tfe_project_variable_set | schubergphilis/mcaf-variable-set/tfe | ~> 0.2.0 |
| tfe_workspace | schubergphilis/mcaf-workspace/aws | ~> 4.0.0 |
| Name | Type |
|---|---|
| aws_account_alternate_contact.billing | resource |
| aws_account_alternate_contact.operations | resource |
| aws_account_alternate_contact.security | resource |
| aws_iam_account_alias.alias | resource |
| aws_iam_openid_connect_provider.tfc_provider | resource |
| aws_iam_policy.workload_boundary | resource |
| aws_iam_policy.workspace_boundary | resource |
| tfe_project.default | resource |
| tfe_project_settings.default | resource |
| tfe_project_variable_set.default | resource |
| tfe_variable.account_variable_set_clear_text_env_variables | resource |
| tfe_variable.account_variable_set_clear_text_hcl_variables | resource |
| tfe_variable.account_variable_set_clear_text_terraform_variables | resource |
| tfe_variable_set.account | resource |
| tls_certificate.oidc_certificate | data source |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| account | AWS account settings | object({ |
n/a | yes |
| name | Name of the account and default TFE workspace | string |
n/a | yes |
| tfe_workspace | TFE workspace settings | object({ |
n/a | yes |
| account_variable_set | Settings of variable set that is attached to each workspace | object({ |
{} |
no |
| additional_tfe_workspaces | Additional TFE workspaces | map(object({ |
{} |
no |
| create_default_workspace | Set to false to skip creating default workspace | bool |
true |
no |
| path | Optional path for all IAM users, user groups, roles, and customer managed policies created by this module | string |
"/" |
no |
| permissions_boundaries | n/a | object({ |
{} |
no |
| tags | A map of tags to assign to all resources | map(string) |
{} |
no |
| tfe_project | TFE project configuration including variable sets and authentication settings. If no name is provided, var.name will be used for the project name & variable set name. | object({ |
{} |
no |
| Name | Description |
|---|---|
| account_variable_set_id | The ID of the account variable set |
| additional_tfe_workspaces | Map of any additional Terraform Cloud workspace names and IDs |
| environment | The environment name |
| id | The AWS account ID |
| name | The AWS account name |
| repository_identifier | The repository identifier if one is specified |
| tfe_project_id | Project ID of default project when tfe_project.enabled is true |
| tfe_workspace_id | Workspace ID of default workspace ID when create_default_workspace is true |
| tfe_workspaces | List of Terraform Cloud workspaces |
| workload_permissions_boundary_arn | The ARN of the workload permissions boundary |
| workload_permissions_boundary_name | The name of the workload permissions boundary |
| workspace_permissions_boundary_arn | The ARN of the workspace permissions boundary |
| workspace_permissions_boundary_name | The name of the workspace permissions boundary |