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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +64 -61
  5. data/README.md +11 -16
  6. data/lib/generators/rails_template18f/active_storage/active_storage_generator.rb +7 -6
  7. data/lib/generators/rails_template18f/circleci/circleci_generator.rb +28 -27
  8. data/lib/generators/rails_template18f/circleci/templates/Dockerfile.ci.tt +0 -1
  9. data/lib/generators/rails_template18f/circleci/templates/circleci/config.yml.tt +152 -163
  10. data/lib/generators/rails_template18f/github_actions/github_actions_generator.rb +31 -30
  11. data/lib/generators/rails_template18f/github_actions/templates/github/actions/compile-assets/action.yml +50 -0
  12. data/lib/generators/rails_template18f/github_actions/templates/github/actions/setup-project/action.yml.tt +4 -8
  13. data/lib/generators/rails_template18f/github_actions/templates/github/dependabot.yml.tt +2 -4
  14. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml +72 -0
  15. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml +72 -0
  16. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-daily-scan.yml.tt +10 -1
  17. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-scan.yml.tt +1 -1
  18. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/pa11y.yml.tt +2 -2
  19. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-production.yml +46 -3
  20. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-staging.yml +46 -3
  21. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/validate-ssp.yml +2 -2
  22. data/lib/generators/rails_template18f/i18n_js/i18n_js_generator.rb +2 -2
  23. data/lib/generators/rails_template18f/i18n_js/templates/app/javascript/{i18n.js → i18n/index.js} +1 -1
  24. data/lib/generators/rails_template18f/i18n_js/templates/config/i18n-js.yml +1 -1
  25. data/lib/generators/rails_template18f/newrelic/newrelic_generator.rb +4 -2
  26. data/lib/generators/rails_template18f/public_egress/public_egress_generator.rb +86 -54
  27. data/lib/generators/rails_template18f/sidekiq/sidekiq_generator.rb +8 -17
  28. data/lib/generators/rails_template18f/terraform/templates/full_bootstrap/imports.tf.tftpl +25 -0
  29. data/lib/generators/rails_template18f/terraform/templates/full_bootstrap/main.tf.tt +159 -0
  30. data/lib/generators/rails_template18f/terraform/templates/sandbox_bootstrap/imports.tf.tftpl +10 -0
  31. data/lib/generators/rails_template18f/terraform/templates/sandbox_bootstrap/main.tf.tt +117 -0
  32. data/lib/generators/rails_template18f/terraform/templates/terraform/README.md.tt +77 -93
  33. data/lib/generators/rails_template18f/terraform/templates/terraform/app.tf.tt +63 -0
  34. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/apply.sh +15 -0
  35. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/templates/backend_config.tftpl +8 -0
  36. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/templates/bot_secrets.tftpl +5 -0
  37. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/users.auto.tfvars +5 -0
  38. data/lib/generators/rails_template18f/terraform/templates/terraform/main.tf.tt +106 -0
  39. data/lib/generators/rails_template18f/terraform/templates/terraform/production.tfvars.tt +10 -0
  40. data/lib/generators/rails_template18f/terraform/templates/terraform/providers.tf.tt +32 -0
  41. data/lib/generators/rails_template18f/terraform/templates/terraform/sandbox_bot/main.tf +74 -0
  42. data/lib/generators/rails_template18f/terraform/templates/terraform/sandbox_bot/run.sh +17 -0
  43. data/lib/generators/rails_template18f/terraform/templates/terraform/staging.tfvars.tt +8 -0
  44. data/lib/generators/rails_template18f/terraform/templates/terraform/terraform.sh.tt +135 -0
  45. data/lib/generators/rails_template18f/terraform/templates/terraform/variables.tf.tt +99 -0
  46. data/lib/generators/rails_template18f/terraform/terraform_generator.rb +13 -4
  47. data/lib/rails_template18f/generators/cloud_gov_options.rb +9 -6
  48. data/lib/rails_template18f/generators/cloud_gov_parsing.rb +7 -7
  49. data/lib/rails_template18f/generators.rb +0 -1
  50. data/lib/rails_template18f/version.rb +1 -1
  51. data/rails-template-18f.gemspec +4 -4
  52. data/railsrc +4 -2
  53. data/railsrc-hotwire +4 -2
  54. data/template.rb +61 -71
  55. data/templates/README.md.tt +21 -8
  56. data/templates/app/assets/stylesheets/uswds-settings.scss +3 -2
  57. data/templates/app/views/application/_header.html.erb +1 -1
  58. data/templates/app/views/application/_usa_banner.html.erb +3 -3
  59. data/templates/bin/ops/create_service_account.sh.tt +20 -11
  60. data/templates/bin/ops/destroy_service_account.sh.tt +3 -3
  61. data/templates/browserslistrc +1 -2
  62. data/templates/doc/compliance/TODO.md +1 -4
  63. metadata +30 -29
  64. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml.tt +0 -50
  65. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml.tt +0 -50
  66. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/import.sh +0 -13
  67. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/main.tf.tt +0 -22
  68. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/providers.tf +0 -16
  69. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/run.sh.tt +0 -40
  70. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/teardown_creds.sh.tt +0 -5
  71. data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/variables.tf +0 -2
  72. data/lib/generators/rails_template18f/terraform/templates/terraform/production/main.tf.tt +0 -90
  73. data/lib/generators/rails_template18f/terraform/templates/terraform/production/providers.tf +0 -23
  74. data/lib/generators/rails_template18f/terraform/templates/terraform/production/variables.tf +0 -2
  75. data/lib/generators/rails_template18f/terraform/templates/terraform/staging/main.tf.tt +0 -70
  76. data/lib/generators/rails_template18f/terraform/templates/terraform/staging/providers.tf +0 -23
  77. data/lib/generators/rails_template18f/terraform/templates/terraform/staging/variables.tf +0 -2
  78. data/lib/rails_template18f/generators/pipeline_options.rb +0 -18
  79. data/templates/app/assets/images/uswds.js +0 -6
  80. data/templates/config/deployment/production.yml +0 -3
  81. data/templates/config/deployment/staging.yml +0 -3
  82. 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 modules for maintaining your complete persistent infrastructure.
3
+ This directory holds the terraform module for maintaining the system infrastructure and deploying the application.
4
4
 
5
- Prerequisite: install the `jq` JSON processor: `brew bundle` or `brew install jq`
5
+ <% unless terraform_manage_spaces? %>
6
+ ## READ ME FIRST
6
7
 
7
- ## Initial project setup
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
- These steps only need to be run once per project.
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
- 1. Import the existing bootstrap resources to your local state with `./import.sh`
26
- 1. Follow instructions under [Use bootstrap credentials](#use-bootstrap-credentials)
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
- ### Bootstrapping the state storage s3 buckets for the first time
25
+ ### Initial project setup
34
26
 
35
- These steps are run once per project.
27
+ These steps only need to be run once per project.
36
28
 
37
- 1. Run `./run.sh init`
38
- 1. Run `./run.sh apply` to set up the bucket and retrieve credentials
39
- 1. Follow instructions under [Use bootstrap credentials](#use-bootstrap-credentials)
40
- 1. Ensure that `import.sh` includes a line and correct IDs for any resources created
41
- 1. Run `./teardown_creds.sh` to remove the space deployer account used to create the s3 bucket
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 `./run.sh plan` to verify the changes are what you expect
49
- 1. Continue from step 2 of the [boostrapping instructions](#bootstrapping-the-state-storage-s3-buckets-for-the-first-time)
43
+ 1. Run `./apply.sh` and verify the plan before entering `yes`
44
+ 1. Commit any changes to `imports.tf`
50
45
 
51
- ### Use bootstrap credentials
46
+ ## Set up a sandbox environment or review app
52
47
 
53
- 1. Add the following to `~/.aws/credentials`
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. Copy `BUCKET` from `run.sh` output to the backend block of `staging/providers.tf` and `production/providers.tf`
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
- ## SpaceDeployers
53
+ ### Steps:
63
54
 
64
- A [SpaceDeployer](https://cloud.gov/docs/services/cloud-gov-service-account/) account is required to run terraform or
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
- `../bin/ops/create_service_account.sh -s <SPACE_NAME> -u <ACCOUNT_NAME> -m`
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
- # create a space deployer service instance that can log in with just a username and password
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
- The script will output the `username` (as `cf_user`) and `password` (as `cf_password`) for your `<ACCOUNT_NAME>`. Read more in the [cloud.gov service account documentation](https://cloud.gov/docs/services/cloud-gov-service-account/).
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 init -backend-config="profile=<%= app_name %>-terraform-backend"
93
- terraform plan
64
+ ./terraform.sh -e <%= terraform_manage_spaces? ? "sandbox-<NAME>" : "staging" %> -c apply
94
65
  ```
95
66
 
96
- 1. Apply changes with `terraform apply`.
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
- # <SPACE_NAME> and <ACCOUNT_NAME> have the same values as used above.
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
- - bootstrap/
110
- |- main.tf
111
- |- providers.tf
112
- |- variables.tf
113
- |- run.sh
114
- |- teardown_creds.sh
115
- |- import.sh
116
- - <env>/
117
- |- main.tf
118
- |- providers.tf
119
- |- variables.tf
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 environment-specific modules:
123
- - `providers.tf` lists the required providers
124
- - `main.tf` calls the shared Terraform code, but this is also a place where you can add any other services, resources, etc, which you would like to set up for that environment
125
- - `variables.tf` lists the variables that will be needed, either to pass through to the child module or for use in this module
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
- - `providers.tf` lists the required providers
129
- - `main.tf` sets up s3 bucket to be shared across all environments. It lives in `prod` to communicate that it should not be deleted
130
- - `variables.tf` lists the variables that will be needed. Most values are hard-coded in this module
131
- - `run.sh` Helper script to set up a space deployer and run terraform. The terraform action (`init`/`show`/`plan`/`apply`/`destroy`) is passed as an argument
132
- - `teardown_creds.sh` Helper script to remove the space deployer setup as part of `run.sh`
133
- - `import.sh` Helper script to create a new local state file when new developers need to access the state file
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}"
@@ -0,0 +1,5 @@
1
+ # Generated via bootstrap module. Remove this file when finished
2
+ # credentials for service "${service_name}"/"${key_name}"
3
+
4
+ cf_user = "${username}"
5
+ cf_password = "${password}"
@@ -0,0 +1,5 @@
1
+ terraform_users = [
2
+ # add your terraform_users list here: example values:
3
+ # "team.member@gsa.gov",
4
+ # "cf_user-guid-value-for-space-developer-service-account",
5
+ ]