--- /dev/null
+# AWSible Infrastructure via Terraform
+
+Ensure the correct profile will be used:
+
+ export AWS_PROFILE=profile
+
+Initialize the shared infrastructure needed by terraform:
+
+ pushd terraform-infrastructure
+ terraform init
+ terraform apply
+ ../generate-backend-configs.sh > backend.tf
+ echo yes | terraform init
+ popd
+
+Create the VPC:
+
+ pushd vpc
+ ../generate-backend-configs.sh > backend.tf
+ terraform init
+ terraform apply
+ popd
+
+Create the management stack:
+
+ pushd management-stack
+ ../generate-backend-configs.sh > backend.tf
+ terraform init
+ terraform apply
+ popd
+
+Create the vpcaccess stack:
+
+ pushd vpcaccess-stack
+ ../generate-backend-configs.sh > backend.tf
+ terraform init
+ terraform apply
+ popd
+
+Run Ansible by hand to configure the vpcaccess server, then connect to the VPN.
+Populate the management EFS.
+Run Ansible by hand to configure a management server, then scale up the management ASG.
+
+Create and deploy any other stacks.
--- /dev/null
+#!/bin/bash
+# this is a rough hack at the moment, it needs to be better
+
+set -e
+set -o pipefail
+
+tf_infra=$(pushd ../terraform-infrastructure >/dev/null && terraform output -json && popd >/dev/null)
+function infra_value(){
+ jq -er ".${1}.value"<<<"${tf_infra}"
+}
+
+cat<<EOF
+terraform {
+ backend "s3" {
+ region = "$(infra_value region)"
+ bucket = "$(infra_value remote_state_bucket)"
+ dynamodb_table = "$(infra_value remote_state_table)"
+ key = "$(infra_value environment)/$(basename $(pwd)).tfstate"
+ }
+}
+EOF
--- /dev/null
+data "terraform_remote_state" "vpc" {
+ backend = "s3"
+ config {
+ bucket = "${var.remote_state_bucket}"
+ region = "${var.region}"
+ key = "${var.environment}/vpc.tfstate"
+ dynamodb_table = "${var.remote_state_table}"
+ }
+}
--- /dev/null
+provider "aws" {
+ region = "${var.region}"
+}
+
+module "management-stack" {
+ source = "../modules/management-stack"
+ vpc_id = "${data.terraform_remote_state.vpc.vpc_id}"
+ acct_name = "${var.acct_name}"
+ key_name = "${var.key_name}"
+ phase = "${var.environment}"
+ management_subnet_ids = ["${data.terraform_remote_state.vpc.private_subnets}"]
+ security_group_ids = ["${data.terraform_remote_state.vpc.general_access_sg_id}"]
+ policy_arns = ["${data.terraform_remote_state.vpc.base_policy_arn}"]
+ management_service_name = "${var.project}-management"
+}
--- /dev/null
+output "alerts_topic_arn" { value = "${module.management-stack.alerts_topic_arn}" }
+output "events_topic_arn" { value = "${module.management-stack.events_topic_arn}" }
+output "events_queue_name" { value = "${module.management-stack.events_queue_name}" }
+output "management_data_efs_id" { value = "${module.management-stack.management_data_efs_id}" }
--- /dev/null
+../terraform.tfvars
\ No newline at end of file
--- /dev/null
+variable "region" {}
+variable "remote_state_bucket" {}
+variable "remote_state_table" {}
+variable "acct_name" {}
+variable "project" {}
+variable "environment" {}
+variable "key_name" {}
--- /dev/null
+# AWSible Management Stack
+
+Creates the infrastructure for an automated Ansible management system within a VPC, with all the quirks to support a certain style of app deployment.
+
+In summary, this creates an ASG of small instances. Each shares an EFS mount which shall contain all the Ansible playbooks and data needed to configure an environment. Each listens to an SQS queue for messages sent from ASG events, and invokes the appropriate playbook when a message is received.
--- /dev/null
+data "aws_iam_policy_document" "instance_trust" {
+ statement {
+ effect = "Allow"
+ actions = [
+ "sts:AssumeRole"
+ ]
+ principals {
+ type = "Service"
+ identifiers = [
+ "ec2.amazonaws.com"
+ ]
+ }
+ }
+}
+
+resource "aws_iam_role" "management" {
+ name = "${var.management_service_name}-role"
+ assume_role_policy = "${data.aws_iam_policy_document.instance_trust.json}"
+}
+
+data "aws_iam_policy_document" "management" {
+ statement {
+ sid = "AWSControl"
+ actions = [
+ "autoscaling:*",
+ "ec2:*",
+ "elasticloadbalancing:*",
+ "iam:PassRole",
+ "iam:GetServerCertificate"
+ ]
+ resources = [
+ "*"
+ ]
+ }
+ statement {
+ sid = "EventQueue"
+ actions = [
+ "sqs:*"
+ ]
+ resources = [ "${aws_sqs_queue.management-events-queue.arn}" ]
+ }
+ statement {
+ sid = "AlertTopic"
+ actions = [
+ "sns:*"
+ ]
+ resources = [ "${aws_sns_topic.management-events.arn}" ]
+ }
+}
+
+resource "aws_iam_policy" "management" {
+ name = "${var.management_service_name}"
+ description = "${var.management_service_name}"
+ path = "/"
+ policy = "${data.aws_iam_policy_document.management.json}"
+}
+
+resource "aws_iam_role_policy_attachment" "management" {
+ role = "${aws_iam_role.management.id}"
+ policy_arn = "${aws_iam_policy.management.arn}"
+}
+
+resource "aws_iam_instance_profile" "management" {
+ name = "${var.management_service_name}-instance-profile"
+ role = "${aws_iam_role.management.name}"
+}
--- /dev/null
+resource "aws_security_group" "management-elb" {
+ count = "${var.management_elb > 0 ? 1 : 0}"
+ vpc_id = "${var.vpc_id}"
+ name = "${var.management_service_name}-elb"
+ description = "${var.management_service_name} internal ELB"
+}
+resource "aws_security_group_rule" "management-elb-out-all" {
+ count = "${var.management_elb > 0 ? 1 : 0}"
+ security_group_id = "${aws_security_group.management-elb.id}"
+ type = "egress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ cidr_blocks = [ "0.0.0.0/0" ]
+}
+resource "aws_security_group_rule" "management-elb-in-ssh" {
+ count = "${var.management_elb > 0 ? 1 : 0}"
+ security_group_id = "${aws_security_group.management-elb.id}"
+ type = "ingress"
+ from_port = 22
+ to_port = 22
+ protocol = "tcp"
+ cidr_blocks = [ "0.0.0.0/0" ]
+}
+
+resource "aws_security_group" "management" {
+ vpc_id = "${var.vpc_id}"
+ name = "${var.management_service_name}"
+ description = "${var.management_service_name} service"
+}
+resource "aws_security_group_rule" "management-out-all" {
+ security_group_id = "${aws_security_group.management.id}"
+ type = "egress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ cidr_blocks = [ "0.0.0.0/0" ]
+}
+resource "aws_security_group_rule" "management-in-self" {
+ security_group_id = "${aws_security_group.management.id}"
+ type = "ingress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ self = true
+}
+resource "aws_security_group_rule" "management-in-elb" {
+ security_group_id = "${aws_security_group.management.id}"
+ type = "ingress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ source_security_group_id = "${aws_security_group.management-elb.id}"
+}
+
+resource "aws_elb" "management" {
+ count = "${var.management_elb > 0 ? 1 : 0}"
+ name = "${var.management_service_name}-int-elb"
+ security_groups = ["${aws_security_group.management-elb.id}"]
+ internal = true
+ listener {
+ instance_port = 22
+ instance_protocol = "TCP"
+ lb_port = 22
+ lb_protocol = "TCP"
+ }
+ health_check {
+ healthy_threshold = 3
+ unhealthy_threshold = 2
+ target = "TCP:22"
+ interval = 30
+ timeout = 10
+ }
+ idle_timeout = 600
+ subnets = ["${var.management_subnet_ids}"]
+}
+
+data "aws_ami" "amazon_linux" {
+ count = "${length(var.ami) > 0 ? 0 : 1}"
+ most_recent = true
+ owners = ["amazon"]
+ filter {
+ name = "name"
+ values = ["amzn-ami-hvm-*-gp2"]
+ }
+ filter {
+ name = "root-device-type"
+ values = ["ebs"]
+ }
+}
+
+data "aws_region" "current" {
+ current = true
+}
+data "template_file" "user_data" {
+ template = "${file("${path.module}/user-data.tpl")}"
+ vars {
+ region = "${data.aws_region.current.name}"
+ app_name = "${var.management_service_name}"
+ stack = ""
+ phase = "${var.phase}"
+ country = ""
+ cluster = "${var.management_service_name}-d0${var.phase}"
+ acct_name = "${var.acct_name}"
+ }
+}
+
+resource "aws_launch_configuration" "management" {
+ name_prefix = "${var.management_service_name}"
+ image_id = "${length(var.ami) > 0 ? var.ami : data.aws_ami.amazon_linux.image_id}"
+ instance_type = "${var.instance_type}"
+ iam_instance_profile = "${aws_iam_instance_profile.management.name}"
+ key_name = "${var.key_name}"
+ security_groups = ["${concat(var.security_group_ids, list(aws_security_group.management.id))}"]
+ associate_public_ip_address = false
+ user_data = "${data.template_file.user_data.rendered}"
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_autoscaling_group" "management" {
+ name = "${var.management_service_name}"
+ launch_configuration = "${aws_launch_configuration.management.name}"
+ vpc_zone_identifier = ["${var.management_subnet_ids}"]
+ min_size = 0
+ max_size = "${length(var.management_subnet_ids)}"
+ default_cooldown = 10
+ health_check_type = "EC2"
+ load_balancers = ["${var.management_elb > 0 ? aws_elb.management.name : ""}"]
+ lifecycle {
+ create_before_destroy = true
+ }
+ tag {
+ propagate_at_launch = true
+ key = "module"
+ value = "${var.management_service_name}"
+ }
+ tag {
+ propagate_at_launch = true
+ key = "phase"
+ value = "${var.phase}"
+ }
+}
+
+resource "aws_autoscaling_notification" "management" {
+ group_names = ["${aws_autoscaling_group.management.name}"]
+ topic_arn = "${aws_sns_topic.management-events.arn}"
+ notifications = [
+ "autoscaling:EC2_INSTANCE_LAUNCH",
+ "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
+ "autoscaling:EC2_INSTANCE_TERMINATE",
+ "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
+ ]
+}
+
+data "aws_subnet" "management" {
+ count = "${length(var.management_subnet_ids)}"
+ id = "${element(var.management_subnet_ids, count.index)}"
+}
+
+resource "aws_ebs_volume" "management-data" {
+ count = "${length(var.management_subnet_ids) * var.management_data_efs}"
+ availability_zone = "${element(data.aws_subnet.management.*.availability_zone, count.index)}"
+ size = "${var.management_data_volume_size}"
+ type = "gp2"
+ tags {
+ module = "${var.management_service_name}"
+ }
+}
+
+resource "aws_efs_file_system" "management-data" {
+ count = "${var.management_data_efs}"
+ creation_token = "${var.management_service_name}-data"
+ tags {
+ Name = "${var.management_service_name}-data"
+ }
+}
+
+resource "aws_efs_mount_target" "management-data" {
+ count = "${length(var.management_subnet_ids) * var.management_data_efs}"
+ file_system_id = "${aws_efs_file_system.management-data.id}"
+ subnet_id = "${element(var.management_subnet_ids, count.index)}"
+ security_groups = ["${aws_security_group.management.id}"]
+}
--- /dev/null
+output "alerts_topic_arn" { value = "${aws_sns_topic.management-alerts.arn}" }
+output "events_topic_arn" { value = "${aws_sns_topic.management-events.arn}" }
+output "events_queue_name" { value = "${aws_sqs_queue.management-events-queue.name}" }
+output "management_data_efs_id" { value = "${var.management_data_efs > 0 ? aws_efs_file_system.management-data.id : ""}" }
--- /dev/null
+resource "aws_sqs_queue" "management-events-dlq" {
+ name = "${length(var.sqs_events_name) > 0 ? var.sqs_events_name : var.management_service_name}${length(var.sqs_events_name) > 0 ? "" : "-events"}-failed"
+ visibility_timeout_seconds = 30
+ message_retention_seconds = 1209600
+ max_message_size = 262144
+ receive_wait_time_seconds = 0
+}
+resource "aws_sqs_queue" "management-events-queue" {
+ name = "${length(var.sqs_events_name) > 0 ? var.sqs_events_name : var.management_service_name}${length(var.sqs_events_name) > 0 ? "" : "-events"}"
+ visibility_timeout_seconds = 60
+ message_retention_seconds = 1209600
+ max_message_size = 262144
+ receive_wait_time_seconds = 20
+ redrive_policy = "{\"deadLetterTargetArn\":\"${aws_sqs_queue.management-events-dlq.arn}\",\"maxReceiveCount\":5}"
+}
+resource "aws_sns_topic" "management-events" {
+ name = "${length(var.sns_events_name) > 0 ? var.sns_events_name : var.management_service_name}${length(var.sns_events_name) > 0 ? "" : "-events"}"
+}
+data "aws_iam_policy_document" "management-queue" {
+ statement {
+ effect = "Allow"
+ sid = "TopicPublish"
+ actions = ["SQS:SendMessage"]
+ resources = ["${aws_sqs_queue.management-events-queue.arn}"]
+ condition {
+ test = "ForAnyValue:ArnEquals"
+ variable = "aws:SourceArn"
+ values = ["${aws_sns_topic.management-events.arn}"]
+ }
+ principals {
+ type = "AWS"
+ identifiers = ["*"]
+ }
+ }
+}
+resource "aws_sqs_queue_policy" "management-events" {
+ queue_url = "${aws_sqs_queue.management-events-queue.id}"
+ policy = "${data.aws_iam_policy_document.management-queue.json}"
+}
+resource "aws_sns_topic_subscription" "management-events-subscription" {
+ topic_arn = "${aws_sns_topic.management-events.arn}"
+ endpoint = "${aws_sqs_queue.management-events-queue.arn}"
+ protocol = "sqs"
+}
+
+resource "aws_sns_topic" "management-alerts" {
+ name = "${length(var.sns_alerts_name) > 0 ? var.sns_alerts_name : var.management_service_name}${length(var.sns_alerts_name) > 0 ? "" : "-alerts"}"
+}
--- /dev/null
+#!/bin/bash
+export EC2_REGION="${region}"
+export CLOUD_APP="${app_name}"
+export CLOUD_STACK="${stack}"
+export CLOUD_DEV_PHASE="${phase}"
+export CLOUD_COUNTRIES="${country}"
+export CLOUD_ENVIRONMENT="${acct_name}"
+export CLOUD_CLUSTER="${cluster}"
--- /dev/null
+variable "vpc_id" {
+ description = "Which VPC to build this in."
+}
+
+variable "acct_name" {
+ description = "Name of AWS account."
+}
+
+variable "instance_type" {
+ default = "t2.small"
+}
+
+variable "key_name" {}
+
+variable "management_subnet_ids" {
+ type = "list"
+ description = "Which subnets the management servers will be in. (Typically private.)"
+}
+
+variable "phase" {
+ description = "Release phase of this environment. (Such as dev, stage, or prod.)"
+ default = "dev"
+}
+
+variable "ami" {
+ description = "Specify an AMI to use; if empty, use most recent amazon linux."
+ default = ""
+}
+
+variable "security_group_ids" {
+ type = "list"
+ description = "Additional security groups the management servers will belong to. (Typically the general-access SG.)"
+ default = []
+}
+
+variable "policy_arns" {
+ type = "list"
+ description = "Additional policy arns the management role will have. (Typically the base policy.)"
+ default = []
+}
+
+variable "management_elb" {
+ default = false
+ description = "Whether to place management servers behind an ELB."
+}
+
+variable "management_data_efs" {
+ default = true
+ description = "Management instances share a common EFS filesystem. If false, each has its own EBS volume."
+}
+
+variable "management_data_volume_size" {
+ default = 20
+ description = "Size of individual data volumes, if used."
+}
+
+variable "management_service_name" {
+ default = "management"
+}
+
+variable "sns_events_name" {
+ default = ""
+ description = "Name of the SNS topic to which ASGs send notifications. Defaults to {management_service_name}-events."
+}
+
+variable "sqs_events_name" {
+ default = ""
+ description = "Name of the SQS queue the events topic feeds, and the management system listens to. Defaults to {management_service_name}-events."
+}
+
+variable "sns_alerts_name" {
+ default = ""
+ description = "Name of the SNS topic to which informational messages are sent, to be mailed to interested meat-based parties. Defaults to {management_service_name}-alerts."
+}
--- /dev/null
+terraform {
+ required_version = ">= 0.10.2"
+}
--- /dev/null
+resource "aws_s3_bucket" "tf_state" {
+ bucket = "${var.remote_state_bucket}"
+ region = "${var.region}"
+ acl = "private"
+ versioning {
+ enabled = true
+ }
+}
+
+resource "aws_dynamodb_table" "tf_lock" {
+ name = "${var.remote_state_table}"
+ read_capacity = 1
+ write_capacity = 1
+ hash_key = "LockID"
+ attribute {
+ name = "LockID"
+ type = "S"
+ }
+}
--- /dev/null
+output "region" { value = "${var.region}" }
+output "remote_state_bucket" { value = "${var.remote_state_bucket}" }
+output "remote_state_table" { value = "${var.remote_state_table}" }
--- /dev/null
+variable "region" {}
+variable "remote_state_bucket" {}
+variable "remote_state_table" {}
--- /dev/null
+# AutoScalingGroup Stack
+
+Creates an ASG, LC, self-allowing SG, and an IAM role for a certain style of application stack.
--- /dev/null
+data "aws_iam_policy_document" "instance_trust" {
+ statement {
+ effect = "Allow"
+ actions = [
+ "sts:AssumeRole"
+ ]
+ principals {
+ type = "Service"
+ identifiers = [
+ "ec2.amazonaws.com"
+ ]
+ }
+ }
+}
+
+resource "aws_iam_role" "default" {
+ name = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}-role"
+ assume_role_policy = "${data.aws_iam_policy_document.instance_trust.json}"
+}
+
+data "aws_iam_policy_document" "default" {
+ statement {
+ effect = "Allow"
+ actions = ["${var.iam_allow_actions}"]
+ resources = ["*"]
+ }
+}
+
+resource "aws_iam_policy" "default" {
+ name = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}"
+ description = "specific policy for ${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}"
+ policy = "${data.aws_iam_policy_document.default.json}"
+}
+
+resource "aws_iam_role_policy_attachment" "default" {
+ role = "${aws_iam_role.default.id}"
+ policy_arn = "${aws_iam_policy.default.arn}"
+}
+
+resource "aws_iam_role_policy_attachment" "extra" {
+ count = "${length(var.iam_policy_arns)}"
+ role = "${aws_iam_role.default.id}"
+ policy_arn = "${element(var.iam_policy_arns, count.index)}"
+}
+
+resource "aws_iam_instance_profile" "default" {
+ name = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}-instance-profile"
+ role = "${aws_iam_role.default.name}"
+}
--- /dev/null
+resource "aws_security_group" "default" {
+ vpc_id = "${var.vpc_id}"
+ name = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}-self"
+ description = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack} self-access"
+}
+resource "aws_security_group_rule" "default-out-all" {
+ security_group_id = "${aws_security_group.default.id}"
+ type = "egress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ cidr_blocks = [ "0.0.0.0/0" ]
+}
+resource "aws_security_group_rule" "default-in-self" {
+ security_group_id = "${aws_security_group.default.id}"
+ type = "ingress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ self = true
+}
+resource "aws_security_group_rule" "default-in-elb" {
+ count = "${length(var.elb_sg_ids)}"
+ security_group_id = "${aws_security_group.default.id}"
+ type = "ingress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ source_security_group_id = "${element(var.elb_sg_ids, count.index)}"
+}
+
+data "aws_ami" "amazon_linux" {
+ count = "${length(var.ami) > 0 ? 0 : 1}"
+ most_recent = true
+ owners = ["amazon"]
+ filter {
+ name = "name"
+ values = ["amzn-ami-hvm-*-gp2"]
+ }
+ filter {
+ name = "root-device-type"
+ values = ["ebs"]
+ }
+}
+
+data "aws_region" "current" {
+ current = true
+}
+data "template_file" "user_data" {
+ template = "${file("${path.module}/user-data.tpl")}"
+ vars {
+ region = "${data.aws_region.current.name}"
+ app_name = "${var.module}"
+ stack = "${var.stack}"
+ phase = "${var.phase}"
+ country = "${var.country}"
+ cluster = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}${length(var.country) > 0 ? "-c0" : ""}${var.country}${length(var.phase) > 0 ? "-d0" : ""}${var.phase}${length(var.suffix) > 0 ? "-" : ""}${var.suffix}"
+ acct_name = "${var.acct_name}"
+ }
+}
+
+resource "aws_launch_configuration" "default" {
+ name_prefix = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}${length(var.country) > 0 ? "-c0" : ""}${var.country}${length(var.phase) > 0 ? "-d0" : ""}${var.phase}${length(var.suffix) > 0 ? "-" : ""}${var.suffix}-"
+ image_id = "${length(var.ami) > 0 ? var.ami : data.aws_ami.amazon_linux.image_id}"
+ instance_type = "${var.instance_type}"
+ iam_instance_profile = "${aws_iam_instance_profile.default.name}"
+ key_name = "${var.key_name}"
+ security_groups = ["${concat(var.security_group_ids, list(aws_security_group.default.id))}"]
+ associate_public_ip_address = "${var.public_ips}"
+ user_data = "${data.template_file.user_data.rendered}"
+ ephemeral_block_device {
+ virtual_name = "ephemeral0"
+ device_name = "/dev/sdb"
+ }
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_autoscaling_group" "default" {
+ name = "${var.module}${length(var.stack) > 0 ? "-" : ""}${var.stack}${length(var.country) > 0 ? "-c0" : ""}${var.country}${length(var.phase) > 0 ? "-d0" : ""}${var.phase}${length(var.suffix) > 0 ? "-" : ""}${var.suffix}"
+ launch_configuration = "${aws_launch_configuration.default.name}"
+ vpc_zone_identifier = ["${var.subnet_ids}"]
+ min_size = "${var.min_size}"
+ max_size = "${var.max_size > 0 ? var.max_size : length(var.subnet_ids)}"
+ default_cooldown = 10
+ health_check_type = "EC2"
+ health_check_grace_period = "${var.health_check_grace_period}"
+ load_balancers = ["${var.elbs}"]
+ lifecycle {
+ create_before_destroy = true
+ }
+ tag {
+ propagate_at_launch = true
+ key = "module"
+ value = "${var.module}"
+ }
+ tag {
+ propagate_at_launch = true
+ key = "stack"
+ value = "${var.stack}"
+ }
+ tag {
+ propagate_at_launch = true
+ key = "country"
+ value = "${var.country}"
+ }
+ tag {
+ propagate_at_launch = true
+ key = "phase"
+ value = "${var.phase}"
+ }
+}
+
+resource "aws_autoscaling_notification" "default" {
+ count = "${length(var.notification_arns)}"
+ group_names = ["${aws_autoscaling_group.default.name}"]
+ topic_arn = "${element(var.notification_arn, count.index)}"
+ notifications = [
+ "autoscaling:EC2_INSTANCE_LAUNCH",
+ "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
+ "autoscaling:EC2_INSTANCE_TERMINATE",
+ "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
+ ]
+}
--- /dev/null
+output "sg_id" { value = "${aws_security_group.default.id}" }
+output "asg_id" { value = "${aws_autoscaling_group.default.id}" }
+output "iam_role_id" { value = "${aws_iam_role.default.id}" }
+output "iam_role_name" { value = "${aws_iam_role.default.name}" }
--- /dev/null
+#!/bin/bash
+export EC2_REGION="${region}"
+export CLOUD_APP="${app_name}"
+export CLOUD_STACK="${stack}"
+export CLOUD_DEV_PHASE="${phase}"
+export CLOUD_COUNTRIES="${country}"
+export CLOUD_ENVIRONMENT="${acct_name}"
+export CLOUD_CLUSTER="${cluster}"
--- /dev/null
+variable "vpc_id" {
+ description = "Which VPC to build this in."
+}
+
+variable "acct_name" {
+ description = "Name of AWS account."
+}
+
+variable "notification_arns" {
+ type = "list"
+ description = "ARNs of SNS topics to send ASG event notifications to."
+ default = ""
+}
+
+variable "module" {
+ description = "Name of this application."
+}
+
+variable "stack" {
+ description = "Subtype of module. (Such as 'master' or 'data'.)"
+ default = ""
+}
+
+variable "country" {
+ description = "'Country' code, which actually represents a specific AZ for us."
+ default = ""
+}
+
+variable "phase" {
+ description = "Release phase of this environment. (Such as dev, stage, or prod.)"
+ default = "dev"
+}
+
+variable "suffix" {
+ description = "Extra stuff tacked on to end of name."
+ default = ""
+}
+
+variable "instance_type" {}
+
+variable "key_name" {}
+
+variable "public_ips" {
+ default = false
+}
+
+variable "subnet_ids" {
+ type = "list"
+ description = "Which subnets the servers will be in."
+}
+
+variable "iam_allow_actions" {
+ type = "list"
+ default = []
+ description = "Allowed actions to associate with the IAM role."
+}
+
+variable "iam_policy_arns" {
+ type = "list"
+ default = []
+ description = "Additional policies to attach to IAM role."
+}
+
+variable "max_size" {
+ description = "Defaults to 1 instance per subnet. (Cannot be zero.)"
+ default = 0
+}
+
+variable "min_size" {
+ default = 0
+}
+
+variable "health_check_grace_period" {
+ default = 600
+}
+
+variable "ami" {
+ description = "Specify an AMI to use; if empty, use most recent amazon linux."
+ default = ""
+}
+
+variable "elb_sg_ids" {
+ type = "list"
+ description = "ID of elb security group, to allow access from."
+ default = []
+}
+
+variable "elbs" {
+ type = "list"
+ default = []
+}
+
+variable "security_group_ids" {
+ type = "list"
+ description = "Additional security groups the servers will belong to."
+ default = []
+}
--- /dev/null
+terraform {
+ required_version = ">= 0.10.2"
+}
+
--- /dev/null
+Creates a basic AWS VPC environment, with a public and private subnet per AZ, NAT gateways, and routing tables, as well as a base iam role and security group.
+
+Required variables:
+
+ region
+ project
+ environment
+ cidr
+ public_azs
+ private_azs
--- /dev/null
+resource "aws_vpc_dhcp_options" "default" {
+ count = "${var.enable_domain_name}"
+ domain_name = "ec2.internal ${var.r53_domain_name}"
+ domain_name_servers = ["AmazonProvidedDNS"]
+ tags {
+ Name = "${var.project}-${var.environment}-dhcp_options_set"
+ service = "${var.project}-${var.environment}-dhcp_options_set"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ role = "dhcp_options_set"
+ }
+}
+
+resource "aws_vpc_dhcp_options_association" "default" {
+ count = "${var.enable_domain_name}"
+ vpc_id = "${aws_vpc.default.id}"
+ dhcp_options_id = "${aws_vpc_dhcp_options.default.id}"
+}
+
+resource "aws_vpc" "default" {
+ cidr_block = "${var.cidr}"
+ enable_dns_hostnames = "${var.enable_dns_hostnames}"
+ enable_dns_support = "${var.enable_dns_support}"
+ instance_tenancy = "default"
+ tags {
+ Name = "${var.project}-${var.environment}-vpc"
+ service = "${var.project}-${var.environment}-vpc"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ role = "vpc"
+ }
+}
+
+resource "aws_internet_gateway" "default" {
+ vpc_id = "${aws_vpc.default.id}"
+ tags {
+ Name = "${var.project}-${var.environment}-igw"
+ service = "${var.project}-${var.environment}-igw"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ role = "igw"
+ }
+}
+
+data "aws_vpc_peering_connection" "peer" {
+ count = "${length(var.peering_connection_ids)}"
+ id = "${element(var.peering_connection_ids, count.index)}"
+}
+
+resource "aws_default_route_table" "default" {
+ default_route_table_id = "${aws_vpc.default.default_route_table_id}"
+}
+
+resource "aws_route" "default_gateway" {
+ route_table_id = "${aws_default_route_table.default.id}"
+ destination_cidr_block = "0.0.0.0/0"
+ gateway_id = "${aws_internet_gateway.default.id}"
+}
+
+resource "aws_route" "default_peer" {
+ count = "${length(var.peering_connection_ids)}"
+ route_table_id = "${aws_default_route_table.default.id}"
+ destination_cidr_block = "${element(data.aws_vpc_peering_connection.peer.*.cidr_block, count.index)}"
+ vpc_peering_connection_id = "${element(data.aws_vpc_peering_connection.peer.*.id, count.index)}"
+}
+
+resource "aws_route_table" "public" {
+ vpc_id = "${aws_vpc.default.id}"
+ tags {
+ Name = "${var.project}-${var.environment}-public"
+ service = "${var.project}-${var.environment}-route-table"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ role = "route-table"
+ }
+}
+
+resource "aws_route" "public_gateway" {
+ route_table_id = "${aws_route_table.public.id}"
+ destination_cidr_block = "0.0.0.0/0"
+ gateway_id = "${aws_internet_gateway.default.id}"
+}
+
+resource "aws_route" "public_peer" {
+ count = "${length(var.peering_connection_ids)}"
+ route_table_id = "${aws_route_table.public.id}"
+ destination_cidr_block = "${element(data.aws_vpc_peering_connection.peer.*.cidr_block, count.index)}"
+ vpc_peering_connection_id = "${element(data.aws_vpc_peering_connection.peer.*.id, count.index)}"
+}
+
+resource "aws_subnet" "public" {
+ count = "${length(var.public_azs)}"
+ vpc_id = "${aws_vpc.default.id}"
+ cidr_block = "${cidrsubnet(var.cidr, 8, count.index + var.subnets_offset_public)}"
+ availability_zone = "${element(var.public_azs, count.index)}"
+ tags {
+ Name = "${var.project}-${var.environment}-public-${element(var.public_azs, count.index)}"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ service = "${var.project}-${var.environment}-subnet-public"
+ role = "subnet"
+ zone = "pub"
+ }
+ map_public_ip_on_launch = true
+}
+
+resource "aws_route_table_association" "public" {
+ count = "${length(var.public_azs)}"
+ subnet_id = "${element(aws_subnet.public.*.id, count.index)}"
+ route_table_id = "${element(aws_route_table.public.*.id, count.index)}"
+}
+
+resource "aws_subnet" "private" {
+ count = "${length(var.private_azs)}"
+ vpc_id = "${aws_vpc.default.id}"
+ cidr_block = "${cidrsubnet(var.cidr, 8, count.index + var.subnets_offset_private)}"
+ availability_zone = "${element(var.private_azs, count.index)}"
+ tags {
+ Name = "${var.project}-${var.environment}-private-${element(var.private_azs, count.index)}"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ service = "${var.project}-${var.environment}-subnet-private"
+ role = "subnet"
+ zone = "priv"
+ }
+ map_public_ip_on_launch = false
+}
+
+resource "aws_route_table_association" "private" {
+ count = "${length(var.private_azs)}"
+ subnet_id = "${element(aws_subnet.private.*.id, count.index)}"
+ route_table_id = "${element(aws_route_table.private.*.id, count.index)}"
+}
+
+resource "aws_route_table" "private" {
+ count = "${length(var.private_azs)}"
+ vpc_id = "${aws_vpc.default.id}"
+ tags {
+ Name = "${var.project}-${var.environment}-private${format("%02d", count.index + 1)}"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ service = "${var.project}-${var.environment}-route-table-private"
+ role = "route-table"
+ }
+}
+
+resource "aws_route" "private_gateway" {
+ count = "${length(var.private_azs)}"
+ route_table_id = "${element(aws_route_table.private.*.id, count.index)}"
+ destination_cidr_block = "0.0.0.0/0"
+ nat_gateway_id = "${element(aws_nat_gateway.default.*.id, count.index)}"
+}
+
+resource "aws_route" "private_peer" {
+ count = "${length(var.peering_connection_ids) * length(var.private_azs)}"
+ route_table_id = "${element(aws_route_table.private.*.id, count.index / length(var.private_azs))}"
+ destination_cidr_block = "${element(data.aws_vpc_peering_connection.peer.*.cidr_block, count.index % length(var.private_azs))}"
+ vpc_peering_connection_id = "${element(data.aws_vpc_peering_connection.peer.*.id, count.index % length(var.private_azs))}"
+}
+
+resource "aws_eip" "nat" {
+ count = "${length(var.private_azs)}"
+ vpc = true
+}
+
+resource "aws_nat_gateway" "default" {
+ count = "${length(var.private_azs)}"
+ allocation_id = "${element(aws_eip.nat.*.id, count.index)}"
+ subnet_id = "${element(aws_subnet.public.*.id, count.index)}"
+}
+
+data "aws_iam_policy_document" "base" {
+ statement {
+ sid = "aws-read"
+ resources = ["*"]
+ actions = [
+ "autoscaling:Describe*",
+ "cloudwatch:ListMetrics",
+ "cloudwatch:GetMetricsStatistics",
+ "cloudwatch:Describe*",
+ "ec2:Describe*",
+ "elasticloadbalancing:Describe*",
+ "logs:CreateLogGroup",
+ "logs:CreateLogStream",
+ "logs:Describe*",
+ "logs:PutLogEvents",
+ "logs:PutMetricFilter"
+ ]
+ }
+}
+
+resource "aws_iam_policy" "base" {
+ name = "base-policy"
+ path = "/"
+ description = "base-policy"
+ policy = "${data.aws_iam_policy_document.base.json}"
+}
+
+resource "aws_security_group" "general-access" {
+ name = "general-access"
+ description = "Allow all ICMP and intra-vpc SSH traffic"
+ vpc_id = "${aws_vpc.default.id}"
+}
+
+resource "aws_security_group_rule" "ga_out_all" {
+ security_group_id = "${aws_security_group.general-access.id}"
+ type = "egress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ cidr_blocks = ["0.0.0.0/0"]
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_security_group_rule" "ga_in_icmp" {
+ security_group_id = "${aws_security_group.general-access.id}"
+ type = "ingress"
+ from_port = -1
+ to_port = -1
+ protocol = "icmp"
+ cidr_blocks = ["0.0.0.0/0"]
+ lifecycle {
+ create_before_destroy = true
+ }
+}
+
+resource "aws_security_group_rule" "ga_in_ssh" {
+ security_group_id = "${aws_security_group.general-access.id}"
+ type = "ingress"
+ from_port = 22
+ to_port = 22
+ protocol = "tcp"
+ cidr_blocks = ["${concat(list(var.cidr), var.ssh_allowed_cidr)}"]
+ lifecycle {
+ create_before_destroy = true
+ }
+}
--- /dev/null
+output "vpc_id" {
+ value = "${aws_vpc.default.id}"
+}
+
+output "vpc_name" {
+ value = "${var.project}-${var.environment}-vpc"
+}
+
+output "public_subnets" {
+ value = "${aws_subnet.public.*.id}"
+}
+
+output "private_subnets" {
+ value = "${aws_subnet.private.*.id}"
+}
+
+output "nat_gateway_ips" {
+ value = "${aws_eip.nat.*.public_ip}"
+}
+
+output "private_route_table_ids" {
+ value = "${aws_route_table.private.*.id}"
+}
+
+output "public_route_table_id" {
+ value = "${aws_route_table.public.id}"
+}
+
+output "base_policy_arn" {
+ value = "${aws_iam_policy.base.arn}"
+}
+
+output "general_access_sg_id" {
+ value = "${aws_security_group.general-access.id}"
+}
--- /dev/null
+variable "project" {
+ description = "topmost classification name"
+}
+
+variable "environment" {
+ description = "deployment environment of this vpc, e.g. prod, stage, dev"
+ default = "dev"
+}
+
+variable "cidr" {
+ description = "cidr block for this vpc"
+}
+
+variable "public_azs" {
+ type = "list"
+ description = "list of azs to use for public subnets in this vpc (full specification, such as us-east-1a)"
+ default = []
+}
+
+variable "private_azs" {
+ type = "list"
+ description = "list of azs to use for private subnets in this vpc (full specification, such as us-east-1a)"
+ default = []
+}
+
+variable "r53_domain_name" {
+ description = "domain name for everything in this vpc"
+ default = ""
+}
+
+variable "enable_dns_hostnames" {
+ description = "should be true if you want to use private DNS within the VPC"
+ default = true
+}
+
+variable "enable_dns_support" {
+ description = "should be true if you want to use private DNS within the VPC"
+ default = true
+}
+
+variable "enable_domain_name" {
+ description = "configure dhcp option with r53_domain_name"
+ default = false
+}
+
+variable "subnets_offset_public" {
+ description = "start numbering public subnets with this value"
+ default = 0
+}
+
+variable "subnets_offset_private" {
+ description = "start numbering private subnets with this value"
+ default = 128
+}
+
+variable "peering_connection_ids" {
+ type = "list"
+ description = "pcx ids of accepted vpc peerings"
+ default = []
+}
+
+variable "ssh_allowed_cidr" {
+ type = "list"
+ description = "list of additional cidr blocks to allow SSH traffic from"
+ default = []
+}
\ No newline at end of file
--- /dev/null
+terraform {
+ required_version = ">= 0.9.11"
+}
+
--- /dev/null
+resource "aws_eip" "vpn" {
+ count = 1
+ vpc = true
+}
+
+resource "aws_security_group" "vpn" {
+ vpc_id = "${var.vpc_id}"
+ name = "${var.name}-vpn"
+ description = "Allow VPN traffic."
+}
+
+resource "aws_security_group_rule" "vpn-out-all" {
+ security_group_id = "${aws_security_group.vpn.id}"
+ type = "egress"
+ from_port = 0
+ to_port = 0
+ protocol = "all"
+ cidr_blocks = ["0.0.0.0/0"]
+}
+
+resource "aws_security_group_rule" "vpn-in-user" {
+ security_group_id = "${aws_security_group.vpn.id}"
+ type = "ingress"
+ from_port = 1195
+ to_port = 1195
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+}
+
+resource "aws_security_group_rule" "vpn-in-bridge" {
+ security_group_id = "${aws_security_group.vpn.id}"
+ type = "ingress"
+ from_port = 1194
+ to_port = 1194
+ protocol = "udp"
+ cidr_blocks = ["0.0.0.0/0"]
+}
+
+resource "aws_security_group_rule" "vpn-in-bastion" {
+ security_group_id = "${aws_security_group.vpn.id}"
+ type = "ingress"
+ from_port = 22
+ to_port = 22
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+}
+
+resource "aws_elb" "default" {
+ count = "${var.vpcaccess_elb}"
+ name = "${var.name}-int-elb"
+ subnets = ["${var.subnet_ids}"]
+ internal = true
+ listener {
+ lb_port = 22
+ lb_protocol = "tcp"
+ instance_port = 22
+ instance_protocol = "tcp"
+ }
+ health_check {
+ healthy_threshold = 3
+ unhealthy_threshold = 2
+ interval = 30
+ timeout = 5
+ target = "TCP:1195"
+ }
+ idle_timeout = 600
+ tags {
+ module = "${var.name}"
+ phase = "${var.environment}"
+ }
+}
+
+module "asg-stack" {
+ source = "../modules/tf_aws_asg_stack"
+ vpc_id = "${var.vpc_id}"
+ acct_name = "${var.acct_name}"
+ notification_arns = ["${var.notification_arns}"]
+ module = "${var.name}"
+ phase = "${var.environment}"
+ instance_type = "${var.instance_type}"
+ key_name = "${var.key_name}"
+ public_ips = true
+ subnet_ids = ["${var.subnet_ids}"]
+ iam_policy_arns = ["${var.role_policy_arns}"]
+ security_group_ids = ["${concat(var.security_group_ids, list(aws_security_group.vpn.id))}"]
+ max_size = 1
+ min_size = 0
+ iam_allow_actions = [
+ "ec2:AssociateAddress",
+ "ec2:ModifyInstanceAttribute",
+ "ec2:ModifyNetworkInterfaceAttribute"
+ ]
+ elbs = ["${var.vpcaccess_elb ? aws_elb.default.id : ""}"]
+}
--- /dev/null
+output "vpn_eip_id" { value = "${aws_eip.vpn.id}" }
+output "vpn_eip_ip" { value = "${aws_eip.vpn.public_ip}"}
--- /dev/null
+variable "vpc_id" {}
+variable "name" { default = "vpcaccess" }
+variable "subnet_ids" { type="list" }
+variable "security_group_ids" { type="list" }
+variable "role_policy_arns" { type="list" }
+variable "key_name" {}
+variable "environment" {}
+variable "acct_name" {}
+variable "notification_arns" { type="list"}
+variable "instance_type" { default = "t2.small" }
+varaible "vpcaccess_elb" { default = false }
--- /dev/null
+provider "aws" {
+ region = "${var.region}"
+}
+
+module "terraform-infrastructure" {
+ source = "../modules/terraform-infrastructure"
+ region = "${var.region}"
+ remote_state_bucket = "${var.remote_state_bucket}"
+ remote_state_table = "${var.remote_state_table}"
+}
--- /dev/null
+output "region" { value = "${var.region}" }
+output "remote_state_bucket" { value = "${var.remote_state_bucket}" }
+output "remote_state_table" { value = "${var.remote_state_table}" }
+output "environment" { value = "${var.environment}" }
--- /dev/null
+../terraform.tfvars
\ No newline at end of file
--- /dev/null
+variable "region" {}
+variable "remote_state_bucket" {}
+variable "remote_state_table" {}
+variable "environment" {}
--- /dev/null
+region = "us-east-1"
+remote_state_bucket = "test-terraform-state"
+remote_state_table = "terraform-state"
+acct_name = "testAcct"
+project = "test"
+environment = "dev"
+vpc_cidr = "10.83.0.0/16"
+vpc_public_azs = ["us-east-1e"]
+vpc_private_azs = ["us-east-1c"]
+key_name = "somekey"
--- /dev/null
+provider "aws" {
+ region = "${var.region}"
+}
+
+module "vpc" {
+ source = "../modules/tf_aws_vpc"
+ project = "${var.project}"
+ environment = "${var.environment}"
+ cidr = "${var.vpc_cidr}"
+ public_azs = "${var.vpc_public_azs}"
+ private_azs = "${var.vpc_private_azs}"
+ ssh_allowed_cidr = "${var.ssh_allowed_cidr}"
+}
--- /dev/null
+output "vpc_id" {
+ value = "${module.vpc.vpc_id}"
+}
+
+output "public_subnets" {
+ value = "${module.vpc.public_subnets}"
+}
+
+output "private_subnets" {
+ value = "${module.vpc.private_subnets}"
+}
+
+output "nat_gateway_ips" {
+ value = "${module.vpc.nat_gateway_ips}"
+}
+
+output "base_policy_arn" {
+ value = "${module.vpc.base_policy_arn}"
+}
+
+output "general_access_sg_id" {
+ value = "${module.vpc.general_access_sg_id}"
+}
--- /dev/null
+../terraform.tfvars
\ No newline at end of file
--- /dev/null
+variable "region" {}
+variable "project" {}
+variable "environment" {}
+variable "vpc_cidr" {}
+variable "vpc_public_azs" {type="list"}
+variable "vpc_private_azs" {type="list"}
+variable "ssh_allowed_cidr" {type="list", default=[], description="List of extra cidrs to allow SSH traffic from"}
--- /dev/null
+data "terraform_remote_state" "management" {
+ backend = "s3"
+ config {
+ bucket = "${var.remote_state_bucket}"
+ region = "${var.region}"
+ key = "${var.environment}/management-stack.tfstate"
+ dynamodb_table = "${var.remote_state_table}"
+ }
+}
--- /dev/null
+data "terraform_remote_state" "vpc" {
+ backend = "s3"
+ config {
+ bucket = "${var.remote_state_bucket}"
+ region = "${var.region}"
+ key = "${var.environment}/vpc.tfstate"
+ dynamodb_table = "${var.remote_state_table}"
+ }
+}
--- /dev/null
+provider "aws" {
+ region = "${var.region}"
+}
+
+module "vpcaccess-stack" {
+ source = "../modules/vpcaccess-stack"
+ vpc_id = "${data.terraform_remote_state.vpc.vpc_id}"
+ subnet_ids = "${data.terraform_remote_state.vpc.public_subnets}"
+ security_group_ids = ["${data.terraform_remote_state.vpc.general_access_sg_id}"]
+ role_policy_arns = ["${data.terraform_remote_state.vpc.role_policy_arn}"]
+ key_name = "${var.key_name}"
+ environment = "${var.environment}"
+ acct_name = "${var.acct_name}"
+ notification_arns = ["${data.terraform_remote_state.management.events_topic_arn}"]
+}
--- /dev/null
+output "vpn_eip_ip" { value = "${module.vpcaccess-stack.vpn_eip_ip}"}
--- /dev/null
+variable "region" {}
+variable "environment" {}
+variable "remote_state_bucket" {}
+variable "remote_state_table" {}
+variable "acct_name" {}
+variable "key_name" {}