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

Support new COOL environment structure #83

Open
wants to merge 7 commits into
base: develop
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ __pycache__
.terraform.lock.hcl
terraform.tfstate
terraform.tfstate.backup
*.tfconfig
*.tfvars
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ At this point the `ProvisionNetworking` policy is attached to the
| public\_subnet\_cidr\_blocks | The CIDR blocks corresponding to the public subnets to be associated with the VPC (e.g. ["10.10.0.0/24", "10.10.1.0/24"]). These must be /24 blocks, since we are using them to create reverse DNS zones. This list must be the same length as private\_subnet\_cidr\_blocks, since each private subnet will be assigned a NAT gateway in a public subnet in the same Availability Zone. | `list(string)` | n/a | yes |
| read\_terraform\_state\_role\_name | The name to assign the IAM role (as well as the corresponding policy) that allows read-only access to the cool-sharedservices-networking state in the S3 bucket where Terraform state is stored. | `string` | `"ReadSharedServicesNetworkingTerraformState"` | no |
| tags | Tags to apply to all AWS resources created. | `map(string)` | `{}` | no |
| terraform\_state\_bucket | The name of the S3 bucket where Terraform state is stored. | `string` | n/a | yes |
| transit\_gateway\_description | The description to associate with the Transit Gateway in the Shared Services account that allows cross-VPC communication. | `string` | `"The Transit Gateway in the Shared Services account that allows cross-VPC communication."` | no |
| vpc\_cidr\_block | The overall CIDR block to be associated with the VPC (e.g. "10.10.0.0/16"). | `string` | n/a | yes |

Expand Down
10 changes: 7 additions & 3 deletions backend.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
terraform {
backend "s3" {
encrypt = true
bucket = "cisa-cool-terraform-state"
# Use a partial configuration to avoid hardcoding the bucket name. This
# allows the bucket name to be set on a per-environment basis via the
# -backend-config command line option or other methods. For details, see:
# https://developer.hashicorp.com/terraform/language/backend#partial-configuration
bucket = ""
dynamodb_table = "terraform-state-lock"
encrypt = true
key = "cool-sharedservices-networking/terraform.tfstate"
profile = "cool-terraform-backend"
region = "us-east-1"
key = "cool-sharedservices-networking/terraform.tfstate"
}
}
49 changes: 24 additions & 25 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,48 +34,47 @@ locals {
# Look up Shared Services account name from AWS organizations
# provider
sharedservices_account_name = [
for account in data.aws_organizations_organization.cool.accounts :
for account in data.aws_organizations_organization.cool.non_master_accounts :
account.name
if account.id == local.sharedservices_account_id
][0]

# Determine Shared Services account type based on account name.
#
# The account name format is "ACCOUNT_NAME (ACCOUNT_TYPE)" - for
# example, "Shared Services (Production)".
sharedservices_account_type = length(regexall("\\(([^()]*)\\)", local.sharedservices_account_name)) == 1 ? regex("\\(([^()]*)\\)", local.sharedservices_account_name)[0] : "Unknown"
# Determine the various account IDs that are the same type (production,
# staging, etc.) as the Shared Services account.
# Account name format: "ACCOUNT_NAME (ACCOUNT_TYPE)"
# For example: "Shared Services (Production)"
# NOTE: Originally, Shared Services, User Services, and dynamic assessment
# environment account names followed the "ACCOUNT_NAME (ACCOUNT_TYPE)" format
# above, but our thinking has changed and in newer environments the accounts
# are simply called "Shared Services", "User Services", and "env0" (for
# example). However, until all legacy environments have been migrated to this
# new naming scheme, we must check the Shared Services account name via the
# regex below to determine whether we are using the legacy naming scheme or
# not.
sharedservices_account_name_type = length(regexall("\\(([^()]*)\\)", local.sharedservices_account_name)) == 1 ? "legacy" : "current"

# Determine the Domain Manager account of the same type
domainmanager_account_same_type = {
for account in data.aws_organizations_organization.cool.accounts :
account.id => account.name
if length(regexall("Domain Manager \\((${local.sharedservices_account_type})\\)", account.name)) > 0
}
assessment_account_name_regex = local.sharedservices_account_name_type == "legacy" ? format("^env[[:digit:]]+ \\(%s\\)$", trim(split("(", local.sharedservices_account_name)[1], ")")) : "^env[[:digit:]]+$"

# Determine the env* accounts of the same type
env_accounts_same_type = {
for account in data.aws_organizations_organization.cool.accounts :
account.id => account.name
if length(regexall("env[0-9]+ \\((${local.sharedservices_account_type})\\)", account.name)) > 0
}
userservices_account_name_regex = local.sharedservices_account_name_type == "legacy" ? format("^User Services \\(%s\\)$", trim(split("(", local.sharedservices_account_name)[1], ")")) : "^User Services$"

# Determine the PCA account of the same type
pca_account_same_type = {
for account in data.aws_organizations_organization.cool.accounts :
# Build a list of dynamic assessment account IDs whose account names match our
# regex.
env_accounts_same_type = {
for account in data.aws_organizations_organization.cool.non_master_accounts :
account.id => account.name
if length(regexall("PCA \\((${local.sharedservices_account_type})\\)", account.name)) > 0
if length(regexall(local.assessment_account_name_regex, account.name)) > 0
}

# Determine the User Services account of the same type
userservices_account_same_type = {
for account in data.aws_organizations_organization.cool.accounts :
for account in data.aws_organizations_organization.cool.non_master_accounts :
account.id => account.name
if length(regexall("User Services \\((${local.sharedservices_account_type})\\)", account.name)) > 0
if length(regexall(local.userservices_account_name_regex, account.name)) > 0
}

# Find the Users account by name.
users_account_id = [
for x in data.aws_organizations_organization.cool.accounts :
for x in data.aws_organizations_organization.cool.non_master_accounts :
x.id if x.name == "Users"
][0]
}
20 changes: 7 additions & 13 deletions remote_states.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,22 @@ data "terraform_remote_state" "master" {
backend = "s3"

config = {
bucket = "cisa-cool-terraform-state"
bucket = var.terraform_state_bucket
dynamodb_table = "terraform-state-lock"
encrypt = true
key = "cool-accounts/master.tfstate"
profile = "cool-terraform-backend"
region = "us-east-1"
}

# There is only one environment for this account, so there is
# no need to match the current Terraform workspace.
workspace = "production"
workspace = terraform.workspace
}

data "terraform_remote_state" "sharedservices" {
backend = "s3"

config = {
bucket = "cisa-cool-terraform-state"
bucket = var.terraform_state_bucket
dynamodb_table = "terraform-state-lock"
encrypt = true
key = "cool-accounts/shared_services.tfstate"
Expand All @@ -40,32 +38,28 @@ data "terraform_remote_state" "terraform" {
backend = "s3"

config = {
bucket = "cisa-cool-terraform-state"
bucket = var.terraform_state_bucket
dynamodb_table = "terraform-state-lock"
encrypt = true
key = "cool-accounts/terraform.tfstate"
profile = "cool-terraform-backend"
region = "us-east-1"
}

# There is only one environment for this account, so there is
# no need to match the current Terraform workspace.
workspace = "production"
workspace = terraform.workspace
}

data "terraform_remote_state" "users" {
backend = "s3"

config = {
bucket = "cisa-cool-terraform-state"
bucket = var.terraform_state_bucket
dynamodb_table = "terraform-state-lock"
encrypt = true
key = "cool-accounts/users.tfstate"
profile = "cool-terraform-backend"
region = "us-east-1"
}

# There is only one environment for this account, so there is
# no need to match the current Terraform workspace.
workspace = "production"
workspace = terraform.workspace
}
8 changes: 4 additions & 4 deletions transitgateway.tf
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ resource "aws_ram_resource_association" "tgw" {

#
# Share the resource with the other accounts that are allowed to access
# it (currently the Domain Manger, env*, PCA, and User Services accounts).
# it (currently the env* and User Services accounts).
#
resource "aws_ram_principal_association" "tgw" {
provider = aws.sharedservicesprovisionaccount

for_each = merge(local.domainmanager_account_same_type, local.env_accounts_same_type, local.pca_account_same_type, local.userservices_account_same_type)
for_each = merge(local.env_accounts_same_type, local.userservices_account_same_type)

principal = each.key
resource_share_arn = aws_ram_resource_share.tgw.id
Expand All @@ -66,15 +66,15 @@ resource "aws_ram_principal_association" "tgw" {
resource "aws_ec2_transit_gateway_route_table" "tgw_attachments" {
provider = aws.sharedservicesprovisionaccount

for_each = merge(local.domainmanager_account_same_type, local.env_accounts_same_type, local.pca_account_same_type)
for_each = local.env_accounts_same_type

transit_gateway_id = aws_ec2_transit_gateway.tgw.id
}
# Add routes to Shared Services to the route tables
resource "aws_ec2_transit_gateway_route" "sharedservices_routes" {
provider = aws.sharedservicesprovisionaccount

for_each = merge(local.domainmanager_account_same_type, local.env_accounts_same_type, local.pca_account_same_type)
for_each = tomap(local.env_accounts_same_type)

destination_cidr_block = aws_vpc.the_vpc.cidr_block
transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.tgw.id
Expand Down
6 changes: 6 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ variable "public_subnet_cidr_blocks" {
type = list(string)
}

variable "terraform_state_bucket" {
description = "The name of the S3 bucket where Terraform state is stored."
nullable = false
type = string
}

variable "vpc_cidr_block" {
description = "The overall CIDR block to be associated with the VPC (e.g. \"10.10.0.0/16\")."
nullable = false
Expand Down