Browse Source

Transit Gateway Modules

This provides 3 base modules:

*transit_gateway_hub*

This creates a transit gateway and shares it with all other accounts in
the same partition and environment (e.g. all 'test' accounts in
govcloud).

*transit_gateway_client*

This (optionally) accepts an invitation for the transit gateway to be
shared with the account.

It then adds a 10.0.0.0/8 route to all of the route tables pointing
to the transit gateway.

*test_instance*

This creates a small test instance. You know, for testing. Can be
disabled via a variable in account.hcl, so we can keep this around
in everybody's directories and turn it up when wanted.

Additional changes:
* Additional outputs from standard_vpc to support the stuff this needs.
* Corresponding changes in '-live' for a test environment.
Fred Damstra 5 years ago
parent
commit
678a5b09fa

+ 10 - 0
base/standard_vpc/outputs.tf

@@ -18,4 +18,14 @@ output allow_all_outbound_sg_id {
   value = module.allow_all_outbound_sg.this_security_group_id
 }
 
+output private_route_tables {
+  value = module.vpc.private_route_table_ids
+}
+
+output public_route_tables {
+  value = module.vpc.public_route_table_ids
+}
 
+output azs {
+  value = module.vpc.azs
+}

+ 6 - 0
base/test_instance/README.md

@@ -0,0 +1,6 @@
+# Test Instance
+
+Nothing special here. Just a test instance for troubleshooting. Set `create_test_instance` to false when not in use.
+
+Base install, full access in and out.
+

+ 21 - 0
base/test_instance/cloud-init/cloud-init.tpl

@@ -0,0 +1,21 @@
+#cloud-config
+# TODO: This needs to be customized/fixed
+preserve_hostname: false
+hostname: ${hostname}
+fqdn: ${fqdn}
+
+runcmd:
+ - echo "${fqdn}" > /etc/salt/minion_id
+ - /bin/systemctl restart salt-minion 
+ - /bin/systemctl enable salt-minion
+ - /bin/systemctl start amazon-ssm-agent
+ - /bin/systemctl enable amazon-ssm-agent
+ - /usr/sbin/aide --update --verbose=0
+ - /bin/cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
+
+# httpd here to avoid a chicken and egg for repo server
+# installing it via salt state won't work because by then
+# the salt states could have us pointed to ourselves as the repo server
+packages:
+ - httpd
+

+ 80 - 0
base/test_instance/main.tf

@@ -0,0 +1,80 @@
+locals {
+  owner = var.aws_partition == "aws-us-gov" ? "513442679011" : "099720109477"
+}
+
+data "aws_ami" "ubuntu" {
+  most_recent = true
+  owners = [ local.owner ]
+
+  filter {
+    name   = "name"
+    values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
+  }
+
+  #filter {
+  #  name   = "image-id"
+  #  values = [ "ami-0a4050943619c0460" ]
+  #}
+
+  filter {
+    name   = "root-device-type"
+    values = ["ebs"]
+  }
+
+  filter {
+    name   = "virtualization-type"
+    values = ["hvm"]
+  }
+}
+
+module "test_instance" {
+  source                 = "terraform-aws-modules/ec2-instance/aws"
+  version                = "~> 2.0"
+
+  name                   = "test_instance"
+  instance_count         = var.create_test_instance ? 1 : 0
+  disable_api_termination = false # the test instance can always be destroyed
+
+  ami                    = data.aws_ami.ubuntu.image_id
+  instance_type          = "t3a.micro"
+  key_name               = var.test_instance_key_name
+  vpc_security_group_ids = var.security_group_ids
+  subnet_id              = var.subnet_id
+  tags = merge(var.standard_tags, var.tags)
+
+  ebs_optimized          = true
+  monitoring             = false # Do we use this?
+
+  user_data_base64 = "${data.template_cloudinit_config.cloud-init.rendered}"
+}
+
+data "template_file" "cloud-init" {
+  # Should these be in a common directory? I suspect they'd be reusable
+  template = "${file("${path.module}/cloud-init/cloud-init.tpl")}"
+
+  vars = {
+    hostname = "test_instance"
+    fqdn = "test_instance.${var.inside_domain}"
+    environment = var.environment
+  }
+}
+
+# Render a multi-part cloud-init config making use of the part
+# above, and other source files
+data "template_cloudinit_config" "cloud-init" {
+  gzip          = true
+  base64_encode = true
+
+  # Main cloud-config configuration file.
+  part {
+    filename     = "init.cfg"
+    content_type = "text/cloud-config"
+    content      = "${data.template_file.cloud-init.rendered}"
+  }
+
+  # Additional parts as needed
+  #part {
+  #  content_type = "text/x-shellscript"
+  #  content      = "ffbaz"
+  #}
+}

+ 11 - 0
base/test_instance/outputs.tf

@@ -0,0 +1,11 @@
+output test_instance_arn {
+  value = module.test_instance.arn
+}
+
+output test_instance_public_ip {
+  value = module.test_instance.public_ip
+}
+
+output test_instance_private_ip {
+  value = module.test_instance.private_ip
+}

+ 62 - 0
base/test_instance/vars.tf

@@ -0,0 +1,62 @@
+variable "subnet_id" {
+  type = string
+}
+
+variable "security_group_ids" {
+  type = list
+}
+
+variable "tags" {
+  description = "Tags to add to the resource (in addition to global standard tags)"
+  type        = map
+  default     = { }
+}
+
+# ----------------------------------
+# Below this line are variables inherited from higher levels, so they
+# do not need to be explicitly passed to this module.
+variable "create_test_instance" {
+  type        = bool
+}
+
+variable "standard_tags" {
+  type        = map
+}
+
+variable "inside_domain" {
+  type        = string
+}
+
+variable "environment" {
+  type        = string
+}
+
+variable "aws_marketplace_ubuntu_owner_id" {
+  type = string
+}
+
+variable "test_instance_key_name" {
+  type = string
+}
+
+# ----------------------------------
+# Required for remote state, though they can be used elsewhere
+variable "remote_state_bucket" {
+  type = string
+}
+
+variable "aws_region" {
+  type = string
+}
+
+variable "aws_partition" {
+  type = string
+}
+
+variable "common_services_account" {
+  type = string
+}
+
+variable "common_profile" {
+  type = string
+}

+ 3 - 0
base/test_instance/version.tf

@@ -0,0 +1,3 @@
+terraform {
+  required_version = "~> 0.12"
+}

+ 3 - 0
base/transit_gateway_client/README.md

@@ -0,0 +1,3 @@
+# Transit Gateway Client
+
+Accepts the invitation to the transit gateway and attaches the VPCs.

+ 34 - 0
base/transit_gateway_client/main.tf

@@ -0,0 +1,34 @@
+data "aws_availability_zones" "available" {
+  state = "available"
+}
+
+resource "aws_ram_resource_share_accepter" "accept_tgw_share" {
+  count = var.accept_invitation ? 1 : 0
+  share_arn = var.share_arn
+}
+
+resource "aws_ec2_transit_gateway_vpc_attachment" "attach_tgw" {
+  depends_on = [ aws_ram_resource_share_accepter.accept_tgw_share ]
+
+  subnet_ids         = var.subnets
+  transit_gateway_id = var.tgw_id
+  vpc_id             = var.vpc_id
+
+  tags = merge(
+    var.standard_tags, 
+    var.tags
+  )
+}
+
+# The VPC automatically tells the TGW about its network, but the
+# VPCs themselves need to know what all to send to the TGW. For
+# our purposes, it's only RFC1918 addresses. In fact, just 
+# 10.0.0.0/8 for now.
+resource "aws_route" "route_to_10" {
+  for_each = toset(var.route_tables)
+
+  route_table_id            = each.value
+  destination_cidr_block    = "10.0.0.0/8"
+  transit_gateway_id        = var.tgw_id
+}
+

+ 1 - 0
base/transit_gateway_client/outputs.tf

@@ -0,0 +1 @@
+

+ 78 - 0
base/transit_gateway_client/vars.tf

@@ -0,0 +1,78 @@
+
+variable "tags" {
+  description = "Tags to add to the resource (in addition to global standard tags)"
+  type        = map
+  default     = { }
+}
+
+variable "accept_invitation" {
+  description = "Whether to accept the sharing invitation. Only done once per account."
+  type        = bool
+}
+
+variable "share_arn" {
+  description = "The ARN of the share to accept."
+  type        = string
+  default     = ""
+}
+
+variable tgw_id {
+  description = "VPC ID of the client"
+  type = string
+}
+
+variable vpc_id {
+  description = "VPC ID of the client"
+  type = string
+}
+
+variable subnets {
+  description = "List of subnets, one per AZ in the VPC"
+  type = list
+}
+
+variable route_tables {
+  description = "List of route tables to be routed to the TGW"
+  type = list
+}
+
+# ----------------------------------
+# Below this line are variables inherited from higher levels, so they
+# do not need to be explicitly passed to this module.
+variable "standard_tags" {
+  type        = map
+}
+
+variable "aws_partition_alias" {
+  type = string
+}
+
+variable "environment" {
+  type = string
+}
+
+variable "aws_account_id" {
+  type = string
+}
+
+# ----------------------------------
+# Required for remote state, though they can be used elsewhere
+variable "remote_state_bucket" {
+  type = string
+}
+
+variable "aws_region" {
+  type = string
+}
+
+variable "aws_partition" {
+  type = string
+}
+
+variable "common_services_account" {
+  type = string
+}
+
+variable "common_profile" {
+  type = string
+}

+ 3 - 0
base/transit_gateway_client/version.tf

@@ -0,0 +1,3 @@
+terraform {
+  required_version = "~> 0.12"
+}

+ 4 - 0
base/transit_gateway_hub/README.md

@@ -0,0 +1,4 @@
+# Transit Gateway Hub
+
+Creates a transit gateway in the hub account.
+

+ 56 - 0
base/transit_gateway_hub/main.tf

@@ -0,0 +1,56 @@
+locals {
+  # We want to share with:
+  #  * The other accounts in our partition and environment
+  #  * The common accounts
+  #  * But not ourselves
+  remote_accounts = toset([
+    for account in concat(var.account_map[var.environment], var.account_map["common"]):
+      account if account != var.aws_account_id
+  ])
+}
+
+data "aws_availability_zones" "available" {
+  state = "available"
+}
+
+resource "aws_ec2_transit_gateway" "tgw" {
+  description = "Transit gateway for ${var.environment} in ${var.aws_partition}."
+  amazon_side_asn = var.asn # may not need, but AWS recommends it for future proofing
+  auto_accept_shared_attachments = "enable" # if we grant them access, they can attach.
+  default_route_table_association = "enable"
+  default_route_table_propagation = "enable"
+  dns_support = "enable"
+
+  tags = merge(
+    { "Name" = var.name },
+    var.tags, 
+    var.standard_tags)
+}
+
+# We require a RAM to share the resource
+resource "aws_ram_resource_share" "share_tgw" {
+  name = var.name
+  allow_external_principals = true # IMPORTANT
+
+  tags = merge(
+    { "Name" = var.name },
+    var.tags, 
+    var.standard_tags
+  )
+}
+
+# Share the tgw
+resource "aws_ram_resource_association" "share_tgw" {
+  resource_arn       = aws_ec2_transit_gateway.tgw.arn
+  resource_share_arn = aws_ram_resource_share.share_tgw.id
+}
+
+# ... with each other account
+resource "aws_ram_principal_association" "share_with_accounts" {
+  for_each = local.remote_accounts
+  
+  principal          = each.value
+  resource_share_arn = aws_ram_resource_share.share_tgw.id
+}
+
+

+ 18 - 0
base/transit_gateway_hub/outputs.tf

@@ -0,0 +1,18 @@
+output remote_accounts {
+  value = local.remote_accounts
+}
+
+output tgw_id {
+  value = aws_ec2_transit_gateway.tgw.id
+}
+
+output tgw_arn {
+  value = aws_ec2_transit_gateway.tgw.arn
+}
+
+output resource_share_arns {
+  value = {
+    for k, v in aws_ram_principal_association.share_with_accounts:
+      k => v.resource_share_arn
+  }
+}

+ 61 - 0
base/transit_gateway_hub/vars.tf

@@ -0,0 +1,61 @@
+variable "name" {
+  description = "Name of the transit gateway."
+  type        = string
+}
+
+variable "asn" {
+  description = "Unique AS Number for the account."
+  type = number
+}
+
+variable "tags" {
+  description = "Tags to add to the resource (in addition to global standard tags)"
+  type        = map
+  default     = { }
+}
+
+# ----------------------------------
+# Below this line are variables inherited from higher levels, so they
+# do not need to be explicitly passed to this module.
+variable "standard_tags" {
+  type        = map
+}
+
+variable "aws_account_id" {
+  type        = string
+}
+
+variable "aws_partition_alias" {
+  type        = string
+}
+
+variable "environment" {
+  type        = string
+}
+
+variable "account_map" {
+  # from partition.hcl
+  type = map
+}
+
+# ----------------------------------
+# Required for remote state, though they can be used elsewhere
+variable "remote_state_bucket" {
+  type = string
+}
+
+variable "aws_region" {
+  type = string
+}
+
+variable "aws_partition" {
+  type = string
+}
+
+variable "common_services_account" {
+  type = string
+}
+
+variable "common_profile" {
+  type = string
+}

+ 3 - 0
base/transit_gateway_hub/version.tf

@@ -0,0 +1,3 @@
+terraform {
+  required_version = "~> 0.12"
+}