Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ jobs:

- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Update dependabot terraform entries
commit_message: Update dependabot Terraform entries
commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
file_pattern: .github/dependabot.yml
skip_checkout: true
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The repo for Answer Digital shared Terraform modules.

## Using these modules

You can use these modules in your own terraform projects as follows:
You can use these modules in your own Terraform projects as follows:

```hcl
module "ec2_setup" {
Expand Down
45 changes: 22 additions & 23 deletions modules/aws/vpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,63 +17,62 @@ traffic, this is good from an auditing perspective, however you will be charged
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.3 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.4.3 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.14.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.0 |
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.4.3 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.14.0 |

## Resources

| Name | Type |
|------|------|
| [aws_cloudwatch_log_group.log_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_flow_log.flow_log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_iam_role.iam_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.iam_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_internet_gateway.ig](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
| [aws_route_table.route_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.public_subnet_rt_asso](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_subnet.private_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.public_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [random_uuid.log_group_guid_identifier](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/uuid) | resource |
| [aws_cloudwatch_log_group.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_flow_log.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_iam_role.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_internet_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
| [aws_route_table.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_subnet.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_azs"></a> [azs](#input\_azs) | This is a list that specifies all the Availability Zones that will have public and private subnets in it. Defaulting this value to an empty list selects of all the Availability Zones in the region you specify when defining the provider in your terraform project. | `list(string)` | `[]` | no |
| <a name="input_azs"></a> [azs](#input\_azs) | This is a list that specifies all the Availability Zones that will have public and private subnets in it. Defaulting this value to an empty list selects of all the Availability Zones in the region you specify when defining the provider in your Terraform project. | `list(string)` | `[]` | no |
| <a name="input_enable_dns_hostnames"></a> [enable\_dns\_hostnames](#input\_enable\_dns\_hostnames) | This allows AWS DNS hostname support to be switched on or off. | `bool` | `true` | no |
| <a name="input_enable_dns_support"></a> [enable\_dns\_support](#input\_enable\_dns\_support) | This allows AWS DNS support to be switched on or off. | `bool` | `true` | no |
| <a name="input_enable_vpc_flow_logs"></a> [enable\_vpc\_flow\_logs](#input\_enable\_vpc\_flow\_logs) | Whether to enable VPC Flow Logs for this VPC, this has cost but is considered a security risk without | `bool` | n/a | yes |
| <a name="input_environment"></a> [environment](#input\_environment) | The environment being deployed to - can only contain lower case letters. | `string` | n/a | yes |
| <a name="input_ig_cidr"></a> [ig\_cidr](#input\_ig\_cidr) | This specifies the CIDR block for the internet gateway. | `string` | `"0.0.0.0/0"` | no |
| <a name="input_ig_ipv6_cidr"></a> [ig\_ipv6\_cidr](#input\_ig\_ipv6\_cidr) | This specifies the IPV6 CIDR block for the internet gateway. | `string` | `"::/0"` | no |
| <a name="input_num_private_subnets"></a> [num\_private\_subnets](#input\_num\_private\_subnets) | This is a number specifying how many private subnets you want. Setting this to its default value of `-1` will result in `x` private subnets where `x` is the number of Availability Zones. If the number of private subnets is greater than the number of Availability Zones the private subnets will be spread out evenly over the available AZs. The CIDR values used are of the form `10.0.{i}.0/24` where `i` starts at 101 and increases by 1 for each private subnet. | `number` | `-1` | no |
| <a name="input_num_public_subnets"></a> [num\_public\_subnets](#input\_num\_public\_subnets) | This is a number specifying how many public subnets you want. Setting this to its default value of `-1` will result in `x` public subnets where `x` is the number of Availability Zones. If the number of public subnets is greater than the number of Availability Zones the public subnets will be spread out evenly over the available AZs. The CIDR values used are of the form `10.0.{i}.0/24` where `i` starts at 1 and increases by 1 for each public subnet. | `number` | `-1` | no |
| <a name="input_owner"></a> [owner](#input\_owner) | This is used to identify AWS resources through its tags. | `string` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | This is used to label the VPC as "`project_name`-vpc". | `string` | n/a | yes |
| <a name="input_owner"></a> [owner](#input\_owner) | The email address of the owner. | `string` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The projects's name - can only contain alphanumeric/underscore chatracters. | `string` | n/a | yes |
| <a name="input_vpc_cidr"></a> [vpc\_cidr](#input\_vpc\_cidr) | This specifies the CIDR block for the VPC. | `string` | `"10.0.0.0/16"` | no |
| <a name="input_vpc_flow_logs_traffic_type"></a> [vpc\_flow\_logs\_traffic\_type](#input\_vpc\_flow\_logs\_traffic\_type) | The Type of traffic to log, Requires vpc\_flow\_logs to be true | `string` | `"ALL"` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_az_zones"></a> [az\_zones](#output\_az\_zones) | A list of the Availability Zones that have been used. This output is of type `string`. |
| <a name="output_az_zones"></a> [az\_zones](#output\_az\_zones) | A list of the Availability Zones that have been used. This output is of type `list(string)`. |
| <a name="output_private_subnet_ids"></a> [private\_subnet\_ids](#output\_private\_subnet\_ids) | A list of the private subnet IDs that have been created. This output is of type `list(string)`. |
| <a name="output_public_subnet_ids"></a> [public\_subnet\_ids](#output\_public\_subnet\_ids) | A list of the public subnet IDs that have been created. This output is of type `list(string)`. |
| <a name="output_vpc_id"></a> [vpc\_id](#output\_vpc\_id) | The ID of the VPC that has been created. This output is of type `list(string)`. |
| <a name="output_vpc_id"></a> [vpc\_id](#output\_vpc\_id) | The ID of the VPC that has been created. This output is of type `string`. |
<!-- END_TF_DOCS -->

# Example Usage

Below are examples of how you would call the `vpc` module in your terraform code.
Below are examples of how you would call the `vpc` module in your Terraform code.

In this example we show two ways the module can be used;
the first uses the module to create a public and private subnet on each Availability Zone in your defined region,
Expand All @@ -83,14 +82,14 @@ and `eu-west-3` respectively.
```hcl
module "vpc_subnet" {
source = "github.com/answerdigital/terraform-modules//modules/aws/vpc?ref=v2"
owner = "joe_blogs"
owner = "joe.blogs@answerdigital.com"
project_name = "example_project_name"
enable_vpc_flow_logs = true
}

module "vpc_subnet" {
source = "github.com/answerdigital/terraform-modules//modules/aws/vpc?ref=v2"
owner = "joe_blogs"
owner = "joe.blogs@answerdigital.com"
project_name = "example_project_name"
azs = ["eu-west-1", "eu-west-3"]
num_public_subnets = 1
Expand Down
2 changes: 2 additions & 0 deletions modules/aws/vpc/data.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
data "aws_availability_zones" "available" {
state = "available"
}

data "aws_region" "current" {}
2 changes: 1 addition & 1 deletion modules/aws/vpc/examples/example.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module "vpc_subnet" {
source = "../."
owner = "joe_bloggs"
owner = "joe.blogs@answerdigital.com"
project_name = "test_person_name"
enable_vpc_flow_logs = true
}
4 changes: 2 additions & 2 deletions modules/aws/vpc/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ locals {
num_az_zones = length(var.azs) == 0 ? length(data.aws_availability_zones.available.names) : length(var.azs)

az_zones = length(var.azs) == 0 ? data.aws_availability_zones.available.names : var.azs
}

locals {
aws_region_short = replace(replace(replace(replace(replace(replace(replace(data.aws_region.current.name, "north", "n"), "south", "s"), "east", "e"), "west", "w"), "central", "c"), "gov", "g"), "-", "")

public_subnet_cidrs = var.num_public_subnets == -1 ? [for i in range(1, local.num_az_zones + 1) : "10.0.${i}.0/24"] : [for i in range(1, var.num_public_subnets + 1) : "10.0.${i}.0/24"]

private_subnet_cidrs = var.num_private_subnets == -1 ? [for i in range(1, local.num_az_zones + 1) : "10.0.10${i}.0/24"] : [for i in range(1, var.num_private_subnets + 1) : "10.0.10${i}.0/24"]
Expand Down
94 changes: 48 additions & 46 deletions modules/aws/vpc/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,33 @@ terraform {
required_version = "~> 1.3"

required_providers {
random = {
source = "hashicorp/random"
version = ">= 3.4.3"
}
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
version = ">= 5.14.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we document (in code, or in wiki) the reasoning/requirement behind minimum versions of providers? It will make it easier to understand cross-module provider requirements

}
}
}

resource "aws_flow_log" "flow_log" {
iam_role_arn = aws_iam_role.iam_role[0].arn
log_destination = aws_cloudwatch_log_group.log_group[0].arn
traffic_type = var.vpc_flow_logs_traffic_type
vpc_id = aws_vpc.vpc.id
count = var.enable_vpc_flow_logs ? 1 : 0
}
resource "aws_flow_log" "this" {
count = var.enable_vpc_flow_logs ? 1 : 0

resource "random_uuid" "log_group_guid_identifier" {
iam_role_arn = aws_iam_role.vpc_flow_logs[0].arn
log_destination = aws_cloudwatch_log_group.vpc_flow_logs[0].arn
traffic_type = var.vpc_flow_logs_traffic_type
vpc_id = aws_vpc.this.id
}

resource "aws_cloudwatch_log_group" "log_group" {
name = "${var.project_name}-vpc-flow-logs-${random_uuid.log_group_guid_identifier.result}"
resource "aws_cloudwatch_log_group" "vpc_flow_logs" {
count = var.enable_vpc_flow_logs ? 1 : 0

name = "${replace("AWS::Logs::LogGroup", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}-vpc_flow_logs"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason to use replace like this throughout? Also should be lower case. Surely we should just use something like log_group-...

}

resource "aws_iam_role" "iam_role" {
name = "${var.project_name}-vpc-logs-iam"
resource "aws_iam_role" "vpc_flow_logs" {
count = var.enable_vpc_flow_logs ? 1 : 0

name = "${replace("AWS::IAM::Role", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}-vpc_flow_logs"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
Expand All @@ -50,10 +46,12 @@ resource "aws_iam_role" "iam_role" {
EOF
}

resource "aws_iam_role_policy" "iam_role_policy" {
name = "${var.project_name}-vpc-iam-logs-policy"
role = aws_iam_role.iam_role[0].id
count = var.enable_vpc_flow_logs ? 1 : 0
resource "aws_iam_role_policy" "vpc_flow_logs" {
count = var.enable_vpc_flow_logs ? 1 : 0

name = "${replace("AWS::IAM::RolePolicy", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}-vpc_flow_logs"
role = aws_iam_role.vpc_flow_logs[0].id

policy = <<EOF
{
"Version": "2012-10-17",
Expand All @@ -73,76 +71,80 @@ resource "aws_iam_role_policy" "iam_role_policy" {
EOF
}


resource "aws_vpc" "vpc" {
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_support = var.enable_dns_support
enable_dns_hostnames = var.enable_dns_hostnames

tags = {
Name = "${var.project_name}-vpc"
Name = "${replace("AWS::EC2::VPC", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}"
Owner = var.owner
}
}

resource "aws_subnet" "public_subnets" {
count = length(local.public_subnet_cidrs)
vpc_id = aws_vpc.vpc.id
resource "aws_subnet" "public" {
count = length(local.public_subnet_cidrs)

vpc_id = aws_vpc.this.id
cidr_block = element(local.public_subnet_cidrs, count.index)
availability_zone = element(local.az_zones, count.index)

tags = {
Name = "${var.project_name}-public-subnet-${count.index + 1}"
Name = "${replace("AWS::EC2::Subnet", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}-public_${count.index + 1}"
Zone = "Public"
Owner = var.owner
}
}

resource "aws_subnet" "private_subnets" {
count = length(local.private_subnet_cidrs)
vpc_id = aws_vpc.vpc.id
resource "aws_subnet" "private" {
count = length(local.private_subnet_cidrs)

vpc_id = aws_vpc.this.id
cidr_block = element(local.private_subnet_cidrs, count.index)
availability_zone = element(local.az_zones, count.index)

tags = {
Name = "${var.project_name}-private-subnet-${count.index + 1}"
Name = "${replace("AWS::EC2::Subnet", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}-private_${count.index + 1}"
Zone = "Private"
Owner = var.owner
}
}

resource "aws_internet_gateway" "ig" {
count = length(local.public_subnet_cidrs) > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
resource "aws_internet_gateway" "this" {
count = length(local.public_subnet_cidrs) > 0 ? 1 : 0

vpc_id = aws_vpc.this.id

tags = {
Name = "${var.project_name}-vpc-ig"
Name = "${replace("AWS::EC2::InternetGateway", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}-${count.index + 1}"
Owner = var.owner
}
}

resource "aws_route_table" "route_table" {
count = length(local.public_subnet_cidrs) > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
resource "aws_route_table" "public" {
count = length(local.public_subnet_cidrs) > 0 ? 1 : 0

vpc_id = aws_vpc.this.id

route {
cidr_block = var.ig_cidr
gateway_id = aws_internet_gateway.ig[0].id
gateway_id = aws_internet_gateway.this[0].id
}

route {
ipv6_cidr_block = var.ig_ipv6_cidr
gateway_id = aws_internet_gateway.ig[0].id
gateway_id = aws_internet_gateway.this[0].id
}

tags = {
Name = "${var.project_name}-public-route-table"
Name = "${replace("AWS::EC2::RouteTable", "::", "-")}-${var.project_name}-${var.environment}-${local.aws_region_short}-public_${count.index + 1}"
Owner = var.owner
}
}

resource "aws_route_table_association" "public_subnet_rt_asso" {
count = length(local.public_subnet_cidrs)
subnet_id = element(aws_subnet.public_subnets[*].id, count.index)
route_table_id = aws_route_table.route_table[0].id
resource "aws_route_table_association" "public" {
count = length(local.public_subnet_cidrs)

subnet_id = element(aws_subnet.public[*].id, count.index)
route_table_id = aws_route_table.public[0].id
}
10 changes: 5 additions & 5 deletions modules/aws/vpc/output.tf
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
output "vpc_id" {
value = aws_vpc.vpc.id
description = "The ID of the VPC that has been created. This output is of type `list(string)`."
value = aws_vpc.this.id
description = "The ID of the VPC that has been created. This output is of type `string`."
}

output "public_subnet_ids" {
value = aws_subnet.public_subnets[*].id
value = aws_subnet.public[*].id
description = "A list of the public subnet IDs that have been created. This output is of type `list(string)`."
}

output "private_subnet_ids" {
value = aws_subnet.private_subnets[*].id
value = aws_subnet.private[*].id
description = "A list of the private subnet IDs that have been created. This output is of type `list(string)`."
}

output "az_zones" {
value = local.az_zones
description = "A list of the Availability Zones that have been used. This output is of type `string`."
description = "A list of the Availability Zones that have been used. This output is of type `list(string)`."
}
Loading