├── .gitignore ├── Makefile ├── README.md ├── app.tf ├── bastion.tf ├── circle.yml ├── cloudfront.tf ├── db.tf ├── elasticache.tf ├── elb.tf ├── envs ├── dev │ ├── main.tf │ └── variables.tf ├── prd │ ├── main.tf │ └── variables.tf └── stg │ ├── main.tf │ └── variables.tf ├── key_pair.tf ├── keys └── site_key.pub ├── modules ├── dns │ ├── dns.tf │ └── variables.tf └── iam │ ├── iam.tf │ ├── outputs.tf │ ├── policies │ └── ec2_assume_role_policy.json │ └── variables.tf ├── network.tf ├── outputs.tf ├── s3.tf ├── security_group.tf ├── sns.tf ├── user_data ├── app_cloud_config.yml └── bastion_cloud_config.yml └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | *tfstate* 3 | keys/* 4 | !keys/*.pub 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUCKET_NAME = terraform-circleci-demo 2 | REGION = ap-northeast-1 3 | CD = [ -d envs/${ENV} ] && cd envs/${ENV} 4 | ENV = $1 5 | ARGS = $2 6 | 7 | terraform: 8 | @${CD} && \ 9 | terraform ${ARGS} 10 | 11 | remote-enable: 12 | @${CD} && \ 13 | terraform remote config \ 14 | -backend=s3 \ 15 | -backend-config='bucket=${BUCKET_NAME}' \ 16 | -backend-config='key=${ENV}/terraform.tfstate' \ 17 | -backend-config='region=${REGION}' 18 | 19 | remote-disable: 20 | @${CD} && \ 21 | terraform remote config \ 22 | -disable 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | deploy-terraform-with-circleci 2 | ============================== 3 | 4 | http://dev.classmethod.jp/devops/deploy-terraform-with-circleci/ 5 | -------------------------------------------------------------------------------- /app.tf: -------------------------------------------------------------------------------- 1 | #data "template_file" "user_data" { 2 | 3 | # template = "${path.module}/user_data/app_cloud_config.yml.tpl" 4 | 5 | # 6 | 7 | # vars { 8 | 9 | # s3_domain 10 | 11 | # } 12 | 13 | #} 14 | 15 | resource "aws_launch_configuration" "app" { 16 | name_prefix = "${var.name}-app-" 17 | image_id = "${var.amazon_linux_id}" 18 | instance_type = "${var.instance_types["app"]}" 19 | key_name = "${aws_key_pair.key_pair.key_name}" 20 | security_groups = ["${aws_security_group.app.id}"] 21 | iam_instance_profile = "${var.instance_profile_id}" 22 | user_data = "${file("${path.module}/user_data/app_cloud_config.yml")}" 23 | associate_public_ip_address = false 24 | 25 | root_block_device { 26 | volume_type = "gp2" 27 | volume_size = 8 28 | } 29 | 30 | lifecycle { 31 | create_before_destroy = true 32 | } 33 | } 34 | 35 | resource "aws_autoscaling_group" "app" { 36 | name = "${var.name}-asg" 37 | launch_configuration = "${aws_launch_configuration.app.name}" 38 | vpc_zone_identifier = ["${aws_subnet.application_subnet.*.id}"] 39 | min_size = "${var.asg_config["min"]}" 40 | max_size = "${var.asg_config["max"]}" 41 | desired_capacity = "${var.asg_config["desired"]}" 42 | health_check_grace_period = 300 43 | health_check_type = "ELB" 44 | force_delete = true 45 | load_balancers = ["${aws_elb.elb.id}"] 46 | 47 | tag { 48 | key = "Name" 49 | value = "${var.name}-asg" 50 | propagate_at_launch = true 51 | } 52 | 53 | lifecycle { 54 | create_before_destroy = true 55 | } 56 | } 57 | 58 | resource "aws_autoscaling_policy" "scale_out" { 59 | name = "${var.name}-scale-out" 60 | scaling_adjustment = 1 61 | adjustment_type = "ChangeInCapacity" 62 | cooldown = 300 63 | autoscaling_group_name = "${aws_autoscaling_group.app.name}" 64 | } 65 | 66 | resource "aws_autoscaling_policy" "scale_in" { 67 | name = "${var.name}-scale-in" 68 | scaling_adjustment = "-1" 69 | adjustment_type = "ChangeInCapacity" 70 | cooldown = 300 71 | autoscaling_group_name = "${aws_autoscaling_group.app.name}" 72 | } 73 | 74 | resource "aws_cloudwatch_metric_alarm" "scale_out" { 75 | alarm_name = "${var.name}-scale-out" 76 | comparison_operator = "GreaterThanOrEqualToThreshold" 77 | evaluation_periods = 1 78 | metric_name = "CPUUtilization" 79 | period = 300 80 | namespace = "AWS/EC2" 81 | statistic = "Average" 82 | threshold = 30 83 | 84 | alarm_actions = [ 85 | "${aws_autoscaling_policy.scale_out.arn}", 86 | "${module.tf_sns_email.arn}", 87 | ] 88 | 89 | dimensions { 90 | AutoScalingGroupName = "${aws_autoscaling_group.app.name}" 91 | } 92 | } 93 | 94 | resource "aws_cloudwatch_metric_alarm" "scale_in" { 95 | alarm_name = "${var.name}-scale-in" 96 | comparison_operator = "LessThanOrEqualToThreshold" 97 | evaluation_periods = 1 98 | metric_name = "CPUUtilization" 99 | period = 300 100 | namespace = "AWS/EC2" 101 | statistic = "Average" 102 | threshold = 10 103 | 104 | alarm_actions = [ 105 | "${aws_autoscaling_policy.scale_in.arn}", 106 | "${module.tf_sns_email.arn}", 107 | ] 108 | 109 | dimensions { 110 | AutoScalingGroupName = "${aws_autoscaling_group.app.name}" 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /bastion.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "bastion" { 2 | ami = "${var.amazon_linux_id}" 3 | instance_type = "${var.instance_types["bastion"]}" 4 | vpc_security_group_ids = ["${aws_security_group.bastion.id}"] 5 | subnet_id = "${element(aws_subnet.frontend_subnet.*.id, 1)}" 6 | key_name = "${aws_key_pair.key_pair.key_name}" 7 | user_data = "${file("${path.module}/user_data/bastion_cloud_config.yml")}" 8 | associate_public_ip_address = false 9 | 10 | root_block_device { 11 | volume_type = "gp2" 12 | volume_size = 8 13 | } 14 | 15 | tags { 16 | Name = "${var.name}-bastion" 17 | } 18 | } 19 | 20 | resource "aws_eip" "eip" { 21 | vpc = true 22 | instance = "${aws_instance.bastion.id}" 23 | } 24 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | environment: 3 | S3_BUCKET: terraform-circleci-demo 4 | AWS_DEFAULT_REGION: ap-northeast-1 5 | TERRAFORM_VER: 0.7.4 6 | PATH: $PATH:$HOME/.local/bin:$HOME/bin 7 | 8 | dependencies: 9 | override: 10 | - | 11 | if [[ ! -f ~/.local/bin/terraform ]]; then 12 | mkdir -p ~/.local/bin 13 | cd ~/.local/bin 14 | wget "https://releases.hashicorp.com/terraform/${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip" 15 | unzip *.zip 16 | rm *.zip 17 | fi 18 | cache_directories: 19 | - ~/.local/bin 20 | 21 | 22 | test: 23 | override: 24 | - | 25 | if [[ "$CIRCLE_BRANCH" =~ (release/)?staging/? ]]; then 26 | make remote-enable ENV=dev 27 | make terraform ENV=dev ARGS="get -update" 28 | make terraform ENV=dev ARGS=plan 29 | elif [[ "$CIRCLE_BRANCH" =~ (release/)?production/?|master ]]; then 30 | make remote-enable ENV=prd 31 | make terraform ENV=prd ARGS="get -update" 32 | make terraform ENV=prd ARGS=plan 33 | fi 34 | 35 | deployment: 36 | staging: 37 | branch: release/staging 38 | commands: 39 | - make terraform ENV=staging ARGS="get -update" 40 | - make terraform ENV=staging ARGS=apply 41 | - make terraform ENV=staging ARGS="remote push" 42 | production: 43 | branch: release/production 44 | commands: 45 | - make terraform ENV=production ARGS="get -update" 46 | - make terraform ENV=production ARGS=apply 47 | - make terraform ENV=production ARGS="remote push" 48 | -------------------------------------------------------------------------------- /cloudfront.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudfront_origin_access_identity" "cf" { 2 | comment = "${var.name}-cf" 3 | } 4 | 5 | resource "aws_cloudfront_distribution" "cf" { 6 | depends_on = ["aws_s3_bucket.s3"] 7 | enabled = true 8 | comment = "${var.name}-cf" 9 | default_root_object = "index.html" 10 | price_class = "${var.cf_config["price_class"]}" 11 | aliases = ["${var.domain_config["cf_sub_domain"]}.${var.domain_config["domain"]}"] 12 | retain_on_delete = true 13 | 14 | origin { 15 | domain_name = "${var.domain_config["elb_sub_domain"]}.${var.domain_config["domain"]}" 16 | origin_id = "ELB-${aws_elb.elb.name}" 17 | 18 | custom_origin_config { 19 | http_port = 80 20 | https_port = 443 21 | origin_protocol_policy = "https-only" 22 | origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2", "SSLv3"] 23 | } 24 | } 25 | 26 | origin { 27 | domain_name = "${aws_s3_bucket.s3.id}.s3.amazonaws.com" 28 | origin_id = "S3-${aws_s3_bucket.s3.id}" 29 | 30 | s3_origin_config { 31 | origin_access_identity = "${aws_cloudfront_origin_access_identity.cf.cloudfront_access_identity_path}" 32 | } 33 | } 34 | 35 | default_cache_behavior { 36 | allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] 37 | cached_methods = ["GET", "HEAD"] 38 | target_origin_id = "ELB-${aws_elb.elb.name}" 39 | 40 | forwarded_values { 41 | query_string = true 42 | headers = ["*"] 43 | 44 | cookies { 45 | forward = "all" 46 | } 47 | } 48 | 49 | viewer_protocol_policy = "redirect-to-https" 50 | min_ttl = 0 51 | default_ttl = 0 52 | max_ttl = 0 53 | } 54 | 55 | cache_behavior { 56 | path_pattern = "img/*.jpg" 57 | allowed_methods = ["GET", "HEAD"] 58 | cached_methods = ["GET", "HEAD"] 59 | target_origin_id = "S3-${aws_s3_bucket.s3.id}" 60 | viewer_protocol_policy = "redirect-to-https" 61 | min_ttl = 0 62 | default_ttl = 3600 63 | max_ttl = 86400 64 | 65 | forwarded_values { 66 | query_string = false 67 | 68 | cookies { 69 | forward = "none" 70 | } 71 | } 72 | } 73 | 74 | restrictions { 75 | geo_restriction { 76 | restriction_type = "none" 77 | } 78 | } 79 | 80 | viewer_certificate { 81 | cloudfront_default_certificate = true 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /db.tf: -------------------------------------------------------------------------------- 1 | resource "aws_db_subnet_group" "db" { 2 | name = "${var.name}-aurora" 3 | description = "${var.name} aurora" 4 | subnet_ids = ["${aws_subnet.datastore_subnet.*.id}"] 5 | 6 | tags { 7 | Name = "${var.name}-aurora" 8 | } 9 | } 10 | 11 | resource "aws_db_parameter_group" "db" { 12 | name = "${var.name}-aurora" 13 | family = "${var.db_config["family"]}" 14 | description = "${var.name} aurora" 15 | } 16 | 17 | resource "aws_rds_cluster" "db" { 18 | cluster_identifier = "${var.name}-aurora" 19 | availability_zones = ["${var.azs}"] 20 | database_name = "${var.db_config["db_name"]}" 21 | master_username = "${var.db_config["username"]}" 22 | master_password = "${var.db_config["password"]}" 23 | backup_retention_period = 5 24 | preferred_backup_window = "07:00-09:00" 25 | vpc_security_group_ids = ["${aws_security_group.db.id}"] 26 | db_subnet_group_name = "${aws_db_subnet_group.db.name}" 27 | } 28 | 29 | resource "aws_rds_cluster_instance" "db" { 30 | cluster_identifier = "${var.name}-aurora" 31 | instance_class = "${var.db_config["instance_class"]}" 32 | db_subnet_group_name = "${aws_db_subnet_group.db.name}" 33 | db_parameter_group_name = "${aws_db_parameter_group.db.id}" 34 | 35 | tags { 36 | Name = "${var.name}-aurora" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /elasticache.tf: -------------------------------------------------------------------------------- 1 | resource "aws_elasticache_subnet_group" "redis" { 2 | name = "${var.name}-redis" 3 | subnet_ids = ["${aws_subnet.datastore_subnet.*.id}"] 4 | description = "${var.name} redis" 5 | } 6 | 7 | resource "aws_elasticache_parameter_group" "redis" { 8 | name = "${var.name}-redis" 9 | family = "${var.elasticache_config["family"]}" 10 | description = "${var.name} redis" 11 | } 12 | 13 | resource "aws_elasticache_cluster" "redis" { 14 | cluster_id = "${var.elasticache_config["cluster_id"]}" 15 | engine = "${var.elasticache_config["engine"]}" 16 | engine_version = "${var.elasticache_config["engine_version"]}" 17 | maintenance_window = "${var.elasticache_config["maintenance_window"]}" 18 | node_type = "${var.elasticache_config["node_type"]}" 19 | num_cache_nodes = 1 20 | parameter_group_name = "${aws_elasticache_parameter_group.redis.id}" 21 | port = 6379 22 | subnet_group_name = "${aws_elasticache_subnet_group.redis.name}" 23 | security_group_ids = ["${aws_security_group.redis.id}"] 24 | 25 | tags { 26 | Name = "${var.name}-redis" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /elb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_elb" "elb" { 2 | name = "${var.name}-elb" 3 | subnets = ["${aws_subnet.frontend_subnet.*.id}"] 4 | security_groups = ["${aws_security_group.elb.id}"] 5 | cross_zone_load_balancing = true 6 | connection_draining = true 7 | connection_draining_timeout = 300 8 | idle_timeout = 60 9 | 10 | listener { 11 | lb_port = 80 12 | lb_protocol = "http" 13 | instance_port = 80 14 | instance_protocol = "http" 15 | } 16 | 17 | health_check { 18 | healthy_threshold = 10 19 | unhealthy_threshold = 2 20 | timeout = 5 21 | target = "HTTP:80/index.html" 22 | interval = 30 23 | } 24 | 25 | tags { 26 | Name = "${var.name}-elb" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /envs/dev/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | module "iam" { 6 | source = "../../modules/iam" 7 | 8 | name = "${var.name}" 9 | } 10 | 11 | module "prd" { 12 | source = "../../" 13 | 14 | name = "${var.name}" 15 | vpc_cidr = "${var.vpc_cidr}" 16 | email_address = "${var.email_address}" 17 | instance_types = "${var.instance_types}" 18 | asg_config = "${var.asg_config}" 19 | db_config = "${var.db_config}" 20 | cf_config = "${var.cf_config}" 21 | elasticache_config = "${var.elasticache_config}" 22 | azs = "${data.aws_availability_zones.az.names}" 23 | amazon_linux_id = "${data.aws_ami.amazon_linux.id}" 24 | instance_profile_id = "${module.iam.instance_profile_id}" 25 | domain_config = "${var.domain_config}" 26 | } 27 | 28 | module "dns" { 29 | source = "../../modules/dns" 30 | 31 | domain_config = "${var.domain_config}" 32 | bastion_public_ip = "${module.prd.bastion_public_ip}" 33 | elb_config = "${module.prd.elb_config}" 34 | cf_config = "${module.prd.cf_config}" 35 | } 36 | -------------------------------------------------------------------------------- /envs/dev/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | default = "dev-terraform-circleci-demo" 3 | } 4 | 5 | variable "region" { 6 | default = "ap-northeast-1" 7 | } 8 | 9 | variable "vpc_cidr" { 10 | default = "172.16.0.0/16" 11 | } 12 | 13 | variable "email_address" { 14 | default = "dev@gmail.com" 15 | } 16 | 17 | variable "instance_types" { 18 | default = { 19 | "bastion" = "t2.nano" 20 | "app" = "t2.nano" 21 | } 22 | } 23 | 24 | variable "asg_config" { 25 | default = { 26 | "min" = 1 27 | "max" = 1 28 | "desired" = 1 29 | } 30 | } 31 | 32 | variable "db_config" { 33 | default = { 34 | "instance_class" = "db.r3.large" 35 | "family" = "aurora5.6" 36 | "db_name" = "aurora" 37 | "username" = "aurora" 38 | "password" = "pAssw0rd" 39 | } 40 | } 41 | 42 | variable "domain_config" { 43 | default = { 44 | "domain" = "dev.example.com" 45 | "cf_sub_domain" = "cf" 46 | "elb_sub_domain" = "elb" 47 | "s3_sub_domain" = "img" 48 | "bastion_sub_domain" = "bastion" 49 | } 50 | } 51 | 52 | variable "cf_config" { 53 | default = { 54 | "price_class" = "PriceClass_200" 55 | } 56 | } 57 | 58 | variable "elasticache_config" { 59 | default = { 60 | "cluster_id" = "redis-cluster" 61 | "engine" = "redis" 62 | "engine_version" = "2.8.24" 63 | "node_type" = "cache.t2.micro" 64 | "maintenance_window" = "sun:05:00-sun:06:00" 65 | "family" = "redis2.8" 66 | } 67 | } 68 | 69 | data "aws_availability_zones" "az" {} 70 | 71 | data "aws_ami" "amazon_linux" { 72 | most_recent = true 73 | owners = ["amazon"] 74 | 75 | filter { 76 | name = "architecture" 77 | values = ["x86_64"] 78 | } 79 | 80 | filter { 81 | name = "root-device-type" 82 | values = ["ebs"] 83 | } 84 | 85 | filter { 86 | name = "name" 87 | values = ["amzn-ami-hvm-*"] 88 | } 89 | 90 | filter { 91 | name = "virtualization-type" 92 | values = ["hvm"] 93 | } 94 | 95 | filter { 96 | name = "block-device-mapping.volume-type" 97 | values = ["gp2"] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /envs/prd/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | module "iam" { 6 | source = "../../modules/iam" 7 | 8 | name = "${var.name}" 9 | } 10 | 11 | module "prd" { 12 | source = "../../" 13 | 14 | name = "${var.name}" 15 | vpc_cidr = "${var.vpc_cidr}" 16 | email_address = "${var.email_address}" 17 | instance_types = "${var.instance_types}" 18 | asg_config = "${var.asg_config}" 19 | db_config = "${var.db_config}" 20 | cf_config = "${var.cf_config}" 21 | elasticache_config = "${var.elasticache_config}" 22 | azs = "${data.aws_availability_zones.az.names}" 23 | amazon_linux_id = "${data.aws_ami.amazon_linux.id}" 24 | instance_profile_id = "${module.iam.instance_profile_id}" 25 | domain_config = "${var.domain_config}" 26 | } 27 | 28 | module "dns" { 29 | source = "../../modules/dns" 30 | 31 | domain_config = "${var.domain_config}" 32 | bastion_public_ip = "${module.prd.bastion_public_ip}" 33 | elb_config = "${module.prd.elb_config}" 34 | cf_config = "${module.prd.cf_config}" 35 | } 36 | -------------------------------------------------------------------------------- /envs/prd/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | default = "prd-terraform-circleci-demo" 3 | } 4 | 5 | variable "region" { 6 | default = "ap-northeast-1" 7 | } 8 | 9 | variable "vpc_cidr" { 10 | default = "172.16.0.0/16" 11 | } 12 | 13 | variable "email_address" { 14 | default = "prd@gmail.com" 15 | } 16 | 17 | variable "instance_types" { 18 | default = { 19 | "bastion" = "t2.nano" 20 | "app" = "t2.nano" 21 | } 22 | } 23 | 24 | variable "asg_config" { 25 | default = { 26 | "min" = 1 27 | "max" = 1 28 | "desired" = 1 29 | } 30 | } 31 | 32 | variable "db_config" { 33 | default = { 34 | "instance_class" = "db.r3.large" 35 | "family" = "aurora5.6" 36 | "db_name" = "aurora" 37 | "username" = "aurora" 38 | "password" = "pAssw0rd" 39 | } 40 | } 41 | 42 | variable "domain_config" { 43 | default = { 44 | "domain" = "prd.example.com" 45 | "cf_sub_domain" = "cf" 46 | "elb_sub_domain" = "elb" 47 | "s3_sub_domain" = "img" 48 | "bastion_sub_domain" = "bastion" 49 | } 50 | } 51 | 52 | variable "cf_config" { 53 | default = { 54 | "price_class" = "PriceClass_200" 55 | } 56 | } 57 | 58 | variable "elasticache_config" { 59 | default = { 60 | "cluster_id" = "redis-cluster" 61 | "engine" = "redis" 62 | "engine_version" = "2.8.24" 63 | "node_type" = "cache.t2.micro" 64 | "maintenance_window" = "sun:05:00-sun:06:00" 65 | "family" = "redis2.8" 66 | } 67 | } 68 | 69 | data "aws_availability_zones" "az" {} 70 | 71 | data "aws_ami" "amazon_linux" { 72 | most_recent = true 73 | owners = ["amazon"] 74 | 75 | filter { 76 | name = "architecture" 77 | values = ["x86_64"] 78 | } 79 | 80 | filter { 81 | name = "root-device-type" 82 | values = ["ebs"] 83 | } 84 | 85 | filter { 86 | name = "name" 87 | values = ["amzn-ami-hvm-*"] 88 | } 89 | 90 | filter { 91 | name = "virtualization-type" 92 | values = ["hvm"] 93 | } 94 | 95 | filter { 96 | name = "block-device-mapping.volume-type" 97 | values = ["gp2"] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /envs/stg/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | module "iam" { 6 | source = "../../modules/iam" 7 | 8 | name = "${var.name}" 9 | } 10 | 11 | module "prd" { 12 | source = "../../" 13 | 14 | name = "${var.name}" 15 | vpc_cidr = "${var.vpc_cidr}" 16 | email_address = "${var.email_address}" 17 | instance_types = "${var.instance_types}" 18 | asg_config = "${var.asg_config}" 19 | db_config = "${var.db_config}" 20 | cf_config = "${var.cf_config}" 21 | elasticache_config = "${var.elasticache_config}" 22 | azs = "${data.aws_availability_zones.az.names}" 23 | amazon_linux_id = "${data.aws_ami.amazon_linux.id}" 24 | instance_profile_id = "${module.iam.instance_profile_id}" 25 | domain_config = "${var.domain_config}" 26 | } 27 | 28 | module "dns" { 29 | source = "../../modules/dns" 30 | 31 | domain_config = "${var.domain_config}" 32 | bastion_public_ip = "${module.prd.bastion_public_ip}" 33 | elb_config = "${module.prd.elb_config}" 34 | cf_config = "${module.prd.cf_config}" 35 | } 36 | -------------------------------------------------------------------------------- /envs/stg/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | default = "stg-terraform-circleci-demo" 3 | } 4 | 5 | variable "region" { 6 | default = "ap-northeast-1" 7 | } 8 | 9 | variable "vpc_cidr" { 10 | default = "172.16.0.0/16" 11 | } 12 | 13 | variable "email_address" { 14 | default = "stg@gmail.com" 15 | } 16 | 17 | variable "instance_types" { 18 | default = { 19 | "bastion" = "t2.nano" 20 | "app" = "t2.nano" 21 | } 22 | } 23 | 24 | variable "asg_config" { 25 | default = { 26 | "min" = 2 27 | "max" = 2 28 | "desired" = 2 29 | } 30 | } 31 | 32 | variable "db_config" { 33 | default = { 34 | "instance_class" = "db.r3.large" 35 | "family" = "aurora5.6" 36 | "db_name" = "aurora" 37 | "username" = "aurora" 38 | "password" = "pAssw0rd" 39 | } 40 | } 41 | 42 | variable "domain_config" { 43 | default = { 44 | "domain" = "stg.example.com" 45 | "cf_sub_domain" = "cf" 46 | "elb_sub_domain" = "elb" 47 | "s3_sub_domain" = "img" 48 | "bastion_sub_domain" = "bastion" 49 | } 50 | } 51 | 52 | variable "cf_config" { 53 | default = { 54 | "price_class" = "PriceClass_200" 55 | } 56 | } 57 | 58 | variable "elasticache_config" { 59 | default = { 60 | "cluster_id" = "redis-cluster" 61 | "engine" = "redis" 62 | "engine_version" = "2.8.24" 63 | "node_type" = "cache.t2.micro" 64 | "maintenance_window" = "sun:05:00-sun:06:00" 65 | "family" = "redis2.8" 66 | } 67 | } 68 | 69 | data "aws_availability_zones" "az" {} 70 | 71 | data "aws_ami" "amazon_linux" { 72 | most_recent = true 73 | owners = ["amazon"] 74 | 75 | filter { 76 | name = "architecture" 77 | values = ["x86_64"] 78 | } 79 | 80 | filter { 81 | name = "root-device-type" 82 | values = ["ebs"] 83 | } 84 | 85 | filter { 86 | name = "name" 87 | values = ["amzn-ami-hvm-*"] 88 | } 89 | 90 | filter { 91 | name = "virtualization-type" 92 | values = ["hvm"] 93 | } 94 | 95 | filter { 96 | name = "block-device-mapping.volume-type" 97 | values = ["gp2"] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /key_pair.tf: -------------------------------------------------------------------------------- 1 | resource "aws_key_pair" "key_pair" { 2 | key_name = "${var.name}-key-pair" 3 | public_key = "${file("${path.module}/keys/site_key.pub")}" 4 | } 5 | -------------------------------------------------------------------------------- /keys/site_key.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLcRUtNOKYFynGCZsL01GB8v4EqicTNdh73NqjuJYFlLDCjZdSFmpW0L8UGRE/pa2Qcbv2ndWnU5Cb/b2YN8UdUP1NazBvqNg8FC+yB3iSZg7Jfzp/UuVIPFBdLqMoU2E/LPCKd+cp3OUMUV0VtQreOwWRZjM0Li9hU38LliCwel7/0VPWJnSy28hICRtDUGVyUCTTk7MB3gpVNK6WQvFJptGZfFTfRyOwOuZuIRCy0ty5E208gYW9/4tFWUjCLiwRHFDu82zSHk5W1zDfk/emEARY1eJEVsjzN13vvcY4w7bLYvHhFhodA89VwAsO+46X6ZkfMSBQbJnsrNoWC9t3 knakayama@ip-172-18-0-159.ap-northeast-1.compute.internal 2 | -------------------------------------------------------------------------------- /modules/dns/dns.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route53_zone" "dns" { 2 | name = "${var.domain_config["domain"]}" 3 | } 4 | 5 | resource "aws_route53_record" "bastion" { 6 | zone_id = "${aws_route53_zone.dns.zone_id}" 7 | name = "${var.domain_config["bastion_sub_domain"]}" 8 | type = "A" 9 | ttl = 300 10 | records = ["${var.bastion_public_ip}"] 11 | } 12 | 13 | resource "aws_route53_record" "elb" { 14 | zone_id = "${aws_route53_zone.dns.zone_id}" 15 | name = "${var.domain_config["elb_sub_domain"]}" 16 | type = "A" 17 | 18 | alias { 19 | name = "${var.elb_config["dns_name"]}" 20 | zone_id = "${var.elb_config["zone_id"]}" 21 | evaluate_target_health = false 22 | } 23 | } 24 | 25 | resource "aws_route53_record" "cf" { 26 | zone_id = "${aws_route53_zone.dns.zone_id}" 27 | name = "${var.domain_config["cf_sub_domain"]}" 28 | type = "A" 29 | 30 | alias { 31 | name = "${var.cf_config["domain_name"]}" 32 | zone_id = "${var.cf_config["hosted_zone_id"]}" 33 | evaluate_target_health = false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/dns/variables.tf: -------------------------------------------------------------------------------- 1 | variable "domain_config" { 2 | type = "map" 3 | } 4 | 5 | variable "elb_config" { 6 | type = "map" 7 | } 8 | 9 | variable "cf_config" { 10 | type = "map" 11 | } 12 | 13 | variable "bastion_public_ip" {} 14 | -------------------------------------------------------------------------------- /modules/iam/iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "ec2_role" { 2 | name = "${var.name}-ec2-role" 3 | assume_role_policy = "${file("${path.module}/policies/ec2_assume_role_policy.json")}" 4 | } 5 | 6 | resource "aws_iam_policy_attachment" "s3_full_access" { 7 | name = "S3FullAccess" 8 | roles = ["${aws_iam_role.ec2_role.name}"] 9 | policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess" 10 | } 11 | 12 | resource "aws_iam_instance_profile" "instance_profile" { 13 | name = "${var.name}-instance-profile" 14 | roles = ["${aws_iam_role.ec2_role.name}"] 15 | } 16 | -------------------------------------------------------------------------------- /modules/iam/outputs.tf: -------------------------------------------------------------------------------- 1 | output "instance_profile_id" { 2 | value = "${aws_iam_instance_profile.instance_profile.id}" 3 | } 4 | -------------------------------------------------------------------------------- /modules/iam/policies/ec2_assume_role_policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "ec2.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /modules/iam/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" {} 2 | -------------------------------------------------------------------------------- /network.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "vpc" { 2 | cidr_block = "${var.vpc_cidr}" 3 | enable_dns_support = true 4 | enable_dns_hostnames = true 5 | } 6 | 7 | resource "aws_internet_gateway" "igw" { 8 | vpc_id = "${aws_vpc.vpc.id}" 9 | } 10 | 11 | resource "aws_subnet" "frontend_subnet" { 12 | count = 2 13 | vpc_id = "${aws_vpc.vpc.id}" 14 | cidr_block = "${cidrsubnet(var.vpc_cidr, 8, count.index+1)}" 15 | availability_zone = "${var.azs[count.index]}" 16 | map_public_ip_on_launch = true 17 | 18 | tags { 19 | Name = "${var.name}-frontend-subnet" 20 | } 21 | } 22 | 23 | resource "aws_route_table" "frontend_subnet" { 24 | vpc_id = "${aws_vpc.vpc.id}" 25 | 26 | route { 27 | cidr_block = "0.0.0.0/0" 28 | gateway_id = "${aws_internet_gateway.igw.id}" 29 | } 30 | 31 | tags { 32 | Name = "${var.name}-frontend-subnet" 33 | } 34 | } 35 | 36 | resource "aws_route_table_association" "frontend_subnet" { 37 | count = 2 38 | subnet_id = "${element(aws_subnet.frontend_subnet.*.id, count.index)}" 39 | route_table_id = "${aws_route_table.frontend_subnet.id}" 40 | } 41 | 42 | resource "aws_eip" "nat" { 43 | vpc = true 44 | } 45 | 46 | resource "aws_nat_gateway" "nat" { 47 | allocation_id = "${aws_eip.nat.id}" 48 | subnet_id = "${element(aws_subnet.frontend_subnet.*.id, 0)}" 49 | depends_on = ["aws_internet_gateway.igw"] 50 | } 51 | 52 | resource "aws_subnet" "application_subnet" { 53 | count = 2 54 | vpc_id = "${aws_vpc.vpc.id}" 55 | cidr_block = "${cidrsubnet(var.vpc_cidr, 8, count.index+101)}" 56 | availability_zone = "${var.azs[count.index]}" 57 | map_public_ip_on_launch = false 58 | 59 | tags { 60 | Name = "${var.name}-application-subnet" 61 | } 62 | } 63 | 64 | resource "aws_route_table" "application_subnet" { 65 | vpc_id = "${aws_vpc.vpc.id}" 66 | 67 | route { 68 | cidr_block = "0.0.0.0/0" 69 | nat_gateway_id = "${aws_nat_gateway.nat.id}" 70 | } 71 | 72 | tags { 73 | Name = "${var.name}-application-subnet" 74 | } 75 | } 76 | 77 | resource "aws_vpc_endpoint" "vpc_endpoint" { 78 | vpc_id = "${aws_vpc.vpc.id}" 79 | service_name = "com.amazonaws.ap-northeast-1.s3" 80 | route_table_ids = ["${aws_route_table.application_subnet.*.id}"] 81 | 82 | policy = <