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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +85 -156
  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 +25 -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 +274 -4
  42. data/lib/terra_boi/railtie.rb +5 -2
  43. data/lib/terra_boi/version.rb +1 -1
  44. metadata +60 -32
  45. data/lib/generators/terra_boi/data_generator.rb +0 -38
  46. data/lib/generators/terra_boi/master_worker_generator.rb +0 -54
  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_start_script.erb +0 -12
  52. data/lib/generators/terra_boi/templates/master_worker_user_data.erb +0 -27
  53. data/lib/generators/terra_boi/templates/packer_ami_build.erb +0 -27
  54. data/lib/generators/terra_boi/templates/packer_application.erb +0 -49
  55. data/lib/generators/terra_boi/templates/web_servers_main.erb +0 -31
  56. data/lib/generators/terra_boi/templates/web_servers_output.erb +0 -14
  57. data/lib/generators/terra_boi/templates/web_servers_user_data.erb +0 -29
  58. 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,274 @@
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"
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