Skip to content

Commit

Permalink
* 2.9.16 -- 2024-05-08
Browse files Browse the repository at this point in the history
  - tag-shared-vpc-resources
    - fix nacl tagging
  • Loading branch information
badra001 committed May 8, 2024
1 parent 4185231 commit 000cbc4
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,7 @@
- flowlogs-transit-gateway
- remove splunk
- add outputs (for generating subscription external to module)

* 2.9.16 -- 2024-05-08
- tag-shared-vpc-resources
- fix nacl tagging
224 changes: 224 additions & 0 deletions tag-shared-vpc-resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,227 @@ No modules.
## Outputs

No outputs.

<!-- BEGIN_TF_DOCS -->
# About aws-vpc-setup :: tag-shared-vpc-resources

This code reads a list of shared VPC resources, through RAM, available to the account and region, and will use the EC2 tag resouce to
set tags within this account.

This is necessary because resources shared by RAM do not include tags in the shared accounts. This is handled per
account and region.

It is required to have access to the appropriate remote account. Currently, it's the standard profile name ({account\_id}-{account\_alias}),but we will set this
up to use one's specific account profile with an assume role to get at the target account where the resources are shared.

For VPCs, it is expected that the account shared is the Network Production account, but any account and profile may be used if it shares VPC resources.

This reads tags and updates them in the local account and region from these resources:

* vpcs
* dhcp-option-sets
* subnets
* route-tables
* network-acls
* transit-gateway

## How it works

We get a list of VPCs, subnets, route tables and network ACLs which are owned by the network source account.
For each of these, other than network ACLs, we read each item with a `data` resource, and then the tags and owner on the
the resource in our account and region

For DHCP options sets, it reads the associated `dhcp_option_set` on the shared VPC. It uses that to obtain the tags
and set them accordingly.

Network ACLS are different. There is no data resource to get a specific network ACL. We read the list of network ACLs, but then
pass that into a `null_resource` to call the `aws` CLI. This gets a JSON file, which is then parsed and Tag and OwnerID extracted
to apply tags on the local network ACLs. There are enhancements issues for this missing resource in the terraform aws provider
[1](https://github.com/hashicorp/terraform-provider-aws/issues/19754),
[2](https://github.com/hashicorp/terraform-provider-aws/issues/4260).

# Usage

```hcl
# use of `tf-control` wrappers sets this value to your Linux username $USER
variable "os_username" {
type = string
default = null
}
provider "aws" {
alias = "network_account"
region = var.region
profile = var.profile
assume_role {
role_arn = "arn:aws-us-gov:iam::057405694017:role/r-inf-tf-remote-shared-vpc"
session_name = var.os_username
}
}
module "tag_shared" {
source = "git@github.e.it.census.gov:terraform-modules/aws-vpc-setup.git//tag-shared-vpc-resources?ref=tf-upgrade"
providers = {
aws = aws
aws.network_account = aws.network_account
}
## optional, with defaults
### tag_enabled_vpcs = true
### tag_enabled_dhcp_options = true
### tag_enabled_route_tables = true
### tag_enabled_subnets = true
### tag_enabled_network_acls = true
### tag_enabled_transit_gateway = true
}
```

**NOTE**
This is a multi-step module, if using the `tag_enabled_network_acls=true` (the default). This is because there is no Terraform
resource to get a network ACL (described above). As such, you must execute a `tf-apply` twice, once to create a file
indicating the AWS CLI has been run to obtain the data, and the second to tag the local network acls.

## Access Requirements

You need to have these AWS CLI IAM profiles and accounts available:

* AWS Profile for account where VPC resources are shared (primarily the ability to tag resoruces)
* The ability from your current profile to assume the role `r-inf-tf-remote-shared-vpc` in the Network account (this would be a different
account for SA, Lab, or EW; that will be updated once such stuff is available). A provider alias will be defined as shown
in the example (though the role ARN may differ in partition or account).

## Application Requirements

This requires the `aws` CLI v2.

## Input Variables

* tag\_enbled\_*

## Output Variables

This module does not have any outputs.

# Terraform Directions

Run this once per account and region. This module call belongs in `vpc/{region}/shared-vpc-setup`. This may be re-run when new
subnets and VPCs are shared to an account, or if tags change in the source account, and they need to be updated in this particular
account.

```script
tf-run apply
```

## tf-run.data

To use this with `tf-run`, you can will need to either use the module name twice, or use the statement `ALL` twice (preferred).

* example with module name twice

```script
VERSION 1.1.0
REMOTE-STATE
COMMAND tf-directory-setup.py -l none -f
COMMAND setup-new-directory.sh
COMMAND tf-init -upgrade
module.tag_shared
module.tag_shared
ALL
COMMAND tf-directory-setup.py -l s3
```

* example with ALL twice (recommended)

```script
VERSION 1.1.0
REMOTE-STATE
COMMAND tf-directory-setup.py -l none -f
COMMAND setup-new-directory.sh
COMMAND tf-init -upgrade
ALL
ALL
COMMAND tf-directory-setup.py -l s3
```

## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.66.0 |
| <a name="requirement_ldap"></a> [ldap](#requirement\_ldap) | >= 0.5.4 |
| <a name="requirement_local"></a> [local](#requirement\_local) | >= 1.0.0 |
| <a name="requirement_null"></a> [null](#requirement\_null) | >= 3.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.0 |
| <a name="requirement_template"></a> [template](#requirement\_template) | >= 2.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.66.0 |
| <a name="provider_aws.network_account"></a> [aws.network\_account](#provider\_aws.network\_account) | >= 3.66.0 |
| <a name="provider_local"></a> [local](#provider\_local) | >= 1.0.0 |
| <a name="provider_null"></a> [null](#provider\_null) | >= 3.0 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [aws_ec2_tag.dhcp_options](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource |
| [aws_ec2_tag.network_acls](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource |
| [aws_ec2_tag.route_tables](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource |
| [aws_ec2_tag.subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource |
| [aws_ec2_tag.transit_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource |
| [aws_ec2_tag.vpcs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_tag) | resource |
| [null_resource.network_acl](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [null_resource.network_acls](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [null_resource.setup_directory](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source |
| [aws_arn.network_account](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_caller_identity.network_account](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_ec2_transit_gateway.transit_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_transit_gateway) | data source |
| [aws_iam_account_alias.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_account_alias) | data source |
| [aws_network_acls.network_acls](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/network_acls) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
| [aws_route_table.route_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route_table) | data source |
| [aws_route_tables.route_tables](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route_tables) | data source |
| [aws_subnet.subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
| [aws_subnets.subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | data source |
| [aws_vpc.vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source |
| [aws_vpc_dhcp_options.dhcp_options](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_dhcp_options) | data source |
| [aws_vpcs.vpcs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpcs) | data source |
| [local_file.network_acl](https://registry.terraform.io/providers/hashicorp/local/latest/docs/data-sources/file) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_account_alias"></a> [account\_alias](#input\_account\_alias) | AWS Account Alias (default: will pull from current account\_alias) | `string` | `""` | no |
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | AWS Account ID (default: will pull from current user) | `string` | `""` | no |
| <a name="input_create"></a> [create](#input\_create) | Flag to indicate whether to create the resources or not (default: true) | `bool` | `true` | no |
| <a name="input_override_prefixes"></a> [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component. This should be used primarily for common infrastructure things | `map(string)` | `{}` | no |
| <a name="input_profile"></a> [profile](#input\_profile) | AWS profile of the account in which this is running | `string` | n/a | yes |
| <a name="input_role_arn"></a> [role\_arn](#input\_role\_arn) | AWS Role ARN of the target account, the network account where the shared VPC resources are configured, from which to pull tag data | `string` | n/a | yes |
| <a name="input_tag_enabled_dhcp_options"></a> [tag\_enabled\_dhcp\_options](#input\_tag\_enabled\_dhcp\_options) | Flag to tag or not tag shared VPC DHCP option sets | `bool` | `true` | no |
| <a name="input_tag_enabled_network_acls"></a> [tag\_enabled\_network\_acls](#input\_tag\_enabled\_network\_acls) | Flag to tag or not tag shared Network ACLs | `bool` | `true` | no |
| <a name="input_tag_enabled_route_tables"></a> [tag\_enabled\_route\_tables](#input\_tag\_enabled\_route\_tables) | Flag to tag or not tag shared VPC route tables | `bool` | `true` | no |
| <a name="input_tag_enabled_subnets"></a> [tag\_enabled\_subnets](#input\_tag\_enabled\_subnets) | Flag to tag or not tag shared VPC subnets | `bool` | `true` | no |
| <a name="input_tag_enabled_transit_gateway"></a> [tag\_enabled\_transit\_gateway](#input\_tag\_enabled\_transit\_gateway) | Flag to tag or not tag shared VPC Transit Gateway (not currently possible in AWS; this has no effect) | `bool` | `true` | no |
| <a name="input_tag_enabled_vpcs"></a> [tag\_enabled\_vpcs](#input\_tag\_enabled\_vpcs) | Flag to tag or not tag shared VPCs | `bool` | `true` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | AWS Tags to apply to appropriate resources (S3, KMS). Do not include safeguard tags here, use the data\_safeguard field for such things. | `map(string)` | `{}` | no |
| <a name="input_vpc_environment"></a> [vpc\_environment](#input\_vpc\_environment) | VPC environment purpose (infrastructure, common, shared, dev, stage, ite, prod) | `string` | `null` | no |
| <a name="input_vpc_full_name"></a> [vpc\_full\_name](#input\_vpc\_full\_name) | VPC full name component (vpc{index}-{vpc\_name}) | `string` | `null` | no |
| <a name="input_vpc_index"></a> [vpc\_index](#input\_vpc\_index) | VPC index number (integer starting at 1) | `number` | `null` | no |
| <a name="input_vpc_name"></a> [vpc\_name](#input\_vpc\_name) | VPC name component used through the VPC descrbing its purpose (ex: dice-dev) | `string` | `null` | no |
| <a name="input_vpc_short_name"></a> [vpc\_short\_name](#input\_vpc\_short\_name) | VPC short name component (vpc{index}) | `string` | `null` | no |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
14 changes: 10 additions & 4 deletions tag-shared-vpc-resources/tag-network-acls.tf
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,17 @@ resource "null_resource" "network_acls" {
## }

locals {
# _nacl_enabled = var.tag_enabled_network_acls
# _network_acls = local._nacl_enabled ? { for k, v in data.local_file.network_acl : k => jsondecode(v.content) } : {}
# network_acls = fileexists(null_resource.network_acls.triggers.filename) ? { for k, v in local._network_acls : k => lookup(v, "NetworkAcls", [{ "Tags" : [], "OwnerId" : "" }])[0] } : {}
# network_acls_tags = fileexists(null_resource.network_acls.triggers.filename) ? { for k, v in local.network_acls : k => merge({ for t in v.Tags : t.Key => t.Value }, { "boc:vpc:owner_id" = v.OwnerId }) } : {}
# network_acls_tags_map = fileexists(null_resource.network_acls.triggers.filename) ? flatten([for k, v in local.network_acls_tags : [for tk, tv in v : { label = format("%v__%v", k, tk), network_acl_id = k, key = tk, value = tv }]]) : []

_nacl_enabled = var.tag_enabled_network_acls
_network_acls = local._nacl_enabled ? { for k, v in data.local_file.network_acl : k => jsondecode(v.content) } : {}
network_acls = fileexists(null_resource.network_acls.triggers.filename) ? { for k, v in local._network_acls : k => lookup(v, "NetworkAcls", [{ "Tags" : [], "OwnerId" : "" }])[0] } : {}
network_acls_tags = fileexists(null_resource.network_acls.triggers.filename) ? { for k, v in local.network_acls : k => merge({ for t in v.Tags : t.Key => t.Value }, { "boc:vpc:owner_id" = v.OwnerId }) } : {}
network_acls_tags_map = fileexists(null_resource.network_acls.triggers.filename) ? flatten([for k, v in local.network_acls_tags : [for tk, tv in v : { label = format("%v__%v", k, tk), network_acl_id = k, key = tk, value = tv }]]) : []
_network_acls = { for k, v in data.local_file.network_acl : k => jsondecode(v.content) if local._nacl_enabled }
network_acls = { for k, v in local._network_acls : k => lookup(v, "NetworkAcls", [{ "Tags" : [], "OwnerId" : "" }])[0] }
network_acls_tags = { for k, v in local.network_acls : k => merge({ for t in v.Tags : t.Key => t.Value }, { "boc:vpc:owner_id" = v.OwnerId }) }
network_acls_tags_map = flatten([for k, v in local.network_acls_tags : [for tk, tv in v : { label = format("%v__%v", k, tk), network_acl_id = k, key = tk, value = tv }]])
}

resource "aws_ec2_tag" "network_acls" {
Expand Down

0 comments on commit 000cbc4

Please sign in to comment.