terra_boi 0.0.12 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +86 -157
  3. data/lib/generators/extensions.rb +5 -5
  4. data/lib/generators/terra_boi/boilerplate_generator.rb +39 -32
  5. data/lib/generators/terra_boi/dockerfile_generator.rb +19 -18
  6. data/lib/generators/terra_boi/host_initializer_generator.rb +18 -18
  7. data/lib/generators/terra_boi/templates/Dockerfile.erb +4 -23
  8. data/lib/generators/terra_boi/templates/cert/main.tf.erb +32 -0
  9. data/lib/generators/terra_boi/templates/cert/var.tf.erb +15 -0
  10. data/lib/generators/terra_boi/templates/data_storage_config.erb +7 -7
  11. data/lib/generators/terra_boi/templates/ecr/ecs_role.tf.erb +41 -0
  12. data/lib/generators/terra_boi/templates/ecr/main.tf.erb +26 -0
  13. data/lib/generators/terra_boi/templates/ecr/output.tf.erb +11 -0
  14. data/lib/generators/terra_boi/templates/ecr/var.tf.erb +15 -0
  15. data/lib/generators/terra_boi/templates/{data_main.erb → env/data/main.tf.erb} +2 -2
  16. data/lib/generators/terra_boi/templates/{data_output.erb → env/data/output.tf.erb} +1 -1
  17. data/lib/generators/terra_boi/templates/env/ecs_cluster/ecs_cluster.tf.erb +24 -0
  18. data/lib/generators/terra_boi/templates/env/head_worker/ecs.tf.erb +55 -0
  19. data/lib/generators/terra_boi/templates/env/web_app/ecs.tf.erb +59 -0
  20. data/lib/generators/terra_boi/templates/lib/scripts/push_to_ecr.sh.erb +21 -0
  21. data/lib/generators/terra_boi/templates/lib/scripts/update_service_pull_from_ecr.sh.erb +18 -0
  22. data/lib/generators/terra_boi/templates/lib/task_templates/head_worker.json.erb +61 -0
  23. data/lib/generators/terra_boi/templates/lib/task_templates/web_app.json.erb +58 -0
  24. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_cluster/main.tf.erb +12 -0
  25. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_cluster/var.tf.erb +20 -0
  26. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/ecs_role.tf.erb +7 -0
  27. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/load_balancer.tf.erb +92 -0
  28. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/main.tf.erb +134 -0
  29. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/output.tf.erb +11 -0
  30. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_web_app/var.tf.erb +63 -0
  31. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/ecs_role.tf.erb +7 -0
  32. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/main.tf.erb +120 -0
  33. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/output.tf.erb +7 -0
  34. data/lib/generators/terra_boi/templates/lib/terraform_modules/ecs_worker/var.tf.erb +57 -0
  35. data/lib/generators/terra_boi/templates/state_main.erb +1 -1
  36. data/lib/generators/terra_boi/tf_cert_generator.rb +28 -0
  37. data/lib/generators/terra_boi/tf_ecr_generator.rb +28 -0
  38. data/lib/generators/terra_boi/tf_env_generator.rb +54 -0
  39. data/lib/generators/terra_boi/tf_lib_generator.rb +57 -0
  40. data/lib/generators/terra_boi/tf_state_generator.rb +24 -0
  41. data/lib/tasks/terra_boi_tasks.rake +280 -4
  42. data/lib/terra_boi/railtie.rb +5 -2
  43. data/lib/terra_boi/version.rb +1 -1
  44. metadata +60 -31
  45. data/lib/generators/terra_boi/data_generator.rb +0 -38
  46. data/lib/generators/terra_boi/master_worker_generator.rb +0 -47
  47. data/lib/generators/terra_boi/packer_generator.rb +0 -26
  48. data/lib/generators/terra_boi/state_generator.rb +0 -25
  49. data/lib/generators/terra_boi/templates/master_worker_main.erb +0 -26
  50. data/lib/generators/terra_boi/templates/master_worker_output.erb +0 -14
  51. data/lib/generators/terra_boi/templates/master_worker_user_data.erb +0 -33
  52. data/lib/generators/terra_boi/templates/packer_ami_build.erb +0 -27
  53. data/lib/generators/terra_boi/templates/packer_application.erb +0 -49
  54. data/lib/generators/terra_boi/templates/web_servers_main.erb +0 -31
  55. data/lib/generators/terra_boi/templates/web_servers_output.erb +0 -14
  56. data/lib/generators/terra_boi/templates/web_servers_user_data.erb +0 -29
  57. 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,7 @@
1
+ output "ecs_service_name" {
2
+ value = aws_ecs_service.app.name
3
+ }
4
+
5
+ output "ecs_cluster_name" {
6
+ value = data.aws_ecs_cluster.ecs_cluster.cluster_name
7
+ }
@@ -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.23"
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,280 @@
1
- # desc "Explaining what the task does"
2
- # task :terra_boi do
3
- # # Task goes here
4
- # end
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" do |ok, res|
159
+ if !ok
160
+ puts "\nTERRA_BOI | Docker container build and push failed (status = #{res.exitstatus})".cyan.bold
161
+ puts "\nTERRA_BOI | Pruning docker (to create more memory) and retrying...\n".cyan.bold
162
+ sh "docker system prune -a && ./terraform/lib/scripts/push_to_ecr.sh"
163
+ end
164
+ end
165
+ end
166
+
167
+ def apply_web_app_and_worker
168
+ directories = [:ecs_cluster, :web_app, :head_worker]
169
+ ENVS.each do |env|
170
+ puts "\nTERRA_BOI | Building web app and worker ECS infrastructure for #{env}...\n".cyan.bold
171
+ directories.each do |dir_name|
172
+ sh "cd terraform/#{env}/#{dir_name} && terraform init && terraform apply -input=false -auto-approve"
173
+ end
174
+ end
175
+ end
176
+
177
+ def puts_urls_for_alb
178
+ ENVS.each do |env|
179
+ url = `cd terraform/#{env}/web_app && terraform output alb_dns`
180
+ puts "\nTERRA_BOI | Public application load balancer URL for #{env}:".cyan.bold
181
+ puts "#{env} alb_dns: #{url}"
182
+ end
183
+ end
184
+
185
+ def puts_how_to_connect_domain_and_load_balancer
186
+ puts "\nTERRA_BOI | GIDDY UP! TERRA_BOI HAS FINISHED CREATING INFRASTRUCTURE FOR YOUR RAILS APPLICATION!".cyan.bold
187
+
188
+ puts "\nTERRA_BOI | To connect your domain name to your application's new AWS infrastructure:".red
189
+ puts "1) Go to your domain register (e.g. Namecheap)"
190
+ 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"
191
+ puts """E.g. to redirect staging.YOUR_DOMAIN_NAME to the load balancer you just deployed, add the following record to your DNS records:
192
+ TYPE: ALIAS
193
+ HOST: staging
194
+ VALUE: alb_dns output value (something like app-staging-725123955.us-east-2.elb.amazonaws.com)"""
195
+
196
+ puts "\nNote: you can have multiple ALIAS records for different subdomains\n"
197
+ end
198
+
199
+ def puts_twitter_plug
200
+ puts "\nTERRA_BOI | Let me know what you think about terra_boi on Twitter @charlieinthe6!".cyan.bold
201
+ end
202
+
203
+ def conditional_push_container_to_ecr
204
+ print "TERRA_BOI | Question: ".red
205
+ puts "Build and push container updates to ECR first (y/n)?"
206
+ puts "Answer y if you haven't built and pushed most recent updates to ECR yet."
207
+ puts "...and answer y if you aren't sure."
208
+ print "==> ".red
209
+ answer = STDIN.gets.downcase.gsub(/[^yn]/, "")
210
+
211
+ push_container_to_ecr if answer == 'y'
212
+ end
213
+
214
+ # ---------------------------------
215
+ # Helper^2 methods
216
+ # ---------------------------------
217
+
218
+ def print_certificate_validation_instructions
219
+ puts "\nTERRA_BOI | Certificate validation instructions:".red
220
+ puts "1) Log into AWS Console and go to AWS Certificate Manager (default region is us-east-2)"
221
+ puts "2) Expand issued certificate for your domain, and find CNAME record for domain validation"
222
+ 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"
223
+ 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
224
+ end
225
+
226
+ def confirm_certificate_successfully_validated
227
+ answer = ''
228
+ until answer == 'y'
229
+ print "TERRA_BOI | Question 3: ".red
230
+ puts "Has your HTTPS / SSL certificate successfully validated in AWS Console - AWS Certificate Manager (y/n)?"
231
+ print "==> ".red
232
+ answer = STDIN.gets.downcase.gsub(/[^yn]/, "")
233
+
234
+ if answer == 'y'
235
+ next
236
+ else
237
+ print_certificate_validation_instructions
238
+ sleep 5
239
+ end
240
+ end
241
+ end
242
+
243
+ def get_ruby_docker_base_image
244
+ print "TERRA_BOI | Question 1: ".red
245
+ puts "Do you want to use the default ruby Docker base image 2.7.1 (y/n)?"
246
+ print "==> ".red
247
+ answer = STDIN.gets.downcase.gsub(/[^yn]/, "")
248
+
249
+ if answer == 'y'
250
+ ruby_docker_base_image = "2.7.1"
251
+ else
252
+ ruby_docker_base_image = ""
253
+ print "\nTERRA_BOI | Question 1 follow-up: ".red
254
+ puts "Which ruby Docker base image would you like to use (https://hub.docker.com/_/ruby/)?"
255
+ puts "Recommended answer: 2.7.1"
256
+
257
+ until ruby_docker_base_image.length > 0
258
+ print "==> ".red
259
+ ruby_docker_base_image = STDIN.gets.gsub(/[^0-9\.]/, "")
260
+ end
261
+ end
262
+
263
+ puts "\nGreat! Using #{ruby_docker_base_image} as the base Docker image for your infrastructure.".bold
264
+ return ruby_docker_base_image
265
+ end
266
+
267
+ def get_domain_name
268
+ print "\nTERRA_BOI | Question 2: ".red
269
+ puts "What domain name will you be using for your project?"
270
+ puts "E.g. example.com (do not include a subdomain or 'www')"
271
+
272
+ domain_name = ""
273
+ until domain_name.length > 2
274
+ print "==> ".red
275
+ domain_name = STDIN.gets.chomp
276
+ end
277
+
278
+ puts "\nRad! Setting #{domain_name} as your domain_name.".bold
279
+ return domain_name
280
+ end