add serialization and deserialization

This commit is contained in:
mhoffm
2021-06-23 20:30:30 +02:00
parent 228a013764
commit 150c676286
891 changed files with 54270 additions and 10 deletions

View File

@@ -0,0 +1,92 @@
### ALB resources
# TODO:
# support not logging
data "template_file" "bucket_policy" {
template = "${file("${path.module}/bucket_policy.json")}"
vars {
log_bucket = "${var.log_bucket}"
log_prefix = "${var.log_prefix}"
account_id = "${var.aws_account_id}"
principle_account_id = "${lookup(var.principle_account_id, var.aws_region)}"
}
}
resource "aws_alb" "main" {
name = "${var.alb_name}"
subnets = ["${var.subnets}"]
security_groups = ["${var.alb_security_groups}"]
internal = "${var.alb_is_internal}"
access_logs {
bucket = "${var.log_bucket}"
prefix = "${var.log_prefix}"
enabled = "${var.log_bucket != ""}"
}
tags = "${merge(var.tags, map("Name", format("%s", var.alb_name)))}"
}
resource "aws_s3_bucket" "log_bucket" {
count = "${var.log_bucket != "" ? 1 : 0}"
bucket = "${var.log_bucket}"
policy = "${data.template_file.bucket_policy.rendered}"
force_destroy = true
tags = "${merge(var.tags, map("Name", format("%s", var.log_bucket)))}"
}
resource "aws_alb_target_group" "target_group" {
name = "${var.alb_name}-tg"
port = "${var.backend_port}"
protocol = "${upper(var.backend_protocol)}"
vpc_id = "${var.vpc_id}"
health_check {
interval = 30
path = "${var.health_check_path}"
port = "traffic-port"
healthy_threshold = 3
unhealthy_threshold = 3
timeout = 5
protocol = "${var.backend_protocol}"
}
stickiness {
type = "lb_cookie"
cookie_duration = "${var.cookie_duration}"
enabled = "${ var.cookie_duration == 1 ? false : true}"
}
tags = "${merge(var.tags, map("Name", format("%s-tg", var.alb_name)))}"
}
resource "aws_alb_listener" "front_end_http" {
load_balancer_arn = "${aws_alb.main.arn}"
port = "80"
protocol = "HTTP"
default_action {
target_group_arn = "${aws_alb_target_group.target_group.id}"
type = "forward"
}
count = "${trimspace(element(split(",", var.alb_protocols), 1)) == "HTTP" || trimspace(element(split(",", var.alb_protocols), 2)) == "HTTP" ? 1 : 0}"
}
resource "aws_alb_listener" "front_end_https" {
load_balancer_arn = "${aws_alb.main.arn}"
port = "443"
protocol = "HTTPS"
certificate_arn = "${var.certificate_arn}"
ssl_policy = "ELBSecurityPolicy-2015-05"
default_action {
target_group_arn = "${aws_alb_target_group.target_group.id}"
type = "forward"
}
count = "${trimspace(element(split(",", var.alb_protocols), 1)) == "HTTPS" || trimspace(element(split(",", var.alb_protocols), 2)) == "HTTPS" ? 1 : 0}"
}

View File

@@ -0,0 +1,19 @@
output "alb_id" {
value = "${aws_alb.main.id}"
}
output "alb_dns_name" {
value = "${aws_alb.main.dns_name}"
}
output "alb_zone_id" {
value = "${aws_alb.main.zone_id}"
}
output "target_group_arn" {
value = "${aws_alb_target_group.target_group.arn}"
}
output "principle_account_id" {
value = "${lookup(var.principle_account_id, var.aws_region)}"
}

View File

@@ -0,0 +1,102 @@
/*
Module variables
*/
variable "alb_is_internal" {
description = "Determines if the ALB is internal. Default: false"
default = false
}
variable "alb_name" {
description = "The name of the ALB as will show in the AWS EC2 ELB console."
default = "my-alb"
}
variable "alb_protocols" {
description = "A comma delimited list of the protocols the ALB accepts. e.g.: HTTPS"
default = "HTTPS"
}
variable "alb_security_groups" {
description = "A comma separated string of security groups with which we associate the ALB. e.g. 'sg-edcd9784,sg-edcd9785'"
type = "list"
}
variable "aws_region" {
description = "AWS region to use."
}
variable "aws_account_id" {
description = "AWS account ID."
}
variable "backend_port" {
description = "The port the service on the EC2 instances listen on."
default = 80
}
variable "backend_protocol" {
description = "The protocol the backend service speaks. Options: HTTP, HTTPS, TCP, SSL (secure tcp)."
default = "HTTP"
}
variable "certificate_arn" {
description = "The ARN of the SSL Certificate. e.g. 'arn:aws:iam::123456789012:server-certificate/ProdServerCert'"
}
variable "cookie_duration" {
description = "If load balancer connection stickiness is desired, set this to the duration that cookie should be valid. If no stickiness is wanted, leave it blank. e.g.: 300"
default = "1"
}
variable "health_check_path" {
description = "The URL the ELB should use for health checks. e.g. /health"
default = "/"
}
variable "log_bucket" {
description = "S3 bucket for storing ALB access logs."
default = ""
}
variable "log_prefix" {
description = "S3 prefix within the log_bucket under which logs are stored."
default = ""
}
variable "principle_account_id" {
description = "A map of ELB/ALB root account numbers used to set up logging."
default = {
us-east-1 = "127311923021"
us-east-2 = "033677994240"
us-west-1 = "027434742980"
us-west-2 = "797873946194"
ca-central-1 = "985666609251"
eu-west-1 = "156460612806"
eu-central-1 = "054676820928"
eu-west-2 = "652711504416"
ap-northeast-1 = "582318560864"
ap-northeast-2 = "600734575887"
ap-southeast-1 = "114774131450"
ap-southeast-2 = "783225319266"
ap-south-1 = "718504428378"
sa-east-1 = "507241528517"
us-gov-west-1 = "048591011584"
cn-north-1 = "638102146993"
}
}
variable "subnets" {
description = "A list of subnets to associate with the ALB. e.g. ['subnet-1a2b3c4d','subnet-1a2b3c4e','subnet-1a2b3c4f']"
type = "list"
}
variable "vpc_id" {
description = "VPC id where the ALB and other resources will be deployed."
}
variable "tags" {
description = "A map of tags to add to all resources"
default = {}
}

View File

@@ -0,0 +1,31 @@
provider "aws" {
region = "${var.aws_region}"
}
module "vpc" {
source = "github.com/terraform-community-modules/tf_aws_vpc"
name = "my-vpc"
cidr = "10.0.0.0/16"
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = "true"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
}
module "sg_https_web" {
source = "github.com/terraform-community-modules/tf_aws_sg//sg_https_only"
security_group_name = "my-sg-https"
vpc_id = "${module.vpc.vpc_id}"
}
module "alb" {
source = "../../alb/"
alb_security_groups = ["${module.sg_https_web.security_group_id_web}"]
aws_account_id = "${var.aws_account_id}"
aws_region = "${var.aws_region}"
certificate_arn = "${var.certificate_arn}"
log_bucket = "${var.log_bucket}"
log_prefix = "${var.log_prefix}"
subnets = "${module.vpc.public_subnets}"
vpc_id = "${module.vpc.vpc_id}"
}

View File

@@ -0,0 +1,11 @@
/*
Outputs used for tests
*/
output "principle_account_id" {
value = "${module.alb.principle_account_id}"
}
output "vpc_id" {
value = "${module.vpc.vpc_id}"
}

View File

@@ -0,0 +1,11 @@
variable "aws_account_id" {}
variable "aws_region" {
default = "us-west-2"
}
variable "certificate_arn" {}
variable "log_bucket" {}
variable "log_prefix" {}

View File

@@ -0,0 +1,48 @@
//
// Module: tf_aws_asg
//
// This template creates the following resources
// - A launch configuration
// - A auto-scaling group
// - It's meant to be used for ASGs that *don't*
// need an ELB associated with them.
// Provider specific configs
provider "aws" {
access_key = "${var.aws_access_key}"
secret_key = "${var.aws_secret_key}"
region = "${var.aws_region}"
}
resource "aws_launch_configuration" "launch_config" {
name = "${var.lc_name}"
image_id = "${var.ami_id}"
instance_type = "${var.instance_type}"
iam_instance_profile = "${var.iam_instance_profile}"
key_name = "${var.key_name}"
security_groups = ["${var.security_group}"]
user_data = "${file(var.user_data)}"
}
resource "aws_autoscaling_group" "main_asg" {
//We want this to explicitly depend on the launch config above
depends_on = ["aws_launch_configuration.launch_config"]
name = "${var.asg_name}"
// Split out the AZs string into an array
// The chosen availability zones *must* match
// the AZs the VPC subnets are tied to.
availability_zones = ["${split(",", var.azs)}"]
// Split out the subnets string into an array
vpc_zone_identifier = ["${split(",", var.subnet_azs)}"]
// Uses the ID from the launch config created above
launch_configuration = "${aws_launch_configuration.launch_config.id}"
max_size = "${var.asg_number_of_instances}"
min_size = "${var.asg_minimum_number_of_instances}"
desired_capacity = "${var.asg_number_of_instances}"
health_check_grace_period = "${var.health_check_grace_period}"
health_check_type = "${var.health_check_type}"
}

View File

@@ -0,0 +1,13 @@
//
// Module: tf_aws_asg
//
// Output the ID of the Launch Config
output "launch_config_id" {
value = "${aws_launch_configuration.launch_config.id}"
}
// Output the ID of the Launch Config
output "asg_id" {
value = "${aws_autoscaling_group.main_asg.id}"
}

View File

@@ -0,0 +1,63 @@
//
// Module: tf_aws_asg
//
// Module specific variables
// Launch Configuration Variables
variable "lc_name" {}
variable "ami_id" {}
variable "instance_type" {}
variable "iam_instance_profile" {}
variable "key_name" {}
variable "security_group" {
description = "The security group the instances to use"
}
variable "user_data" {
description = "The path to a file with user_data for the instances"
}
// Auto-Scaling Group
variable "asg_name" {}
variable "asg_number_of_instances" {
description = "The number of instances we want in the ASG"
// We use this to populate the following ASG settings
// - max_size
// - desired_capacity
}
variable "asg_minimum_number_of_instances" {
description = "The minimum number of instances the ASG should maintain"
default = 1
// Defaults to 1
// Can be set to 0 if you never want the ASG to replace failed instances
}
variable "health_check_grace_period" {
description = "Number of seconds for a health check to time out"
default = 300
}
variable "health_check_type" {
default = "EC2"
//Types available are:
// - ELB
// - EC2
// * http://docs.aws.amazon.com/cli/latest/reference/autoscaling/create-auto-scaling-group.html#options
}
variable "subnet_azs" {
description = "The VPC subnet IDs"
// comma separated list
}
variable "azs" {
description = "Availability Zones"
// comma separated list
}
// Variables for providers used in this module
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "aws_region" {}

View File

@@ -0,0 +1,29 @@
module "my_autoscaling_group" {
source = "../"
lc_name = "${var.lc_name}"
ami_id = "${var.ami_id}"
instance_type = "${var.instance_type}"
iam_instance_profile = "${var.iam_instance_profile}"
key_name = "${var.key_name}"
security_group = "${var.security_group_id}"
user_data = "${var.user_data_file}"
asg_name = "${var.asg_name}"
asg_number_of_instances = "${var.asg_number_of_instances}"
asg_minimum_number_of_instances = "${var.asg_minimum_number_of_instances}"
load_balancer_names = "${var.elb_names}"
health_check_type = "${var.health_check_type}"
availability_zones = "${var.availability_zones}"
vpc_zone_subnets = "${var.vpc_zone_subnets}"
}

View File

@@ -0,0 +1,43 @@
variable "lc_name" {
default = "example_lc"
}
variable "ami_id" {
default = "ami-sadfasd"
}
variable "instance_type" {
default = "m3.medium"
}
variable "iam_instance_profile" {
default = "test_profile"
}
variable "key_name" {
default = "my_keypair_name"
}
variable "security_group_id" {
default = "sg-abcdef"
}
variable "user_data_file" {
default = "user-data.sh"
}
variable "asg_name" {
default = "my-custom-asg"
}
variable "asg_number_of_instances" {
default = 2
}
variable "asg_minimum_number_of_instances" {
default = 1
}
variable "elb_names" {
default = "my-elb-name"
}
variable "health_check_type" {
default = "ELB"
}
variable "availability_zones" {
default = "us-west-2a,us-west-2b"
}
variable "vpc_zone_subnets" {
default = "subnet-d2jdfd,subnet-2ell2kd"
}

View File

@@ -0,0 +1,42 @@
/*
* Module: tf_aws_asg_elb
*
* This template creates the following resources
* - A launch configuration
* - A auto-scaling group
*
* It requires you create an ELB instance before you use it.
*/
resource "aws_launch_configuration" "launch_config" {
name = "${var.lc_name}"
image_id = "${var.ami_id}"
instance_type = "${var.instance_type}"
iam_instance_profile = "${var.iam_instance_profile}"
key_name = "${var.key_name}"
security_groups = ["${var.security_group}"]
user_data = "${file(var.user_data)}"
}
resource "aws_autoscaling_group" "main_asg" {
# We want this to explicitly depend on the launch config above
depends_on = ["aws_launch_configuration.launch_config"]
name = "${var.asg_name}"
# The chosen availability zones *must* match the AZs the VPC subnets are tied to.
availability_zones = ["${split(",", var.availability_zones)}"]
vpc_zone_identifier = ["${split(",", var.vpc_zone_subnets)}"]
# Uses the ID from the launch config created above
launch_configuration = "${aws_launch_configuration.launch_config.id}"
max_size = "${var.asg_number_of_instances}"
min_size = "${var.asg_minimum_number_of_instances}"
desired_capacity = "${var.asg_number_of_instances}"
health_check_grace_period = "${var.health_check_grace_period}"
health_check_type = "${var.health_check_type}"
load_balancers = ["${split(",", var.load_balancer_names)}"]
}

View File

@@ -0,0 +1,17 @@
/*
* Module: tf_aws_asg_elb
*
* Outputs:
* - launch_config_id
* - asg_id
*/
# Output the ID of the Launch Config
output "launch_config_id" {
value = "${aws_launch_configuration.launch_config.id}"
}
# Output the ID of the Launch Config
output "asg_id" {
value = "${aws_autoscaling_group.main_asg.id}"
}

View File

@@ -0,0 +1,81 @@
/*
* Module: tf_aws_asg_elb
*/
#
# Launch Configuration Variables
#
variable "lc_name" {}
variable "ami_id" {
description = "The AMI to use with the launch configuration"
}
variable "instance_type" {}
variable "iam_instance_profile" {
description = "The IAM role the launched instance will use"
}
variable "key_name" {
description = "The SSH public key name (in EC2 key-pairs) to be injected into instances"
}
variable "security_group" {
description = "ID of SG the launched instance will use"
}
variable "user_data" {
description = "The path to a file with user_data for the instances"
}
#
# Auto-Scaling Group
#
variable "asg_name" {}
/* We use this to populate the following ASG settings
* - max_size
* - desired_capacity
*/
variable "asg_number_of_instances" {
description = "The number of instances we want in the ASG"
}
/*
* Can be set to 0 if you never want the ASG to replace failed instances
*/
variable "asg_minimum_number_of_instances" {
description = "The minimum number of instances the ASG should maintain"
default = 1
}
variable "health_check_grace_period" {
description = "Number of seconds for a health check to time out"
default = 300
}
/*
* Types available are:
* - ELB
* - EC2
*
* @see-also: http://docs.aws.amazon.com/cli/latest/reference/autoscaling/create-auto-scaling-group.html#options
*/
variable "health_check_type" {
description = "The health check used by the ASG to determine health"
default = "ELB"
}
variable "load_balancer_names" {
description = "A comma seperated list string of ELB names the ASG should associate instances with"
}
/*
* A string list of AZs, ex:
* "us-east-1a,us-east-1c,us-east-1e"
*/
variable "availability_zones" {
description = "A comma seperated list string of AZs the ASG will be associated with"
}
/*
* A string list of VPC subnet IDs, ex:
* "subnet-d2t4sad,subnet-434ladkn"
*/
variable "vpc_zone_subnets" {
description = "A comma seperated list string of VPC subnets to associate with ASG, should correspond with var.availability_zones zones"
}

View File

@@ -0,0 +1,10 @@
resource "aws_kms_key" "aurora" {
description = "RDS master key for ${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
deletion_window_in_days = 30
enable_key_rotation = "true"
}
resource "aws_kms_alias" "aurora" {
name = "alias/${var.name}-${data.aws_vpc.vpc.tags["Name"]}-rds-key"
target_key_id = "${aws_kms_key.aurora.key_id}"
}

View File

@@ -0,0 +1,97 @@
data "aws_vpc" "vpc" {
id = "${var.vpc_id}"
}
resource "aws_rds_cluster" "aurora" {
cluster_identifier = "tf-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
availability_zones = ["${var.azs}"]
database_name = "${var.database_name}"
master_username = "${var.master_username}"
master_password = "${var.master_password}"
engine = "${var.engine}"
backup_retention_period = "${var.backup_retention_period}"
preferred_backup_window = "${var.preferred_backup_window}"
vpc_security_group_ids = ["${aws_security_group.aurora_security_group.id}"]
storage_encrypted = "${var.storage_encrypted}"
kms_key_id = "${aws_kms_key.aurora.arn}"
apply_immediately = "${var.apply_immediately}"
db_subnet_group_name = "${aws_db_subnet_group.aurora_subnet_group.id}"
db_cluster_parameter_group_name = "${aws_rds_cluster_parameter_group.aurora_cluster_parameter_group.id}"
final_snapshot_identifier = "final-snapshot-${var.name}-${data.aws_vpc.vpc.tags["Name"]}" # Useful in dev
#skip_final_snapshot = true # Useful in dev - defaults to false
iam_database_authentication_enabled = "${var.iam_database_authentication_enabled}"
lifecycle {
prevent_destroy = "true" # https://www.terraform.io/docs/configuration/resources.html#prevent_destroy
}
}
resource "aws_rds_cluster_instance" "aurora_instance" {
count = "${var.cluster_size}"
identifier = "tf-rds-aurora-${var.name}-${data.aws_vpc.vpc.tags["Name"]}-${count.index}"
engine = "${var.engine}"
cluster_identifier = "${aws_rds_cluster.aurora.id}"
instance_class = "${var.instance_class}"
publicly_accessible = "${var.publicly_accessible}"
db_subnet_group_name = "${aws_db_subnet_group.aurora_subnet_group.id}"
db_parameter_group_name = "${aws_db_parameter_group.aurora_parameter_group.id}"
apply_immediately = "${var.apply_immediately}"
monitoring_role_arn = "${aws_iam_role.aurora_instance_role.arn}"
monitoring_interval = "5"
tags {
Name = "tf-rds-aurora-${var.name}-${data.aws_vpc.vpc.tags["Name"]}-${count.index}"
}
}
resource "aws_db_subnet_group" "aurora_subnet_group" {
name = "tf-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
subnet_ids = ["${var.subnets}"]
tags {
Name = "tf-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
}
}
resource "aws_db_parameter_group" "aurora_parameter_group" {
name = "tf-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
family = "${var.family}"
description = "Terraform-managed parameter group for ${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
parameter = ["${var.db_parameters}"]
tags {
Name = "tf-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
}
}
resource "aws_rds_cluster_parameter_group" "aurora_cluster_parameter_group" {
name = "tf-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
family = "${var.family}"
description = "Terraform-managed cluster parameter group for ${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
parameter = ["${var.cluster_parameters}"]
tags {
Name = "tf-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
}
}
resource "aws_db_option_group" "aurora_option_group" {
name = "tf-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
option_group_description = "Terraform-managed option group for ${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
engine_name = "${var.engine}"
major_engine_version = "${var.major_engine_version}"
}
resource "aws_iam_role" "aurora_instance_role" {
name = "tf-role-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
assume_role_policy = "${file("${path.module}/files/iam/assume_role_rds_monitoring.json")}"
path = "/tf/${var.env}/${var.name}-${data.aws_vpc.vpc.tags["Name"]}/" # edits?
}
resource "aws_iam_role_policy_attachment" "aurora_policy_rds_monitoring" {
role = "${aws_iam_role.aurora_instance_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"
}

View File

@@ -0,0 +1,15 @@
output "rds_cluster_id" {
value = "${aws_rds_cluster.aurora.id}"
}
output "writer_endpoint" {
value = "${aws_rds_cluster.aurora.endpoint}"
}
output "reader_endpoint" {
value = "${aws_rds_cluster.aurora.reader_endpoint}"
}
output "security_group_id"{
value = "${aws_security_group.aurora_security_group.id}"
}

View File

@@ -0,0 +1,28 @@
resource "aws_security_group" "aurora_security_group" {
name = "tf-sg-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
description = "Terraform-managed RDS security group for ${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
vpc_id = "${data.aws_vpc.vpc.id}"
tags {
Name = "tf-sg-rds-${var.name}-${data.aws_vpc.vpc.tags["Name"]}"
}
}
resource "aws_security_group_rule" "aurora_ingress" {
count = "${length(var.allowed_security_groups)}"
type = "ingress"
from_port = "${var.db_port}"
to_port = "${var.db_port}"
protocol = "tcp"
source_security_group_id = "${element(var.allowed_security_groups, count.index)}"
security_group_id = "${aws_security_group.aurora_security_group.id}"
}
resource "aws_security_group_rule" "aurora_networks_ingress" {
type = "ingress"
from_port = "${var.db_port}"
to_port = "${var.db_port}"
protocol = "tcp"
cidr_blocks = ["${var.allowed_cidr}"]
security_group_id = "${aws_security_group.aurora_security_group.id}"
}

View File

@@ -0,0 +1,83 @@
variable "env" {}
variable "allowed_cidr" {
type = "list"
default = ["127.0.0.1/32"]
description = "A list of Security Group ID's to allow access to."
}
variable "allowed_security_groups" {
type = "list"
default = []
description = "A list of Security Group ID's to allow access to."
}
variable "azs" {
description = "A list of Availability Zones in the Region"
type = "list"
}
variable "cluster_size" {
description = "Number of cluster instances to create"
}
variable "db_port" {
default = 3306
}
variable "instance_class" {
description = "Instance class to use when creating RDS cluster"
default = "db.t2.medium"
}
variable "publicly_accessible" {
description = "Should the instance get a public IP address?"
default = "false"
}
variable "name" {
description = "Name for the Redis replication group i.e. cmsCommon"
}
variable "subnets" {
description = "Subnets to use in creating RDS subnet group (must already exist)"
type = "list"
}
variable "cluster_parameters" {
description = "A list of cluster parameter maps to apply"
type = "list"
default = []
}
variable "db_parameters" {
description = "A list of db parameter maps to apply"
type = "list"
default = []
}
# see aws_rds_cluster documentation for these variables
variable "database_name" { }
variable "master_username" { }
variable "master_password" { }
variable "backup_retention_period" {
description = "The days to retain backups for"
default = "30"
}
variable "preferred_backup_window" {
description = "The daily time range during which automated backups are created"
default = "01:00-03:00"
}
variable "storage_encrypted" { default = true }
variable "apply_immediately" { default = false }
variable "iam_database_authentication_enabled" { default = false }
variable "major_engine_version" { default = "5.6" }
variable "engine" { default = "aurora" }
variable "family" { default = "aurora5.6"}
variable "vpc_id" {
description = "VPC ID"
}

View File

@@ -0,0 +1,148 @@
resource "aws_security_group" "bastion" {
name = var.name
vpc_id = var.vpc_id
description = "Bastion security group (only SSH inbound access is allowed)"
tags = {
Name = var.name
}
}
resource "aws_security_group_rule" "ssh_ingress" {
type = "ingress"
from_port = "22"
to_port = "22"
protocol = "tcp"
cidr_blocks = var.allowed_cidr
ipv6_cidr_blocks = var.allowed_ipv6_cidr
security_group_id = aws_security_group.bastion.id
}
resource "aws_security_group_rule" "ssh_sg_ingress" {
count = length(var.allowed_security_groups)
type = "ingress"
from_port = "22"
to_port = "22"
protocol = "tcp"
source_security_group_id = element(var.allowed_security_groups, count.index)
security_group_id = aws_security_group.bastion.id
}
resource "aws_security_group_rule" "bastion_all_egress" {
type = "egress"
from_port = "0"
to_port = "65535"
protocol = "all"
cidr_blocks = [
"0.0.0.0/0",
]
ipv6_cidr_blocks = [
"::/0",
]
security_group_id = aws_security_group.bastion.id
}
data "template_file" "user_data" {
template = file("${path.module}/${var.user_data_file}")
vars = {
s3_bucket_name = var.s3_bucket_name
s3_bucket_uri = var.s3_bucket_uri
ssh_user = var.ssh_user
keys_update_frequency = var.keys_update_frequency
enable_hourly_cron_updates = var.enable_hourly_cron_updates
additional_user_data_script = var.additional_user_data_script
}
}
//resource "aws_instance" "bastion" {
// ami = "${var.ami}"
// instance_type = "${var.instance_type}"
// iam_instance_profile = "${var.iam_instance_profile}"
// subnet_id = "${var.subnet_id}"
// vpc_security_group_ids = ["${aws_security_group.bastion.id}"]
// user_data = "${template_file.user_data.rendered}"
//
// count = 1
//
// tags {
// Name = "${var.name}"
// }
//}
resource "aws_launch_configuration" "bastion" {
name_prefix = "${var.name}-"
image_id = var.ami
instance_type = var.instance_type
user_data = data.template_file.user_data.rendered
enable_monitoring = var.enable_monitoring
security_groups = compact(
concat(
[aws_security_group.bastion.id],
split(",", var.security_group_ids),
),
)
root_block_device {
volume_size = var.instance_volume_size_gb
}
iam_instance_profile = var.iam_instance_profile
associate_public_ip_address = var.associate_public_ip_address
key_name = var.key_name
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "bastion" {
name = var.apply_changes_immediately ? aws_launch_configuration.bastion.name : var.name
vpc_zone_identifier = var.subnet_ids
desired_capacity = "1"
min_size = "1"
max_size = "1"
health_check_grace_period = "60"
health_check_type = "EC2"
force_delete = false
wait_for_capacity_timeout = 0
launch_configuration = aws_launch_configuration.bastion.name
enabled_metrics = [
"GroupMinSize",
"GroupMaxSize",
"GroupDesiredCapacity",
"GroupInServiceInstances",
"GroupPendingInstances",
"GroupStandbyInstances",
"GroupTerminatingInstances",
"GroupTotalInstances",
]
tags = concat(
[
{
"key" = "Name"
"value" = var.name
"propagate_at_launch" = true
},
{
"key" = "EIP"
"value" = var.eip
"propagate_at_launch" = true
},
],
var.extra_tags,
)
lifecycle {
create_before_destroy = true
}
}

View File

@@ -0,0 +1,12 @@
output "ssh_user" {
value = var.ssh_user
}
output "security_group_id" {
value = aws_security_group.bastion.id
}
output "asg_id" {
value = aws_autoscaling_group.bastion.id
}

View File

@@ -0,0 +1,7 @@
# This is just a sample definition for bastion AMI
module "bastion_ami" {
source = "github.com/terraform-community-modules/tf_aws_ubuntu_ami/ebs"
instance_type = "t2.micro"
region = "eu-west-1"
distribution = "trusty"
}

View File

@@ -0,0 +1,49 @@
# This is just a sample definition of IAM instance profile which is allowed to read-only from S3, and associate ElasticIP addresses.
resource "aws_iam_instance_profile" "s3_readonly-allow_associateaddress" {
name = "s3_readonly-allow_associateaddress"
role = "${aws_iam_role.s3_readonly-allow_associateaddress.name}"
}
resource "aws_iam_role" "s3_readonly-allow_associateaddress" {
name = "s3_readonly-allow_associateaddress-role"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "s3_readonly-allow_associateaddress_policy" {
name = "s3_readonly-allow_associateaddress-policy"
role = "${aws_iam_role.s3_readonly-allow_associateaddress.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1425916919000",
"Effect": "Allow",
"Action": [
"ec2:AssociateAddress",
"s3:List*",
"s3:Get*"
],
"Resource": "*"
}
]
}
EOF
}

View File

@@ -0,0 +1,48 @@
# This is just a sample definition of IAM instance profile which is allowed to read-only from S3.
resource "aws_iam_instance_profile" "s3_readonly" {
name = "s3_readonly"
role = "${aws_iam_role.s3_readonly.name}"
}
resource "aws_iam_role" "s3_readonly" {
name = "s3_readonly"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "s3_readonly_policy" {
name = "s3_readonly-policy"
role = "${aws_iam_role.s3_readonly.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1425916919000",
"Effect": "Allow",
"Action": [
"s3:List*",
"s3:Get*"
],
"Resource": "*"
}
]
}
EOF
}

View File

@@ -0,0 +1,44 @@
# This is an example of how to put public keys into S3 bucket and manage them in Terraform
variable "ssh_public_key_names" {
default = ["user1", "user2", "admin"]
type = list(string)
}
resource "aws_s3_bucket" "ssh_public_keys" {
region = "eu-west-1"
bucket = "public-keys-demo-bucket"
acl = "private"
policy = <<EOF
{
"Version": "2008-10-17",
"Id": "Policy142469412148",
"Statement": [
{
"Sid": "Stmt1424694110324",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": [
"s3:List*",
"s3:Get*"
],
"Resource": "arn:aws:s3:::public-keys-demo-bucket"
}
]
}
EOF
}
resource "aws_s3_bucket_object" "ssh_public_keys" {
count = length(var.ssh_public_key_names)
bucket = aws_s3_bucket.ssh_public_keys.bucket
key = "${element(var.ssh_public_key_names, count.index)}.pub"
# Make sure that you put files into correct location and name them accordingly (`public_keys/{keyname}.pub`)
source = "public_keys/${element(var.ssh_public_key_names, count.index)}.pub"
depends_on = [aws_s3_bucket.ssh_public_keys]
}

View File

@@ -0,0 +1,116 @@
variable "allowed_cidr" {
type = list(string)
default = [
"0.0.0.0/0",
]
description = "A list of CIDR Networks to allow ssh access to."
}
variable "allowed_ipv6_cidr" {
type = list(string)
default = [
"::/0",
]
description = "A list of IPv6 CIDR Networks to allow ssh access to."
}
variable "allowed_security_groups" {
type = list(string)
default = []
description = "A list of Security Group ID's to allow access to."
}
variable "name" {
default = "bastion"
}
variable "extra_tags" {
type = list(object({ key = string, value = string, propagate_at_launch = bool }))
default = []
description = "A list of tags to associate to the bastion instance."
}
variable "ami" {
}
variable "instance_type" {
default = "t2.micro"
}
variable "instance_volume_size_gb" {
description = "The root volume size, in gigabytes"
default = "8"
}
variable "iam_instance_profile" {
}
variable "user_data_file" {
default = "user_data.sh"
}
variable "s3_bucket_name" {
}
variable "s3_bucket_uri" {
default = ""
}
variable "enable_monitoring" {
default = true
}
variable "ssh_user" {
default = "ubuntu"
}
variable "enable_hourly_cron_updates" {
default = "false"
}
variable "keys_update_frequency" {
default = ""
}
variable "additional_user_data_script" {
default = ""
}
variable "region" {
default = "eu-west-1"
}
variable "vpc_id" {
}
variable "security_group_ids" {
description = "Comma seperated list of security groups to apply to the bastion."
default = ""
}
variable "subnet_ids" {
default = []
description = "A list of subnet ids"
}
variable "eip" {
default = ""
}
variable "associate_public_ip_address" {
default = false
}
variable "key_name" {
default = ""
}
variable "apply_changes_immediately" {
description = "Whether to apply the changes at once and recreate auto-scaling group"
default = false
}

View File

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

View File

@@ -0,0 +1,158 @@
# --- IAM user allowing access to the bucket ---
data "aws_iam_policy_document" "iam_policy" {
statement {
actions = [
"s3:GetObject",
"s3:GetObjectAcl",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:DeleteObject",
"s3:*"
]
resources = ["arn:aws:s3:::${var.bucket_name}/*"]
}
statement {
actions = [
"s3:ListBucket"
]
resources = ["arn:aws:s3:::${var.bucket_name}"]
}
statement {
actions = [
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
]
resources = ["arn:aws:s3:::${var.bucket_name}"]
}
}
resource "aws_iam_user" "u" {
name = "cf-user-${var.bucket_name}"
path = "/"
count = var.create_user_with_policy == true ? 1 : 0
}
resource "aws_iam_access_key" "k" {
user = aws_iam_user.u.name
pgp_key = var.pgp_key
count = var.create_user_with_policy == true ? 1 : 0
}
resource "aws_iam_user_policy" "up" {
name = "cf-policy-${var.bucket_name}"
user = aws_iam_user.u.name
policy = var.iam_policy == "" ? format("%s", data.aws_iam_policy_document.iam_policy.json) : var.iam_policy
count = var.create_user_with_policy == true ? 1 : 0
}
# --- S3 bucket ---
data "aws_iam_policy_document" "s3_policy_cf_bucket" {
statement {
actions = ["s3:GetObject"]
resources = ["arn:aws:s3:::${var.bucket_name}/*"]
principals {
type = "AWS"
identifiers = ["${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}"]
}
}
statement {
actions = ["s3:ListBucket"]
resources = ["arn:aws:s3:::${var.bucket_name}"]
principals {
type = "AWS"
identifiers = ["${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}"]
}
}
}
resource "aws_s3_bucket" "bucket" {
bucket = var.bucket_name
acl = "private"
policy = data.aws_iam_policy_document.s3_policy_cf_bucket.json
tags = merge("${var.tags}", map("Name", format("%s-bucket", var.name)))
}
# --- CloudFront ---
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
comment = "access-identity-${var.name}"
}
resource "aws_s3_bucket" "log_bucket" {
bucket = var.log_bucket
#policy = "${data.aws_iam_policy_document.s3_policy_cf_logs.json}"
force_destroy = true
tags = merge(var.tags, map("Name", format("%s", var.log_bucket)))
}
resource "aws_cloudfront_distribution" "cf" {
enabled = "true"
is_ipv6_enabled = var.ipv6_enabled
comment = var.comment
default_root_object = "index.html"
price_class = var.price_class
logging_config = {
include_cookies = "${var.log_include_cookies}"
bucket = "${var.log_bucket}.s3.amazonaws.com"
prefix = "${var.log_prefix}"
}
aliases = ["${var.domains}"]
origin {
domain_name = aws_s3_bucket.bucket.bucket_domain_name
origin_id = var.bucket_name
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path
}
}
viewer_certificate {
acm_certificate_arn = var.certificate_arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1"
}
default_cache_behavior {
allowed_methods = var.allowed_methods
cached_methods = var.cached_methods
target_origin_id = var.bucket_name
compress = var.compress
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = var.viewer_protocol_policy
min_ttl = var.min_ttl
default_ttl = var.default_ttl
max_ttl = var.max_ttl
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
tags = merge("${var.tags}", map("Name", format("%s", var.name)))
depends_on = ["aws_s3_bucket.log_bucket"]
}

View File

@@ -0,0 +1,70 @@
#The identifier for the distribution. For example: EDFDVBD632BHDS5.
output "cf_id" {
value = "${aws_cloudfront_distribution.cf.id}"
}
#The ARN (Amazon Resource Name) for the distribution. For example: arn:aws:cloudfront::123456789012:distribution/EDFDVBD632BHDS5, where 123456789012 is your AWS account ID.
output "cf_arn" {
value = "${aws_cloudfront_distribution.cf.arn}"
}
#The current status of the distribution. Deployed if the distribution's information is fully propagated throughout the Amazon CloudFront system.
output "cf_status" {
value = "${aws_cloudfront_distribution.cf.status}"
}
#active_trusted_signers - The key pair IDs that CloudFront is aware of for each trusted signer, if the distribution is set up to serve private content with signed URLs.
output "cf_active_trusted_signers" {
value = "${aws_cloudfront_distribution.cf.active_trusted_signers}"
}
#The domain name corresponding to the distribution. For example: d604721fxaaqy9.cloudfront.net.
output "cf_domain_name" {
value = "${aws_cloudfront_distribution.cf.domain_name}"
}
#The current version of the distribution's information. For example: E2QWRUHAPOMQZL.
output "cf_etag" {
value = "${aws_cloudfront_distribution.cf.etag}"
}
#The CloudFront Route 53 zone ID that can be used to route an Alias Resource Record Set to. This attribute is simply an alias for the zone ID Z2FDTNDATAQYW2.
output "cf_hosted_zone_id" {
value = "${aws_cloudfront_distribution.cf.hosted_zone_id}"
}
#S3 bucket id
output "s3_bucket_id" {
value = "${aws_s3_bucket.bucket.id}"
}
#S3 bucket arn
output "s3_bucket_arn" {
value = "${aws_s3_bucket.bucket.arn}"
}
#The access key ID.
output "iam_access_key_id" {
value = "${aws_iam_access_key.k.id}"
}
#The IAM user associated with this access key.
output "iam_access_user" {
value = "${aws_iam_access_key.k.user}"
}
#The fingerprint of the PGP key used to encrypt the secret
output "iam_access_key_fingerprint" {
value = "${aws_iam_access_key.k.key_fingerprint}"
}
# The secret access key. Note that this will be written to the state file. Please supply a pgp_key instead, which will prevent the secret from being stored in plain text
output "iam_access_secret" {
value = "${aws_iam_access_key.k.secret}"
}
# The encrypted secret, base64 encoded. ~> NOTE: The encrypted secret may be decrypted using the command line, for example: terraform output secret | base64 --decode | keybase pgp decrypt.
output "iam_access_encrypted_secret" {
value = "${aws_iam_access_key.k.encrypted_secret}"
}

View File

@@ -0,0 +1,81 @@
variable "name" {}
variable "certificate_arn" {
description = "Existing certificate arn."
}
variable "domains" {
type = "list"
default = []
}
variable "bucket_name" {
default = "tf-cf-bucket"
}
variable "compress" {
default = "false"
}
variable "ipv6_enabled" {
default = "true"
}
variable "comment" {
default = "Managed by Terraform"
}
variable "log_include_cookies" {
default = "false"
}
variable "log_bucket" { }
variable "log_prefix" {
default = "cf_logs"
}
variable "price_class" {
default = "PriceClass_100"
}
variable "viewer_protocol_policy" {
#default = "allow-all"
default = "redirect-to-https"
}
variable "allowed_methods" {
type = "list"
default = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
}
variable "cached_methods" {
type = "list"
default = ["GET", "HEAD"]
}
variable "min_ttl" {
default = "0"
}
variable "max_ttl" {
default = "31536000"
}
variable "default_ttl" {
default = "60"
}
variable "tags" {
default = {}
}
variable "create_user_with_policy" {
default = "false"
}
variable "iam_policy" {
type = "string"
default = ""
}
variable "pgp_key" {
default = ""
}

View File

@@ -0,0 +1,65 @@
resource "aws_customer_gateway" "default" {
count = "${var.customer_gateway_id == "" ? 1 : 0}"
bgp_asn = "${var.bgp_asn}"
ip_address = "${var.ip_address}"
type = "ipsec.1"
tags {
Name = "${var.name}"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpn_connection" "default" {
vpn_gateway_id = "${var.vpn_gateway_id}"
customer_gateway_id = "${coalesce(var.customer_gateway_id, aws_customer_gateway.default.id)}"
type = "ipsec.1"
static_routes_only = "${var.static_routes_only}"
tags {
Name = "${var.name}"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpn_connection_route" "default" {
count = "${length(var.destination_cidr_blocks)}"
destination_cidr_block = "${element(var.destination_cidr_blocks, count.index)}"
vpn_connection_id = "${aws_vpn_connection.default.id}"
lifecycle {
create_before_destroy = true
}
}
#if route_source=static and there are more route_tables than cidr_blocks(or an equal amount)
# then add static routes in every specified route table for each destination_cidr_block=>VPN Gateway
resource "aws_route" "more_tables" {
count = "${(var.add_static_routes_to_tables == "true" ? 1 : 0) * (var.route_table_count >= length(var.destination_cidr_blocks) ? 1 : 0 ) * var.route_table_count * length(var.destination_cidr_blocks)}"
route_table_id = "${element(var.route_table_ids, count.index % floor(max(length(var.route_table_ids),length(var.destination_cidr_blocks))))}"
destination_cidr_block = "${element(var.destination_cidr_blocks, floor(count.index / max(length(var.route_table_ids),length(var.destination_cidr_blocks))))}"
gateway_id = "${var.vpn_gateway_id}"
lifecycle {
create_before_destroy = true
}
}
#if route_source=static and there are more cidr_blocks than route_tables
# then add static routes in every specified route table for each destination_cidr_block=>VPN Gateway
resource "aws_route" "more_cidrs" {
count = "${(var.add_static_routes_to_tables == "true" ? 1 : 0) * (length(var.destination_cidr_blocks) > var.route_table_count ? 1 : 0 ) * var.route_table_count * length(var.destination_cidr_blocks)}"
route_table_id = "${element(var.route_table_ids, floor(count.index / max(length(var.route_table_ids),length(var.destination_cidr_blocks))))}"
destination_cidr_block = "${element(var.destination_cidr_blocks, count.index % floor(max(length(var.route_table_ids),length(var.destination_cidr_blocks))))}"
gateway_id = "${var.vpn_gateway_id}"
lifecycle {
create_before_destroy = true
}
}

View File

@@ -0,0 +1,11 @@
output "cgw_id" {
value = "${aws_customer_gateway.default.id}"
}
output "cgw_ip_address" {
value = "${aws_customer_gateway.default.ip_address}"
}
output "cgw_bgp_asn" {
value = "${aws_customer_gateway.default.bgp_asn}"
}

View File

@@ -0,0 +1,45 @@
variable "name" {
description = "Decriptive name used to label tagged resources."
}
variable "vpn_gateway_id" {
description = "Specify which VPN Gateway the Customer Gateway will be associated with."
}
variable "customer_gateway_id" {
description = "Specify which Customer Gateway to use. If specified the variables ip_address and bgp_asn will not be used"
default = ""
}
variable "ip_address" {
description = "IP address of the Customer Gateway external interface."
default = ""
}
variable "bgp_asn" {
description = "BGP ASN of the Customer Gateway. By convention, use 65000 if you are not running BGP."
default = 65000
}
variable "destination_cidr_blocks" {
type = "list"
description = "List of comman separated CIDR blocks which should be routed to the Customer Gateway(s)."
}
variable "route_table_ids" {
type = "list"
description = "List of command separated Route Table IDs where routes to destination_cidr_blocks will be created."
}
variable "route_table_count" {
description = "Number of elements in the route_table_ids list. Here because Terraform cannot calculate count from dynamic values. This should be removed when Terraform 0.9 is released."
}
variable "static_routes_only" {
description = "Whether the VPN connection uses static routes exclusively. Static routes must be used for devices that don't support BGP. Accepts either true or false."
}
variable "add_static_routes_to_tables" {
description = "Determines whether static routes will be added to all route tables in route_table_ids list or if vgw route propagation will be used instead. If set to true, then route_table_ids, route_table_count, and destination_cidr_blocks must also be provided."
}

View File

@@ -0,0 +1,22 @@
// Provider specific configs
provider "aws" {
access_key = "${var.aws_access_key}"
secret_key = "${var.aws_secret_key}"
region = "${var.aws_region}"
}
// EC2 Instance Resource for Module
resource "aws_instance" "ec2_instance" {
ami = "${var.ami_id}"
count = "${var.number_of_instances}"
subnet_id = "${var.subnet_id}"
instance_type = "${var.instance_type}"
user_data = "${file(var.user_data)}"
tags {
created_by = "${lookup(var.tags,"created_by")}"
// Takes the instance_name input variable and adds
// the count.index to the name., e.g.
// "example-host-web-1"
Name = "${var.instance_name}-${count.index}"
}
}

View File

@@ -0,0 +1,4 @@
// Output the ID of the EC2 instance created
output "ec2_instance_id" {
value = "${aws_instance.ec2_instance.id}"
}

View File

@@ -0,0 +1,35 @@
// Module specific variables
variable "instance_name" {
description = "Used to populate the Name tag. This is done in main.tf"
}
variable "instance_type" {}
variable "subnet_id" {
description = "The VPC subnet the instance(s) will go in"
}
variable "ami_id" {
description = "The AMI to use"
}
variable "number_of_instances" {
description = "number of instances to make"
default = 1
}
variable "user_data" {
description = "The path to a file with user_data for the instances"
}
variable "tags" {
default = {
created_by = "terraform"
}
}
// Variables for providers used in this module
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "aws_region" {}

View File

@@ -0,0 +1,57 @@
data "template_file" "consul" {
template = "${file("${path.module}/templates/consul.json")}"
vars {
env = "${aws_ecs_cluster.cluster.name}"
image = "${var.consul_image}"
registrator_image = "${var.registrator_image}"
consul_memory_reservation = "${var.consul_memory_reservation}"
registrator_memory_reservation = "${var.registrator_memory_reservation}"
awslogs_group = "consul-agent-${aws_ecs_cluster.cluster.name}"
awslogs_stream_prefix = "consul-agent-${aws_ecs_cluster.cluster.name}"
awslogs_region = "${var.region}"
}
}
# End Data block
resource "aws_ecs_task_definition" "consul" {
count = "${var.enable_agents ? 1 : 0}"
family = "consul-agent-${aws_ecs_cluster.cluster.name}"
container_definitions = "${data.template_file.consul.rendered}"
network_mode = "host"
task_role_arn = "${aws_iam_role.consul_task.arn}"
volume {
name = "consul-config-dir"
host_path = "/etc/consul"
}
volume {
name = "docker-sock"
host_path = "/var/run/docker.sock"
}
}
resource "aws_cloudwatch_log_group" "consul" {
count = "${var.enable_agents ? 1 : 0}"
name = "${aws_ecs_task_definition.consul.family}"
tags {
VPC = "${data.aws_vpc.vpc.tags["Name"]}"
Application = "${aws_ecs_task_definition.consul.family}"
}
}
resource "aws_ecs_service" "consul" {
count = "${var.enable_agents ? 1 : 0}"
name = "consul-agent-${aws_ecs_cluster.cluster.name}"
cluster = "${aws_ecs_cluster.cluster.id}"
task_definition = "${aws_ecs_task_definition.consul.arn}"
desired_count = "${var.servers}"
deployment_minimum_healthy_percent = "60"
placement_constraints {
type = "distinctInstance"
}
}

View File

@@ -0,0 +1,10 @@
# Thank God for Circle CI for this post
# https://circleci.com/blog/graceful-shutdown-using-aws/
resource "aws_autoscaling_lifecycle_hook" "graceful_shutdown_asg_hook" {
name = "graceful_shutdown_asg"
autoscaling_group_name = "${aws_autoscaling_group.ecs.name}"
default_result = "CONTINUE"
heartbeat_timeout = "${var.heartbeat_timeout}"
lifecycle_transition = "autoscaling:EC2_INSTANCE_TERMINATING"
}

View File

@@ -0,0 +1,119 @@
resource "aws_iam_instance_profile" "ecs_profile" {
name_prefix = "${replace(format("%.102s", replace("tf-ECSProfile-${var.name}-", "_", "-")), "/\\s/", "-")}"
role = "${aws_iam_role.ecs_role.name}"
path = "${var.iam_path}"
}
resource "aws_iam_role" "ecs_role" {
name_prefix = "${replace(format("%.32s", replace("tf-ECSInRole-${var.name}-", "_", "-")), "/\\s/", "-")}"
path = "${var.iam_path}"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": ["ecs.amazonaws.com", "ec2.amazonaws.com"]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
# It may be useful to add the following for troubleshooting the InstanceStatus
# Health check if using the fitnesskeeper/consul docker image
# "ec2:Describe*",
# "autoscaling:Describe*",
resource "aws_iam_policy" "ecs_policy" {
name_prefix = "${replace(format("%.102s", replace("tf-ECSInPol-${var.name}-", "_", "-")), "/\\s/", "-")}"
description = "A terraform created policy for ECS"
path = "${var.iam_path}"
count = "${length(var.custom_iam_policy) > 0 ? 0 : 1}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:CreateCluster",
"ecs:DeregisterContainerInstance",
"ecs:DiscoverPollEndpoint",
"ecs:Poll",
"ecs:RegisterContainerInstance",
"ecs:StartTelemetrySession",
"ecs:Submit*",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_policy" "custom_ecs_policy" {
name_prefix = "${replace(format("%.102s", replace("tf-ECSInPol-${var.name}-", "_", "-")), "/\\s/", "-")}"
description = "A terraform created policy for ECS"
path = "${var.iam_path}"
count = "${length(var.custom_iam_policy) > 0 ? 1 : 0}"
policy = "${var.custom_iam_policy}"
}
resource "aws_iam_policy_attachment" "attach_ecs" {
name = "ecs-attachment"
roles = ["${aws_iam_role.ecs_role.name}"]
policy_arn = "${element(concat(aws_iam_policy.ecs_policy.*.arn, aws_iam_policy.custom_ecs_policy.*.arn), 0)}"
}
# IAM Resources for Consul and Registrator Agents
data "aws_iam_policy_document" "consul_task_policy" {
statement {
actions = [
"ec2:Describe*",
"autoscaling:Describe*",
]
resources = ["*"]
}
}
data "aws_iam_policy_document" "assume_role_consul_task" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "consul_task" {
count = "${var.enable_agents ? 1 : 0}"
name_prefix = "${replace(format("%.32s", replace("tf-agentTaskRole-${var.name}-", "_", "-")), "/\\s/", "-")}"
path = "${var.iam_path}"
assume_role_policy = "${data.aws_iam_policy_document.assume_role_consul_task.json}"
}
resource "aws_iam_role_policy" "consul_ecs_task" {
count = "${var.enable_agents ? 1 : 0}"
name_prefix = "${replace(format("%.102s", replace("tf-agentTaskPol-${var.name}-", "_", "-")), "/\\s/", "-")}"
role = "${aws_iam_role.consul_task.id}"
policy = "${data.aws_iam_policy_document.consul_task_policy.json}"
}

View File

@@ -0,0 +1,113 @@
data "aws_ami" "ecs_ami" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn-ami-${var.ami_version}-amazon-ecs-optimized"]
}
}
data "template_file" "user_data" {
template = "${file("${path.module}/templates/user_data.tpl")}"
vars {
additional_user_data_script = "${var.additional_user_data_script}"
cluster_name = "${aws_ecs_cluster.cluster.name}"
docker_storage_size = "${var.docker_storage_size}"
dockerhub_token = "${var.dockerhub_token}"
dockerhub_email = "${var.dockerhub_email}"
}
}
data "aws_vpc" "vpc" {
id = "${var.vpc_id}"
}
resource "aws_launch_configuration" "ecs" {
name_prefix = "${coalesce(var.name_prefix, "ecs-${var.name}-")}"
image_id = "${var.ami == "" ? format("%s", data.aws_ami.ecs_ami.id) : var.ami}" # Workaround until 0.9.6
instance_type = "${var.instance_type}"
key_name = "${var.key_name}"
iam_instance_profile = "${aws_iam_instance_profile.ecs_profile.name}"
security_groups = ["${concat(list(aws_security_group.ecs.id), var.security_group_ids)}"]
associate_public_ip_address = "${var.associate_public_ip_address}"
spot_price = "${var.spot_bid_price}"
ebs_block_device {
device_name = "${var.ebs_block_device}"
volume_size = "${var.docker_storage_size}"
volume_type = "gp2"
delete_on_termination = true
}
user_data = "${coalesce(var.user_data, data.template_file.user_data.rendered)}"
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "ecs" {
name_prefix = "asg-${aws_launch_configuration.ecs.name}-"
vpc_zone_identifier = ["${var.subnet_id}"]
launch_configuration = "${aws_launch_configuration.ecs.name}"
min_size = "${var.min_servers}"
max_size = "${var.max_servers}"
desired_capacity = "${var.servers}"
termination_policies = ["OldestLaunchConfiguration", "ClosestToNextInstanceHour", "Default"]
load_balancers = ["${var.load_balancers}"]
enabled_metrics = ["${var.enabled_metrics}"]
tags = [{
key = "Name"
value = "${var.name} ${var.tagName}"
propagate_at_launch = true
}]
tags = ["${var.extra_tags}"]
lifecycle {
create_before_destroy = true
}
timeouts {
delete = "${var.heartbeat_timeout + var.asg_delete_extra_timeout}s"
}
}
resource "aws_security_group" "ecs" {
name = "ecs-sg-${var.name}"
description = "Container Instance Allowed Ports"
vpc_id = "${data.aws_vpc.vpc.id}"
ingress {
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = "${var.allowed_cidr_blocks}"
}
ingress {
from_port = 0
to_port = 65535
protocol = "udp"
cidr_blocks = "${var.allowed_cidr_blocks}"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags {
Name = "ecs-sg-${var.name}"
}
}
# Make this a var that an get passed in?
resource "aws_ecs_cluster" "cluster" {
name = "${var.name}"
}

View File

@@ -0,0 +1,38 @@
output "cluster_id" {
value = "${aws_ecs_cluster.cluster.id}"
}
output "cluster_name" {
value = "${aws_ecs_cluster.cluster.name}"
}
output "instance_role_arn" {
value = "${aws_iam_role.ecs_role.arn}"
}
output "instance_role_id" {
value = "${aws_iam_role.ecs_role.id}"
}
output "autoscaling_group" {
value = {
id = "${aws_autoscaling_group.ecs.id}"
name = "${aws_autoscaling_group.ecs.name}"
arn = "${aws_autoscaling_group.ecs.arn}"
}
}
output "iam_role" {
value = {
name = "${aws_iam_role.ecs_role.name}"
arn = "${aws_iam_role.ecs_role.arn}"
}
}
output "security_group" {
value = {
id = "${aws_security_group.ecs.id}"
name = "${aws_security_group.ecs.name}"
arn = "${aws_security_group.ecs.arn}"
}
}

View File

@@ -0,0 +1,169 @@
variable "additional_user_data_script" {
default = ""
}
variable "allowed_cidr_blocks" {
default = ["0.0.0.0/0"]
type = "list"
description = "List of subnets to allow into the ECS Security Group. Defaults to ['0.0.0.0/0']"
}
variable "ami" {
default = ""
}
variable "ami_version" {
default = "*"
}
variable "associate_public_ip_address" {
default = false
}
variable "consul_image" {
description = "Image to use when deploying consul, defaults to the hashicorp consul image"
default = "consul:latest"
}
variable "consul_memory_reservation" {
description = "The soft limit (in MiB) of memory to reserve for the container, defaults 20"
default = "32"
}
variable "docker_storage_size" {
default = "22"
description = "EBS Volume size in Gib that the ECS Instance uses for Docker images and metadata "
}
variable "dockerhub_email" {
default = ""
description = "Email Address used to authenticate to dockerhub. http://docs.aws.amazon.com/AmazonECS/latest/developerguide/private-auth.html"
}
variable "dockerhub_token" {
default = ""
description = "Auth Token used for dockerhub. http://docs.aws.amazon.com/AmazonECS/latest/developerguide/private-auth.html"
}
variable "enable_agents" {
default = false
description = "Enable Consul Agent and Registrator tasks on each ECS Instance"
}
variable "ebs_block_device" {
default = "/dev/xvdcz"
description = "EBS block devices to attach to the instance. (default: /dev/xvdcz)"
}
variable "extra_tags" {
type = "list"
default = []
}
variable "heartbeat_timeout" {
description = "Heartbeat Timeout setting for how long it takes for the graceful shutodwn hook takes to timeout. This is useful when deploying clustered applications like consul that benifit from having a deploy between autoscaling create/destroy actions. Defaults to 180"
default = "180"
}
variable "asg_delete_extra_timeout" {
description = "Extra time that `terraform apply` will wait for ASG deletion (default 600). This is added on top of `heartbeat_timeout`. This variable is customizable for when the instances take longer than 600sec to shut down once shutdown is initiated."
default = "600"
}
variable "iam_path" {
default = "/"
description = "IAM path, this is useful when creating resources with the same name across multiple regions. Defaults to /"
}
variable "custom_iam_policy" {
default = ""
description = "Custom IAM policy (JSON). If set will overwrite the default one"
}
variable "instance_type" {
default = "t2.micro"
description = "AWS Instance type, if you change, make sure it is compatible with AMI, not all AMIs allow all instance types "
}
variable "key_name" {
description = "SSH key name in your AWS account for AWS instances."
}
variable "load_balancers" {
type = "list"
default = []
description = "A list of elastic load balancer names to add to the autoscaling group names. Only valid for classic load balancers."
}
variable "min_servers" {
description = "Minimum number of ECS servers to run."
default = 1
}
variable "max_servers" {
description = "Maximum number of ECS servers to run."
default = 10
}
variable "name" {
description = "AWS ECS Cluster Name"
}
variable "name_prefix" {
default = ""
}
variable "region" {
default = "us-east-1"
description = "The region of AWS, for AMI lookups."
}
variable "registrator_image" {
default = "gliderlabs/registrator:latest"
description = "Image to use when deploying registrator agent, defaults to the gliderlabs registrator:latest image"
}
variable "registrator_memory_reservation" {
description = "The soft limit (in MiB) of memory to reserve for the container, defaults 20"
default = "32"
}
variable "security_group_ids" {
type = "list"
description = "A list of Security group IDs to apply to the launch configuration"
default = []
}
variable "servers" {
default = "1"
description = "The number of servers to launch."
}
variable "spot_bid_price" {
default = ""
description = "If specified, spot instances will be requested at this bid price. If not specified, on-demand instances will be used."
}
variable "subnet_id" {
type = "list"
description = "The AWS Subnet ID in which you want to delpoy your instances"
}
variable "tagName" {
default = "ECS Node"
description = "Name tag for the servers"
}
variable "user_data" {
default = ""
}
variable "vpc_id" {
description = "The AWS VPC ID which you want to deploy your instances"
}
variable "enabled_metrics" {
description = "A list of metrics to collect"
type = "list"
default = []
}

View File

@@ -0,0 +1,164 @@
resource "aws_iam_role" "sns" {
name = "${var.autoscaling_group_name}-notifies-sns"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "autoscaling.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
# creating policy document and attaching as inline policies instead of using the AutoScalingNotificationAccessRole
# managed policy due to Terraform issue https://github.com/hashicorp/terraform/issues/5979.
data "aws_iam_policy_document" "auto_scaling_notification_access" {
statement {
sid = "1"
actions = [
"sqs:SendMessage",
"sqs:GetQueueUrl",
"sns:Publish",
]
resources = [
"*",
]
}
}
resource "aws_iam_role_policy" "asg_notification_sns" {
name = "${aws_iam_role.sns.name}-asg-notification-policy"
role = "${aws_iam_role.sns.id}"
policy = "${data.aws_iam_policy_document.auto_scaling_notification_access.json}"
}
resource "aws_iam_role" "lambda" {
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
data "aws_iam_policy_document" "lambda" {
statement {
sid = "1"
actions = [
"autoscaling:CompleteLifecycleAction",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ec2:DescribeInstances",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstanceStatus",
"ec2:DescribeHosts",
"ecs:ListContainerInstances",
"ecs:SubmitContainerStateChange",
"ecs:SubmitTaskStateChange",
"ecs:DescribeContainerInstances",
"ecs:UpdateContainerInstancesState",
"ecs:ListTasks",
"ecs:DescribeTasks",
"sns:Publish",
"sns:ListSubscriptions",
]
resources = [
"*",
]
}
}
resource "aws_iam_role_policy" "lambda" {
name = "${aws_iam_role.lambda.name}-policy"
role = "${aws_iam_role.lambda.id}"
policy = "${data.aws_iam_policy_document.lambda.json}"
}
resource "aws_iam_role_policy" "asg_notification_lambda" {
name = "${aws_iam_role.lambda.name}-asg-notification-policy"
role = "${aws_iam_role.lambda.id}"
policy = "${data.aws_iam_policy_document.auto_scaling_notification_access.json}"
}
data "archive_file" "index" {
type = "zip"
source_dir = "${path.module}/index"
output_path = "${path.module}/files/index.zip"
}
resource "aws_lambda_function" "lambda" {
runtime = "python3.6"
filename = "${path.module}/files/index.zip"
function_name = "${substr(var.autoscaling_group_name,0,min(64, length(var.autoscaling_group_name)))}"
role = "${aws_iam_role.lambda.arn}"
handler = "index.lambda_handler"
timeout = "${var.function_sleep_time * 2}"
source_code_hash = "${data.archive_file.index.output_base64sha256}"
environment {
variables = {
REGION = "${var.region}"
CLUSTER_NAME = "${var.cluster_name}"
SLEEP_TIME = "${var.function_sleep_time}"
}
}
lifecycle {
# A workaround when running this code on different machines is to ignore changes, as described here:
# https://github.com/hashicorp/terraform/issues/7613#issuecomment-241603087
ignore_changes = ["filename"]
}
}
resource "aws_lambda_permission" "sns" {
statement_id = "AllowExecutionFromSNS"
function_name = "${aws_lambda_function.lambda.arn}"
action = "lambda:InvokeFunction"
principal = "sns.amazonaws.com"
source_arn = "${aws_sns_topic.asg_sns.arn}"
}
resource "aws_sns_topic" "asg_sns" {
name = "${var.autoscaling_group_name}-sns-topic"
}
resource "aws_sns_topic_subscription" "asg_sns" {
topic_arn = "${aws_sns_topic.asg_sns.arn}"
protocol = "lambda"
endpoint = "${aws_lambda_function.lambda.arn}"
}
resource "aws_autoscaling_lifecycle_hook" "terminate" {
count = "${var.lambda_enabled}"
name = "${var.autoscaling_group_name}-terminate-hook"
autoscaling_group_name = "${var.autoscaling_group_name}"
default_result = "${var.hook_default_result}"
heartbeat_timeout = "${var.hook_heartbeat_timeout}"
lifecycle_transition = "autoscaling:EC2_INSTANCE_TERMINATING"
notification_target_arn = "${aws_sns_topic.asg_sns.arn}"
role_arn = "${aws_iam_role.sns.arn}"
}

View File

@@ -0,0 +1,22 @@
variable "region" {}
variable "cluster_name" {}
variable "autoscaling_group_name" {}
variable "function_sleep_time" {
description = "Number of seconds the function should sleep before checking ECS Instance Task Count again"
default = 15
}
variable "lambda_enabled" {
default = true
}
variable "hook_heartbeat_timeout" {
default = 900
}
variable "hook_default_result" {
default = "ABANDON"
}

View File

@@ -0,0 +1,91 @@
data "aws_iam_policy_document" "pganalyze_task_policy" {
statement {
actions = [
"ec2:Describe*",
"autoscaling:Describe*",
"ec2:DescribeAddresses",
"ec2:DescribeInstances",
"ec2:DescribeTags",
]
resources = ["*"]
}
statement {
actions = [
"cloudwatch:GetMetricStatistics",
"logs:DescribeLogStreams",
"logs:GetLogEvents",
"logs:PutLogEvents",
]
resources = [
"*",
]
}
statement {
actions = [
"rds:DownloadDBLogFilePortion",
]
resources = [
"*",
]
}
}
data "aws_iam_policy_document" "assume_role_pganalyze_task" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "pganalyze_task" {
count = "${local.service_count}"
name = "tf-pganalyze-${var.task_identifier}-ecsTaskRole"
path = "/"
assume_role_policy = "${data.aws_iam_policy_document.assume_role_pganalyze_task.json}"
}
resource "aws_iam_role_policy" "pganalyze_ecs_task" {
count = "${local.service_count}"
name = "tf-pganalyze-${var.task_identifier}-ecsTaskPolicy"
role = "${aws_iam_role.pganalyze_task.id}"
policy = "${data.aws_iam_policy_document.pganalyze_task_policy.json}"
}
# ecsServiceRole for pganalyze
resource "aws_iam_role" "ecsServiceRole" {
count = "${local.service_count}"
name = "tf-pganalyze-${var.task_identifier}-ecsSvcRole"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": ["ecs.amazonaws.com"]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "attach-ecsServiceRole" {
count = "${local.service_count}"
role = "${aws_iam_role.ecsServiceRole.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"
}

View File

@@ -0,0 +1,48 @@
locals {
service_count = "${length(var.pga_api_key) > 0 ? 1 : 0}"
}
data "aws_ecs_cluster" "ecs" {
cluster_name = "${var.ecs_cluster}"
}
data "aws_region" "current" {
current = true
}
data "template_file" "pganalyze" {
template = "${file("${path.module}/files/pganalyze.json")}"
vars {
task_identifier = "${var.task_identifier}"
db_url = "postgres://${var.db_username}:${var.db_password}@${var.rds_endpoint}/${var.db_name}"
image = "${var.docker_image}"
pga_api_key = "${var.pga_api_key}"
aws_instance_id = "${var.aws_instance_id}" # we can almost certainly derive this
aws_region = "${data.aws_region.current.name}"
awslogs_group = "${var.log_group}"
awslogs_region = "${data.aws_region.current.name}"
awslogs_stream_prefix = "tf"
}
}
resource "aws_ecs_task_definition" "pganalyze" {
count = "${local.service_count}"
family = "pganalyze-${var.env}-${var.task_identifier}"
container_definitions = "${data.template_file.pganalyze.rendered}"
network_mode = "bridge"
task_role_arn = "${aws_iam_role.pganalyze_task.arn}"
}
resource "aws_ecs_service" "pganalyze" {
count = "${local.service_count}"
name = "pganalyze-${var.env}-${var.task_identifier}"
cluster = "${data.aws_ecs_cluster.ecs.id}"
task_definition = "${aws_ecs_task_definition.pganalyze.arn}"
desired_count = 1
placement_strategy {
type = "binpack"
field = "memory"
}
}

View File

@@ -0,0 +1,45 @@
variable "env" {
description = "Environment tag (default 'dev')"
default = "dev"
}
variable "ecs_cluster" {
description = "Name of ECS cluster in which the service will be deployed"
}
variable "docker_image" {
description = "Docker image containing pganalyze collector (default is upstream stable)"
default = "quay.io/pganalyze/collector:stable"
}
variable "pga_api_key" {
description = "pganalyze API key associated with database (register the database in the pganalyze console to obtain this)"
}
variable "aws_instance_id" {
description = "some AWS instance id?"
}
variable "task_identifier" {
description = "Unique identifier for this pganalyze task (used in log prefix, service name etc.)"
}
variable "db_username" {
description = "Username of pganalyze monitoring role"
}
variable "db_password" {
description = "Password of pganalyze monitoring role"
}
variable "db_name" {
description = "Name of database to be monitored"
}
variable "rds_endpoint" {
description = "Endpoint of RDS instance to be monitored"
}
variable "log_group" {
description = "CloudWatch Log Group that will receive collector logs (must exist already)"
}

View File

@@ -0,0 +1,48 @@
# Cloudwatch resources inspired by https://github.com/azavea/terraform-aws-redis-elasticache
# Stubbing this out, we should have conversation about monitoring before we make this the default behavior
# For this module
/*
resource "aws_cloudwatch_metric_alarm" "cache_cpu" {
count = "${var.redis_clusters}"
alarm_name = "alarm-${var.name}-${local.vpc_name}-CacheCluster00${count.index + 1}CPUUtilization"
alarm_description = "Redis cluster CPU utilization"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "CPUUtilization"
namespace = "AWS/ElastiCache"
period = "300"
statistic = "Average"
threshold = "${var.alarm_cpu_threshold}"
dimensions {
CacheClusterId = "${aws_elasticache_replication_group.redis.id}-00${count.index + 1}"
}
alarm_actions = ["${var.alarm_actions}"]
}
resource "aws_cloudwatch_metric_alarm" "cache_memory" {
count = "${var.redis_clusters}"
alarm_name = "alarm-${var.name}-${local.vpc_name}-CacheCluster00${count.index + 1}FreeableMemory"
alarm_description = "Redis cluster freeable memory"
comparison_operator = "LessThanThreshold"
evaluation_periods = "1"
metric_name = "FreeableMemory"
namespace = "AWS/ElastiCache"
period = "60"
statistic = "Average"
threshold = "${var.alarm_memory_threshold}"
dimensions {
CacheClusterId = "${aws_elasticache_replication_group.redis.id}-00${count.index + 1}"
}
alarm_actions = ["${var.alarm_actions}"]
}
*/

View File

@@ -0,0 +1,69 @@
data "aws_vpc" "vpc" {
id = var.vpc_id
}
locals {
vpc_name = lookup(data.aws_vpc.vpc.tags, "Name", var.vpc_id)
}
resource "random_id" "salt" {
byte_length = 8
keepers = {
redis_version = var.redis_version
}
}
resource "aws_elasticache_replication_group" "redis" {
replication_group_id = format("%.20s", "${var.name}-${var.env}")
replication_group_description = "Terraform-managed ElastiCache replication group for ${var.name}-${var.env}-${local.vpc_name}"
number_cache_clusters = var.redis_clusters
node_type = var.redis_node_type
automatic_failover_enabled = var.redis_failover
auto_minor_version_upgrade = var.auto_minor_version_upgrade
availability_zones = var.availability_zones
multi_az_enabled = var.multi_az_enabled
engine = "redis"
at_rest_encryption_enabled = var.at_rest_encryption_enabled
kms_key_id = var.kms_key_id
transit_encryption_enabled = var.transit_encryption_enabled
auth_token = var.transit_encryption_enabled ? var.auth_token : null
engine_version = var.redis_version
port = var.redis_port
parameter_group_name = aws_elasticache_parameter_group.redis_parameter_group.id
subnet_group_name = aws_elasticache_subnet_group.redis_subnet_group.id
security_group_names = var.security_group_names
security_group_ids = [aws_security_group.redis_security_group.id]
snapshot_arns = var.snapshot_arns
snapshot_name = var.snapshot_name
apply_immediately = var.apply_immediately
maintenance_window = var.redis_maintenance_window
notification_topic_arn = var.notification_topic_arn
snapshot_window = var.redis_snapshot_window
snapshot_retention_limit = var.redis_snapshot_retention_limit
tags = merge(tomap({"Name" = format("tf-elasticache-%s-%s", var.name, local.vpc_name)}), var.tags)
}
resource "aws_elasticache_parameter_group" "redis_parameter_group" {
name = replace(format("%.255s", lower(replace("tf-redis-${var.name}-${var.env}-${local.vpc_name}-${random_id.salt.hex}", "_", "-"))), "/\\s/", "-")
description = "Terraform-managed ElastiCache parameter group for ${var.name}-${var.env}-${local.vpc_name}"
# Strip the patch version from redis_version var
family = "redis${replace(var.redis_version, "/\\.[\\d]+$/", "")}"
dynamic "parameter" {
for_each = var.redis_parameters
content {
name = parameter.value.name
value = parameter.value.value
}
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_elasticache_subnet_group" "redis_subnet_group" {
name = replace(format("%.255s", lower(replace("tf-redis-${var.name}-${var.env}-${local.vpc_name}", "_", "-"))), "/\\s/", "-")
subnet_ids = var.subnets
}

View File

@@ -0,0 +1,23 @@
output "redis_security_group_id" {
value = aws_security_group.redis_security_group.id
}
output "parameter_group" {
value = aws_elasticache_parameter_group.redis_parameter_group.id
}
output "redis_subnet_group_name" {
value = aws_elasticache_subnet_group.redis_subnet_group.name
}
output "id" {
value = aws_elasticache_replication_group.redis.id
}
output "port" {
value = var.redis_port
}
output "endpoint" {
value = aws_elasticache_replication_group.redis.primary_endpoint_address
}

View File

@@ -0,0 +1,28 @@
resource "aws_security_group" "redis_security_group" {
name = format("%.255s", "tf-sg-ec-${var.name}-${var.env}-${local.vpc_name}")
description = "Terraform-managed ElastiCache security group for ${var.name}-${var.env}-${local.vpc_name}"
vpc_id = data.aws_vpc.vpc.id
tags = {
Name = "tf-sg-ec-${var.name}-${var.env}-${local.vpc_name}"
}
}
resource "aws_security_group_rule" "redis_ingress" {
count = length(var.allowed_security_groups)
type = "ingress"
from_port = var.redis_port
to_port = var.redis_port
protocol = "tcp"
source_security_group_id = element(var.allowed_security_groups, count.index)
security_group_id = aws_security_group.redis_security_group.id
}
resource "aws_security_group_rule" "redis_networks_ingress" {
type = "ingress"
from_port = var.redis_port
to_port = var.redis_port
protocol = "tcp"
cidr_blocks = var.allowed_cidr
security_group_id = aws_security_group.redis_security_group.id
}

View File

@@ -0,0 +1,176 @@
/*
# These vars would be used by cloudwatch.tf and should be uncommented if we decide to use them.
variable "alarm_cpu_threshold" {
default = "75"
}
variable "alarm_memory_threshold" {
# 10MB
default = "10000000"
}
variable "alarm_actions" {
type = "list"
}
*/
variable "apply_immediately" {
description = "Specifies whether any modifications are applied immediately, or during the next maintenance window. Default is false."
type = bool
default = false
}
variable "allowed_cidr" {
description = "A list of Security Group ID's to allow access to."
type = list(string)
default = ["127.0.0.1/32"]
}
variable "allowed_security_groups" {
description = "A list of Security Group ID's to allow access to."
type = list(string)
default = []
}
variable "env" {
description = "env to deploy into, should typically dev/staging/prod"
type = string
}
variable "name" {
description = "Name for the Redis replication group i.e. UserObject"
type = string
}
variable "redis_clusters" {
description = "Number of Redis cache clusters (nodes) to create"
type = string
}
variable "redis_failover" {
type = bool
default = false
}
variable "multi_az_enabled" {
type = bool
default = false
}
variable "redis_node_type" {
description = "Instance type to use for creating the Redis cache clusters"
type = string
default = "cache.m3.medium"
}
variable "redis_port" {
type = number
default = 6379
}
variable "subnets" {
type = list(string)
description = "List of VPC Subnet IDs for the cache subnet group"
}
# might want a map
variable "redis_version" {
description = "Redis version to use, defaults to 3.2.10"
type = string
default = "3.2.10"
}
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "redis_parameters" {
description = "additional parameters modifyed in parameter group"
type = list(map(any))
default = []
}
variable "redis_maintenance_window" {
description = "Specifies the weekly time range for when maintenance on the cache cluster is performed. The format is ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). The minimum maintenance window is a 60 minute period"
type = string
default = "fri:08:00-fri:09:00"
}
variable "redis_snapshot_window" {
description = "The daily time range (in UTC) during which ElastiCache will begin taking a daily snapshot of your cache cluster. The minimum snapshot window is a 60 minute period"
type = string
default = "06:30-07:30"
}
variable "redis_snapshot_retention_limit" {
description = "The number of days for which ElastiCache will retain automatic cache cluster snapshots before deleting them. For example, if you set SnapshotRetentionLimit to 5, then a snapshot that was taken today will be retained for 5 days before being deleted. If the value of SnapshotRetentionLimit is set to zero (0), backups are turned off. Please note that setting a snapshot_retention_limit is not supported on cache.t1.micro or cache.t2.* cache nodes"
type = number
default = 0
}
variable "tags" {
description = "Tags for redis nodes"
type = map(string)
default = {}
}
variable "auto_minor_version_upgrade" {
description = "Specifies whether a minor engine upgrades will be applied automatically to the underlying Cache Cluster instances during the maintenance window"
type = bool
default = true
}
variable "availability_zones" {
description = "A list of EC2 availability zones in which the replication group's cache clusters will be created. The order of the availability zones in the list is not important"
type = list(string)
default = []
}
variable "at_rest_encryption_enabled" {
description = "Whether to enable encryption at rest"
type = bool
default = false
}
variable "kms_key_id" {
description = "The ARN of the key that you wish to use if encrypting at rest. If not supplied, uses service managed encryption. Can be specified only if at_rest_encryption_enabled = true"
type = string
default = ""
}
variable "transit_encryption_enabled" {
description = "Whether to enable encryption in transit. Requires 3.2.6 or >=4.0 redis_version"
type = bool
default = false
}
variable "auth_token" {
description = "The password used to access a password protected server. Can be specified only if transit_encryption_enabled = true. If specified must contain from 16 to 128 alphanumeric characters or symbols"
type = string
default = null
}
variable "security_group_names" {
description = "A list of cache security group names to associate with this replication group"
type = list(string)
default = []
}
variable "snapshot_arns" {
description = "A single-element string list containing an Amazon Resource Name (ARN) of a Redis RDB snapshot file stored in Amazon S3. Example: arn:aws:s3:::my_bucket/snapshot1.rdb"
type = list(string)
default = []
}
variable "snapshot_name" {
description = " The name of a snapshot from which to restore data into the new node group. Changing the snapshot_name forces a new resource"
type = string
default = ""
}
variable "notification_topic_arn" {
description = "An Amazon Resource Name (ARN) of an SNS topic to send ElastiCache notifications to. Example: arn:aws:sns:us-east-1:012345678999:my_sns_topic"
type = string
default = ""
}

View File

@@ -0,0 +1,4 @@
locals {
domain_name = var.use_prefix ? join("", [var.domain_prefix, var.domain_name]) : var.domain_name
inside_vpc = length(var.vpc_options["subnet_ids"]) > 0 ? true : false
}

View File

@@ -0,0 +1,106 @@
data "aws_iam_policy_document" "es_management_access" {
count = false == local.inside_vpc ? 1 : 0
statement {
actions = [
"es:*",
]
resources = [
aws_elasticsearch_domain.es[0].arn,
"${aws_elasticsearch_domain.es[0].arn}/*",
]
principals {
type = "AWS"
identifiers = distinct(compact(var.management_iam_roles))
}
condition {
test = "IpAddress"
variable = "aws:SourceIp"
values = distinct(compact(var.management_public_ip_addresses))
}
}
}
resource "aws_elasticsearch_domain" "es" {
count = false == local.inside_vpc ? 1 : 0
depends_on = [aws_iam_service_linked_role.es]
domain_name = local.domain_name
elasticsearch_version = var.es_version
encrypt_at_rest {
enabled = var.encrypt_at_rest
kms_key_id = var.kms_key_id
}
domain_endpoint_options {
enforce_https = var.enforce_https
tls_security_policy = var.tls_security_policy
}
cluster_config {
instance_type = var.instance_type
instance_count = var.instance_count
dedicated_master_enabled = var.instance_count >= var.dedicated_master_threshold ? true : false
dedicated_master_count = var.instance_count >= var.dedicated_master_threshold ? 3 : 0
dedicated_master_type = var.instance_count >= var.dedicated_master_threshold ? var.dedicated_master_type != "false" ? var.dedicated_master_type : var.instance_type : ""
zone_awareness_enabled = var.es_zone_awareness
dynamic "zone_awareness_config" {
for_each = var.es_zone_awareness ? [var.es_zone_awareness_count] : []
content {
availability_zone_count = zone_awareness_config.value
}
}
}
advanced_options = var.advanced_options
dynamic "log_publishing_options" {
for_each = var.log_publishing_options
content {
# TF-UPGRADE-TODO: The automatic upgrade tool can't predict
# which keys might be set in maps assigned here, so it has
# produced a comprehensive set here. Consider simplifying
# this after confirming which keys can be set in practice.
cloudwatch_log_group_arn = log_publishing_options.value.cloudwatch_log_group_arn
enabled = lookup(log_publishing_options.value, "enabled", null)
log_type = log_publishing_options.value.log_type
}
}
node_to_node_encryption {
enabled = var.node_to_node_encryption_enabled
}
ebs_options {
ebs_enabled = var.ebs_volume_size > 0 ? true : false
volume_size = var.ebs_volume_size
volume_type = var.ebs_volume_type
}
snapshot_options {
automated_snapshot_start_hour = var.snapshot_start_hour
}
tags = merge(
{
"Domain" = local.domain_name
},
var.tags,
)
}
resource "aws_elasticsearch_domain_policy" "es_management_access" {
count = false == local.inside_vpc ? 1 : 0
domain_name = local.domain_name
access_policies = data.aws_iam_policy_document.es_management_access[0].json
}

View File

@@ -0,0 +1,112 @@
/*Add a new set of data.aws_iam_policy_document, aws_elasticsearch_domain, aws_elasticsearch_domain_policy. Because currently terraform/aws_elasticsearch_domain
does not handle properly null/empty "vpc_options" */
data "aws_iam_policy_document" "es_vpc_management_access" {
count = local.inside_vpc ? 1 : 0
statement {
actions = [
"es:*",
]
resources = [
aws_elasticsearch_domain.es_vpc[0].arn,
"${aws_elasticsearch_domain.es_vpc[0].arn}/*",
]
principals {
type = "AWS"
identifiers = distinct(compact(var.management_iam_roles))
}
}
}
resource "aws_iam_service_linked_role" "es" {
count = var.create_iam_service_linked_role ? 1 : 0
aws_service_name = "es.amazonaws.com"
}
resource "aws_elasticsearch_domain" "es_vpc" {
count = local.inside_vpc ? 1 : 0
depends_on = [aws_iam_service_linked_role.es]
domain_name = local.domain_name
elasticsearch_version = var.es_version
encrypt_at_rest {
enabled = var.encrypt_at_rest
kms_key_id = var.kms_key_id
}
domain_endpoint_options {
enforce_https = var.enforce_https
tls_security_policy = var.tls_security_policy
}
cluster_config {
instance_type = var.instance_type
instance_count = var.instance_count
dedicated_master_enabled = var.instance_count >= var.dedicated_master_threshold ? true : false
dedicated_master_count = var.instance_count >= var.dedicated_master_threshold ? 3 : 0
dedicated_master_type = var.instance_count >= var.dedicated_master_threshold ? var.dedicated_master_type != "false" ? var.dedicated_master_type : var.instance_type : ""
zone_awareness_enabled = var.es_zone_awareness
dynamic "zone_awareness_config" {
for_each = var.es_zone_awareness ? [var.es_zone_awareness_count] : []
content {
availability_zone_count = zone_awareness_config.value
}
}
}
advanced_options = var.advanced_options
dynamic "log_publishing_options" {
for_each = var.log_publishing_options
content {
# TF-UPGRADE-TODO: The automatic upgrade tool can't predict
# which keys might be set in maps assigned here, so it has
# produced a comprehensive set here. Consider simplifying
# this after confirming which keys can be set in practice.
cloudwatch_log_group_arn = log_publishing_options.value.cloudwatch_log_group_arn
enabled = lookup(log_publishing_options.value, "enabled", null)
log_type = log_publishing_options.value.log_type
}
}
node_to_node_encryption {
enabled = var.node_to_node_encryption_enabled
}
vpc_options {
subnet_ids = var.vpc_options["subnet_ids"]
security_group_ids = var.vpc_options["security_group_ids"]
}
ebs_options {
ebs_enabled = var.ebs_volume_size > 0 ? true : false
volume_size = var.ebs_volume_size
volume_type = var.ebs_volume_type
}
snapshot_options {
automated_snapshot_start_hour = var.snapshot_start_hour
}
tags = merge(
{
"Domain" = local.domain_name
},
var.tags,
)
}
resource "aws_elasticsearch_domain_policy" "es_vpc_management_access" {
count = local.inside_vpc ? 1 : 0
domain_name = local.domain_name
access_policies = data.aws_iam_policy_document.es_vpc_management_access[0].json
}

View File

@@ -0,0 +1,60 @@
output "arn" {
description = "Amazon Resource Name (ARN) of the domain"
value = element(
concat(
aws_elasticsearch_domain.es_vpc.*.arn,
aws_elasticsearch_domain.es.*.arn,
[""],
),
0,
)
}
output "domain_id" {
description = "Unique identifier for the domain"
value = element(
concat(
aws_elasticsearch_domain.es_vpc.*.domain_id,
aws_elasticsearch_domain.es.*.domain_id,
[""],
),
0,
)
}
output "domain_name" {
description = "The name of the Elasticsearch domain"
value = element(
concat(
aws_elasticsearch_domain.es_vpc.*.domain_name,
aws_elasticsearch_domain.es.*.domain_name,
[""],
),
0,
)
}
output "endpoint" {
description = "Domain-specific endpoint used to submit index, search, and data upload requests"
value = element(
concat(
aws_elasticsearch_domain.es_vpc.*.endpoint,
aws_elasticsearch_domain.es.*.endpoint,
[""],
),
0,
)
}
output "kibana_endpoint" {
description = "Domain-specific endpoint for kibana without https scheme"
value = element(
concat(
aws_elasticsearch_domain.es_vpc.*.kibana_endpoint,
aws_elasticsearch_domain.es.*.kibana_endpoint,
[""],
),
0,
)
}

View File

@@ -0,0 +1,153 @@
variable "create_iam_service_linked_role" {
description = "Whether to create IAM service linked role for AWS ElasticSearch service. Can be only one per AWS account."
type = bool
default = true
}
variable "domain_name" {
description = "Domain name for Elasticsearch cluster"
type = string
default = "es-domain"
}
variable "es_version" {
description = "Version of Elasticsearch to deploy (default 5.1)"
type = string
default = "5.1"
}
variable "instance_type" {
description = "ES instance type for data nodes in the cluster (default t2.small.elasticsearch)"
type = string
default = "t2.small.elasticsearch"
}
variable "instance_count" {
description = "Number of data nodes in the cluster (default 6)"
type = number
default = 6
}
variable "dedicated_master_type" {
description = "ES instance type to be used for dedicated masters (default same as instance_type)"
type = string
default = "false"
}
variable "encrypt_at_rest" {
description = "Enable encrption at rest (only specific instance family types support it: m4, c4, r4, i2, i3 default: false)"
type = bool
default = false
}
variable "management_iam_roles" {
description = "List of IAM role ARNs from which to permit management traffic (default ['*']). Note that a client must match both the IP address and the IAM role patterns in order to be permitted access."
type = list(string)
default = ["*"]
}
variable "management_public_ip_addresses" {
description = "List of IP addresses from which to permit management traffic (default []). Note that a client must match both the IP address and the IAM role patterns in order to be permitted access."
type = list(string)
default = []
}
variable "es_zone_awareness" {
description = "Enable zone awareness for Elasticsearch cluster (default false)"
type = bool
default = false
}
variable "es_zone_awareness_count" {
description = "Number of availability zones used for data nodes (default 2)"
type = number
default = 2
}
variable "ebs_volume_size" {
description = "Optionally use EBS volumes for data storage by specifying volume size in GB (default 0)"
type = number
default = 0
}
variable "ebs_volume_type" {
description = "Storage type of EBS volumes, if used (default gp2)"
type = string
default = "gp2"
}
variable "kms_key_id" {
description = "KMS key used for elasticsearch"
type = string
default = ""
}
variable "snapshot_start_hour" {
description = "Hour at which automated snapshots are taken, in UTC (default 0)"
type = number
default = 0
}
variable "vpc_options" {
description = "A map of supported vpc options"
type = map(list(string))
default = {
security_group_ids = []
subnet_ids = []
}
}
variable "tags" {
description = "tags to apply to all resources"
type = map(string)
default = {}
}
variable "use_prefix" {
description = "Flag indicating whether or not to use the domain_prefix. Default: true"
type = bool
default = true
}
variable "domain_prefix" {
description = "String to be prefixed to search domain. Default: tf-"
type = string
default = "tf-"
}
variable "dedicated_master_threshold" {
description = "The number of instances above which dedicated master nodes will be used. Default: 10"
type = number
default = 10
}
variable "advanced_options" {
description = "Map of key-value string pairs to specify advanced configuration options. Note that the values for these configuration options must be strings (wrapped in quotes) or they may be wrong and cause a perpetual diff, causing Terraform to want to recreate your Elasticsearch domain on every apply."
type = map(string)
default = {}
}
variable "log_publishing_options" {
description = "List of maps of options for publishing slow logs to CloudWatch Logs."
type = list(map(string))
default = []
}
variable "node_to_node_encryption_enabled" {
description = "Whether to enable node-to-node encryption."
type = bool
default = false
}
variable "enforce_https" {
description = "Whether or not to require HTTPS."
type = bool
default = false
}
variable "tls_security_policy" {
description = "The name of the TLS security policy that needs to be applied to the HTTPS endpoint. Example values: Policy-Min-TLS-1-0-2019-07 and Policy-Min-TLS-1-2-2019-07. Terraform will only perform drift detection if a configuration value is provided."
type = string
default = null
}

View File

@@ -0,0 +1,39 @@
//
// Module: tf_aws_elb/elb_http
//
// Provider specific configs
provider "aws" {
access_key = "${var.aws_access_key}"
secret_key = "${var.aws_secret_key}"
region = "${var.aws_region}"
}
// ELB Resource for Module
// A note about instances:
// - This module assumes your instances will be made
// by an ASG and the ASG will associate them with
// the ELB.
resource "aws_elb" "elb" {
name = "${var.elb_name}"
subnets = ["${var.subnet_az1}","${var.subnet_az2}"]
internal = "${var.elb_is_internal}"
security_groups = ["${var.elb_security_group}"]
listener {
instance_port = "${var.backend_port}"
instance_protocol = "${var.backend_protocol}"
lb_port = 80
lb_protocol = "http"
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = "${var.health_check_target}"
interval = 30
}
cross_zone_load_balancing = true
}

View File

@@ -0,0 +1,15 @@
//
// Module: tf_aws_elb/elb_http
//
output "elb_id" {
value = "${aws_elb.elb.id}"
}
output "elb_name" {
value = "${aws_elb.elb.name}"
}
output "elb_dns_name" {
value = "${aws_elb.elb.dns_name}"
}

View File

@@ -0,0 +1,47 @@
//
// Module: tf_aws_elb/elb_http
//
// Module specific variables
variable "elb_name" {}
variable "elb_is_internal" {
description = "Determines if the ELB is internal or not"
default = false
// Defaults to false, which results in an external IP for the ELB
}
variable "elb_security_group" {}
variable "subnet_az1" {
description = "The subnet for AZ1"
}
variable "subnet_az2" {
description = "The subnet for AZ2"
}
variable "backend_port" {
description = "The port the service on the EC2 instances listens on"
}
variable "backend_protocol" {
description = "The protocol the backend service speaks"
// Possible options are
// - http
// - https
// - tcp
// - ssl (secure tcp)
}
variable "health_check_target" {
description = "The URL the ELB should use for health checks"
// This is primarily used with `http` or `https` backend protocols
// The format is like `HTTPS:443/health`
}
// Variables for providers used in this module
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "aws_region" {}

View File

@@ -0,0 +1,40 @@
//
// Module: tf_aws_elb/elb_https
//
// Provider specific configs
provider "aws" {
access_key = "${var.aws_access_key}"
secret_key = "${var.aws_secret_key}"
region = "${var.aws_region}"
}
// ELB Resource for Module
// A note about instances:
// - This module assumes your instances will be made
// by an ASG and the ASG will associate them with
// the ELB.
resource "aws_elb" "elb" {
name = "${var.elb_name}"
subnets = ["${var.subnet_az1}","${var.subnet_az2}"]
internal = "${var.elb_is_internal}"
security_groups = ["${var.elb_security_group}"]
listener {
instance_port = "${var.backend_port}"
instance_protocol = "${var.backend_protocol}"
lb_port = 443
lb_protocol = "https"
ssl_certificate_id = "${var.ssl_certificate_id}"
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = "${var.health_check_target}"
interval = 30
}
cross_zone_load_balancing = true
}

View File

@@ -0,0 +1,15 @@
//
// Module: tf_aws_elb/elb_https
//
output "elb_id" {
value = "${aws_elb.elb.id}"
}
output "elb_name" {
value = "${aws_elb.elb.name}"
}
output "elb_dns_name" {
value = "${aws_elb.elb.dns_name}"
}

View File

@@ -0,0 +1,53 @@
//
// Module: tf_aws_elb/elb_https
//
// Module specific variables
variable "elb_name" {}
variable "elb_is_internal" {
description = "Determines if the ELB is internal or not"
default = false
// Defaults to false, which results in an external IP for the ELB
}
variable "elb_security_group" {}
// See README.md for details on finding the
// ARN of an SSL certificate in EC2
variable "ssl_certificate_id" {
description = "The ARN of the SSL Certificate in EC2"
}
variable "subnet_az1" {
description = "The subnet for AZ1"
}
variable "subnet_az2" {
description = "The subnet for AZ2"
}
variable "backend_port" {
description = "The port the service on the EC2 instances listens on"
}
variable "backend_protocol" {
description = "The protocol the backend service speaks"
// Possible options are
// - http
// - https
// - tcp
// - ssl (secure tcp)
}
variable "health_check_target" {
description = "The URL the ELB should use for health checks"
// This is primarily used with `http` or `https` backend protocols
// The format is like `HTTPS:443/health`
}
// Variables for providers used in this module
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "aws_region" {}

View File

@@ -0,0 +1,14 @@
variable "name" { default = "igw" }
variable "vpc_id" {}
variable "tags" {
description = "A map of tags to add to all resources"
default = {}
}
resource "aws_internet_gateway" "igw" {
vpc_id = "${var.vpc_id}"
tags = "${merge(var.tags, map("Name", format("%s", var.name)))}"
}
output "igw_id" { value = "${aws_internet_gateway.igw.id}" }

View File

@@ -0,0 +1,65 @@
resource "aws_iam_role" "lambda" {
name = "${var.lambda_name}"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy" "lambda" {
name = "${var.lambda_name}"
role = "${aws_iam_role.lambda.name}"
policy = "${var.iam_policy_document}"
}
resource "aws_lambda_function" "lambda" {
memory_size = "${var.memory_size}"
runtime = "${var.runtime}"
filename = "${var.lambda_zipfile}"
function_name = "${var.lambda_name}"
role = "${aws_iam_role.lambda.arn}"
handler = "${var.handler}"
source_code_hash = "${var.source_code_hash}"
count = "${var.enabled}"
timeout = "${var.timeout}"
vpc_config {
subnet_ids = ["${var.subnet_ids}"]
security_group_ids = ["${var.security_group_ids}"]
}
}
resource "aws_lambda_permission" "cloudwatch" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.lambda.arn}"
principal = "events.amazonaws.com"
source_arn = "${aws_cloudwatch_event_rule.lambda.arn}"
count = "${var.enabled}"
}
resource "aws_cloudwatch_event_rule" "lambda" {
name = "${var.lambda_name}"
schedule_expression = "${var.schedule_expression}"
count = "${var.enabled}"
}
resource "aws_cloudwatch_event_target" "lambda" {
target_id = "${var.lambda_name}"
rule = "${aws_cloudwatch_event_rule.lambda.name}"
arn = "${aws_lambda_function.lambda.arn}"
count = "${var.enabled}"
}

View File

@@ -0,0 +1,11 @@
output "lambda_arn" {
value = "${aws_lambda_function.lambda.arn}"
}
output "role_arn" {
value = "${aws_iam_role.lambda.arn}"
}
output "role_name" {
value = "${aws_iam_role.lambda.name}"
}

View File

@@ -0,0 +1,33 @@
variable "lambda_name" {}
variable "runtime" {}
variable "memory_size" {
default = 128
}
variable "lambda_zipfile" {}
variable "source_code_hash" {}
variable "handler" {}
variable "schedule_expression" {}
variable "iam_policy_document" {}
variable "enabled" {
default = true
}
variable "timeout" {
default = 3
}
variable "subnet_ids" {
default = []
}
variable "security_group_ids" {
default = []
}

View File

@@ -0,0 +1,48 @@
resource "aws_iam_instance_profile" "nat_profile" {
name = "${var.name}-nat_ha_profile"
role = "${aws_iam_role.role.name}"
}
resource "aws_iam_role" "role" {
name = "${var.name}-nat_ha_role"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2008-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {"AWS": "*"},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy" "modify_routes" {
name = "nat_ha_modify_routes"
role = "${aws_iam_role.role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ec2:ReplaceRoute",
"ec2:CreateRoute",
"ec2:DeleteRoute",
"ec2:DescribeRouteTables",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeInstanceAttribute"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}

View File

@@ -0,0 +1,70 @@
data "aws_ami" "ami" {
most_recent = true
filter {
name = "name"
values = ["${var.ami_name_pattern}"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["${var.ami_publisher}"]
}
data "aws_subnet" "first" {
id = "${var.public_subnet_ids[0]}"
}
data "aws_vpc" "vpc" {
id = "${data.aws_subnet.first.vpc_id}"
}
data "aws_region" "current" { }
data "template_file" "user_data" {
template = "${file("${path.module}/nat-user-data.conf.tmpl")}"
count = "${var.instance_count}"
vars = {
name = "${var.name}"
mysubnet = "${element(var.private_subnet_ids, count.index)}"
vpc_cidr = "${data.aws_vpc.vpc.cidr_block}"
region = "${data.aws_region.current.name}"
awsnycast_deb_url = "${var.awsnycast_deb_url}"
identifier = "${var.route_table_identifier}"
}
}
resource "aws_instance" "nat" {
count = "${var.instance_count}"
ami = "${data.aws_ami.ami.id}"
instance_type = "${var.instance_type}"
source_dest_check = false
iam_instance_profile = "${aws_iam_instance_profile.nat_profile.id}"
key_name = "${var.aws_key_name}"
subnet_id = "${element(var.public_subnet_ids, count.index)}"
vpc_security_group_ids = "${var.vpc_security_group_ids}"
tags = "${merge(var.tags, map("Name", format("%s-nat%d", var.name, count.index+1)))}"
user_data = "${element(data.template_file.user_data.*.rendered, count.index)}"
provisioner "remote-exec" {
inline = [
"while sudo pkill -0 cloud-init; do sleep 2; done",
]
connection {
user = "ubuntu"
# If we are using a bastion host ssh in via the private IP
# If we set this to an empty string we get the default behaviour.
host = "${var.ssh_bastion_host != "" ? self.private_ip : ""}"
private_key = "${var.aws_private_key}"
bastion_host = "${var.ssh_bastion_host}"
bastion_user = "${var.ssh_bastion_user}"
}
}
}

View File

@@ -0,0 +1,11 @@
output "private_ips" {
value = ["${aws_instance.nat.*.private_ip}"]
}
output "public_ips" {
value = ["${aws_instance.nat.*.public_ip}"]
}
output "instance_ids" {
value = ["${aws_instance.nat.*.id}"]
}

View File

@@ -0,0 +1,60 @@
variable "name" {}
variable "tags" {
type = "map"
default = {}
description = "A map of tags to add to all resources"
}
variable "ami_name_pattern" {
default = "ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"
description = "The name filter to use in data.aws_ami"
}
variable "ami_publisher" {
default = "099720109477" # Canonical
description = "The AWS account ID of the AMI publisher"
}
variable "instance_type" {}
variable "instance_count" {}
variable "az_list" {
type = "list"
}
variable "public_subnet_ids" {
type = "list"
}
variable "private_subnet_ids" {
type = "list"
}
variable "subnets_count" {
description = "The number of subnets in public_subnet_ids. Required because of hashicorp/terraform#1497"
}
variable "vpc_security_group_ids" {
type = "list"
}
variable "aws_key_name" {}
variable "aws_private_key" {}
variable "ssh_bastion_host" {
default = ""
}
variable "ssh_bastion_user" {
default = ""
}
variable "awsnycast_deb_url" {
default = "https://github.com/bobtfish/AWSnycast/releases/download/v0.1.5/awsnycast_0.1.5-425_amd64.deb"
}
variable "route_table_identifier" {
description = "Indentifier used by AWSnycast route table regexp"
default = "rt-private"
}

View File

@@ -0,0 +1,140 @@
#----------------------------------------------------------------
# This module creates all resources necessary for OpenVPN in AWS
#----------------------------------------------------------------
resource "aws_security_group" "openvpn" {
name = "${var.name}"
vpc_id = "${var.vpc_id}"
description = "OpenVPN security group"
tags {
Name = "${var.name}"
}
ingress {
protocol = -1
from_port = 0
to_port = 0
cidr_blocks = ["${var.vpc_cidr}"]
}
# For OpenVPN Client Web Server & Admin Web UI
ingress {
protocol = "tcp"
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
protocol = "tcp"
from_port = 443
to_port = 443
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
protocol = "udp"
from_port = 1194
to_port = 1194
cidr_blocks = ["0.0.0.0/0"]
}
egress {
protocol = -1
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "openvpn" {
ami = "${var.ami}"
instance_type = "${var.instance_type}"
key_name = "${var.key_name}"
subnet_id = "${element(var.public_subnet_ids, count.index)}"
vpc_security_group_ids = ["${aws_security_group.openvpn.id}"]
tags {
Name = "${var.name}"
}
# `admin_user` and `admin_pw` need to be passed in to the appliance through `user_data`, see docs -->
# https://docs.openvpn.net/how-to-tutorialsguides/virtual-platforms/amazon-ec2-appliance-ami-quick-start-guide/
user_data = <<USERDATA
admin_user=${var.openvpn_admin_user}
admin_pw=${var.openvpn_admin_pw}
USERDATA
provisioner "remote-exec" {
connection {
user = "${var.openvpn_user}"
host = "${self.public_ip}"
private_key = "${var.private_key}"
timeout = "10m"
}
inline = [
# Sleep 60 seconds until AMI is ready
"sleep 60",
# Set VPN network info
"sudo /usr/local/openvpn_as/scripts/sacli -k vpn.daemon.0.client.network -v ${element(split("/", var.vpn_cidr), 0)} ConfigPut",
"sudo /usr/local/openvpn_as/scripts/sacli -k vpn.daemon.0.client.netmask_bits -v ${element(split("/", var.vpn_cidr), 1)} ConfigPut",
# Do a warm restart so the config is picked up
"sudo /usr/local/openvpn_as/scripts/sacli start",
]
}
}
resource "aws_elb" "openvpn" {
name = "openvpn-elb"
subnets = ["${var.public_subnet_ids}"]
internal = false
idle_timeout = 30
connection_draining = true
connection_draining_timeout = 30
instances = ["${aws_instance.openvpn.id}"]
security_groups = ["${aws_security_group.openvpn.id}"]
listener {
instance_port = 443
instance_protocol = "https"
lb_port = 443
lb_protocol = "https"
ssl_certificate_id = "${var.cert_arn}"
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 5
target = "TCP:443"
interval = 20
}
tags {
Name = "openvpn-elb"
}
}
resource "aws_route53_record" "openvpn-web" {
zone_id = "${var.route_zone_id}"
name = "vpn-web.${var.domain_name}"
type = "A"
alias {
name = "${aws_elb.openvpn.dns_name}"
zone_id = "${aws_elb.openvpn.zone_id}"
evaluate_target_health = false
}
}
resource "aws_route53_record" "openvpn" {
zone_id = "${var.route_zone_id}"
name = "vpn.${var.domain_name}"
type = "A"
ttl = 300
records = ["${aws_instance.openvpn.public_ip}"]
}

View File

@@ -0,0 +1,15 @@
output "private_ip" {
value = "${aws_instance.openvpn.private_ip}"
}
output "public_ip" {
value = "${aws_instance.openvpn.public_ip}"
}
output "public_web_fqdn" {
value = "${aws_route53_record.openvpn-web.fqdn}"
}
output "public_fqdn" {
value = "${aws_route53_record.openvpn.fqdn}"
}

View File

@@ -0,0 +1,22 @@
variable "name" {
default = "openvpn"
}
variable "vpc_id" {}
variable "vpc_cidr" {}
variable "public_subnet_ids" {
type = "list"
}
variable "cert_arn" {}
variable "key_name" {}
variable "private_key" {}
variable "ami" {}
variable "instance_type" {}
variable "openvpn_user" {}
variable "openvpn_admin_user" {}
variable "openvpn_admin_pw" {}
variable "vpn_cidr" {}
variable "domain_name" {}
variable "route_zone_id" {}

View File

@@ -0,0 +1,93 @@
variable "name" {
default = "private"
}
variable "cidrs" {
description = "A list of CIDR"
default = []
}
variable "azs" {
description = "A list of availability zones"
default = []
}
variable "vpc_id" {
}
variable "public_subnet_ids" {
description = "A list of public subnet ids"
default = []
}
variable "nat_gateways_count" {
}
variable "map_public_ip_on_launch" {
default = true
}
variable "tags" {
description = "A map of tags to add to all resources"
default = {}
}
# Subnet
resource "aws_subnet" "private" {
vpc_id = "${var.vpc_id}"
cidr_block = "${element(var.cidrs, count.index)}"
availability_zone = "${element(var.azs, count.index)}"
count = "${length(var.cidrs)}"
map_public_ip_on_launch = "${var.map_public_ip_on_launch}"
tags = "${merge(var.tags, map("Name", format("%s.%s", var.name, element(var.azs, count.index))))}"
}
# Routes
resource "aws_route_table" "private" {
vpc_id = "${var.vpc_id}"
count = "${length(var.cidrs)}"
tags = "${merge(var.tags, map("Name", format("%s.%s", var.name, element(var.azs, count.index))))}"
}
resource "aws_route_table_association" "private" {
subnet_id = "${element(aws_subnet.private.*.id, count.index)}"
route_table_id = "${element(aws_route_table.private.*.id, count.index)}"
count = "${length(var.cidrs)}"
}
resource "aws_route" "nat_gateway" {
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.nat.*.id, count.index)}"
count = "${length(var.cidrs)}"
depends_on = [
"aws_route_table.private"
]
}
# NAT
resource "aws_eip" "nat" {
vpc = true
count = "${var.nat_gateways_count}"
}
resource "aws_nat_gateway" "nat" {
allocation_id = "${element(aws_eip.nat.*.id, count.index)}"
subnet_id = "${element(var.public_subnet_ids, count.index)}"
count = "${var.nat_gateways_count}"
}
# Output
output "subnet_ids" {
value = [
"${aws_subnet.private.*.id}"
]
}
output "private_route_table_ids" {
value = [
"${aws_route_table.private.*.id}"
]
}
output "nat_eips" {
value = [
"${aws_eip.nat.*.public_ip}"
]
}

View File

@@ -0,0 +1,80 @@
variable "name" {
default = "public"
}
variable "cidrs" {
default = []
}
variable "azs" {
description = "A list of availability zones"
default = []
}
variable "vpc_id" {
}
variable "igw_id" {
}
variable "map_public_ip_on_launch" {
default = true
}
variable "tags" {
description = "A map of tags to add to all resources"
default = {}
}
# Subnet
resource "aws_subnet" "public" {
vpc_id = "${var.vpc_id}"
cidr_block = "${element(var.cidrs, count.index)}"
availability_zone = "${element(var.azs, count.index)}"
count = "${length(var.cidrs)}"
map_public_ip_on_launch = "${var.map_public_ip_on_launch}"
lifecycle {
create_before_destroy = true
}
tags = "${merge(var.tags, map("Name", format("%s.%s", var.name, element(var.azs, count.index))))}"
}
# Routes
resource "aws_route_table" "public" {
vpc_id = "${var.vpc_id}"
count = "${length(var.cidrs)}"
tags = "${merge(var.tags, map("Name", format("%s.%s", var.name, element(var.azs, count.index))))}"
}
resource "aws_route_table_association" "public" {
subnet_id = "${element(aws_subnet.public.*.id, count.index)}"
route_table_id = "${element(aws_route_table.public.*.id, count.index)}"
count = "${length(var.cidrs)}"
lifecycle {
create_before_destroy = true
}
}
resource "aws_route" "igw" {
route_table_id = "${element(aws_route_table.public.*.id, count.index)}"
destination_cidr_block = "0.0.0.0/0"
gateway_id = "${var.igw_id}"
count = "${length(var.cidrs)}"
depends_on = [
"aws_route_table.public"
]
lifecycle {
create_before_destroy = true
}
}
output "subnet_ids" {
value = [
"${aws_subnet.public.*.id}"
]
}
output "public_route_table_ids" {
value = [
"${aws_route_table.public.*.id}"
]
}

View File

@@ -0,0 +1,21 @@
module "ami" {
source = "github.com/terraform-community-modules/tf_aws_ubuntu_ami/ebs"
region = var.region
distribution = "trusty"
instance_type = var.instance_type
}
resource "aws_instance" "puppet-client" {
ami = module.ami.ami_id
instance_type = var.instance_type
iam_instance_profile = var.iam_instance_profile
tags {
Name = "puppet client"
puppet_role = var.puppet_role
}
key_name = var.aws_key_name
subnet_id = var.subnet_id
security_groups = ["${var.security_group}"]
user_data = replace(file("${path.module}/puppetagent.conf"), "__PUPPETMASTER_IP__", "${var.puppetmaster_ip}")
}

View File

@@ -0,0 +1,4 @@
output "private_ip" {
value = "${aws_instance.puppet-client.private_ip}"
}

View File

@@ -0,0 +1,12 @@
variable "region" {}
variable "instance_type" {
default = "t2.micro"
}
variable "subnet_id" {}
variable "aws_key_name" {}
variable "iam_instance_profile" {
default = "describe-instances"
}
variable "security_group" {}
variable "puppetmaster_ip" {}
variable "puppet_role" {}

View File

@@ -0,0 +1,21 @@
module "ami" {
source = "github.com/terraform-community-modules/tf_aws_ubuntu_ami/ebs"
region = "${var.region}"
distribution = "trusty"
instance_type = "${var.instance_type}"
}
resource "aws_instance" "puppetmaster" {
ami = "${module.ami.ami_id}"
instance_type = "${var.instance_type}"
iam_instance_profile = "${var.iam_instance_profile}"
tags {
Name = "puppetmaster"
puppet_role = "puppetmaster"
}
key_name = "${var.aws_key_name}"
subnet_id = "${var.subnet_id}"
security_groups = ["${var.security_group}"]
user_data = "${replace(file("${path.module}/puppetmaster.conf"), "__REPOSITORY__", "${var.repository}")}"
}

View File

@@ -0,0 +1,4 @@
output "private_ip" {
value = "${aws_instance.puppetmaster.private_ip}"
}

View File

@@ -0,0 +1,11 @@
variable "region" {}
variable "instance_type" {
default = "t2.micro"
}
variable "subnet_id" {}
variable "aws_key_name" {}
variable "iam_instance_profile" {
default = "describe-instances"
}
variable "security_group" {}
variable "repository" {}

View File

@@ -0,0 +1,112 @@
//
// Module: tf_aws_rds
//
// This template creates the following resources
// - An RDS instance
// - A database subnet group
// - You should want your RDS instance in a VPC
resource "aws_db_instance" "main_rds_instance" {
identifier = "${var.rds_instance_identifier}"
allocated_storage = "${var.rds_allocated_storage}"
engine = "${var.rds_engine_type}"
engine_version = "${var.rds_engine_version}"
instance_class = "${var.rds_instance_class}"
name = "${var.database_name}"
username = "${var.database_user}"
password = "${var.database_password}"
port = "${var.database_port}"
# Because we're assuming a VPC, we use this option, but only one SG id
vpc_security_group_ids = ["${aws_security_group.main_db_access.id}"]
# We're creating a subnet group in the module and passing in the name
db_subnet_group_name = "${aws_db_subnet_group.main_db_subnet_group.id}"
parameter_group_name = "${var.use_external_parameter_group ? var.parameter_group_name : aws_db_parameter_group.main_rds_instance.id}"
# We want the multi-az setting to be toggleable, but off by default
multi_az = "${var.rds_is_multi_az}"
storage_type = "${var.rds_storage_type}"
iops = "${var.rds_iops}"
publicly_accessible = "${var.publicly_accessible}"
# Upgrades
allow_major_version_upgrade = "${var.allow_major_version_upgrade}"
auto_minor_version_upgrade = "${var.auto_minor_version_upgrade}"
apply_immediately = "${var.apply_immediately}"
maintenance_window = "${var.maintenance_window}"
# Snapshots and backups
skip_final_snapshot = "${var.skip_final_snapshot}"
copy_tags_to_snapshot = "${var.copy_tags_to_snapshot}"
backup_retention_period = "${var.backup_retention_period}"
backup_window = "${var.backup_window}"
# enhanced monitoring
monitoring_interval = "${var.monitoring_interval}"
tags = "${merge(var.tags, map("Name", format("%s", var.rds_instance_identifier)))}"
}
resource "aws_db_parameter_group" "main_rds_instance" {
count = "${var.use_external_parameter_group ? 0 : 1}"
name = "${var.rds_instance_identifier}-${replace(var.db_parameter_group, ".", "")}-custom-params"
family = "${var.db_parameter_group}"
# Example for MySQL
# parameter {
# name = "character_set_server"
# value = "utf8"
# }
# parameter {
# name = "character_set_client"
# value = "utf8"
# }
tags = "${merge(var.tags, map("Name", format("%s", var.rds_instance_identifier)))}"
}
resource "aws_db_subnet_group" "main_db_subnet_group" {
name = "${var.rds_instance_identifier}-subnetgrp"
description = "RDS subnet group"
subnet_ids = ["${var.subnets}"]
tags = "${merge(var.tags, map("Name", format("%s", var.rds_instance_identifier)))}"
}
# Security groups
resource "aws_security_group" "main_db_access" {
name = "${var.rds_instance_identifier}-access"
description = "Allow access to the database"
vpc_id = "${var.rds_vpc_id}"
tags = "${merge(var.tags, map("Name", format("%s", var.rds_instance_identifier)))}"
}
resource "aws_security_group_rule" "allow_db_access" {
type = "ingress"
from_port = "${var.database_port}"
to_port = "${var.database_port}"
protocol = "tcp"
cidr_blocks = ["${var.private_cidr}"]
security_group_id = "${aws_security_group.main_db_access.id}"
}
resource "aws_security_group_rule" "allow_all_outbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = "${aws_security_group.main_db_access.id}"
}

View File

@@ -0,0 +1,28 @@
#
# Module: tf_aws_rds
#
# Output the ID of the RDS instance
output "rds_instance_id" {
value = "${aws_db_instance.main_rds_instance.id}"
}
# Output the address (aka hostname) of the RDS instance
output "rds_instance_address" {
value = "${aws_db_instance.main_rds_instance.address}"
}
# Output endpoint (hostname:port) of the RDS instance
output "rds_instance_endpoint" {
value = "${aws_db_instance.main_rds_instance.endpoint}"
}
# Output the ID of the Subnet Group
output "subnet_group_id" {
value = "${aws_db_subnet_group.main_db_subnet_group.id}"
}
# Output DB security group ID
output "security_group_id" {
value = "${aws_security_group.main_db_access.id}"
}

View File

@@ -0,0 +1,159 @@
#
# Module: tf_aws_rds
#
# RDS Instance Variables
variable "rds_instance_identifier" {
description = "Custom name of the instance"
}
variable "rds_is_multi_az" {
description = "Set to true on production"
default = false
}
variable "rds_storage_type" {
description = "One of 'standard' (magnetic), 'gp2' (general purpose SSD), or 'io1' (provisioned IOPS SSD)."
default = "standard"
}
variable "rds_iops" {
description = "The amount of provisioned IOPS. Setting this implies a storage_type of 'io1', default is 0 if rds storage type is not io1"
default = "0"
}
variable "rds_allocated_storage" {
description = "The allocated storage in GBs"
# You just give it the number, e.g. 10
}
variable "rds_engine_type" {
description = "Database engine type"
# Valid types are
# - mysql
# - postgres
# - oracle-*
# - sqlserver-*
# See http://docs.aws.amazon.com/cli/latest/reference/rds/create-db-instance.html
# --engine
}
variable "rds_engine_version" {
description = "Database engine version, depends on engine type"
# For valid engine versions, see:
# See http://docs.aws.amazon.com/cli/latest/reference/rds/create-db-instance.html
# --engine-version
}
variable "rds_instance_class" {
description = "Class of RDS instance"
# Valid values
# https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.DBInstanceClass.html
}
variable "auto_minor_version_upgrade" {
description = "Allow automated minor version upgrade"
default = true
}
variable "allow_major_version_upgrade" {
description = "Allow major version upgrade"
default = false
}
variable "apply_immediately" {
description = "Specifies whether any database modifications are applied immediately, or during the next maintenance window"
default = false
}
variable "maintenance_window" {
description = "The window to perform maintenance in. Syntax: 'ddd:hh24:mi-ddd:hh24:mi' UTC "
default = "Mon:03:00-Mon:04:00"
}
variable "database_name" {
description = "The name of the database to create"
}
# Self-explainatory variables
variable "database_user" {}
variable "database_password" {}
variable "database_port" {}
# This is for a custom parameter to be passed to the DB
# We're "cloning" default ones, but we need to specify which should be copied
variable "db_parameter_group" {
description = "Parameter group, depends on DB engine used"
# default = "mysql5.6"
# default = "postgres9.5"
}
variable "use_external_parameter_group" {
description = "Use parameter group specified by `parameter_group_name` instead of built-in one"
default = false
}
# Use an external parameter group (i.e. defined in caller of this module)
variable "parameter_group_name" {
description = "Parameter group to use instead of the default"
default = ""
}
variable "publicly_accessible" {
description = "Determines if database can be publicly available (NOT recommended)"
default = false
}
# RDS Subnet Group Variables
variable "subnets" {
description = "List of subnets DB should be available at. It might be one subnet."
type = "list"
}
variable "private_cidr" {
description = "VPC private addressing, used for a security group"
type = "list"
}
variable "rds_vpc_id" {
description = "VPC to connect to, used for a security group"
type = "string"
}
variable "skip_final_snapshot" {
description = "If true (default), no snapshot will be made before deleting DB"
default = true
}
variable "copy_tags_to_snapshot" {
description = "Copy tags from DB to a snapshot"
default = true
}
variable "backup_window" {
description = "When AWS can run snapshot, can't overlap with maintenance window"
default = "22:00-03:00"
}
variable "backup_retention_period" {
type = "string"
description = "How long will we retain backups"
default = 0
}
variable "tags" {
description = "A map of tags to add to all resources"
default = {}
}
variable "monitoring_interval" {
description = "To disable collecting Enhanced Monitoring metrics, specify 0. The default is 0. Valid Values: 0, 1, 5, 10, 15, 30, 60."
default = "0"
}

View File

@@ -0,0 +1,100 @@
#
# Module: tf_aws_redshift
#
# This template creates the following resources
# - An Redshift cluster
# - A Redshift subnet group
# - You should want your Redshift cluster in a VPC
resource "aws_redshift_cluster" "main_redshift_cluster" {
cluster_identifier = "${var.cluster_identifier}"
cluster_version = "${var.cluster_version}"
node_type = "${var.cluster_node_type}"
number_of_nodes = "${var.cluster_number_of_nodes}"
database_name = "${var.cluster_database_name}"
master_username = "${var.cluster_master_username}"
master_password = "${var.cluster_master_password}"
port = "${var.cluster_port}"
# Because we're assuming a VPC, we use this option, but only one SG id
vpc_security_group_ids = ["${aws_security_group.main_redshift_access.id}"]
# We're creating a subnet group in the module and passing in the name
cluster_subnet_group_name = "${aws_redshift_subnet_group.main_redshift_subnet_group.name}"
cluster_parameter_group_name = "${aws_redshift_parameter_group.main_redshift_cluster.id}"
publicly_accessible = "${var.publicly_accessible}"
# Snapshots and backups
skip_final_snapshot = "${var.skip_final_snapshot}"
automated_snapshot_retention_period = "${var.automated_snapshot_retention_period }"
preferred_maintenance_window = "${var.preferred_maintenance_window}"
# IAM Roles
iam_roles = ["${var.cluster_iam_roles}"]
lifecycle {
prevent_destroy = true
}
tags = "${var.default_tags}"
# Encryption
encrypted = "${var.encrypted}"
kms_key_id = "${var.kms_key_id}"
}
resource "aws_redshift_parameter_group" "main_redshift_cluster" {
name = "${var.cluster_identifier}-${replace(var.cluster_parameter_group, ".", "-")}-custom-params"
family = "${var.cluster_parameter_group}"
parameter {
name = "wlm_json_configuration"
value = "${var.wlm_json_configuration}"
}
}
resource "aws_redshift_subnet_group" "main_redshift_subnet_group" {
name = "${var.cluster_identifier}-redshift-subnetgrp"
description = "Redshift subnet group of ${var.cluster_identifier}"
subnet_ids = ["${var.subnets}"]
tags = "${var.default_tags}"
}
# Security groups
resource "aws_security_group" "main_redshift_access" {
name = "${var.cluster_identifier}-redshift-access"
description = "Allow access to the cluster: ${var.cluster_identifier}"
vpc_id = "${var.redshift_vpc_id}"
tags = "${merge(var.default_tags, map(
"Name", "${var.cluster_identifier}-redshift-access"
))}"
}
# Keep rules separated to not recreate the cluster when deleting/adding rules
resource "aws_security_group_rule" "allow_port_inbound" {
type = "ingress"
from_port = "${var.cluster_port}"
to_port = "${var.cluster_port}"
protocol = "tcp"
cidr_blocks = ["${var.private_cidr}"]
security_group_id = "${aws_security_group.main_redshift_access.id}"
}
resource "aws_security_group_rule" "allow_all_outbound" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = "${aws_security_group.main_redshift_access.id}"
}

View File

@@ -0,0 +1,28 @@
#
# Module: tf_aws_redshift
#
# Output the ID of the Redshift cluster
output "redshift_cluster_id" {
value = "${aws_redshift_cluster.main_redshift_cluster.id}"
}
# Output address (hostname) of the Redshift cluster
output "redshift_cluster_address" {
value = "${replace(aws_redshift_cluster.main_redshift_cluster.endpoint, format(":%s", aws_redshift_cluster.main_redshift_cluster.port), "")}"
}
# Output endpoint (hostname:port) of the Redshift cluster
output "redshift_cluster_endpoint" {
value = "${aws_redshift_cluster.main_redshift_cluster.endpoint}"
}
# Output the ID of the Subnet Group
output "subnet_group_id" {
value = "${aws_redshift_subnet_group.main_redshift_subnet_group.id}"
}
# Output DB security group ID
output "security_group_id" {
value = "${aws_security_group.main_redshift_access.id}"
}

View File

@@ -0,0 +1,110 @@
#
# Module: tf_aws_redshift
#
# Redshift Cluster Variables
variable "cluster_identifier" {
description = "Custom name of the cluster"
}
variable "cluster_version" {
description = "Version of Redshift engine cluster"
default = "1.0"
# Constraints: Only version 1.0 is currently available.
# http://docs.aws.amazon.com/cli/latest/reference/redshift/create-cluster.html
}
variable "cluster_node_type" {
description = "Node Type of Redshift cluster"
# Valid Values: ds1.xlarge | ds1.8xlarge | ds2.xlarge | ds2.8xlarge | dc1.large | dc1.8xlarge.
# http://docs.aws.amazon.com/cli/latest/reference/redshift/create-cluster.html
}
variable "cluster_number_of_nodes" {
description = "Number of Node in the cluster"
default = 3
}
variable "cluster_database_name" {
description = "The name of the database to create"
}
# Self-explainatory variables
variable "cluster_master_username" {}
variable "cluster_master_password" {}
variable "cluster_port" {
default = 5439
}
# This is for a custom parameter to be passed to the DB
# We're "cloning" default ones, but we need to specify which should be copied
variable "cluster_parameter_group" {
description = "Parameter group, depends on DB engine used"
default = "redshift-1.0"
}
variable "cluster_iam_roles" {
description = "A list of IAM Role ARNs to associate with the cluster. A Maximum of 10 can be associated to the cluster at any time."
type = "list"
default = []
}
variable "publicly_accessible" {
description = "Determines if Cluster can be publicly available (NOT recommended)"
default = false
}
# Redshift Subnet Group Variables
variable "subnets" {
description = "List of subnets DB should be available at. It might be one subnet."
type = "list"
}
variable "private_cidr" {
description = "VPC private addressing, used for a security group"
type = "list"
}
variable "redshift_vpc_id" {
description = "VPC to connect to, used for a security group"
type = "string"
}
variable "skip_final_snapshot" {
description = "If true (default), no snapshot will be made before deleting DB"
default = true
}
variable "preferred_maintenance_window" {
description = "When AWS can run snapshot, can't overlap with maintenance window"
default = "sat:10:00-sat:10:30"
}
variable "automated_snapshot_retention_period" {
type = "string"
description = "How long will we retain backups"
default = 0
}
variable "wlm_json_configuration" {
default = "[{\"query_concurrency\": 5}]"
}
variable "default_tags" {
type = "map"
}
variable "encrypted" {
description = "(Optional) If true , the data in the cluster is encrypted at rest."
default = false
}
variable "kms_key_id" {
description = "(Optional) The ARN for the KMS encryption key. When specifying kms_key_id, encrypted needs to be set to true."
default = ""
}

View File

@@ -0,0 +1,80 @@
//
// Module: tf_aws_sg/sg_carbon-relay-ng
//
//
resource "aws_security_group" "main_security_group" {
name = "${var.security_group_name}"
description = "tf-sg-${var.security_group_name}"
vpc_id = "${var.vpc_id}"
}
// Allow any internal network flow.
resource "aws_security_group_rule" "ingress_any_any_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 0
to_port = 65535
protocol = "-1"
self = true
type = "ingress"
}
// Allow: TCP:2003 for carbon line-in.
resource "aws_security_group_rule" "ingress_tcp_2003_cidr" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 2003
to_port = 2003
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow: UDP:2013 for carbon line-in.
resource "aws_security_group_rule" "ingress_udp_2003_cidr" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 2003
to_port = 2003
protocol = "udp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow: TCP:2013 for Pickle.
resource "aws_security_group_rule" "ingress_tcp_2013_cidr" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 2013
to_port = 2013
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow: UDP:2013 for Pickle.
resource "aws_security_group_rule" "ingress_udp_2013_cidr" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 2013
to_port = 2013
protocol = "udp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow: TCP:2004 for Admin port.
resource "aws_security_group_rule" "ingress_tcp_2004_cidr" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 2004
to_port = 2004
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow: TCP:8081 for graphical user interface.
resource "aws_security_group_rule" "ingress_tcp_8081_cidr" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8081
to_port = 8081
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}

View File

@@ -0,0 +1,4 @@
// Output ID of sg_carbon-relay-ng SG we made
output "security_group_id" {
value = "${aws_security_group.main_security_group.id}"
}

View File

@@ -0,0 +1,14 @@
// Module specific variables
variable "security_group_name" {
description = "The name for the security group"
}
variable "vpc_id" {
description = "The VPC this security group will go in"
}
variable "source_cidr_block" {
description = "The source CIDR block to allow traffic from"
default = ["0.0.0.0/0"]
type = "list"
}

View File

@@ -0,0 +1,50 @@
//
// Module: tf_aws_sg/sg_cassandra
//
//
resource "aws_security_group" "main_security_group" {
name = "${var.security_group_name}"
description = "tf-sg-${var.security_group_name}"
vpc_id = "${var.vpc_id}"
}
// Allow any internal network flow.
resource "aws_security_group_rule" "ingress_any_any_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 0
to_port = 65535
protocol = "-1"
self = true
type = "ingress"
}
// Allow TCP:9042 (Cassandra clients).
resource "aws_security_group_rule" "ingress_tcp_9042_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 9042
to_port = 9042
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow TCP:9160 (Cassandra Thrift clients)
resource "aws_security_group_rule" "ingress_tcp_9160_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 9160
to_port = 9160
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow TCP:7199 (JMX)
resource "aws_security_group_rule" "ingress_tcp_7199_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 7199
to_port = 7199
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}

View File

@@ -0,0 +1,4 @@
// Output ID of sg_cassandra SG we made
output "security_group_id" {
value = "${aws_security_group.main_security_group.id}"
}

View File

@@ -0,0 +1,14 @@
// Module specific variables
variable "security_group_name" {
description = "The name for the security group"
}
variable "vpc_id" {
description = "The VPC this security group will go in"
}
variable "source_cidr_block" {
description = "The source CIDR block to allow traffic from"
default = ["0.0.0.0/0"]
type = "list"
}

View File

@@ -0,0 +1,100 @@
//
// Module: tf_aws_sg/sg_consul
//
//
resource "aws_security_group" "main_security_group" {
name = "${var.security_group_name}"
description = "tf-sg-${var.security_group_name}"
vpc_id = "${var.vpc_id}"
}
// Allow any internal network flow.
resource "aws_security_group_rule" "ingress_any_any_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 0
to_port = 65535
protocol = "-1"
self = true
type = "ingress"
}
// Allow TCP:8300 (Server RPC).
resource "aws_security_group_rule" "ingress_tcp_8300_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8300
to_port = 8300
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow TCP:8500 (Consul Web UI).
resource "aws_security_group_rule" "ingress_tcp_8500_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8500
to_port = 8500
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow TCP:8301 (Serf LAN).
resource "aws_security_group_rule" "ingress_tcp_8301_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8301
to_port = 8301
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow UDP:8301 (Serf LAN).
resource "aws_security_group_rule" "ingress_udp_8301_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8301
to_port = 8301
protocol = "udp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow TCP:8302 (Serf WAN).
resource "aws_security_group_rule" "ingress_tcp_8302_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8302
to_port = 8302
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow UDP:8302 (Serf WAN).
resource "aws_security_group_rule" "ingress_udp_8302_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8302
to_port = 8302
protocol = "udp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow TCP:8600 (Consul DNS).
resource "aws_security_group_rule" "ingress_tcp_8600_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8600
to_port = 8600
protocol = "tcp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}
// Allow UDP:8600 (Consul DNS).
resource "aws_security_group_rule" "ingress_udp_8600_self" {
security_group_id = "${aws_security_group.main_security_group.id}"
from_port = 8600
to_port = 8600
protocol = "udp"
cidr_blocks = "${var.source_cidr_block}"
type = "ingress"
}

View File

@@ -0,0 +1,4 @@
// Output ID of sg_consul SG we made
output "security_group_id" {
value = "${aws_security_group.main_security_group.id}"
}

View File

@@ -0,0 +1,14 @@
// Module specific variables
variable "security_group_name" {
description = "The name for the security group"
}
variable "vpc_id" {
description = "The VPC this security group will go in"
}
variable "source_cidr_block" {
description = "The source CIDR block to allow traffic from"
default = ["0.0.0.0/0"]
type = "list"
}

View File

@@ -0,0 +1,3 @@
output "sg_id" {
value = "${aws_security_group.default_sg.id}"
}

View File

@@ -0,0 +1,26 @@
resource "aws_security_group" "default_sg" {
name = "${var.sg_name}"
description = "${var.sg_description}"
vpc_id = "${var.vpc_id}"
tags = "${var.tags}"
}
resource "aws_security_group_rule" "ingress_rule" {
count = "${length(var.inbound_rules)}"
type = "ingress"
cidr_blocks = ["${element(var.inbound_rules[count.index], 0)}"]
from_port = "${element(var.inbound_rules[count.index], 1)}"
to_port = "${element(var.inbound_rules[count.index], 2)}"
protocol = "${element(var.inbound_rules[count.index], 3)}"
security_group_id = "${aws_security_group.default_sg.id}"
}
resource "aws_security_group_rule" "egress_rule" {
count = "${length(var.outbound_rules)}"
type = "egress"
cidr_blocks = ["${element(var.outbound_rules[count.index], 0)}"]
from_port = "${element(var.outbound_rules[count.index], 1)}"
to_port = "${element(var.outbound_rules[count.index], 2)}"
protocol = "${element(var.outbound_rules[count.index], 3)}"
security_group_id = "${aws_security_group.default_sg.id}"
}

View File

@@ -0,0 +1,18 @@
variable "vpc_id" {}
variable "sg_name" {}
variable "sg_description" {
default = "Security Group managed by Terraform"
}
variable "inbound_rules" {
type = "map"
}
variable "outbound_rules" {
type = "map"
}
variable "tags" {
type = "map"
}

Some files were not shown because too many files have changed in this diff Show More