├── .circleci └── config.yml ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .terraform-version ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── examples ├── basic_usage.tf ├── custom_azs.tf └── vpn_provided.tf ├── main.tf ├── outputs.tf ├── tests ├── test1 │ └── main.tf ├── test2 │ └── main.tf └── test3 │ └── main.tf └── variables.tf /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | test: 4 | docker: 5 | - image: 891714082543.dkr.ecr.us-west-2.amazonaws.com/rackspace-tf-toolbox 6 | working_directory: ~/ 7 | steps: 8 | - attach_workspace: 9 | at: ~/workspace 10 | - checkout: 11 | path: ~/branches/${CIRCLE_BRANCH} 12 | - run: 13 | name: Run Test 14 | command: MODULE_CI_JOB='test' python3 orchestrate.py 15 | - run: 16 | name: destroy 17 | command: cd ~ && ~/bin/destroy.sh && ~/bin/destroy_s3_buckets.sh # must succeed or we have something to clean up manually 18 | when: always 19 | - persist_to_workspace: 20 | root: ~/workspace 21 | paths: 22 | - plan_results/* 23 | - artifacts/* 24 | check_destruction: 25 | docker: 26 | - image: 891714082543.dkr.ecr.us-west-2.amazonaws.com/rackspace-tf-toolbox 27 | working_directory: ~/ 28 | steps: 29 | - attach_workspace: 30 | at: ~/workspace 31 | - checkout: 32 | path: ~/module 33 | - run: 34 | name: Check Destruction 35 | command: MODULE_CI_JOB='check_destruction' python3 orchestrate.py 36 | - store_artifacts: 37 | path: ~/workspace/artifacts 38 | 39 | 40 | workflows: 41 | version: 2 42 | build_and_test: 43 | jobs: 44 | - test: 45 | filters: 46 | branches: 47 | ignore: master 48 | - check_destruction: 49 | requires: 50 | - test 51 | filters: 52 | branches: 53 | ignore: master 54 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ##### Corresponding Issue(s): 2 | - PRs should have a corresponding issue. If no issue exists, provide details in the **Reason for Change(s)** section 3 | 4 | ##### Summary of change(s): 5 | 6 | ##### Reason for Change(s): 7 | 8 | - If a bug, describe error scenario, including expected behavior and actual behavior. 9 | 10 | - If an enhancement, describe the use case and the perceived benefit(s). 11 | 12 | ##### Will the change trigger resource destruction or replacement? If yes, please provide justification: 13 | 14 | ##### Does this update/change involve issues with other external modules? If so, please describe the scenario. 15 | 16 | ##### If input variables or output variables have changed or has been added, have you updated the README? 17 | 18 | ##### Do examples need to be updated based on changes? 19 | 20 | ##### Note to the PR requester about Closing PR's 21 | Please message the person that opened the issue when auto closing it on slack, as well as any other stake holders of deep interest. Only close the issue if you believe that the issue is fully resolved with this PR. 22 | 23 | #### This PR may auto close the issue associated with it. If you feel the issue is not resolved please reopen the issue. 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # .tfvars files 9 | *.tfvars 10 | 11 | # .terraform.lock.hcl -- not currently using 12 | .terraform.lock.hcl 13 | 14 | # Plans 15 | *.plans 16 | 17 | # Secrets 18 | secrets.tf 19 | 20 | # Mac 21 | .DS_Store 22 | 23 | # Idea 24 | .idea/* -------------------------------------------------------------------------------- /.terraform-version: -------------------------------------------------------------------------------- 1 | 1.1.9 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | This document is a set of guidelines to contribute to the [Rackspace](https://www.rackspace.com) [Terraform](https://www.terraform.io/) modules. These modules were created to enable and empower our Managed Infrastructure as Code product. While these modules are designed for Rackspace environments, we hope they may be of use to the community at large. 4 | 5 | ## Reporting issues 6 | 7 | Rackspace customers should open a normal Rackspace support ticket to report any problems or feedback for these modules. Our support Rackers will then work with you and our product team to address those issues. 8 | 9 | ## Submitting Changes 10 | 11 | Pull requests are welcome. Currently our CI solution cannot run against pull requests from forks. Despite this, these submissions are still welcome. All pull requests will be evaluated against Rackspace best practices, and after approval, will be migrated to a branch for testing. 12 | 13 | For significant or critical features, a new test should be created, or if appropriate, and existing test should be updated. This will serve to ensure the any failures related to the feature are caught. 14 | 15 | When submitting a pull request please explain the changes in detail, and try to minimize the scope to related changes. For example, changes resolving separate bugs should be broken out into separate pull requests. If there are relevant documentation or references, such as terraform bug reports, links to those references are greatly appreciated. 16 | 17 | ## Testing 18 | 19 | Pull requests must pass a linting and build test prior to acceptance. This test ensures all submissions will produce a stable and working deployment. In addition, our CI jobs will run a check_destruction test to help determine if any breaking changes will occur with this change. This test will show a failed result if breaking changes are detected, but this will not prevent the submission from being approved. 20 | 21 | ## Coding and Style guidelines 22 | 23 | All submissions should comply with the coding and style guides outlined at [General Terraform Style Guide](https://manage.rackspace.com/aws/docs/product-guide/miac/terraform-standards.html#general-terraform-style-guide) and [Rackspace Module Standards](https://manage.rackspace.com/aws/docs/product-guide/miac/terraform-standards.html#rackspace-module-). CI testing will run and flag code that does not meet these defined standards whenever possible. 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Rackspace, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-terraform-vpc\_basenetwork 2 | 3 | This module sets up basic network components for an account in a specific region. Optionally it will setup a basic VPN gateway and VPC flow logs. 4 | 5 | ## Basic Usage 6 | 7 | ```HCL 8 | module "vpc" { 9 | source = "git@github.com:rackspace-infrastructure-automation/aws-terraform-vpc_basenetwork//?ref=v0.12.2" 10 | 11 | vpc_name = "MyVPC" 12 | } 13 | ``` 14 | 15 | Full working references are available at [examples](examples) 16 | ## Default Resources 17 | 18 | By default only `vpc_name` is required to be set. Unless changed `aws_region` defaults to `us-west-2` and will need to be updated for other regions. `source` will also need to be declared depending on where the module lives. Given default settings the following resources are created: 19 | 20 | - VPC Flow Logs 21 | - 2 AZs with public/private subnets from the list of 3 static CIDRs ranges available for each as defaults 22 | - Public/private subnets with the count related to custom\_azs if defined or region AZs automatically calculated by Terraform otherwise 23 | - NAT Gateways will be created in each AZ's first public subnet 24 | - EIPs will be created in all public subnets for NAT gateways to use 25 | - Route Tables, including routes to NAT gateways if applicable 26 | 27 | ## Terraform 0.12 upgrade 28 | 29 | Several changes were required while adding terraform 0.12 compatibility. The following changes should be 30 | made when upgrading from a previous release to version 0.12.0 or higher. 31 | 32 | ### Module variables 33 | 34 | The following module variables were updated to better meet current Rackspace style guides: 35 | 36 | - `custom_tags` -> `tags` 37 | - `vpc_name` -> `name` 38 | 39 | ## Requirements 40 | 41 | | Name | Version | 42 | |------|---------| 43 | | terraform | >= 0.13 | 44 | | aws | >= 4.0 | 45 | 46 | ## Providers 47 | 48 | | Name | Version | 49 | |------|---------| 50 | | aws | >= 4.0 | 51 | 52 | ## Modules 53 | 54 | No Modules. 55 | 56 | ## Resources 57 | 58 | | Name | 59 | |------| 60 | | [aws_availability_zones](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/data-sources/availability_zones) | 61 | | [aws_cloudwatch_log_group](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/cloudwatch_log_group) | 62 | | [aws_eip](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/eip) | 63 | | [aws_flow_log](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/flow_log) | 64 | | [aws_iam_role](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/iam_role) | 65 | | [aws_iam_role_policy](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/iam_role_policy) | 66 | | [aws_internet_gateway](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/internet_gateway) | 67 | | [aws_nat_gateway](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/nat_gateway) | 68 | | [aws_region](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/data-sources/region) | 69 | | [aws_route](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/route) | 70 | | [aws_route_table](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/route_table) | 71 | | [aws_route_table_association](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/route_table_association) | 72 | | [aws_s3_bucket](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/s3_bucket) | 73 | | [aws_s3_bucket_acl](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/s3_bucket_acl) | 74 | | [aws_s3_bucket_lifecycle_configuration](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/s3_bucket_lifecycle_configuration) | 75 | | [aws_s3_bucket_server_side_encryption_configuration](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/s3_bucket_server_side_encryption_configuration) | 76 | | [aws_subnet](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/subnet) | 77 | | [aws_vpc](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/vpc) | 78 | | [aws_vpc_dhcp_options](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/vpc_dhcp_options) | 79 | | [aws_vpc_dhcp_options_association](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/vpc_dhcp_options_association) | 80 | | [aws_vpn_gateway](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/vpn_gateway) | 81 | | [aws_vpn_gateway_route_propagation](https://registry.terraform.io/providers/hashicorp/aws/4.0/docs/resources/vpn_gateway_route_propagation) | 82 | 83 | ## Inputs 84 | 85 | | Name | Description | Type | Default | Required | 86 | |------|-------------|------|---------|:--------:| 87 | | az\_count | Number of AZs to utilize for the subnets | `number` | `2` | no | 88 | | build\_flow\_logs | Whether or not to build flow log components in Cloudwatch Logs | `bool` | `false` | no | 89 | | build\_igw | Whether or not to build an internet gateway. If disabled, no public subnets or route tables, internet gateway, or NAT Gateways will be created. | `bool` | `true` | no | 90 | | build\_nat\_gateways | Whether or not to build a NAT gateway per AZ. if `build_igw` is set to false, this value is ignored. | `bool` | `true` | no | 91 | | build\_s3\_flow\_logs | Whether or not to build flow log components in s3 | `bool` | `false` | no | 92 | | build\_vpn | Whether or not to build a VPN gateway | `bool` | `false` | no | 93 | | cidr\_range | CIDR range for the VPC | `string` | `"172.18.0.0/19"` | no | 94 | | cloudwatch\_flowlog\_retention | The number of days to retain flowlogs in CLoudwatch Logs. Valid values are: [0, 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]. A value of `0` will retain indefinitely. | `number` | `14` | no | 95 | | custom\_azs | A list of AZs that VPC resources will reside in | `list(string)` | `[]` | no | 96 | | default\_tenancy | Default tenancy for instances. Either multi-tenant (default) or single-tenant (dedicated) | `string` | `"default"` | no | 97 | | domain\_name | Custom domain name for the VPC | `string` | `""` | no | 98 | | domain\_name\_servers | Array of custom domain name servers | `list(string)` |
[
"AmazonProvidedDNS"
]
| no | 99 | | enable\_dns\_hostnames | Whether or not to enable DNS hostnames for the VPC | `bool` | `true` | no | 100 | | enable\_dns\_support | Whether or not to enable DNS support for the VPC | `bool` | `true` | no | 101 | | environment | Application environment for which this network is being created. e.g. Development/Production | `string` | `"Development"` | no | 102 | | logging\_bucket\_access\_control | Define ACL for Bucket from one of the [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl): private, public-read, public-read-write, aws-exec-read, authenticated-read, log-delivery-write | `string` | `"private"` | no | 103 | | logging\_bucket\_encryption | Enable default bucket encryption. i.e. AES256 or aws:kms | `string` | `"AES256"` | no | 104 | | logging\_bucket\_encryption\_kms\_mster\_key | The AWS KMS master key ID used for the SSE-KMS encryption. This can only be used when you set the value of sse\_algorithm as aws:kms. | `string` | `""` | no | 105 | | logging\_bucket\_force\_destroy | Whether all objects should be deleted from the bucket so that the bucket can be destroyed without error. These objects are not recoverable. ie. true | `bool` | `false` | no | 106 | | logging\_bucket\_name | Bucket name to store s3 flow logs. If empty, a random bucket name is generated. Use in conjuction with `build_s3_flow_logs` | `string` | `""` | no | 107 | | logging\_bucket\_prefix | The prefix for the location in the S3 bucket. If you don't specify a prefix, the access logs are stored in the root of the bucket. | `string` | `""` | no | 108 | | name | Name prefix for the VPC and related resources | `string` | n/a | yes | 109 | | private\_cidr\_ranges | An array of CIDR ranges to use for private subnets | `list(string)` |
[
"172.18.16.0/22",
"172.18.20.0/22",
"172.18.24.0/22"
]
| no | 110 | | private\_subnet\_names | Text that will be included in generated name for private subnets. Given the default value of `["Private"]`, subnet
names in the form \"-Private\", e.g. \"MyVpc-Public2\" will be produced. Otherwise, given a
list of names with length the same as the value of `az_count`, the first `az_count` subnets will be named using
the first string in the list, the second `az_count` subnets will be named using the second string, and so on. | `list(string)` |
[
"Private"
]
| no | 111 | | private\_subnet\_tags | A list of maps containing tags to be applied to private subnets. List should either be the same length as the number of AZs to apply different tags per set of subnets, or a length of 1 to apply the same tags across all private subnets. | `list(map(string))` |
[
{}
]
| no | 112 | | private\_subnets\_per\_az | Number of private subnets to create in each AZ. NOTE: This value, when multiplied by the value of `az_count`,
should not exceed the length of the `private_cidr_ranges` list! | `number` | `1` | no | 113 | | public\_cidr\_ranges | An array of CIDR ranges to use for public subnets | `list(string)` |
[
"172.18.0.0/22",
"172.18.4.0/22",
"172.18.8.0/22"
]
| no | 114 | | public\_subnet\_names | Text that will be included in generated name for public subnets. Given the default value of `["Public"]`, subnet
names in the form \"-Public\", e.g. \"MyVpc-Public1\" will be produced. Otherwise, given a
list of names with length the same as the value of `az_count`, the first `az_count` subnets will be named using
the first string in the list, the second `az_count` subnets will be named using the second string, and so on. | `list(string)` |
[
"Public"
]
| no | 115 | | public\_subnet\_tags | A list of maps containing tags to be applied to public subnets. List should either be the same length as the number of AZs to apply different tags per set of subnets, or a length of 1 to apply the same tags across all public subnets. | `list(map(string))` |
[
{}
]
| no | 116 | | public\_subnets\_per\_az | Number of public subnets to create in each AZ. NOTE: This value, when multiplied by the value of `az_count`,
should not exceed the length of the `public_cidr_ranges` list! | `number` | `1` | no | 117 | | s3\_flowlog\_retention | The number of days to retain flowlogs in s3. A value of `0` will retain indefinitely. | `number` | `14` | no | 118 | | single\_nat | Deploy VPC in single NAT mode. | `bool` | `false` | no | 119 | | spoke\_vpc | Whether or not the VPN gateway is a spoke of a Transit VPC | `bool` | `false` | no | 120 | | tags | Optional tags to be applied on top of the base tags on all resources | `map(string)` | `{}` | no | 121 | 122 | ## Outputs 123 | 124 | | Name | Description | 125 | |------|-------------| 126 | | default\_sg | The ID of the default SG for the VPC | 127 | | flowlog\_log\_group\_arn | The ARN of the flow log CloudWatch log group if one was created | 128 | | flowlog\_log\_group\_name | The name of the flow log CloudWatch log group if one was created | 129 | | internet\_gateway | The ID of the Internet Gateway | 130 | | nat\_gateway | The ID of the NAT Gateway if one was created | 131 | | nat\_gateway\_ids | The ID of the NAT Gateway if one was created | 132 | | nat\_gateway\_private\_ips | The private IPs of the NAT Gateway if one was created | 133 | | nat\_gateway\_public\_ips | The public IPs of the NAT Gateway if one was created | 134 | | private\_route\_tables | The IDs for the private route tables | 135 | | private\_subnets | The IDs for the private subnets | 136 | | public\_route\_tables | The IDs for the public route tables | 137 | | public\_subnets | The IDs of the public subnets | 138 | | vpc\_id | The ID of the VPC | 139 | | vpn\_gateway | The ID of the VPN gateway if one was created | 140 | -------------------------------------------------------------------------------- /examples/basic_usage.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-west-2" 14 | } 15 | 16 | module "vpc" { 17 | source = "git@github.com:rackspace-infrastructure-automation/aws-terraform-vpc_basenetwork//?ref=v0.12.9" 18 | 19 | name = "MyVPC" 20 | } 21 | -------------------------------------------------------------------------------- /examples/custom_azs.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-west-2" 14 | } 15 | 16 | module "vpc" { 17 | source = "git@github.com:rackspace-infrastructure-automation/aws-terraform-vpc_basenetwork//?ref=v0.12.9" 18 | 19 | name = "MyVPC" 20 | custom_azs = ["us-west-2a", "us-west-2b"] 21 | 22 | cidr_range = "10.0.0.0/19" 23 | public_cidr_ranges = ["10.0.4.0/22", "10.0.8.0/22"] 24 | public_subnets_per_az = 1 25 | private_cidr_ranges = ["10.0.12.0/22", "10.0.16.0/22"] 26 | private_subnets_per_az = 1 27 | } 28 | -------------------------------------------------------------------------------- /examples/vpn_provided.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-west-2" 14 | } 15 | 16 | module "vpc" { 17 | source = "git@github.com:rackspace-infrastructure-automation/aws-terraform-vpc_basenetwork//?ref=v0.12.9" 18 | 19 | name = "MyVPC" 20 | build_vpn = true 21 | spoke_vpc = true 22 | } 23 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * # aws-terraform-vpc_basenetwork 3 | * 4 | * This module sets up basic network components for an account in a specific region. Optionally it will setup a basic VPN gateway and VPC flow logs. 5 | * 6 | * ## Basic Usage 7 | * 8 | * ```HCL 9 | * module "vpc" { 10 | * source = "git@github.com:rackspace-infrastructure-automation/aws-terraform-vpc_basenetwork//?ref=v0.12.2" 11 | * 12 | * vpc_name = "MyVPC" 13 | * } 14 | * ``` 15 | * 16 | * Full working references are available at [examples](examples) 17 | * ## Default Resources 18 | * 19 | * By default only `vpc_name` is required to be set. Unless changed `aws_region` defaults to `us-west-2` and will need to be updated for other regions. `source` will also need to be declared depending on where the module lives. Given default settings the following resources are created: 20 | * 21 | * - VPC Flow Logs 22 | * - 2 AZs with public/private subnets from the list of 3 static CIDRs ranges available for each as defaults 23 | * - Public/private subnets with the count related to custom_azs if defined or region AZs automatically calculated by Terraform otherwise 24 | * - NAT Gateways will be created in each AZ's first public subnet 25 | * - EIPs will be created in all public subnets for NAT gateways to use 26 | * - Route Tables, including routes to NAT gateways if applicable 27 | * 28 | * ## Terraform 0.12 upgrade 29 | * 30 | * Several changes were required while adding terraform 0.12 compatibility. The following changes should be 31 | * made when upgrading from a previous release to version 0.12.0 or higher. 32 | * 33 | * ### Module variables 34 | * 35 | * The following module variables were updated to better meet current Rackspace style guides: 36 | * 37 | * - `custom_tags` -> `tags` 38 | * - `vpc_name` -> `name` 39 | */ 40 | 41 | terraform { 42 | required_version = ">= 0.13" 43 | 44 | required_providers { 45 | aws = { 46 | source = "hashicorp/aws" 47 | version = ">= 4.0" 48 | } 49 | } 50 | } 51 | 52 | locals { 53 | default_domain_name = data.aws_region.current.name == "us-east-1" ? "ec2.internal" : format("%s.compute.internal", data.aws_region.current.name) 54 | domain_name = var.domain_name == "" ? local.default_domain_name : var.domain_name 55 | 56 | base_tags = { 57 | ServiceProvider = "Rackspace" 58 | Environment = var.environment 59 | } 60 | 61 | single_nat_tag = { 62 | true = { 63 | HA = "Disabled" 64 | } 65 | 66 | false = {} 67 | } 68 | 69 | nat_count = var.single_nat ? 1 : var.az_count 70 | 71 | tags = merge( 72 | var.tags, 73 | local.base_tags, 74 | ) 75 | 76 | azs = slice( 77 | coalescelist(var.custom_azs, data.aws_availability_zones.available.names), 78 | 0, 79 | var.az_count, 80 | ) 81 | } 82 | 83 | data "aws_availability_zones" "available" {} 84 | 85 | data "aws_region" "current" {} 86 | 87 | ############# 88 | # Basic VPC 89 | ############# 90 | 91 | resource "aws_vpc" "vpc" { 92 | cidr_block = var.cidr_range 93 | enable_dns_hostnames = var.enable_dns_hostnames 94 | enable_dns_support = var.enable_dns_support 95 | instance_tenancy = var.default_tenancy 96 | 97 | tags = merge( 98 | local.tags, 99 | local.single_nat_tag[var.single_nat], 100 | { 101 | Name = var.name 102 | }, 103 | ) 104 | 105 | lifecycle { 106 | create_before_destroy = true 107 | } 108 | } 109 | 110 | resource "aws_vpc_dhcp_options" "dhcp_options" { 111 | domain_name = local.domain_name 112 | domain_name_servers = var.domain_name_servers 113 | 114 | tags = merge( 115 | local.tags, 116 | { 117 | Name = "${var.name}-DHCPOptions" 118 | }, 119 | ) 120 | } 121 | 122 | resource "aws_vpc_dhcp_options_association" "dhcp_options_association" { 123 | dhcp_options_id = aws_vpc_dhcp_options.dhcp_options.id 124 | vpc_id = aws_vpc.vpc.id 125 | } 126 | 127 | resource "aws_internet_gateway" "igw" { 128 | count = var.build_igw ? 1 : 0 129 | 130 | vpc_id = aws_vpc.vpc.id 131 | 132 | tags = merge( 133 | local.tags, 134 | { 135 | Name = format("%s-IGW", var.name) 136 | }, 137 | ) 138 | } 139 | 140 | ############# 141 | # NAT Gateway 142 | ############# 143 | 144 | resource "aws_eip" "nat_eip" { 145 | count = var.build_nat_gateways && var.build_igw ? local.nat_count : 0 146 | 147 | domain = "vpc" 148 | 149 | tags = merge( 150 | local.tags, 151 | { 152 | Name = format("%s-NATEIP%d", var.name, count.index + 1) 153 | }, 154 | ) 155 | 156 | depends_on = [aws_internet_gateway.igw] 157 | } 158 | 159 | resource "aws_nat_gateway" "nat" { 160 | count = var.build_nat_gateways && var.build_igw ? local.nat_count : 0 161 | 162 | allocation_id = element(aws_eip.nat_eip.*.id, count.index) 163 | subnet_id = element(aws_subnet.public_subnet.*.id, count.index) 164 | 165 | tags = merge( 166 | local.tags, 167 | local.single_nat_tag[var.single_nat], 168 | { 169 | Name = format("%s-NATGW%d", var.name, count.index + 1) 170 | }, 171 | ) 172 | 173 | depends_on = [aws_internet_gateway.igw] 174 | } 175 | 176 | ############# 177 | # Subnets 178 | ############# 179 | 180 | resource "aws_subnet" "public_subnet" { 181 | count = var.build_igw ? var.az_count * var.public_subnets_per_az : 0 182 | 183 | availability_zone = element(local.azs, count.index) 184 | cidr_block = var.public_cidr_ranges[count.index] 185 | map_public_ip_on_launch = true 186 | vpc_id = aws_vpc.vpc.id 187 | 188 | tags = merge( 189 | var.public_subnet_tags[length(var.public_subnet_tags) == 1 ? 0 : floor(count.index / var.az_count)], 190 | local.tags, 191 | local.single_nat_tag[var.single_nat], 192 | { 193 | Name = format( 194 | "%s-%s%d", 195 | var.name, 196 | element(var.public_subnet_names, floor(count.index / var.az_count)), 197 | count.index % var.az_count + 1, 198 | ) 199 | }, 200 | ) 201 | } 202 | 203 | resource "aws_subnet" "private_subnet" { 204 | count = var.az_count * var.private_subnets_per_az 205 | 206 | availability_zone = element(local.azs, count.index) 207 | cidr_block = var.private_cidr_ranges[count.index] 208 | map_public_ip_on_launch = false 209 | vpc_id = aws_vpc.vpc.id 210 | 211 | tags = merge( 212 | var.private_subnet_tags[length(var.private_subnet_tags) == 1 ? 0 : floor(count.index / var.az_count)], 213 | local.tags, 214 | local.single_nat_tag[var.single_nat], 215 | { 216 | Name = format( 217 | "%s-%s%d", 218 | var.name, 219 | element(var.private_subnet_names, floor(count.index / var.az_count)), 220 | count.index % var.az_count + 1, 221 | ) 222 | }, 223 | ) 224 | } 225 | 226 | ######################### 227 | # Route Tables and Routes 228 | ######################### 229 | 230 | resource "aws_route_table" "public_route_table" { 231 | count = var.build_igw ? 1 : 0 232 | 233 | vpc_id = aws_vpc.vpc.id 234 | 235 | tags = merge( 236 | local.tags, 237 | { 238 | Name = format("%s-PublicRouteTable", var.name) 239 | }, 240 | ) 241 | } 242 | 243 | resource "aws_route_table" "private_route_table" { 244 | count = var.az_count 245 | 246 | vpc_id = aws_vpc.vpc.id 247 | 248 | tags = merge( 249 | local.tags, 250 | local.single_nat_tag[var.single_nat], 251 | { 252 | Name = format("%s-PrivateRouteTable%d", var.name, count.index + 1) 253 | }, 254 | ) 255 | } 256 | 257 | resource "aws_route" "public_routes" { 258 | count = var.build_igw ? 1 : 0 259 | 260 | destination_cidr_block = "0.0.0.0/0" 261 | gateway_id = aws_internet_gateway.igw[0].id 262 | route_table_id = aws_route_table.public_route_table[0].id 263 | } 264 | 265 | resource "aws_route" "private_routes" { 266 | count = var.build_nat_gateways && var.build_igw ? var.az_count : 0 267 | 268 | destination_cidr_block = "0.0.0.0/0" 269 | nat_gateway_id = element(aws_nat_gateway.nat.*.id, count.index) 270 | route_table_id = element(aws_route_table.private_route_table.*.id, count.index) 271 | } 272 | 273 | resource "aws_route_table_association" "public_route_association" { 274 | count = var.build_igw ? var.az_count * var.public_subnets_per_az : 0 275 | 276 | route_table_id = aws_route_table.public_route_table[0].id 277 | subnet_id = element(aws_subnet.public_subnet.*.id, count.index) 278 | } 279 | 280 | resource "aws_route_table_association" "private_route_association" { 281 | count = var.az_count * var.private_subnets_per_az 282 | 283 | route_table_id = element(aws_route_table.private_route_table.*.id, count.index) 284 | subnet_id = element(aws_subnet.private_subnet.*.id, count.index) 285 | } 286 | 287 | ##### 288 | # VPN 289 | ##### 290 | 291 | resource "aws_vpn_gateway" "vpn_gateway" { 292 | count = var.build_vpn ? 1 : 0 293 | 294 | vpc_id = aws_vpc.vpc.id 295 | 296 | tags = merge( 297 | local.tags, 298 | { 299 | Name = format("%s-VPNGateway", var.name) 300 | "transitvpc:spoke" = var.spoke_vpc 301 | }, 302 | ) 303 | } 304 | 305 | resource "aws_vpn_gateway_route_propagation" "vpn_routes_public" { 306 | count = var.build_vpn ? 1 : 0 307 | 308 | route_table_id = aws_route_table.public_route_table[0].id 309 | vpn_gateway_id = aws_vpn_gateway.vpn_gateway[0].id 310 | } 311 | 312 | resource "aws_vpn_gateway_route_propagation" "vpn_routes_private" { 313 | count = var.build_vpn ? length(var.private_cidr_ranges) : 0 314 | 315 | route_table_id = element(aws_route_table.private_route_table.*.id, count.index) 316 | vpn_gateway_id = aws_vpn_gateway.vpn_gateway[0].id 317 | } 318 | 319 | ########### 320 | # Flow Logs 321 | ########### 322 | 323 | resource "aws_flow_log" "s3_vpc_log" { 324 | count = var.build_s3_flow_logs ? 1 : 0 325 | 326 | log_destination = aws_s3_bucket.vpc_log_bucket[0].arn 327 | log_destination_type = "s3" 328 | traffic_type = "ALL" 329 | vpc_id = aws_vpc.vpc.id 330 | } 331 | 332 | resource "aws_s3_bucket" "vpc_log_bucket" { 333 | count = var.build_s3_flow_logs ? 1 : 0 334 | 335 | bucket = var.logging_bucket_name 336 | force_destroy = var.logging_bucket_force_destroy 337 | tags = local.tags 338 | 339 | } 340 | 341 | resource "aws_s3_bucket_acl" "private_acl" { 342 | count = var.build_s3_flow_logs ? 1 : 0 343 | 344 | bucket = aws_s3_bucket.vpc_log_bucket[count.index] 345 | acl = var.logging_bucket_access_control 346 | } 347 | 348 | resource "aws_s3_bucket_server_side_encryption_configuration" "s3_sse" { 349 | count = var.build_s3_flow_logs ? 1 : 0 350 | 351 | bucket = aws_s3_bucket.vpc_log_bucket[count.index] 352 | rule { 353 | apply_server_side_encryption_by_default { 354 | kms_master_key_id = var.logging_bucket_encryption_kms_mster_key 355 | sse_algorithm = var.logging_bucket_encryption 356 | } 357 | } 358 | } 359 | 360 | resource "aws_s3_bucket_lifecycle_configuration" "s3_lifecycle" { 361 | count = var.build_s3_flow_logs ? 1 : 0 362 | 363 | bucket = aws_s3_bucket.vpc_log_bucket[count.index] 364 | rule { 365 | id = "Expiration" 366 | status = "Enabled" 367 | prefix = var.logging_bucket_prefix 368 | expiration { 369 | days = var.s3_flowlog_retention 370 | } 371 | } 372 | } 373 | 374 | resource "aws_flow_log" "cw_vpc_log" { 375 | count = var.build_flow_logs ? 1 : 0 376 | 377 | iam_role_arn = aws_iam_role.flowlog_role[0].arn 378 | log_destination = aws_cloudwatch_log_group.flowlog_group[0].arn 379 | traffic_type = "ALL" 380 | vpc_id = aws_vpc.vpc.id 381 | } 382 | 383 | resource "aws_cloudwatch_log_group" "flowlog_group" { 384 | count = var.build_flow_logs ? 1 : 0 385 | 386 | name = "${var.name}-FlowLogs" 387 | retention_in_days = var.cloudwatch_flowlog_retention 388 | } 389 | 390 | resource "aws_iam_role" "flowlog_role" { 391 | count = var.build_flow_logs ? 1 : 0 392 | 393 | name = "${var.name}-FlowLogsRole" 394 | 395 | assume_role_policy = <-Private\", e.g. \"MyVpc-Public2\" will be produced. Otherwise, given a 147 | list of names with length the same as the value of `az_count`, the first `az_count` subnets will be named using 148 | the first string in the list, the second `az_count` subnets will be named using the second string, and so on. 149 | EOF 150 | 151 | type = list(string) 152 | default = ["Private"] 153 | } 154 | 155 | variable "private_subnet_tags" { 156 | description = "A list of maps containing tags to be applied to private subnets. List should either be the same length as the number of AZs to apply different tags per set of subnets, or a length of 1 to apply the same tags across all private subnets." 157 | type = list(map(string)) 158 | 159 | default = [{}] 160 | } 161 | 162 | variable "private_subnets_per_az" { 163 | description = <-Public\", e.g. \"MyVpc-Public1\" will be produced. Otherwise, given a 187 | list of names with length the same as the value of `az_count`, the first `az_count` subnets will be named using 188 | the first string in the list, the second `az_count` subnets will be named using the second string, and so on. 189 | EOF 190 | 191 | type = list(string) 192 | default = ["Public"] 193 | } 194 | 195 | variable "public_subnet_tags" { 196 | description = "A list of maps containing tags to be applied to public subnets. List should either be the same length as the number of AZs to apply different tags per set of subnets, or a length of 1 to apply the same tags across all public subnets." 197 | type = list(map(string)) 198 | 199 | default = [{}] 200 | } 201 | 202 | variable "public_subnets_per_az" { 203 | description = <