terra_boi 0.0.13 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +85 -156
- data/lib/generators/extensions.rb +5 -5
- data/lib/generators/terra_boi/boilerplate_generator.rb +39 -32
- data/lib/generators/terra_boi/dockerfile_generator.rb +19 -18
- data/lib/generators/terra_boi/host_initializer_generator.rb +18 -18
- data/lib/generators/terra_boi/templates/Dockerfile.erb +4 -23
- data/lib/generators/terra_boi/templates/cert/main.tf.erb +32 -0
- data/lib/generators/terra_boi/templates/cert/var.tf.erb +15 -0
- data/lib/generators/terra_boi/templates/data_storage_config.erb +7 -7
- data/lib/generators/terra_boi/templates/ecr/ecs_role.tf.erb +41 -0
- data/lib/generators/terra_boi/templates/ecr/main.tf.erb +26 -0
- data/lib/generators/terra_boi/templates/ecr/output.tf.erb +11 -0
- data/lib/generators/terra_boi/templates/ecr/var.tf.erb +15 -0
- data/lib/generators/terra_boi/templates/{data_main.erb → env/data/main.tf.erb} +2 -2
- data/lib/generators/terra_boi/templates/{data_output.erb → env/data/output.tf.erb} +1 -1
- data/lib/generators/terra_boi/templates/env/ecs_cluster/ecs_cluster.tf.erb +24 -0
- data/lib/generators/terra_boi/templates/env/head_worker/ecs.tf.erb +55 -0
- data/lib/generators/terra_boi/templates/env/web_app/ecs.tf.erb +59 -0
- data/lib/generators/terra_boi/templates/lib/scripts/push_to_ecr.sh.erb +25 -0
- data/lib/generators/terra_boi/templates/lib/scripts/update_service_pull_from_ecr.sh.erb +18 -0
- data/lib/generators/terra_boi/templates/lib/task_templates/head_worker.json.erb +61 -0
- data/lib/generators/terra_boi/templates/lib/task_templates/web_app.json.erb +58 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_cluster/main.tf.erb +12 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_cluster/var.tf.erb +20 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/ecs_role.tf.erb +7 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/load_balancer.tf.erb +92 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/main.tf.erb +134 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/output.tf.erb +11 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/var.tf.erb +63 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/ecs_role.tf.erb +7 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/main.tf.erb +120 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/output.tf.erb +7 -0
- data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/var.tf.erb +57 -0
- data/lib/generators/terra_boi/templates/state_main.erb +1 -1
- data/lib/generators/terra_boi/tf_cert_generator.rb +28 -0
- data/lib/generators/terra_boi/tf_ecr_generator.rb +28 -0
- data/lib/generators/terra_boi/tf_env_generator.rb +54 -0
- data/lib/generators/terra_boi/tf_lib_generator.rb +57 -0
- data/lib/generators/terra_boi/tf_state_generator.rb +24 -0
- data/lib/tasks/terra_boi_tasks.rake +274 -4
- data/lib/terra_boi/railtie.rb +5 -2
- data/lib/terra_boi/version.rb +1 -1
- metadata +60 -32
- data/lib/generators/terra_boi/data_generator.rb +0 -38
- data/lib/generators/terra_boi/master_worker_generator.rb +0 -54
- data/lib/generators/terra_boi/packer_generator.rb +0 -26
- data/lib/generators/terra_boi/state_generator.rb +0 -25
- data/lib/generators/terra_boi/templates/master_worker_main.erb +0 -26
- data/lib/generators/terra_boi/templates/master_worker_output.erb +0 -14
- data/lib/generators/terra_boi/templates/master_worker_start_script.erb +0 -12
- data/lib/generators/terra_boi/templates/master_worker_user_data.erb +0 -27
- data/lib/generators/terra_boi/templates/packer_ami_build.erb +0 -27
- data/lib/generators/terra_boi/templates/packer_application.erb +0 -49
- data/lib/generators/terra_boi/templates/web_servers_main.erb +0 -31
- data/lib/generators/terra_boi/templates/web_servers_output.erb +0 -14
- data/lib/generators/terra_boi/templates/web_servers_user_data.erb +0 -29
- data/lib/generators/terra_boi/web_servers_generator.rb +0 -48
@@ -0,0 +1,7 @@
|
|
1
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
2
|
+
# 1. ECS IAM ROLE (Created in web_app)
|
3
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
4
|
+
|
5
|
+
data "aws_iam_role" "ecs_execution_role" {
|
6
|
+
name = "ecs_${var.app_name}_execution_role"
|
7
|
+
}
|
@@ -0,0 +1,120 @@
|
|
1
|
+
provider "aws" {
|
2
|
+
version = "~> 2.0"
|
3
|
+
region = var.region
|
4
|
+
}
|
5
|
+
|
6
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
7
|
+
# 1. ECS TASK
|
8
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
data "aws_ecr_repository" "ecr_repo" {
|
11
|
+
name = "${var.app_name}-ecr-repo"
|
12
|
+
}
|
13
|
+
|
14
|
+
data "aws_ecs_cluster" "ecs_cluster" {
|
15
|
+
cluster_name = "${var.app_name}-${var.environment}"
|
16
|
+
}
|
17
|
+
|
18
|
+
data "terraform_remote_state" "db" {
|
19
|
+
backend = "s3"
|
20
|
+
|
21
|
+
config = {
|
22
|
+
bucket = "${var.app_name}-terraform-state-storage"
|
23
|
+
key = "terraform/${var.environment}-state/terra-boi-data"
|
24
|
+
region = "us-east-2"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
data "template_file" "task" {
|
29
|
+
template = "${file("../../lib/task_templates/${var.template_filename}")}"
|
30
|
+
|
31
|
+
vars = {
|
32
|
+
image = data.aws_ecr_repository.ecr_repo.repository_url
|
33
|
+
app_name = var.app_name
|
34
|
+
app_type = var.app_type
|
35
|
+
environment = var.environment
|
36
|
+
region = var.region
|
37
|
+
memory = var.worker_task.memory
|
38
|
+
cpu = var.worker_task.cpu
|
39
|
+
db_host = data.terraform_remote_state.db.outputs.address
|
40
|
+
db_username = data.terraform_remote_state.db.outputs.db_username
|
41
|
+
db_password = var.db_password
|
42
|
+
aws_access_key = var.aws_access_key
|
43
|
+
aws_secret_key = var.aws_secret_key
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
resource "aws_ecs_task_definition" "web_app" {
|
48
|
+
family = var.app_type # Naming our first task
|
49
|
+
requires_compatibilities = ["FARGATE"] # Stating that we are using ECS Fargate
|
50
|
+
network_mode = "awsvpc" # Using awsvpc as our network mode as this is required for Fargate
|
51
|
+
memory = var.worker_task.memory # Specifying the memory our container requires
|
52
|
+
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html
|
53
|
+
cpu = var.worker_task.cpu # Specifying the CPU our container requires
|
54
|
+
task_role_arn = data.aws_iam_role.ecs_execution_role.arn
|
55
|
+
execution_role_arn = data.aws_iam_role.ecs_execution_role.arn
|
56
|
+
container_definitions = data.template_file.task.rendered
|
57
|
+
}
|
58
|
+
|
59
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
60
|
+
# 2. ECS SERVICE
|
61
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
62
|
+
|
63
|
+
resource "aws_ecs_service" "app" {
|
64
|
+
name = var.app_type # Naming our first service
|
65
|
+
cluster = data.aws_ecs_cluster.ecs_cluster.id # Referencing our created Cluster
|
66
|
+
task_definition = aws_ecs_task_definition.web_app.arn # Referencing the task our service will spin up
|
67
|
+
launch_type = "FARGATE"
|
68
|
+
desired_count = var.worker_task.desired_count
|
69
|
+
|
70
|
+
network_configuration {
|
71
|
+
subnets = [aws_default_subnet.default_subnet_a.id, aws_default_subnet.default_subnet_b.id, aws_default_subnet.default_subnet_c.id]
|
72
|
+
assign_public_ip = true
|
73
|
+
security_groups = [aws_security_group.service_security_group.id]
|
74
|
+
}
|
75
|
+
|
76
|
+
depends_on = [aws_cloudwatch_log_group.logs]
|
77
|
+
}
|
78
|
+
|
79
|
+
resource "aws_security_group" "service_security_group" {
|
80
|
+
egress {
|
81
|
+
from_port = 0
|
82
|
+
to_port = 0
|
83
|
+
protocol = "-1"
|
84
|
+
cidr_blocks = ["0.0.0.0/0"]
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
89
|
+
# 3. REFERENCE NETWORK RESOURCES (to default VPC)
|
90
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
91
|
+
|
92
|
+
# # Providing a reference to our default VPC
|
93
|
+
resource "aws_default_vpc" "default_vpc" {
|
94
|
+
}
|
95
|
+
|
96
|
+
# Providing a reference to our default subnets
|
97
|
+
resource "aws_default_subnet" "default_subnet_a" {
|
98
|
+
availability_zone = "${var.region}a"
|
99
|
+
}
|
100
|
+
|
101
|
+
resource "aws_default_subnet" "default_subnet_b" {
|
102
|
+
availability_zone = "${var.region}b"
|
103
|
+
}
|
104
|
+
|
105
|
+
resource "aws_default_subnet" "default_subnet_c" {
|
106
|
+
availability_zone = "${var.region}c"
|
107
|
+
}
|
108
|
+
|
109
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
110
|
+
# 4. Logging
|
111
|
+
# ---------------------------------------------------------------------------------------------------------------------
|
112
|
+
|
113
|
+
resource "aws_cloudwatch_log_group" "logs" {
|
114
|
+
name = "/fargate/service/${var.app_name}-${var.environment}-${var.app_type}"
|
115
|
+
retention_in_days = 90
|
116
|
+
|
117
|
+
tags = {
|
118
|
+
app = var.app_name
|
119
|
+
}
|
120
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -----------------------------
|
2
|
+
# 1. GENERAL
|
3
|
+
# -----------------------------
|
4
|
+
|
5
|
+
# Optional
|
6
|
+
|
7
|
+
variable "region" {
|
8
|
+
type = string
|
9
|
+
default = "us-east-2"
|
10
|
+
}
|
11
|
+
|
12
|
+
variable "app_name" {
|
13
|
+
type = string
|
14
|
+
default = "<%= application_name %>"
|
15
|
+
}
|
16
|
+
|
17
|
+
variable "environment" {
|
18
|
+
type = string
|
19
|
+
default = "dev"
|
20
|
+
}
|
21
|
+
|
22
|
+
variable "app_type" {
|
23
|
+
type = string
|
24
|
+
default = "web_app"
|
25
|
+
}
|
26
|
+
|
27
|
+
variable "template_filename" {
|
28
|
+
type = string
|
29
|
+
default = "web_app.json"
|
30
|
+
}
|
31
|
+
|
32
|
+
variable "db_password" {
|
33
|
+
description = "The password for the database"
|
34
|
+
type = string
|
35
|
+
}
|
36
|
+
|
37
|
+
variable "aws_access_key" {
|
38
|
+
type = string
|
39
|
+
}
|
40
|
+
|
41
|
+
variable "aws_secret_key" {
|
42
|
+
type = string
|
43
|
+
}
|
44
|
+
|
45
|
+
# -----------------------------
|
46
|
+
# 2. WEB APP TASK
|
47
|
+
# -----------------------------
|
48
|
+
|
49
|
+
variable "worker_task" {
|
50
|
+
type = map
|
51
|
+
|
52
|
+
default = {
|
53
|
+
memory = 512
|
54
|
+
cpu = 256
|
55
|
+
desired_count = 2
|
56
|
+
}
|
57
|
+
}
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# ---------------------------------------------------------------------------------------------------------------------
|
4
4
|
|
5
5
|
module "remote_state_locking" {
|
6
|
-
source = "github.com/charliereese/terraform_modules//state?ref=v0.0.
|
6
|
+
source = "github.com/charliereese/terraform_modules//state?ref=v0.0.25"
|
7
7
|
|
8
8
|
app_name = "<%= application_name %>"
|
9
9
|
region = "us-east-2"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "generators/extensions"
|
2
|
+
|
3
|
+
module TerraBoi
|
4
|
+
class TfCertGenerator < Rails::Generators::Base
|
5
|
+
attr_accessor :application_name, :class_options
|
6
|
+
class_option :domain_name, type: :string, default: 'example.com', aliases: ["d"]
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
8
|
+
|
9
|
+
desc (<<-EOF
|
10
|
+
Generate HTTPS cert for domain
|
11
|
+
|
12
|
+
To execute, run rails generate terra_boi:tf_cert
|
13
|
+
EOF
|
14
|
+
.gsub(/\t/, '')
|
15
|
+
)
|
16
|
+
|
17
|
+
def init
|
18
|
+
# defined in lib/generators/extensions
|
19
|
+
self.application_name = generate_application_name
|
20
|
+
self.class_options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_cert
|
24
|
+
template "cert/main.tf.erb", "terraform/cert/main.tf"
|
25
|
+
template "cert/var.tf.erb", "terraform/cert/var.tf"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "generators/extensions"
|
2
|
+
|
3
|
+
module TerraBoi
|
4
|
+
class TfEcrGenerator < Rails::Generators::Base
|
5
|
+
attr_accessor :application_name
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
|
8
|
+
desc (<<-EOF
|
9
|
+
Generate AWS Elastic Container Registry (for storing container images)
|
10
|
+
|
11
|
+
To execute, run rails generate terra_boi:tf_ecr
|
12
|
+
EOF
|
13
|
+
.gsub(/\t/, '')
|
14
|
+
)
|
15
|
+
|
16
|
+
def init
|
17
|
+
# defined in lib/generators/extensions
|
18
|
+
self.application_name = generate_application_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_ecr
|
22
|
+
template "ecr/ecs_role.tf.erb", "terraform/ecr/ecs_role.tf"
|
23
|
+
template "ecr/main.tf.erb", "terraform/ecr/main.tf"
|
24
|
+
template "ecr/output.tf.erb", "terraform/ecr/output.tf"
|
25
|
+
template "ecr/var.tf.erb", "terraform/ecr/var.tf"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "generators/extensions"
|
2
|
+
|
3
|
+
module TerraBoi
|
4
|
+
class TfEnvGenerator < Rails::Generators::Base
|
5
|
+
attr_accessor :application_name, :class_options
|
6
|
+
class_option :envs, type: :array, default: ['staging', 'prod'], aliases: ["e"]
|
7
|
+
class_option :domain_name, type: :string, default: 'example.com', aliases: ["d"]
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
9
|
+
|
10
|
+
TEMPLATES = {
|
11
|
+
ecs_cluster: [
|
12
|
+
"ecs_cluster.tf",
|
13
|
+
],
|
14
|
+
head_worker: [
|
15
|
+
"ecs.tf",
|
16
|
+
],
|
17
|
+
web_app: [
|
18
|
+
"ecs.tf",
|
19
|
+
],
|
20
|
+
data: [
|
21
|
+
"main.tf",
|
22
|
+
"output.tf",
|
23
|
+
]
|
24
|
+
}
|
25
|
+
|
26
|
+
desc (<<-EOF
|
27
|
+
Generate ecs directory (with cluster, web app service and head worker service) for each env
|
28
|
+
|
29
|
+
To execute, run rails generate terra_boi:tf_ecs
|
30
|
+
|
31
|
+
Note: use -e or --env flag to specify list of infrastructure environments. Defaults to staging and prod
|
32
|
+
EOF
|
33
|
+
.gsub(/\t/, '')
|
34
|
+
)
|
35
|
+
|
36
|
+
def init
|
37
|
+
# defined in lib/generators/extensions
|
38
|
+
self.application_name = generate_application_name
|
39
|
+
self.class_options = options
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_ecs
|
43
|
+
class_options[:envs].each do |env|
|
44
|
+
TEMPLATES.each do |dir, file_arr|
|
45
|
+
file_arr.each do |filename|
|
46
|
+
template "env/#{dir}/#{filename}.erb", "terraform/#{env}/#{dir}/#{filename}", {
|
47
|
+
env: env,
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "generators/extensions"
|
2
|
+
|
3
|
+
module TerraBoi
|
4
|
+
class TfLibGenerator < Rails::Generators::Base
|
5
|
+
attr_accessor :application_name, :class_options
|
6
|
+
class_option :domain_name, type: :string, default: 'example.com', aliases: ["d"]
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
8
|
+
|
9
|
+
TEMPLATES = {
|
10
|
+
scripts: [
|
11
|
+
"push_to_ecr.sh",
|
12
|
+
"update_service_pull_from_ecr.sh",
|
13
|
+
],
|
14
|
+
task_templates: [
|
15
|
+
"web_app.json",
|
16
|
+
"head_worker.json",
|
17
|
+
],
|
18
|
+
terraform_modules: [
|
19
|
+
"ecs_cluster/main.tf",
|
20
|
+
"ecs_cluster/var.tf",
|
21
|
+
|
22
|
+
"ecs_web_app/ecs_role.tf",
|
23
|
+
"ecs_web_app/load_balancer.tf",
|
24
|
+
"ecs_web_app/main.tf",
|
25
|
+
"ecs_web_app/output.tf",
|
26
|
+
"ecs_web_app/var.tf",
|
27
|
+
|
28
|
+
"ecs_worker/ecs_role.tf",
|
29
|
+
"ecs_worker/main.tf",
|
30
|
+
"ecs_worker/output.tf",
|
31
|
+
"ecs_worker/var.tf",
|
32
|
+
]
|
33
|
+
}
|
34
|
+
|
35
|
+
desc (<<-EOF
|
36
|
+
Generate lib directory with templates, scripts, and modules for deploying application containers to AWS ECS
|
37
|
+
|
38
|
+
To execute, run rails generate terra_boi:tf_lib
|
39
|
+
EOF
|
40
|
+
.gsub(/\t/, '')
|
41
|
+
)
|
42
|
+
|
43
|
+
def init
|
44
|
+
# defined in lib/generators/extensions
|
45
|
+
self.application_name = generate_application_name
|
46
|
+
self.class_options = options
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_ecr
|
50
|
+
TEMPLATES.each do |dir, file_arr|
|
51
|
+
file_arr.each do |filename|
|
52
|
+
template "lib/#{dir}/#{filename}.erb", "terraform/lib/#{dir}/#{filename}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "generators/extensions"
|
2
|
+
|
3
|
+
module TerraBoi
|
4
|
+
class TfStateGenerator < Rails::Generators::Base
|
5
|
+
attr_accessor :application_name
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
|
8
|
+
desc (<<-EOF
|
9
|
+
Generate DB and S3 bucket for storing and locking terraform state
|
10
|
+
|
11
|
+
To execute, run rails generate terra_boi:tf_state
|
12
|
+
EOF
|
13
|
+
.gsub(/\t/, '')
|
14
|
+
)
|
15
|
+
|
16
|
+
def init
|
17
|
+
self.application_name = generate_application_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_main_terraform_file
|
21
|
+
template "state_main.erb", "terraform/state/main.tf"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,4 +1,274 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
namespace :terra_boi do
|
4
|
+
desc """
|
5
|
+
Generate terraform (AWS) infrastructure code for your rails application.
|
6
|
+
|
7
|
+
Creates infrastructure code for RDS DBs, ALBs, ECRs, ECS clusters, services,
|
8
|
+
and tasks, and various security groups and other resources for each
|
9
|
+
deployment environment (e.g. staging and prod)
|
10
|
+
|
11
|
+
Usage without ENVS arg (creates staging and prod envs by default):
|
12
|
+
rake \"terra_boi:generate_infra\"
|
13
|
+
|
14
|
+
Usage with ENVS arg:
|
15
|
+
rake \"terra_boi:generate_infra[ENV1 ENV2 ENVn]\"
|
16
|
+
e.g. rake \"terra_boi:generate_infra[dev staging prod]\"
|
17
|
+
|
18
|
+
Takes one optional arg:
|
19
|
+
arg 1: envs (default value: staging prod)
|
20
|
+
"""
|
21
|
+
task :generate_infra, [:envs] => [:environment] do |_, args|
|
22
|
+
ENVS = get_envs(args)
|
23
|
+
|
24
|
+
create_boilerplate_files
|
25
|
+
# apply_terraform_state
|
26
|
+
# apply_terraform_cert
|
27
|
+
# apply_ecr
|
28
|
+
# apply_data
|
29
|
+
# push_container_to_ecr
|
30
|
+
# apply_web_app_and_worker
|
31
|
+
# puts_urls_for_alb
|
32
|
+
# puts_how_to_connect_domain_and_load_balancer
|
33
|
+
# puts_twitter_plug
|
34
|
+
end
|
35
|
+
|
36
|
+
desc """
|
37
|
+
Destroy terraform (AWS) infrastructure code for your rails application.
|
38
|
+
|
39
|
+
Usage without ENVS arg (creates staging and prod envs by default):
|
40
|
+
rake \"terra_boi:destroy_infra\"
|
41
|
+
|
42
|
+
Usage with ENVS arg:
|
43
|
+
rake \"terra_boi:destroy_infra[ENV1 ENV2 ENVn]\"
|
44
|
+
e.g. rake \"terra_boi:destroy_infra[dev staging prod]\"
|
45
|
+
|
46
|
+
Takes one optional arg:
|
47
|
+
arg 1: envs (default value: staging prod)
|
48
|
+
"""
|
49
|
+
task :destroy_infra, [:envs] => [:environment] do |_, args|
|
50
|
+
ENVS = get_envs(args)
|
51
|
+
|
52
|
+
ENVS.each do |env|
|
53
|
+
puts "\nDestroying application infrastructure for #{env}...\n".cyan.bold
|
54
|
+
|
55
|
+
directories = [:head_worker, :web_app, :ecs_cluster, :data]
|
56
|
+
directories.each do |dir_name|
|
57
|
+
sh "cd terraform/#{env}/#{dir_name} && terraform destroy"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sh "cd terraform/ecr && terraform destroy"
|
62
|
+
sh "cd terraform/cert && terraform destroy"
|
63
|
+
sh "cd terraform/state && terraform destroy"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
desc """
|
68
|
+
Deploy your rails application.
|
69
|
+
|
70
|
+
By default, deploys to staging and prod.
|
71
|
+
|
72
|
+
Usage without ENVS arg (deploys to staging and prod envs by default):
|
73
|
+
rake \"terra_boi:deploy\"
|
74
|
+
|
75
|
+
Usage with ENVS arg:
|
76
|
+
rake \"terra_boi:deploy[ENV1 ENV2 ENVn]\"
|
77
|
+
e.g. rake \"terra_boi:deploy[staging prod]\"
|
78
|
+
|
79
|
+
Takes one arg:
|
80
|
+
arg 1: envs (default value: staging prod)
|
81
|
+
"""
|
82
|
+
task :deploy, [:envs] => [:environment] do |task, args|
|
83
|
+
ENVS = get_envs(args)
|
84
|
+
puts "\nDeploying rails application to #{ENVS.to_sentence} infrastructure\n".cyan.bold
|
85
|
+
|
86
|
+
conditional_push_container_to_ecr
|
87
|
+
|
88
|
+
ENVS.each do |env|
|
89
|
+
ecs_tasks = [:web_app, :head_worker]
|
90
|
+
ecs_tasks.each do |task_name|
|
91
|
+
puts "\nDeploying #{env} #{task_name} task\n".cyan.bold
|
92
|
+
sh "./terraform/lib/scripts/update_service_pull_from_ecr.sh #{env} #{task_name}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# ---------------------------------
|
98
|
+
# Helper methods
|
99
|
+
# ---------------------------------
|
100
|
+
|
101
|
+
def get_envs(args)
|
102
|
+
if args[:envs]
|
103
|
+
args[:envs].split(' ')
|
104
|
+
else
|
105
|
+
[:staging, :prod]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_boilerplate_files
|
110
|
+
config = {}
|
111
|
+
|
112
|
+
puts "\nTERRA_BOI | Generating boilerplate infrastructure as code with terra_boi for your rails project...\n".cyan.bold
|
113
|
+
sleep 1
|
114
|
+
config[:ruby_version] = get_ruby_docker_base_image
|
115
|
+
sleep 1
|
116
|
+
config[:domain_name] = get_domain_name
|
117
|
+
sleep 1
|
118
|
+
|
119
|
+
puts "\nTERRA_BOI | Generating infrastructure code using the configuration you provided...\n".cyan.bold
|
120
|
+
sleep 1
|
121
|
+
|
122
|
+
sh "rails g terra_boi:boilerplate -d #{config[:domain_name]} -r #{config[:ruby_version]}"
|
123
|
+
|
124
|
+
sleep 1
|
125
|
+
puts "\nTERRA_BOI | Marking deployment scripts executable...\n".bold
|
126
|
+
sh "chmod +x ./terraform/lib/scripts/*"
|
127
|
+
end
|
128
|
+
|
129
|
+
def apply_terraform_state
|
130
|
+
puts "\nTERRA_BOI | Creating terraform state...\n".cyan.bold
|
131
|
+
sh "cd terraform/state && terraform init && terraform apply -input=false -auto-approve"
|
132
|
+
end
|
133
|
+
|
134
|
+
def apply_terraform_cert
|
135
|
+
puts "\nTERRA_BOI | Creating HTTPS certificate in AWS Certificate Manager...\n".cyan.bold
|
136
|
+
sh "cd terraform/cert && terraform init"
|
137
|
+
|
138
|
+
print_certificate_validation_instructions
|
139
|
+
sleep 2
|
140
|
+
sh "cd terraform/cert && terraform apply -input=false -auto-approve"
|
141
|
+
confirm_certificate_successfully_validated
|
142
|
+
end
|
143
|
+
|
144
|
+
def apply_data
|
145
|
+
ENVS.each do |env|
|
146
|
+
puts "\nTERRA_BOI | Creating RDS DB instance and S3 bucket for #{env}...\n".cyan.bold
|
147
|
+
sh "cd terraform/#{env}/data && terraform init && terraform apply -input=false -auto-approve"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def apply_ecr
|
152
|
+
puts "\nTERRA_BOI | Creating AWS ECR (Elastic Container Registry) for your application's docker images...\n".cyan.bold
|
153
|
+
sh "cd terraform/ecr && terraform init && terraform apply -input=false -auto-approve"
|
154
|
+
end
|
155
|
+
|
156
|
+
def push_container_to_ecr
|
157
|
+
puts "\nTERRA_BOI | Building application docker container then pushing to ECR...\n".cyan.bold
|
158
|
+
sh "./terraform/lib/scripts/push_to_ecr.sh"
|
159
|
+
end
|
160
|
+
|
161
|
+
def apply_web_app_and_worker
|
162
|
+
directories = [:ecs_cluster, :web_app, :head_worker]
|
163
|
+
ENVS.each do |env|
|
164
|
+
puts "\nTERRA_BOI | Building web app and worker ECS infrastructure for #{env}...\n".cyan.bold
|
165
|
+
directories.each do |dir_name|
|
166
|
+
sh "cd terraform/#{env}/#{dir_name} && terraform init && terraform apply -input=false -auto-approve"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def puts_urls_for_alb
|
172
|
+
ENVS.each do |env|
|
173
|
+
url = `cd terraform/#{env}/web_app && terraform output alb_dns`
|
174
|
+
puts "\nTERRA_BOI | Public application load balancer URL for #{env}:".cyan.bold
|
175
|
+
puts "#{env} alb_dns: #{url}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def puts_how_to_connect_domain_and_load_balancer
|
180
|
+
puts "\nTERRA_BOI | GIDDY UP! TERRA_BOI HAS FINISHED CREATING INFRASTRUCTURE FOR YOUR RAILS APPLICATION!".cyan.bold
|
181
|
+
|
182
|
+
puts "\nTERRA_BOI | To connect your domain name to your application's new AWS infrastructure:".red
|
183
|
+
puts "1) Go to your domain register (e.g. Namecheap)"
|
184
|
+
puts "2) Add the alb_dns output value (i.e. the public URL for your load balancer) to your domain's DNS records. alb_dns public URL values are output above"
|
185
|
+
puts """E.g. to redirect staging.YOUR_DOMAIN_NAME to the load balancer you just deployed, add the following record to your DNS records:
|
186
|
+
TYPE: ALIAS
|
187
|
+
HOST: staging
|
188
|
+
VALUE: alb_dns output value (something like app-staging-725123955.us-east-2.elb.amazonaws.com)"""
|
189
|
+
|
190
|
+
puts "\nNote: you can have multiple ALIAS records for different subdomains\n"
|
191
|
+
end
|
192
|
+
|
193
|
+
def puts_twitter_plug
|
194
|
+
puts "\nTERRA_BOI | Let me know what you think about terra_boi on Twitter @charlieinthe6!".cyan.bold
|
195
|
+
end
|
196
|
+
|
197
|
+
def conditional_push_container_to_ecr
|
198
|
+
print "TERRA_BOI | Question: ".red
|
199
|
+
puts "Build and push container updates to ECR first (y/n)?"
|
200
|
+
puts "Answer y if you haven't built and pushed most recent updates to ECR yet."
|
201
|
+
puts "...and answer y if you aren't sure."
|
202
|
+
print "==> ".red
|
203
|
+
answer = STDIN.gets.downcase.gsub(/[^yn]/, "")
|
204
|
+
|
205
|
+
push_container_to_ecr if answer == 'y'
|
206
|
+
end
|
207
|
+
|
208
|
+
# ---------------------------------
|
209
|
+
# Helper^2 methods
|
210
|
+
# ---------------------------------
|
211
|
+
|
212
|
+
def print_certificate_validation_instructions
|
213
|
+
puts "\nTERRA_BOI | Certificate validation instructions:".red
|
214
|
+
puts "1) Log into AWS Console and go to AWS Certificate Manager (default region is us-east-2)"
|
215
|
+
puts "2) Expand issued certificate for your domain, and find CNAME record for domain validation"
|
216
|
+
puts "3) Add CNAME record listed for your domain to your domain's DNS records (i.e. log into where you purchased your domain and add CNAME record to it)\n\n"
|
217
|
+
puts "NOTE: the Host field for your CNAME record will be something like _123f2cc99f15298ff717ac26dd6993. The Value field for your CNAME record will be something like _bf123a01234a134123412341324.dasfjkhasd.acm-validation.aws.\n\n".bold
|
218
|
+
end
|
219
|
+
|
220
|
+
def confirm_certificate_successfully_validated
|
221
|
+
answer = ''
|
222
|
+
until answer == 'y'
|
223
|
+
print "TERRA_BOI | Question 3: ".red
|
224
|
+
puts "Has your HTTPS / SSL certificate successfully validated in AWS Console - AWS Certificate Manager (y/n)?"
|
225
|
+
print "==> ".red
|
226
|
+
answer = STDIN.gets.downcase.gsub(/[^yn]/, "")
|
227
|
+
|
228
|
+
if answer == 'y'
|
229
|
+
next
|
230
|
+
else
|
231
|
+
print_certificate_validation_instructions
|
232
|
+
sleep 5
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def get_ruby_docker_base_image
|
238
|
+
print "TERRA_BOI | Question 1: ".red
|
239
|
+
puts "Do you want to use the default ruby Docker base image 2.7.1 (y/n)?"
|
240
|
+
print "==> ".red
|
241
|
+
answer = STDIN.gets.downcase.gsub(/[^yn]/, "")
|
242
|
+
|
243
|
+
if answer == 'y'
|
244
|
+
ruby_docker_base_image = "2.7.1"
|
245
|
+
else
|
246
|
+
ruby_docker_base_image = ""
|
247
|
+
print "\nTERRA_BOI | Question 1 follow-up: ".red
|
248
|
+
puts "Which ruby Docker base image would you like to use (https://hub.docker.com/_/ruby/)?"
|
249
|
+
puts "Recommended answer: 2.7.1"
|
250
|
+
|
251
|
+
until ruby_docker_base_image.length > 0
|
252
|
+
print "==> ".red
|
253
|
+
ruby_docker_base_image = STDIN.gets.gsub(/[^0-9\.]/, "")
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
puts "\nGreat! Using #{ruby_docker_base_image} as the base Docker image for your infrastructure.".bold
|
258
|
+
return ruby_docker_base_image
|
259
|
+
end
|
260
|
+
|
261
|
+
def get_domain_name
|
262
|
+
print "\nTERRA_BOI | Question 2: ".red
|
263
|
+
puts "What domain name will you be using for your project?"
|
264
|
+
puts "E.g. example.com (do not include a subdomain or 'www')"
|
265
|
+
|
266
|
+
domain_name = ""
|
267
|
+
until domain_name.length > 2
|
268
|
+
print "==> ".red
|
269
|
+
domain_name = STDIN.gets.chomp
|
270
|
+
end
|
271
|
+
|
272
|
+
puts "\nRad! Setting #{domain_name} as your domain_name.".bold
|
273
|
+
return domain_name
|
274
|
+
end
|