rails_template_18f 1.3.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +64 -61
- data/README.md +11 -16
- data/lib/generators/rails_template18f/active_storage/active_storage_generator.rb +7 -6
- data/lib/generators/rails_template18f/circleci/circleci_generator.rb +28 -27
- data/lib/generators/rails_template18f/circleci/templates/Dockerfile.ci.tt +0 -1
- data/lib/generators/rails_template18f/circleci/templates/circleci/config.yml.tt +152 -163
- data/lib/generators/rails_template18f/github_actions/github_actions_generator.rb +31 -30
- data/lib/generators/rails_template18f/github_actions/templates/github/actions/compile-assets/action.yml +50 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/actions/setup-project/action.yml.tt +4 -8
- data/lib/generators/rails_template18f/github_actions/templates/github/dependabot.yml.tt +2 -4
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml +72 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml +72 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-daily-scan.yml.tt +10 -1
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-scan.yml.tt +1 -1
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/pa11y.yml.tt +2 -2
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-production.yml +46 -3
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-staging.yml +46 -3
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/validate-ssp.yml +2 -2
- data/lib/generators/rails_template18f/i18n_js/i18n_js_generator.rb +2 -2
- data/lib/generators/rails_template18f/i18n_js/templates/app/javascript/{i18n.js → i18n/index.js} +1 -1
- data/lib/generators/rails_template18f/i18n_js/templates/config/i18n-js.yml +1 -1
- data/lib/generators/rails_template18f/newrelic/newrelic_generator.rb +4 -2
- data/lib/generators/rails_template18f/public_egress/public_egress_generator.rb +86 -54
- data/lib/generators/rails_template18f/sidekiq/sidekiq_generator.rb +8 -17
- data/lib/generators/rails_template18f/terraform/templates/full_bootstrap/imports.tf.tftpl +25 -0
- data/lib/generators/rails_template18f/terraform/templates/full_bootstrap/main.tf.tt +159 -0
- data/lib/generators/rails_template18f/terraform/templates/sandbox_bootstrap/imports.tf.tftpl +10 -0
- data/lib/generators/rails_template18f/terraform/templates/sandbox_bootstrap/main.tf.tt +117 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/README.md.tt +77 -93
- data/lib/generators/rails_template18f/terraform/templates/terraform/app.tf.tt +63 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/apply.sh +15 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/templates/backend_config.tftpl +8 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/templates/bot_secrets.tftpl +5 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/users.auto.tfvars +5 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/main.tf.tt +106 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/production.tfvars.tt +10 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/providers.tf.tt +32 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/sandbox_bot/main.tf +74 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/sandbox_bot/run.sh +17 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/staging.tfvars.tt +8 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/terraform.sh.tt +135 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/variables.tf.tt +99 -0
- data/lib/generators/rails_template18f/terraform/terraform_generator.rb +13 -4
- data/lib/rails_template18f/generators/cloud_gov_options.rb +9 -6
- data/lib/rails_template18f/generators/cloud_gov_parsing.rb +7 -7
- data/lib/rails_template18f/generators.rb +0 -1
- data/lib/rails_template18f/version.rb +1 -1
- data/rails-template-18f.gemspec +4 -4
- data/railsrc +4 -2
- data/railsrc-hotwire +4 -2
- data/template.rb +61 -71
- data/templates/README.md.tt +21 -8
- data/templates/app/assets/stylesheets/uswds-settings.scss +3 -2
- data/templates/app/views/application/_header.html.erb +1 -1
- data/templates/app/views/application/_usa_banner.html.erb +3 -3
- data/templates/bin/ops/create_service_account.sh.tt +20 -11
- data/templates/bin/ops/destroy_service_account.sh.tt +3 -3
- data/templates/browserslistrc +1 -2
- data/templates/doc/compliance/TODO.md +1 -4
- metadata +30 -29
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml.tt +0 -50
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml.tt +0 -50
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/import.sh +0 -13
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/main.tf.tt +0 -22
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/providers.tf +0 -16
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/run.sh.tt +0 -40
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/teardown_creds.sh.tt +0 -5
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/variables.tf +0 -2
- data/lib/generators/rails_template18f/terraform/templates/terraform/production/main.tf.tt +0 -90
- data/lib/generators/rails_template18f/terraform/templates/terraform/production/providers.tf +0 -23
- data/lib/generators/rails_template18f/terraform/templates/terraform/production/variables.tf +0 -2
- data/lib/generators/rails_template18f/terraform/templates/terraform/staging/main.tf.tt +0 -70
- data/lib/generators/rails_template18f/terraform/templates/terraform/staging/providers.tf +0 -23
- data/lib/generators/rails_template18f/terraform/templates/terraform/staging/variables.tf +0 -2
- data/lib/rails_template18f/generators/pipeline_options.rb +0 -18
- data/templates/app/assets/images/uswds.js +0 -6
- data/templates/config/deployment/production.yml +0 -3
- data/templates/config/deployment/staging.yml +0 -3
- data/templates/manifest.yml.tt +0 -17
@@ -0,0 +1,159 @@
|
|
1
|
+
terraform {
|
2
|
+
required_version = "~> 1.10"
|
3
|
+
required_providers {
|
4
|
+
cloudfoundry = {
|
5
|
+
source = "cloudfoundry/cloudfoundry"
|
6
|
+
version = "1.2.0"
|
7
|
+
}
|
8
|
+
}
|
9
|
+
}
|
10
|
+
# empty config will let terraform borrow cf-cli's auth
|
11
|
+
provider "cloudfoundry" {}
|
12
|
+
|
13
|
+
variable "terraform_users" {
|
14
|
+
type = set(string)
|
15
|
+
description = "The list of developer emails and service account usernames who should be granted access to retrieve state bucket credentials"
|
16
|
+
|
17
|
+
validation {
|
18
|
+
condition = length(var.terraform_users) > 0
|
19
|
+
error_message = "terraform_users must include at least the current user calling apply.sh"
|
20
|
+
}
|
21
|
+
}
|
22
|
+
variable "mgmt_space_name" {
|
23
|
+
type = string
|
24
|
+
default = "<%= cloud_gov_production_space %>-mgmt"
|
25
|
+
description = "The name of the mgmt space"
|
26
|
+
}
|
27
|
+
variable "create_bot_secrets_file" {
|
28
|
+
type = bool
|
29
|
+
default = false
|
30
|
+
description = "Flag whether to create secrets.cicd.tfvars file"
|
31
|
+
}
|
32
|
+
|
33
|
+
locals {
|
34
|
+
org_name = "<%= cloud_gov_organization %>"
|
35
|
+
# s3_plan_name should be basic when holding production data, though basic-sandbox will make early iterations easier
|
36
|
+
s3_plan_name = "basic"
|
37
|
+
}
|
38
|
+
module "mgmt_space" {
|
39
|
+
source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v2.1.0"
|
40
|
+
|
41
|
+
cf_org_name = local.org_name
|
42
|
+
cf_space_name = var.mgmt_space_name
|
43
|
+
developers = var.terraform_users
|
44
|
+
}
|
45
|
+
|
46
|
+
module "s3" {
|
47
|
+
source = "github.com/gsa-tts/terraform-cloudgov//s3?ref=v2.1.0"
|
48
|
+
|
49
|
+
cf_space_id = module.mgmt_space.space_id
|
50
|
+
name = "<%= app_name %>-terraform-state"
|
51
|
+
s3_plan_name = local.s3_plan_name
|
52
|
+
depends_on = [module.mgmt_space]
|
53
|
+
}
|
54
|
+
|
55
|
+
data "cloudfoundry_service_plans" "cg_service_account" {
|
56
|
+
name = "space-deployer"
|
57
|
+
service_offering_name = "cloud-gov-service-account"
|
58
|
+
}
|
59
|
+
locals {
|
60
|
+
sa_service_name = "<%= app_name %>-cicd-deployer"
|
61
|
+
sa_key_name = "cicd-deployer-access-key"
|
62
|
+
sa_bot_credentials = jsondecode(data.cloudfoundry_service_credential_binding.runner_sa_key.credential_bindings.0.credential_binding).credentials
|
63
|
+
sa_cf_username = nonsensitive(local.sa_bot_credentials.username)
|
64
|
+
sa_cf_password = local.sa_bot_credentials.password
|
65
|
+
}
|
66
|
+
resource "cloudfoundry_service_instance" "runner_service_account" {
|
67
|
+
name = local.sa_service_name
|
68
|
+
type = "managed"
|
69
|
+
space = module.mgmt_space.space_id
|
70
|
+
service_plan = data.cloudfoundry_service_plans.cg_service_account.service_plans.0.id
|
71
|
+
depends_on = [module.mgmt_space]
|
72
|
+
}
|
73
|
+
resource "cloudfoundry_service_credential_binding" "runner_sa_key" {
|
74
|
+
name = local.sa_key_name
|
75
|
+
service_instance = cloudfoundry_service_instance.runner_service_account.id
|
76
|
+
type = "key"
|
77
|
+
}
|
78
|
+
data "cloudfoundry_service_credential_binding" "runner_sa_key" {
|
79
|
+
name = local.sa_key_name
|
80
|
+
service_instance = cloudfoundry_service_instance.runner_service_account.id
|
81
|
+
depends_on = [cloudfoundry_service_credential_binding.runner_sa_key]
|
82
|
+
}
|
83
|
+
data "cloudfoundry_org" "org" {
|
84
|
+
name = local.org_name
|
85
|
+
}
|
86
|
+
data "cloudfoundry_user" "sa_user" {
|
87
|
+
name = local.sa_cf_username
|
88
|
+
}
|
89
|
+
resource "cloudfoundry_org_role" "sa_org_manager" {
|
90
|
+
user = data.cloudfoundry_user.sa_user.users.0.id
|
91
|
+
type = "organization_manager"
|
92
|
+
org = data.cloudfoundry_org.org.id
|
93
|
+
}
|
94
|
+
|
95
|
+
locals {
|
96
|
+
bucket_creds_key_name = "backend-state-bucket-creds"
|
97
|
+
}
|
98
|
+
resource "cloudfoundry_service_credential_binding" "bucket_creds" {
|
99
|
+
name = local.bucket_creds_key_name
|
100
|
+
service_instance = module.s3.bucket_id
|
101
|
+
type = "key"
|
102
|
+
}
|
103
|
+
data "cloudfoundry_service_credential_binding" "bucket_creds" {
|
104
|
+
name = local.bucket_creds_key_name
|
105
|
+
service_instance = module.s3.bucket_id
|
106
|
+
depends_on = [cloudfoundry_service_credential_binding.bucket_creds]
|
107
|
+
}
|
108
|
+
|
109
|
+
locals {
|
110
|
+
import_map = {
|
111
|
+
"module.mgmt_space.cloudfoundry_space.space" = module.mgmt_space.space_id
|
112
|
+
"module.s3.cloudfoundry_service_instance.bucket" = module.s3.bucket_id
|
113
|
+
"cloudfoundry_service_credential_binding.bucket_creds" = cloudfoundry_service_credential_binding.bucket_creds.id
|
114
|
+
"cloudfoundry_service_instance.runner_service_account" = cloudfoundry_service_instance.runner_service_account.id
|
115
|
+
"cloudfoundry_service_credential_binding.runner_sa_key" = cloudfoundry_service_credential_binding.runner_sa_key.id
|
116
|
+
"cloudfoundry_org_role.sa_org_manager" = cloudfoundry_org_role.sa_org_manager.id
|
117
|
+
}
|
118
|
+
|
119
|
+
recreate_state_template = templatefile("${path.module}/templates/imports.tf.tftpl", {
|
120
|
+
import_map = local.import_map,
|
121
|
+
developer_map = { for username, id in module.mgmt_space.developer_role_ids : username => id },
|
122
|
+
manager_map = { for username, id in module.mgmt_space.manager_role_ids : username => id }
|
123
|
+
})
|
124
|
+
}
|
125
|
+
resource "local_file" "recreate_script" {
|
126
|
+
content = local.recreate_state_template
|
127
|
+
filename = "${path.module}/imports.tf"
|
128
|
+
file_permission = "0644"
|
129
|
+
}
|
130
|
+
|
131
|
+
locals {
|
132
|
+
bucket_creds = jsondecode(data.cloudfoundry_service_credential_binding.bucket_creds.credential_bindings.0.credential_binding).credentials
|
133
|
+
backend_config = templatefile("${path.module}/templates/backend_config.tftpl", { creds = local.bucket_creds })
|
134
|
+
}
|
135
|
+
resource "local_sensitive_file" "bucket_creds" {
|
136
|
+
content = local.backend_config
|
137
|
+
filename = "${path.module}/../secrets.backend.tfvars"
|
138
|
+
file_permission = "0600"
|
139
|
+
}
|
140
|
+
|
141
|
+
resource "local_sensitive_file" "bot_secrets_file" {
|
142
|
+
count = (var.create_bot_secrets_file ? 1 : 0)
|
143
|
+
filename = "${path.module}/../secrets.cicd.tfvars"
|
144
|
+
file_permission = "0600"
|
145
|
+
|
146
|
+
content = templatefile("${path.module}/templates/bot_secrets.tftpl", {
|
147
|
+
service_name = local.sa_service_name,
|
148
|
+
key_name = local.sa_key_name,
|
149
|
+
username = local.sa_cf_username,
|
150
|
+
password = local.sa_cf_password
|
151
|
+
})
|
152
|
+
}
|
153
|
+
|
154
|
+
output "mgmt_space_id" {
|
155
|
+
value = module.mgmt_space.space_id
|
156
|
+
}
|
157
|
+
output "mgmt_org_id" {
|
158
|
+
value = data.cloudfoundry_org.org.id
|
159
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# This file takes care of importing bootstrap
|
2
|
+
# resources onto a new developer's machine if needed
|
3
|
+
# import happens automatically on a normal ./apply.sh run
|
4
|
+
|
5
|
+
%{ for resource_name, id in import_map ~}
|
6
|
+
import {
|
7
|
+
to = ${resource_name}
|
8
|
+
id = "${id}"
|
9
|
+
}
|
10
|
+
%{ endfor ~}
|
@@ -0,0 +1,117 @@
|
|
1
|
+
terraform {
|
2
|
+
required_version = "~> 1.10"
|
3
|
+
required_providers {
|
4
|
+
cloudfoundry = {
|
5
|
+
source = "cloudfoundry/cloudfoundry"
|
6
|
+
version = "1.2.0"
|
7
|
+
}
|
8
|
+
}
|
9
|
+
}
|
10
|
+
# empty config will let terraform borrow cf-cli's auth
|
11
|
+
provider "cloudfoundry" {}
|
12
|
+
|
13
|
+
variable "create_bot_secrets_file" {
|
14
|
+
type = bool
|
15
|
+
default = false
|
16
|
+
description = "Flag whether to create secrets.cicd.tfvars file"
|
17
|
+
}
|
18
|
+
|
19
|
+
locals {
|
20
|
+
org_name = "<%= cloud_gov_organization %>"
|
21
|
+
cf_space_name = "<%= cloud_gov_staging_space %>"
|
22
|
+
}
|
23
|
+
|
24
|
+
data "cloudfoundry_org" "org" {
|
25
|
+
name = local.org_name
|
26
|
+
}
|
27
|
+
data "cloudfoundry_space" "space" {
|
28
|
+
name = local.cf_space_name
|
29
|
+
org = data.cloudfoundry_org.org.id
|
30
|
+
}
|
31
|
+
|
32
|
+
module "s3" {
|
33
|
+
source = "github.com/gsa-tts/terraform-cloudgov//s3?ref=v2.1.0"
|
34
|
+
|
35
|
+
cf_space_id = data.cloudfoundry_space.space.id
|
36
|
+
name = "<%= app_name %>-terraform-state"
|
37
|
+
s3_plan_name = "basic-sandbox"
|
38
|
+
}
|
39
|
+
|
40
|
+
data "cloudfoundry_service_plans" "cg_service_account" {
|
41
|
+
name = "space-deployer"
|
42
|
+
service_offering_name = "cloud-gov-service-account"
|
43
|
+
}
|
44
|
+
locals {
|
45
|
+
sa_service_name = "<%= app_name %>-cicd-deployer"
|
46
|
+
sa_key_name = "cicd-deployer-access-key"
|
47
|
+
sa_bot_credentials = jsondecode(data.cloudfoundry_service_credential_binding.runner_sa_key.credential_bindings.0.credential_binding).credentials
|
48
|
+
}
|
49
|
+
resource "cloudfoundry_service_instance" "runner_service_account" {
|
50
|
+
name = local.sa_service_name
|
51
|
+
type = "managed"
|
52
|
+
space = data.cloudfoundry_space.space.id
|
53
|
+
service_plan = data.cloudfoundry_service_plans.cg_service_account.service_plans.0.id
|
54
|
+
}
|
55
|
+
resource "cloudfoundry_service_credential_binding" "runner_sa_key" {
|
56
|
+
name = local.sa_key_name
|
57
|
+
service_instance = cloudfoundry_service_instance.runner_service_account.id
|
58
|
+
type = "key"
|
59
|
+
}
|
60
|
+
data "cloudfoundry_service_credential_binding" "runner_sa_key" {
|
61
|
+
name = local.sa_key_name
|
62
|
+
service_instance = cloudfoundry_service_instance.runner_service_account.id
|
63
|
+
depends_on = [cloudfoundry_service_credential_binding.runner_sa_key]
|
64
|
+
}
|
65
|
+
|
66
|
+
locals {
|
67
|
+
bucket_creds_key_name = "backend-state-bucket-creds"
|
68
|
+
}
|
69
|
+
resource "cloudfoundry_service_credential_binding" "bucket_creds" {
|
70
|
+
name = local.bucket_creds_key_name
|
71
|
+
service_instance = module.s3.bucket_id
|
72
|
+
type = "key"
|
73
|
+
}
|
74
|
+
data "cloudfoundry_service_credential_binding" "bucket_creds" {
|
75
|
+
name = local.bucket_creds_key_name
|
76
|
+
service_instance = module.s3.bucket_id
|
77
|
+
depends_on = [cloudfoundry_service_credential_binding.bucket_creds]
|
78
|
+
}
|
79
|
+
|
80
|
+
locals {
|
81
|
+
import_map = {
|
82
|
+
"module.s3.cloudfoundry_service_instance.bucket" = module.s3.bucket_id
|
83
|
+
"cloudfoundry_service_credential_binding.bucket_creds" = cloudfoundry_service_credential_binding.bucket_creds.id
|
84
|
+
"cloudfoundry_service_instance.runner_service_account" = cloudfoundry_service_instance.runner_service_account.id
|
85
|
+
"cloudfoundry_service_credential_binding.runner_sa_key" = cloudfoundry_service_credential_binding.runner_sa_key.id
|
86
|
+
}
|
87
|
+
|
88
|
+
recreate_state_template = templatefile("${path.module}/templates/imports.tf.tftpl", { import_map = local.import_map })
|
89
|
+
}
|
90
|
+
resource "local_file" "recreate_script" {
|
91
|
+
content = local.recreate_state_template
|
92
|
+
filename = "${path.module}/imports.tf"
|
93
|
+
file_permission = "0644"
|
94
|
+
}
|
95
|
+
|
96
|
+
locals {
|
97
|
+
bucket_creds = jsondecode(data.cloudfoundry_service_credential_binding.bucket_creds.credential_bindings.0.credential_binding).credentials
|
98
|
+
backend_config = templatefile("${path.module}/templates/backend_config.tftpl", { creds = local.bucket_creds })
|
99
|
+
}
|
100
|
+
resource "local_sensitive_file" "bucket_creds" {
|
101
|
+
content = local.backend_config
|
102
|
+
filename = "${path.module}/../secrets.backend.tfvars"
|
103
|
+
file_permission = "0600"
|
104
|
+
}
|
105
|
+
|
106
|
+
resource "local_sensitive_file" "bot_secrets_file" {
|
107
|
+
count = (var.create_bot_secrets_file ? 1 : 0)
|
108
|
+
filename = "${path.module}/../secrets.cicd.tfvars"
|
109
|
+
file_permission = "0600"
|
110
|
+
|
111
|
+
content = templatefile("${path.module}/templates/bot_secrets.tftpl", {
|
112
|
+
service_name = local.sa_service_name,
|
113
|
+
key_name = local.sa_key_name,
|
114
|
+
username = local.sa_bot_credentials.username,
|
115
|
+
password = local.sa_bot_credentials.password
|
116
|
+
})
|
117
|
+
}
|
@@ -1,133 +1,117 @@
|
|
1
1
|
# Terraform
|
2
2
|
|
3
|
-
This directory holds the terraform
|
3
|
+
This directory holds the terraform module for maintaining the system infrastructure and deploying the application.
|
4
4
|
|
5
|
-
|
5
|
+
<% unless terraform_manage_spaces? %>
|
6
|
+
## READ ME FIRST
|
6
7
|
|
7
|
-
|
8
|
+
Due to users not having `OrgManager` permission in the `sandbox-gsa` organization, this version of the terraform module
|
9
|
+
is very limited.
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
1. Manually [bootstrap the state storage bucket](#bootstrapping-the-state-storage-s3-buckets-for-the-first-time) within the `bootstrap` directory
|
12
|
-
1. Setup CI/CD Pipeline to run Terraform
|
13
|
-
1. Copy bootstrap credentials to your CI/CD secrets using the instructions in the base README
|
14
|
-
1. Create a cloud.gov SpaceDeployer by following the instructions under `SpaceDeployers`
|
15
|
-
1. Copy SpaceDeployer credentials to your CI/CD secrets using the instructions in the base README
|
16
|
-
1. Manually Running Terraform
|
17
|
-
1. Follow instructions under `Set up a new environment` to create your infrastructure
|
18
|
-
|
19
|
-
## Initial developer setup
|
20
|
-
|
21
|
-
These steps should be run for any developer that needs to start running terraform or who just moved to a new machine.
|
22
|
-
|
23
|
-
They are not necessary for the developer who runs the [initial project setup](#initial-project-setup)
|
11
|
+
When you are ready to move the application to a non-sandbox cloud.gov organization, please re-run the terraform generator with…
|
24
12
|
|
25
|
-
|
26
|
-
|
13
|
+
```bash
|
14
|
+
bin/rails generate rails_template18f:terraform --cg-org=<ORG_NAME> --cg-staging=<STAGING_SPACE_NAME> --cg-prod=<PRODUCTION_SPACE_NAME>
|
15
|
+
```
|
27
16
|
|
17
|
+
…to take full advantage of the generator, and then re-run your CI generator of choice to add production terraform plan and apply steps to your workflow.
|
18
|
+
<% end %>
|
28
19
|
|
29
20
|
## Terraform State Credentials
|
30
21
|
|
31
|
-
The `bootstrap` module is used to create an s3 bucket for later terraform runs to store their state in
|
22
|
+
The `bootstrap` module is used to create an s3 bucket for later terraform runs to store their state in as well as
|
23
|
+
create credentials files so developers can use that s3 bucket to create their own sandbox environments.
|
32
24
|
|
33
|
-
###
|
25
|
+
### Initial project setup
|
34
26
|
|
35
|
-
These steps
|
27
|
+
These steps only need to be run once per project.
|
36
28
|
|
37
|
-
1.
|
38
|
-
1.
|
39
|
-
1.
|
40
|
-
1.
|
41
|
-
1.
|
29
|
+
1. `cd bootstrap`<% if terraform_manage_spaces? %>
|
30
|
+
1. Add any users who should have access to the terraform state bucket to `users.auto.tfvars`<% end %>
|
31
|
+
1. Run `./apply.sh -var create_bot_secrets_file=true`
|
32
|
+
1. Add `imports.tf` to git and commit the changes
|
33
|
+
1. Setup your CI/CD Pipeline to run terraform and deploy your staging and production environments
|
34
|
+
1. Copy backend credentials from `/terraform/secrets.backend.tfvars` to your CI/CD secrets using the instructions in the base README
|
35
|
+
1. Copy the cf_user and cf_password credentials from `/terraform/secrets.cicd.tfvars` to your CI/CD secrets using the instructions in the base README
|
36
|
+
1. Delete the two secrets files
|
42
37
|
|
43
38
|
### To make changes to the bootstrap module
|
44
39
|
|
45
|
-
*This should not be necessary in most cases
|
40
|
+
*This should not be necessary in most cases<% if terraform_manage_spaces? %>, other than adding or removing users who should have access to the state bucket in `bootstrap/users.auto.tfvars`<% end %>*
|
46
41
|
|
47
42
|
1. Make your changes
|
48
|
-
1. Run `./
|
49
|
-
1.
|
43
|
+
1. Run `./apply.sh` and verify the plan before entering `yes`
|
44
|
+
1. Commit any changes to `imports.tf`
|
50
45
|
|
51
|
-
|
46
|
+
## Set up a sandbox environment or review app
|
52
47
|
|
53
|
-
|
54
|
-
```
|
55
|
-
[<%= app_name %>-terraform-backend]
|
56
|
-
aws_access_key_id = <AWS_ACCESS_KEY_ID from run.sh output>
|
57
|
-
aws_secret_access_key = <AWS_SECRET_ACCESS_KEY from run.sh output>
|
58
|
-
```
|
48
|
+
### Pre-requisites:
|
59
49
|
|
60
|
-
1.
|
50
|
+
1. Someone on the team has run the [Initial project setup](#initial-project-setup) steps and `imports.tf` is up-to-date on your branch.
|
51
|
+
<% if terraform_manage_spaces? %>1. You are included in the list of users in `bootstrap/users.auto.tfvars` and `bootstrap/imports.tf`<% end %>
|
61
52
|
|
62
|
-
|
53
|
+
### Steps:
|
63
54
|
|
64
|
-
|
65
|
-
deploy the application from the CI/CD pipeline. Create a new account by running:
|
55
|
+
<% if terraform_manage_spaces? %>1. Create a new `sandbox-<NAME>.tfvars` file to hold variable values for your environment. A good starting point is copying `staging.tfvars` and editing it with your values.<% end %>
|
66
56
|
|
67
|
-
|
68
|
-
|
69
|
-
## Set up a new environment manually
|
70
|
-
|
71
|
-
The below steps rely on you first configuring access to the Terraform state in s3 as described in [initial project setup](#initial-project-setup) or [initial developer setup](#initial-developer-setup).
|
72
|
-
|
73
|
-
1. `cd` to the environment you are working in
|
74
|
-
|
75
|
-
1. Set up a SpaceDeployer and save the credentials in a file named `secrets.auto.tfvars`
|
57
|
+
1. Run terraform plan with:
|
76
58
|
```bash
|
77
|
-
|
78
|
-
# the value of < SPACE_NAME > should be `staging` or `prod` depending on where you are working
|
79
|
-
# the value for < ACCOUNT_NAME > can be anything, although we recommend
|
80
|
-
# something that communicates the purpose of the deployer
|
81
|
-
# for example: circleci-deployer for the credentials CircleCI uses to
|
82
|
-
# deploy the application or <your_name>-terraform for credentials to run terraform manually
|
83
|
-
../../bin/ops/create_service_account.sh -s <SPACE_NAME> -u <ACCOUNT_NAME> -m > secrets.auto.tfvars
|
59
|
+
./terraform.sh -e <%= terraform_manage_spaces? ? "sandbox-<NAME>" : "staging" %>
|
84
60
|
```
|
85
61
|
|
86
|
-
|
87
|
-
|
88
|
-
The easiest way to use this script locally is to redirect the output directly to the `secrets.auto.tfvars` file it needs to be used in
|
89
|
-
|
90
|
-
1. Run terraform from your new environment directory with
|
62
|
+
1. Apply changes with:
|
91
63
|
```bash
|
92
|
-
terraform
|
93
|
-
terraform plan
|
64
|
+
./terraform.sh -e <%= terraform_manage_spaces? ? "sandbox-<NAME>" : "staging" %> -c apply
|
94
65
|
```
|
95
66
|
|
96
|
-
1.
|
97
|
-
|
98
|
-
1. Remove the space deployer service instance if it doesn't need to be used again, such as when manually running terraform plan before letting CI/CD apply the changes.
|
67
|
+
1. <%= terraform_manage_spaces? ? "Optional: tear down the sandbox if" : "Destroy the app when" %> it does not need to be used anymore
|
99
68
|
```bash
|
100
|
-
|
101
|
-
../../bin/ops/destroy_service_account.sh -s <SPACE_NAME> -u <ACCOUNT_NAME>
|
69
|
+
./terraform.sh -e <%= terraform_manage_spaces? ? "sandbox-<NAME>" : "staging" %> -c destroy
|
102
70
|
```
|
103
71
|
|
104
72
|
## Structure
|
105
73
|
|
106
|
-
Each environment has its own module.
|
107
|
-
|
108
74
|
```
|
109
|
-
|
110
|
-
|- main.tf
|
111
|
-
|-
|
112
|
-
|-
|
113
|
-
|-
|
114
|
-
|-
|
115
|
-
|-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
75
|
+
|- bootstrap/
|
76
|
+
| |- main.tf
|
77
|
+
| |- apply.sh
|
78
|
+
| |- imports.tf (automatically generated)
|
79
|
+
| |- users.auto.tfvars
|
80
|
+
| |- terraform.tfstate(.backup) (automatically generated)
|
81
|
+
| |- templates/
|
82
|
+
| |- backend_config.tftpl
|
83
|
+
| |- bot_secrets.tftpl
|
84
|
+
| |- imports.tf.tftpl<% if terraform_manage_spaces? %>
|
85
|
+
|- sandbox_bot/
|
86
|
+
| |- main.tf
|
87
|
+
| |- run.sh
|
88
|
+
| |- <sandbox_name>/ (automatically generated)
|
89
|
+
| |- terraform.tfstate(.backup) (automatically generated)<% end %>
|
90
|
+
|- dist/
|
91
|
+
| |- src.zip (automatically generated)
|
92
|
+
|- README.md
|
93
|
+
|- app.tf
|
94
|
+
|- main.tf
|
95
|
+
|- providers.tf
|
96
|
+
|- terraform.sh
|
97
|
+
|- variables.tf
|
98
|
+
|- <env>.tfvars
|
120
99
|
```
|
121
100
|
|
122
|
-
In the
|
123
|
-
- `
|
124
|
-
- `
|
125
|
-
- `
|
101
|
+
In the root module:
|
102
|
+
- `<env>.tfvars` is where to set variable values for the given environment name
|
103
|
+
- `terraform.sh` Helper script to setup terraform to point to the correct state file, create a service account to run the root module, and apply the root module.
|
104
|
+
- `app.tf` defines the application resource and configuration
|
105
|
+
- `main.tf` defines the persistent infrastructure
|
106
|
+
- `providers.tf` lists the required providers and shell backend config
|
107
|
+
- `variables.tf` lists the variables that will be needed
|
126
108
|
|
127
109
|
In the bootstrap module:
|
128
|
-
- `
|
129
|
-
- `
|
130
|
-
- `
|
131
|
-
- `
|
132
|
-
|
133
|
-
|
110
|
+
- `main.tf` sets up a management space, an s3 bucket to store terraform state files, and an initial SpaceDeployer for the system
|
111
|
+
- `apply.sh` Helper script to either recreate the state locally or call `terraform apply` Any arguments are passed through to the `apply` call
|
112
|
+
- `imports.tf` import blocks to create a new local state file when new developers need to access the state file. This file is automatically generated by calling `./apply.sh` and should be checked into git on any changes
|
113
|
+
- `users.auto.tfvars` this file defines the list of cloud.gov accounts that should have access to the terraform state bucket
|
114
|
+
|
115
|
+
In the sandbox_bot module:
|
116
|
+
- `main.tf` sets up a cloud.gov SpaceDeployer to manage the sandbox environment and outputs its credentials into the main module `secrets.auto.tfvars`
|
117
|
+
- `run.sh` Helper script to set up a separate local state file for each sandbox name. In normal use this will only ever be called by `./terraform.sh`
|
@@ -0,0 +1,63 @@
|
|
1
|
+
data "archive_file" "src" {
|
2
|
+
type = "zip"
|
3
|
+
source_dir = "${path.module}/.."
|
4
|
+
output_path = "${path.module}/dist/src.zip"
|
5
|
+
excludes = [
|
6
|
+
".git*",
|
7
|
+
".circleci/*",
|
8
|
+
".bundle/*",
|
9
|
+
"node_modules/*",
|
10
|
+
"tmp/**/*",
|
11
|
+
"terraform/*",
|
12
|
+
"log/*",
|
13
|
+
"doc/*"
|
14
|
+
]
|
15
|
+
}
|
16
|
+
|
17
|
+
locals {
|
18
|
+
host_name = coalesce(var.host_name, "${local.app_name}-${var.env}")
|
19
|
+
domain = coalesce(var.custom_domain_name, "app.cloud.gov")
|
20
|
+
}
|
21
|
+
|
22
|
+
resource "cloudfoundry_app" "app" {
|
23
|
+
name = "${local.app_name}-${var.env}"
|
24
|
+
space_name = var.cf_space_name
|
25
|
+
org_name = local.cf_org_name
|
26
|
+
|
27
|
+
path = data.archive_file.src.output_path
|
28
|
+
source_code_hash = data.archive_file.src.output_base64sha256
|
29
|
+
buildpacks = ["ruby_buildpack"]
|
30
|
+
strategy = "rolling"
|
31
|
+
routes = [{ route = "${local.host_name}.${local.domain}" }]
|
32
|
+
|
33
|
+
environment = {
|
34
|
+
RAILS_ENV = var.env
|
35
|
+
RAILS_MASTER_KEY = var.rails_master_key
|
36
|
+
RAILS_LOG_TO_STDOUT = "true"
|
37
|
+
RAILS_SERVE_STATIC_FILES = "true"
|
38
|
+
}
|
39
|
+
|
40
|
+
processes = [
|
41
|
+
{
|
42
|
+
type = "web"
|
43
|
+
instances = var.web_instances
|
44
|
+
memory = var.web_memory
|
45
|
+
health_check_http_endpoint = "/up"
|
46
|
+
health_check_type = "http"
|
47
|
+
command = "./bin/rake cf:on_first_instance db:migrate && exec env HTTP_PORT=$PORT ./bin/thrust ./bin/rails server"
|
48
|
+
}
|
49
|
+
]
|
50
|
+
|
51
|
+
service_bindings = [
|
52
|
+
<% if has_active_job? %> { service_instance = "${local.app_name}-redis-${var.env}" },<% end %>
|
53
|
+
<% if has_active_storage? %> { service_instance = "${local.app_name}-s3-${var.env}" },<% end %>
|
54
|
+
{ service_instance = "${local.app_name}-rds-${var.env}" }
|
55
|
+
]
|
56
|
+
|
57
|
+
depends_on = [
|
58
|
+
<% if has_active_job? %> module.redis,<% end %>
|
59
|
+
<% if has_active_storage? %> module.s3,<% end %>
|
60
|
+
<% if terraform_manage_spaces? %> module.app_space,<% end %>
|
61
|
+
module.database
|
62
|
+
]
|
63
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
if ! command -v terraform &> /dev/null
|
4
|
+
then
|
5
|
+
echo "terraform must be installed before running this script"
|
6
|
+
exit 1
|
7
|
+
fi
|
8
|
+
|
9
|
+
set -e
|
10
|
+
|
11
|
+
# ensure we're logged in via cli
|
12
|
+
cf spaces &> /dev/null || cf login -a api.fr.cloud.gov --sso
|
13
|
+
|
14
|
+
terraform init
|
15
|
+
terraform apply "$@"
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# remove this file after initializing your terraform
|
2
|
+
# you can always regenerate it by running ./apply.sh
|
3
|
+
# within the bootstrap module
|
4
|
+
|
5
|
+
bucket = "${creds.bucket}"
|
6
|
+
region = "${creds.region}"
|
7
|
+
access_key = "${creds.access_key_id}"
|
8
|
+
secret_key = "${creds.secret_access_key}"
|