diff --git a/.github/workflows/build-and-deploy-dev-waf.yml b/.github/workflows/build-and-deploy-dev-waf.yml
index 74f0a3da3..684545a5a 100644
--- a/.github/workflows/build-and-deploy-dev-waf.yml
+++ b/.github/workflows/build-and-deploy-dev-waf.yml
@@ -23,8 +23,7 @@ jobs:
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
- run: |
- source ./scripts/pipeline/cloud-gov-login.sh
+ run: source ./scripts/pipeline/cloud-gov-login.sh
- name: Check version
id: version
env:
@@ -35,7 +34,7 @@ jobs:
source ./scripts/pipeline/cloud-gov-waf-version.sh
outputs:
current_nginx_version: ${{ steps.version.outputs.current_nginx_version }}
- nginx_version: ${{ steps.version.outputs.nginx_version }}
+ new_nginx_version: ${{ steps.version.outputs.new_nginx_version }}
cloudgov_wf_version: ${{ steps.version.outputs.cloudgov_wf_version }}
cloudgov_bp_version: ${{ steps.version.outputs.cloudgov_bp_version }}
update: ${{ steps.version.outputs.update }}
@@ -50,81 +49,101 @@ jobs:
- name: Set env.BRANCH
run: echo "BRANCH=dev" >> $GITHUB_ENV
- name: Install basic dependancies
- run: |
- ./scripts/pipeline/deb-basic-deps.sh
+ run: ./scripts/pipeline/deb-basic-deps.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
- id: cglogin
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ TF_BACKEND_SPACE: '${{ secrets.TF_BACKEND_SPACE }}'
run: |
source ./scripts/pipeline/cloud-gov-login.sh
+ cf target -s "${TF_BACKEND_SPACE}" >/dev/null 2>&1
+ - name: Start Bastion
+ env:
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ run: |
+ cf start "${TF_BASTION}" >/dev/null 2>&1
+ ./scripts/pipeline/cloud-gov-wait-for-app-start.sh "${TF_BASTION}"
+ - name: Cloud.gov bastion git checkout
+ env:
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ run: |
+ declare -a commands=("rm -rf px-benefit-finder" "git clone https://github.com/GSA/px-benefit-finder.git && cd px-benefit-finder && git checkout ${BRANCH}")
+ for command in "${commands[@]}"; do
+ ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "${command}" 1
+ done
- name: Build nginx WAF Plugin
env:
ubuntu_version: '${{ vars.UBUNTU_VERSION }}'
modsecurity_nginx_version: '${{ vars.MODSECURITY_NGINX_VERSION }}'
- nginx_version: ${{ needs.checkVersion.outputs.nginx_version }}
+ new_nginx_version: ${{ needs.checkVersion.outputs.new_nginx_version }}
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
run: |
- CWD=$(pwd)
- cd "${CWD}/infra/applications/nginx-waf/.docker" || exit 1
- make
- cd "${CWD}" || exit 1
- - name: Configure Terraform Provider
+ source ./scripts/pipeline/terraform-build-waf-plugin.sh
+ - name: Configure Terraform
env:
- bucket_name: ${{ secrets.BUCKET_NAME_TERRAFORM }}
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
run: |
CWD=$(pwd)
- cf target -s "${PROJECT}-dev" >/dev/null 2>&1
- source ./scripts/local/cloud-gov-s3-creds.sh
- cd "${CWD}/infra" || exit 1
- echo "cloudgov_password=\"${{ secrets.CF_PASSWORD }}\"" >> terraform.tfvars
- echo "cloudgov_username=\"${{ secrets.CF_USER }}\"" >> terraform.tfvars
- envsubst < provider.tf.tmpl > provider.tf
- cd "${CWD}" || exit 1
- - uses: hashicorp/setup-terraform@v3
+ cd terraform/infra
+ envsubst < terraform.tfvars.tmpl > terraform.tfvars
+ ${CWD}/scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "terraform.tfvars" "px-benefit-finder/terraform/infra"
+ cd "${CWD}"
- name: Terraform Init
env:
- GITHUB_TOKEN: ${{ secrets.CR_PAT }}
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
id: init
- run: terraform init
- working-directory: 'infra'
+ run : ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "tofu -chdir=px-benefit-finder/terraform/infra init" 1
- name: Terraform Validate
+ env:
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
id: validate
- run: TF_WORKSPACE=${BRANCH} terraform validate -no-color
+ run : |
+ stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh ${TF_BASTION} "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra validate -no-color" 1))
+ stdout=$(echo $stdout | sed '$ d')
+ echo ${stdout}
+ echo "stdout=${stdout}" >> $GITHUB_OUTPUT
- name: Terraform Plan
env:
- GITHUB_TOKEN: ${{ secrets.CR_PAT }}
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
id: plan
- run: TF_WORKSPACE=${BRANCH} terraform plan -no-color
- working-directory: 'infra'
+ run : |
+ stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra plan -no-color" 1)
+ stdout=$(echo $stdout | sed '$ d')
+ echo ${stdout}
+ echo "stdout=${stdout}" >> $GITHUB_OUTPUT
- name: Terraform Apply
- id: apply
env:
- GITHUB_TOKEN: ${{ secrets.CR_PAT }}
- run: TF_WORKSPACE=${BRANCH} terraform apply -auto-approve
- working-directory: 'infra'
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ id: apply
+ run : |
+ stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra apply -auto-approve" 1)
+ stdout=$(echo $stdout | sed '$ d')
+ echo ${stdout}
+ echo "stdout=${stdout}" >> $GITHUB_OUTPUT
- name: Create Issue For Review
id: issue
uses: dacbd/create-issue-action@main
with:
token: ${{ secrets.ADD_TO_PROJECT_PAT }}
assignees: ${{ vars.TECH_LEAD }}
- title: "Update to NGINX v${{ needs.checkVersion.outputs.nginx_version }} (dev)"
+ title: "Update to NGINX v${{ needs.checkVersion.outputs.new_nginx_version }} (dev)"
body: |
## Automated update of NGINX WAF
This is an automated build and deploy of the NGINX WAF application in dev.
Build pack: ${{ needs.checkVersion.outputs.cloudgov_wf_version }} => ${{ needs.checkVersion.outputs.cloudgov_bp_version }}
- NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.nginx_version }}
+ NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.new_nginx_version }}
#### Terraform Initialization ⚙`${{ steps.init.outcome }}`
#### Terraform Validation 🤖`${{ steps.validate.outcome }}`
@@ -154,4 +173,32 @@ jobs:
GH_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }}
ISSUE_NUMBER: ${{ steps.issue.outputs.number }}
run: source ./scripts/pipeline/github-update-issue-status.sh
-
\ No newline at end of file
+ stopBastion:
+ name: Stop Bastion
+ runs-on: ubuntu-latest
+ needs: updateWAF
+ if: ${{ always() }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Set env.BRANCH
+ run: echo "BRANCH=dev" >> $GITHUB_ENV
+ - name: Install basic dependancies
+ run: ./scripts/pipeline/deb-basic-deps.sh
+ - name: Install Cloudfoundry CLI
+ run: ./scripts/pipeline/deb-cf-install.sh
+ - name: Cloud.gov login
+ env:
+ CF_USER: '${{ secrets.CF_USER }}'
+ CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
+ CF_ORG: '${{ secrets.CF_ORG }}'
+ PROJECT: '${{ secrets.PROJECT }}'
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ TF_BACKEND_SPACE: '${{ secrets.TF_BACKEND_SPACE }}'
+ run: |
+ source ./scripts/pipeline/cloud-gov-login.sh
+ cf target -s "${TF_BACKEND_SPACE}" >/dev/null 2>&1
+ - name: Stop Bastion
+ env:
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ run: cf stop "${TF_BASTION}" >/dev/null 2>&1
\ No newline at end of file
diff --git a/.github/workflows/build-and-deploy-main-waf.yml b/.github/workflows/build-and-deploy-main-waf.yml
index f487716be..d10ba3ced 100644
--- a/.github/workflows/build-and-deploy-main-waf.yml
+++ b/.github/workflows/build-and-deploy-main-waf.yml
@@ -2,7 +2,7 @@ name: Update Main WAF
on:
schedule:
- - cron: '30 5 * * 4'
+ - cron: '0 5 * * 4'
jobs:
checkVersion:
@@ -23,8 +23,7 @@ jobs:
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
- run: |
- source ./scripts/pipeline/cloud-gov-login.sh
+ run: source ./scripts/pipeline/cloud-gov-login.sh
- name: Check version
id: version
env:
@@ -35,9 +34,9 @@ jobs:
source ./scripts/pipeline/cloud-gov-waf-version.sh
outputs:
current_nginx_version: ${{ steps.version.outputs.current_nginx_version }}
- nginx_version: ${{ steps.version.outputs.nginx_version }}
- cloudgov_wf_version: ${{ steps.version.outputs.cloudgov_wf_version }}
- cloudgov_bp_version: ${{ steps.version.outputs.cloudgov_bp_version }}
+ new_nginx_version: ${{ steps.version.outputs.new_nginx_version }}
+ current_bp_version: ${{ steps.version.outputs.current_bp_version }}
+ new_bp_version: ${{ steps.version.outputs.new_bp_version }}
update: ${{ steps.version.outputs.update }}
updateWAF:
name: Update WAF
@@ -50,83 +49,104 @@ jobs:
- name: Set env.BRANCH
run: echo "BRANCH=main" >> $GITHUB_ENV
- name: Install basic dependancies
- run: |
- ./scripts/pipeline/deb-basic-deps.sh
+ run: ./scripts/pipeline/deb-basic-deps.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
- id: cglogin
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ TF_BACKEND_SPACE: '${{ secrets.TF_BACKEND_SPACE }}'
run: |
source ./scripts/pipeline/cloud-gov-login.sh
+ cf target -s "${TF_BACKEND_SPACE}" >/dev/null 2>&1
+ - name: Start Bastion
+ env:
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ run: |
+ cf start "${TF_BASTION}" >/dev/null 2>&1
+ ./scripts/pipeline/cloud-gov-wait-for-app-start.sh "${TF_BASTION}"
+ - name: Cloud.gov bastion git checkout
+ env:
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ run: |
+ declare -a commands=("rm -rf px-benefit-finder" "git clone https://github.com/GSA/px-benefit-finder.git && cd px-benefit-finder && git checkout ${BRANCH}")
+ for command in "${commands[@]}"; do
+ ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "${command}" 1
+ done
- name: Build nginx WAF Plugin
env:
ubuntu_version: '${{ vars.UBUNTU_VERSION }}'
modsecurity_nginx_version: '${{ vars.MODSECURITY_NGINX_VERSION }}'
- nginx_version: ${{ needs.checkVersion.outputs.nginx_version }}
+ new_nginx_version: ${{ needs.checkVersion.outputs.new_nginx_version }}
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
run: |
- CWD=$(pwd)
- cd "${CWD}/infra/applications/nginx-waf/.docker" || exit 1
- make
- cd "${CWD}" || exit 1
- - name: Configure Terraform Provider
+ source ./scripts/pipeline/terraform-build-waf-plugin.sh
+ - name: Configure Terraform
env:
- bucket_name: ${{ secrets.BUCKET_NAME_TERRAFORM }}
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
run: |
CWD=$(pwd)
- cf target -s "${PROJECT}-dev" >/dev/null 2>&1
- source ./scripts/local/cloud-gov-s3-creds.sh
- cd "${CWD}/infra" || exit 1
- echo "cloudgov_password=\"${{ secrets.CF_PASSWORD }}\"" >> terraform.tfvars
- echo "cloudgov_username=\"${{ secrets.CF_USER }}\"" >> terraform.tfvars
- envsubst < provider.tf.tmpl > provider.tf
- cd "${CWD}" || exit 1
- - uses: hashicorp/setup-terraform@v3
+ cd terraform/infra
+ envsubst < terraform.tfvars.tmpl > terraform.tfvars
+ ${CWD}/scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "terraform.tfvars" "px-benefit-finder/terraform/infra"
+ cd "${CWD}"
- name: Terraform Init
env:
- GITHUB_TOKEN: ${{ secrets.CR_PAT }}
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
id: init
- run: terraform init
- working-directory: 'infra'
+ run : ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "tofu -chdir=px-benefit-finder/terraform/infra init" 1
- name: Terraform Validate
+ env:
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
id: validate
- run: TF_WORKSPACE=${BRANCH} terraform validate -no-color
+ run : |
+ stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh ${TF_BASTION} "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra validate -no-color" 1))
+ stdout=$(echo $stdout | sed '$ d')
+ echo ${stdout}
+ echo "stdout=${stdout}" >> $GITHUB_OUTPUT
- name: Terraform Plan
env:
- GITHUB_TOKEN: ${{ secrets.CR_PAT }}
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
id: plan
- run: TF_WORKSPACE=${BRANCH} terraform plan -no-color
- working-directory: 'infra'
+ run : |
+ stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra plan -no-color" 1)
+ stdout=$(echo $stdout | sed '$ d')
+ echo ${stdout}
+ echo "stdout=${stdout}" >> $GITHUB_OUTPUT
- name: Terraform Apply
- id: apply
env:
- GITHUB_TOKEN: ${{ secrets.CR_PAT }}
- run: TF_WORKSPACE=${BRANCH} terraform apply -auto-approve
- working-directory: 'infra'
+ TF_BASTION: '${{ secrets.TF_BASTION }}'
+ id: apply
+ run : |
+ stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra apply -auto-approve" 1)
+ stdout=$(echo $stdout | sed '$ d')
+ echo ${stdout}
+ echo "stdout=${stdout}" >> $GITHUB_OUTPUT
- name: Create Issue For Review
+ id: issue
uses: dacbd/create-issue-action@main
with:
token: ${{ secrets.ADD_TO_PROJECT_PAT }}
assignees: ${{ vars.TECH_LEAD }}
- title: "Update to NGINX v${{ needs.checkVersion.outputs.nginx_version }} (main)"
+ title: "Update to NGINX v${{ needs.checkVersion.outputs.new_nginx_version }} (main)"
body: |
## Automated update of NGINX WAF
This is an automated build and deploy of the NGINX WAF application in main.
- Build pack: ${{ needs.checkVersion.outputs.cloudgov_wf_version }} => ${{ needs.checkVersion.outputs.cloudgov_bp_version }}
- NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.nginx_version }}
+ Build pack: ${{ needs.checkVersion.outputs.current_bp_version }} => ${{ needs.checkVersion.outputs.new_bp_version }}
+ NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.new_nginx_version }}
- #### Terraform Initialization ⚙️`${{ steps.init.outcome }}`
- #### Terraform Validation ✅`${{ steps.validate.outcome }}`
+ #### Terraform Initialization ⚙`${{ steps.init.outcome }}`
+ #### Terraform Validation 🤖`${{ steps.validate.outcome }}`
Validation Output
@@ -136,7 +156,7 @@ jobs:
Show Plan
@@ -145,3 +165,40 @@ jobs:
```
{| no | - -## Outputs - -| Name | Description | -|------|-------------| -| [apps](#output\_apps) | A `map` of [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource outputs. The key is the app name. | -| [external\_endpoints](#output\_external\_endpoints) | A map of external URL's (app.cloud.gov) to used to reach an application. The key is the app name. | -| [internal\_endpoints](#output\_internal\_endpoints) | A map of internal URL's (apps.internal) to used to reach an application. The key is the app name. | - -## Example - -```terraform -module "applications" { - source = "./modules/application" - - cloudfoundry = local.cloudfoundry - env = local.env - secrets = local.secrets - services = local.services -} -``` - -## Variables - -### cloudfoundry - -A variable that contains a `map(string)` of data lookups for pre-existing resources from Cloud.gov. This includes thing such as the organization and space ids. These are defined in `data.tf` in the root directory. - -### env - -A mixed type `object` variable that contains application settings. It is passed as an `any` type to allow optional variables to be ommitted from the object. It is defined in `locals.tf`, in the root directory. The object `local.env[terraform.workspace].apps` stores the values for the specific application that is to be deployed. - -Valid options are the attributes for the [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource. - -### secrets - -A variable that has secrets and other credentials that the application uses. The `local.secrets` variable is generated in `locals_dynamic.tf`, as it merges a variety of credentials from the random and services modules. - -### services - -A variable that contains a `map(map(string))` of the services deployed in the environment. `local.services` is generated in `locals_dynamic.tf`, due to needing to be generated after the creation of the services, after the instance id are known. The services are then bound to the application. - -See the [service module](../service/readme.MD) for more information. - -## Usage - -Here is an example of how to define an application in `locals.tf`. - -```terraform -locals { - env = { - workspace1 = { - apps = { - application1 = { - buildpacks = [ - "staticfile_buildpack" - ] - command = "./start" - disk_quota = 256 - enable_ssh = true - environment = { - environment = terraform.workspace - LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/" - } - health_check_timeout = 180 - health_check_type = "port" - instances = 1 - labels = { - environment = terraform.workspace - } - memory = 64 - port = 8080 - public_route = false - - source = "/path/to/application/directory" - - templates = [ - { - source = "${path.cwd}/path/to/templates/template.tmpl" - destination = "${path.cwd}}/path/to/templates/file" - } - ] - } - } - } - } -} -``` - -## Additional Notes - -- Buildpacks - - Valid built-in Cloud.gov buildpacks can be found by running `cf buildpacks` from the CLI. - - External buildpacks, such as the `apt-buildpack` by referencing the URL to the buildpack repository: [https://github.com/cloudfoundry/apt-buildpack](https://github.com/cloudfoundry/apt-buildpack). - \ No newline at end of file diff --git a/infra/modules/application/variables.tf b/infra/modules/application/variables.tf deleted file mode 100755 index 082a65f93..000000000 --- a/infra/modules/application/variables.tf +++ /dev/null @@ -1,25 +0,0 @@ -variable "cloudfoundry" { - description = "The settings object for Cloudfoundry." - type = any -} - -variable "env" { - description = "The settings object for this environment." - type = any -} - -variable "secrets" { - description = "Sensitive credentials to be used to set application environmental variables." - type = map - default = {} -} - -variable "services" { - description = "Services generated from the service module." - type = any - default = { - instance = null - user_provided = null - service_key = null - } -} \ No newline at end of file diff --git a/infra/modules/certificate/.terraform-docs/footer.md b/infra/modules/certificate/.terraform-docs/footer.md deleted file mode 100755 index eeabe85c5..000000000 --- a/infra/modules/certificate/.terraform-docs/footer.md +++ /dev/null @@ -1,10 +0,0 @@ -## Usage - -```terraform -module "certificates" { - source = "./modules/certificates" - - project_name = local.env.project - workspace = local.env.bootstrap_workspace -} -``` diff --git a/infra/modules/certificate/.terraform-docs/header.md b/infra/modules/certificate/.terraform-docs/header.md deleted file mode 100755 index af1e49904..000000000 --- a/infra/modules/certificate/.terraform-docs/header.md +++ /dev/null @@ -1,7 +0,0 @@ -# CloudFoundry Certificates Module - -## Introduction - -This module is used to create self-signed certificates. - -The outputs of this module are `certificate` and `key`. This is a `map` with they keys `certificate` and `base64`, which is the base64 encoded value of the certificate value. \ No newline at end of file diff --git a/infra/modules/certificate/README.md b/infra/modules/certificate/README.md deleted file mode 100644 index bcdec91a6..000000000 --- a/infra/modules/certificate/README.md +++ /dev/null @@ -1,57 +0,0 @@ - -# CloudFoundry Certificates Module - -## Introduction - -This module is used to create self-signed certificates. - -The outputs of this module are `certificate` and `key`. This is a `map` with they keys `certificate` and `base64`, which is the base64 encoded value of the certificate value. - -## Requirements - -| Name | Version | -|------|---------| -| [tls](#requirement\_tls) | 4.0.4 | - -## Providers - -| Name | Version | -|------|---------| -| [tls](#provider\_tls) | 4.0.4 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [tls_private_key.this](https://registry.terraform.io/providers/hashicorp/tls/4.0.4/docs/resources/private_key) | resource | -| [tls_self_signed_cert.this](https://registry.terraform.io/providers/hashicorp/tls/4.0.4/docs/resources/self_signed_cert) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [project\_name](#input\_project\_name) | The project name. | `string` | n/a | yes | -| [workspace](#input\_workspace) | Workspace to generate certificates in. | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [certificate](#output\_certificate) | The CA certificate generated by the module. | -| [key](#output\_key) | The CA key generated by the module. | - -## Usage - -```terraform -module "certificates" { - source = "./modules/certificates" - - project_name = local.env.project - workspace = local.env.bootstrap_workspace -} -``` - \ No newline at end of file diff --git a/infra/modules/certificate/main.tf b/infra/modules/certificate/main.tf deleted file mode 100644 index 27b55d6c4..000000000 --- a/infra/modules/certificate/main.tf +++ /dev/null @@ -1,33 +0,0 @@ -locals { - is_deployment_workspace = terraform.workspace == var.workspace ? true : false -} - -resource "tls_private_key" "this" { - count = !local.is_deployment_workspace ? 1 : 0 - algorithm = "RSA" - rsa_bits = 4096 -} - -resource "tls_self_signed_cert" "this" { - count = !local.is_deployment_workspace ? 1 : 0 - private_key_pem = tls_private_key.this[0].private_key_pem - - is_ca_certificate = true - - subject { - country = "US" - province = "Washington" - locality = "DC" - common_name = "${var.project_name} CA" - organization = "${var.project_name}" - organizational_unit = "${var.project_name}" - } - - validity_period_hours = try(var.env.certificate_authority_validity, 8766) - early_renewal_hours = try(var.env.certificate_authority_renewal, 1461) - allowed_uses = [ - "digital_signature", - "cert_signing", - "crl_signing", - ] -} diff --git a/infra/modules/certificate/outputs.tf b/infra/modules/certificate/outputs.tf deleted file mode 100755 index 201dbe588..000000000 --- a/infra/modules/certificate/outputs.tf +++ /dev/null @@ -1,15 +0,0 @@ -output "certificate" { - description = "The CA certificate generated by the module." - value = { - certificate = local.is_deployment_workspace ? tls_self_signed_cert.this[0].cert_pem : null - base64 = local.is_deployment_workspace ? base64encode(tls_self_signed_cert.this[0].cert_pem) : null - } -} - -output "key" { - description = "The CA key generated by the module." - value = { - certificate = local.is_deployment_workspace ? tls_private_key.this[0].private_key_pem : null - base64 = local.is_deployment_workspace ? base64encode(tls_private_key.this[0].private_key_pem) : null - } -} \ No newline at end of file diff --git a/infra/modules/certificate/providers.tf b/infra/modules/certificate/providers.tf deleted file mode 100644 index a27a4fac0..000000000 --- a/infra/modules/certificate/providers.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - tls = { - source = "hashicorp/tls" - version = "4.0.4" - } - } -} diff --git a/infra/modules/certificate/variables.tf b/infra/modules/certificate/variables.tf deleted file mode 100755 index c1a0a965b..000000000 --- a/infra/modules/certificate/variables.tf +++ /dev/null @@ -1,14 +0,0 @@ -variable "env" { - description = "The settings object for this environment." - type = any -} - -variable "project_name" { - description = "The project name." - type = string -} - -variable "workspace" { - description = "Workspace to generate certificates in." - type = string -} \ No newline at end of file diff --git a/infra/modules/random/variables.tf b/infra/modules/random/variables.tf deleted file mode 100755 index bf8c4279a..000000000 --- a/infra/modules/random/variables.tf +++ /dev/null @@ -1,22 +0,0 @@ -variable "expiration" { - type = number - description = "A number, in days, when the password should be rotated. If none is set, the password will not rotate." - default = 0 -} - -variable "names" { - type = list(string) - description = "List of unique names for the multiple resources." - default = [] -} - -variable "passwords" { - type = any - description = "A map of objects with password settings." -} - -variable "per_workspace" { - type = bool - description = "Generate a password for each workspace." - default = false -} \ No newline at end of file diff --git a/infra/modules/service/README.md b/infra/modules/service/README.md deleted file mode 100644 index ecf6e52d4..000000000 --- a/infra/modules/service/README.md +++ /dev/null @@ -1,88 +0,0 @@ - -# CloudFoundry Service Module - -## Requirements - -| Name | Version | -|------|---------| -| [cloudfoundry](#requirement\_cloudfoundry) | 0.51.2 | - -## Providers - -| Name | Version | -|------|---------| -| [cloudfoundry](#provider\_cloudfoundry) | 0.51.2 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [cloudfoundry_service_instance.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/service_instance) | resource | -| [cloudfoundry_service_key.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/service_key) | resource | -| [cloudfoundry_user_provided_service.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/user_provided_service) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [cloudfoundry](#input\_cloudfoundry) | Cloudfoundry settings. | `any` | n/a | yes | -| [env](#input\_env) | The settings map for this environment. | `any` | n/a | yes | -| [passwords](#input\_passwords) | Sensitive strings to be added to the apps environmental variables. | `map` | `{}` | no | -| [secrets](#input\_secrets) | Sensitive strings to be added to the apps environmental variables. | `map` | `{}` | no | -| [skip\_service\_instances](#input\_skip\_service\_instances) | Allows the skipping of service instances. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | -| [skip\_user\_provided\_services](#input\_skip\_user\_provided\_services) | Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [name](#output\_name) | n/a | -| [results](#output\_results) | n/a | - -## Examples - -### Basic -```terraform -module "services" { - source = "./modules/service" - - cloudfoundry = local.cloudfoundry - env = local.env -} -``` - -### Advanced - -This advanced example will first generate service instances, such as RDS, along with other defined services, except for the `user defined` services. `User defined` services are useful for providing variables at runtime to applications. The issue is that until a service, such as RDS is deployed, their isn't a username and password created for that instance. - -The first step is to initalize any services that are not `user defined`, but setting `skip_user_provided_services` to `true`. - -```terraform -module "services" { - source = "./modules/service" - - cloudfoundry = local.cloudfoundry - env = local.env - - skip_user_provided_services = true -} -``` - -After the services are generated, another module block can be defined, which will pass a merged `map(string)` called `secrets`, that have the various information that is to be added to the `user defined` service. Setting the `skip_service_instances` to `true` will prevent the module from trying to redploy any non `user defined` service. - -```terraform -module "secrets" { - source = "./modules/service" - - cloudfoundry = local.cloudfoundry - env = local.env - - secrets = local.secrets - skip_service_instances = true -} -``` - \ No newline at end of file diff --git a/infra/modules/service/variables.tf b/infra/modules/service/variables.tf deleted file mode 100755 index 3ecec7f35..000000000 --- a/infra/modules/service/variables.tf +++ /dev/null @@ -1,33 +0,0 @@ -variable "cloudfoundry" { - description = "Cloudfoundry settings." - type = any -} - -variable "env" { - description = "The settings map for this environment." - type = any -} - -variable "passwords" { - description = "Sensitive strings to be added to the apps environmental variables." - type = map - default = {} -} - -variable "skip_service_instances" { - description = "Allows the skipping of service instances. Useful to inject service secrets into a user provided secret." - type = bool - default = false -} - -variable "skip_user_provided_services" { - description = "Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret." - type = bool - default = false -} - -variable "secrets" { - description = "Sensitive strings to be added to the apps environmental variables." - type = map - default = {} -} \ No newline at end of file diff --git a/infra/terraform.tfvars.tmpl b/infra/terraform.tfvars.tmpl deleted file mode 100644 index a32c21261..000000000 --- a/infra/terraform.tfvars.tmpl +++ /dev/null @@ -1,12 +0,0 @@ -backend_aws_bucket_name="$backend_aws_bucket_name" -backend_aws_bucket_region="$backend_aws_bucket_region" - -#circleci_token="$circleci_token" - -cloudgov_password="$cloudgov_password" -cloudgov_username="$cloudgov_username" - -newrelic_key="$newrelic_key" - -proxy_username="$proxy_username" -proxy_password="$proxy_password" \ No newline at end of file diff --git a/infra/variables.tf b/infra/variables.tf deleted file mode 100755 index 2828f864a..000000000 --- a/infra/variables.tf +++ /dev/null @@ -1,78 +0,0 @@ -# variable "circleci_token" { -# description = "CircleCI token." -# type = string -# sensitive = true -# } - -variable "cloudgov_username" { - description = "The username for the cloudfoundry account." - type = string - sensitive = true -} - -variable "cloudgov_password" { - description = "The password for the cloud.gov account." - type = string - sensitive = true -} - -variable "mtls_port" { - description = "The default port to direct traffic to. Envoy proxy listens on 61443 and redirects to 8080, which the application should listen on." - type = number - default = 61443 -} - -# variable "newrelic_key" { -# description = "The API key for New Relic." -# type = string -# sensitive = true -# } - -# variable "no_proxy" { -# description = "URIs that shouldn't be using the proxy to communicate." -# type = string -# default = "apps.internal,localhost,127.0.0.1" -# } - -# variable "proxy_password" { -# description = "The proxy password." -# type = string -# sensitive = true -# } - -# variable "proxy_username" { -# description = "The proxy username." -# type = string -# sensitive = true -# } - -# variable "sso_x509_cert" { -# description = "x509 cert used for Secure Auth SSO." -# type = string -# sensitive = true -# } - -# may be needed for bootstrap but variables have been removed -#variable "backup_aws_bucket_name" { -#description = "The S3 bucket used for persistent file backups." -#type = string -#sensitive = true -#} - -#variable "backup_aws_region_name" { -#description = "The S3 region used for persistent file backups." -#type = string -#sensitive = true -#} - -#variable "backend_aws_bucket_name" { -#description = "The S3 bucket used for persistent file backends." -#type = string -#sensitive = true -#} - -#variable "backend_aws_region_name" { -#description = "The S3 region used for persistent file backends." -#type = string -#sensitive = true -#} diff --git a/scripts/local/cloud-gov-s3-creds.sh b/scripts/local/cloud-gov-s3-creds.sh index 16b15d383..811ee52a0 100755 --- a/scripts/local/cloud-gov-s3-creds.sh +++ b/scripts/local/cloud-gov-s3-creds.sh @@ -11,7 +11,7 @@ if [ -z "${bucket_name}" ]; then return fi -echo "Getting bucket credentials..." +echo "Deleting old credentials..." { service_key="${bucket_name}-${user}-key" @@ -33,4 +33,27 @@ echo "Creating key..." export AWS_BUCKET=${aws_bucket_name} export AWS_DEFAULT_REGION=${aws_bucket_region} export AWS_SECRET_ACCESS_KEY=${aws_secret_key} -} >/dev/null 2>&1 \ No newline at end of file + + cat >~/current_creds.sh << EOT +export AWS_ACCESS_KEY_ID=${aws_access_key} +export AWS_BUCKET=${aws_bucket_name} +export AWS_DEFAULT_REGION=${aws_bucket_region} +export AWS_SECRET_ACCESS_KEY=${aws_secret_key} +EOT +chmod +x ~/current_creds.sh +} >/dev/null 2>&1 + +declare eror >/dev/null 2>&1 + +[ -z "${AWS_ACCESS_KEY_ID}" ] && eror=true + +if [ -n "${eror}" ]; then + echo -e "Error setting AWS credentials." + echo -e "Please ensure you're logged in to Cloud.gov." + echo -e "You can check by running: cf target -s
"instance": null,
"service_key": null,
"user_provided": null
}
object(| n/a | yes | +| [env](#input\_env) | The settings object for this environment. |
{
domain_external = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
domain_internal = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
external_applications = optional(
map(
object(
{
name = string
environement = string
port = optional(number, 61443)
}
)
),{}
)
organization = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
}
)
services = map(
object(
{
id = string
name = string
service_broker_guid = string
service_broker_name = string
service_plans = map(string)
space = string
}
)
)
space = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
org = string
org_name = string
quota = string
}
)
}
)
object({| n/a | yes | +| [secrets](#input\_secrets) | Sensitive credentials to be used to set application environmental variables. | `map(string)` | `{}` | no | +| [services](#input\_services) | Services generated from the service module. |
api_url = optional(string, "https://api.fr.cloud.gov")
apps = optional(
map(
object({
allow_egress = optional(bool, true)
buildpacks = list(string)
command = optional(string, "entrypoint.sh")
disk_quota = optional(number, 1024)
enable_ssh = optional(bool, false)
environment = optional(map(string), {})
health_check_timeout = optional(number, 180)
health_check_type = optional(string, "port")
instances = optional(number, 1)
labels = optional(map(string), {})
memory = optional(number, 96)
network_policies = optional(map(number),{})
port = optional(number, 80)
public_route = optional(bool, false)
space = optional(string ,null)
source = optional(string, null)
templates = list(map(string))
})
), {}
)
bootstrap_workspace = optional(string, "bootstrap")
defaults = object(
{
disk_quota = optional(number, 2048)
enable_ssh = optional(bool, true)
health_check_timeout = optional(number, 60)
health_check_type = optional(string, "port")
instances = optional(number, 1)
memory = optional(number, 64)
port = optional(number, 8080)
stack = optional(string, "cflinuxfs4")
stopped = optional(bool, false)
strategy = optional(string, "none")
timeout = optional(number, 300)
}
)
external_applications = optional(
map(
object({
enable_ssh = optional(bool, false)
instances = optional(number, 1)
memory = optional(number, 96)
port = optional(number, 61443)
})
), {}
)
external_domain = optional(string, "app.cloud.gov")
internal_domain = optional(string, "apps.internal")
name_pattern = string
organization = optional(string, "gsa-tts-usagov")
passwords = optional(
list(
object(
{
length = optional(number, 32)
}
)
), []
)
project = string
secrets = optional(
map(
object(
{
encrypted = bool
key = string
}
)
), {}
)
services = optional(
map(
object(
{
applications = optional(list(string), [])
environement = optional(string, "dev")
service_key = optional(bool, true)
service_plan = optional(string, "basic")
service_type = optional(string, "s3")
tags = optional(list(string), [])
}
)
), {}
)
space = string
})
object(| `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [apps](#output\_apps) | A `map` of [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource outputs. The key is the app name. | +| [external\_endpoints](#output\_external\_endpoints) | A map of external URL's (app.cloud.gov) to used to reach an application. The key is the app name. | +| [internal\_endpoints](#output\_internal\_endpoints) | A map of internal URL's (apps.internal) to used to reach an application. The key is the app name. | + +## Example + +```terraform +module "applications" { + source = "./modules/application" + + cloudfoundry = local.cloudfoundry + env = local.env + secrets = local.secrets + services = local.services +} +``` + +## Variables + +### cloudfoundry + +A variable that contains a `map(string)` of data lookups for pre-existing resources from Cloud.gov. This includes thing such as the organization and space ids. These are defined in `data.tf` in the root directory. + +### env + +A mixed type `object` variable that contains application settings. It is passed as an `any` type to allow optional variables to be ommitted from the object. It is defined in `locals.tf`, in the root directory. The object `local.env[terraform.workspace].apps` stores the values for the specific application that is to be deployed. + +Valid options are the attributes for the [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource. + +### secrets + +A variable that has secrets and other credentials that the application uses. The `local.secrets` variable is generated in `locals_dynamic.tf`, as it merges a variety of credentials from the random and services modules. + +### services + +A variable that contains a `map(map(string))` of the services deployed in the environment. `local.services` is generated in `locals_dynamic.tf`, due to needing to be generated after the creation of the services, after the instance id are known. The services are then bound to the application. + +See the [service module](../service/readme.MD) for more information. + +## Usage + +Here is an example of how to define an application in `locals.tf`. + +```terraform +locals { + env = { + workspace1 = { + apps = { + application1 = { + buildpacks = [ + "staticfile_buildpack" + ] + command = "./start" + disk_quota = 256 + enable_ssh = true + environment = { + environment = terraform.workspace + LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/" + } + health_check_timeout = 180 + health_check_type = "port" + instances = 1 + labels = { + environment = terraform.workspace + } + memory = 64 + port = 8080 + public_route = false + + source = "/path/to/application/directory" + + templates = [ + { + source = "${path.cwd}/path/to/templates/template.tmpl" + destination = "${path.cwd}}/path/to/templates/file" + } + ] + } + } + } + } +} +``` + +## Additional Notes + +- Buildpacks + - Valid built-in Cloud.gov buildpacks can be found by running `cf buildpacks` from the CLI. + - External buildpacks, such as the `apt-buildpack` by referencing the URL to the buildpack repository: [https://github.com/cloudfoundry/apt-buildpack](https://github.com/cloudfoundry/apt-buildpack). + \ No newline at end of file diff --git a/terraform/modules/application/data.tf b/terraform/modules/application/data.tf new file mode 100644 index 000000000..d820d7325 --- /dev/null +++ b/terraform/modules/application/data.tf @@ -0,0 +1,29 @@ +locals { + + ## Create a single list of external service names. Multiple applications + ## could reference the same service, but the GUID only needs to be looked up once. + services_external = distinct( + flatten( + [ + for key, value in try(var.env.apps, {}) : [ + try(var.env.apps[key].services_external, []) + ] + ] + ) + ) +} + +output "name" { + value = data.cloudfoundry_service_instance.this +} + +## Lookup up service instance GUID's for existing services. +## These can be externally deployed services or services deployed from different code sources. +## The GUID can then be refrenced by data.cloudfoundry_service_instance.this["service-name"].id +data "cloudfoundry_service_instance" "this" { + for_each = { + for key, value in local.services_external : value => value + } + name_or_id = each.value + space = try(var.cloudfoundry.space.id, null) +} \ No newline at end of file diff --git a/infra/modules/application/main.tf b/terraform/modules/application/main.tf similarity index 66% rename from infra/modules/application/main.tf rename to terraform/modules/application/main.tf index 26fb0fbb7..4e9c76c90 100755 --- a/infra/modules/application/main.tf +++ b/terraform/modules/application/main.tf @@ -1,36 +1,41 @@ locals { - - apps = merge( - try(var.env.apps, {}), - try(var.env.external_applications, {}) - ) - domains = merge( merge( flatten([ - for key, value in try(local.apps, {}) : { - "${key}_internal_endpoint" = "${format(var.env.name_pattern, key)}.${var.env.internal_domain}" - } if !try(value.public_route, false) + for key, value in try(var.env.apps, {}) : { + "${key}_internal_endpoint" = try(value.public_route, false) ? "${format(var.env.name_pattern, key)}.${var.env.external_domain}" : "${format(var.env.name_pattern, key)}.${var.env.internal_domain}" + } ]) ...), - merge( + merge( flatten([ - for key, value in try(local.apps, {}) : { - "${key}_external_endpoint" = "${format(var.env.name_pattern, key)}.${var.env.external_domain}" - } if try(value.public_route, false) + for key, value in try(var.env.external_applications, {}) : { + "${key}_internal_endpoint" = try(value.public_route, false) ? "${format(var.env.name_pattern, key)}.${var.env.external_domain}" : "${format(var.env.name_pattern, key)}.${var.env.internal_domain}" + } ]) ...) ) service_keys = merge( flatten([ - for key, value in try(var.env.services, {}) : [ - for k, v in try(var.services.service_key[key].credentials, {}) : { - "${key}_${k}" = v - } - ] if try(var.services.service_key[key].credentials, null) != null + for service_key, service_value in try(var.env.services, {}) : [ + for key, value in try(var.services.service_key[service_key].credentials, {}) : { + "${service_key}_${key}" = value + } if try(var.services.service_key[service_key].credentials, null) != null + ] ]) ...) + + service_bindings = merge( + flatten( + [ + for key, value in try(var.env.services, {}) : { + #svc_value.name => svc_value + "${key}" = value + } + ] + ) + ...) } resource "local_sensitive_file" "this" { @@ -50,8 +55,8 @@ resource "local_sensitive_file" "this" { each.value.source, merge( var.secrets, - local.service_keys, - local.domains + local.domains, + local.service_keys ) ) filename = each.value.destination @@ -60,8 +65,7 @@ resource "local_sensitive_file" "this" { data "archive_file" "this" { for_each = { for key, value in try(var.env.apps, {}) : key => value - if try(value.source, null) != null && - !endswith(try(value.source, ""), ".zip") + if try(value.source, null) != null && !endswith(try(value.source, ""), ".zip") } type = "zip" @@ -69,12 +73,13 @@ data "archive_file" "this" { output_path = "/tmp/${var.env.project}-${each.key}-${terraform.workspace}.zip" depends_on = [ - local_sensitive_file .this + local_sensitive_file.this ] } resource "cloudfoundry_app" "this" { - for_each = { for key, value in try(var.env.apps, {}) : key => value + for_each = { + for key, value in try(var.env.apps, {}) : key => value } buildpack = try(each.value.buildpack, null) @@ -85,7 +90,7 @@ resource "cloudfoundry_app" "this" { docker_image = try(each.value.docker_image, null) enable_ssh = try(each.value.enable_ssh, try(var.env.defaults.enable_ssh, true)) environment = try(each.value.environment, {}) - health_check_http_endpoint = try(each.value.health_check_http_endpoint, try(var.env.defaults.health_check_http_endpoint, "/")) + health_check_http_endpoint = try(each.value.health_check_http_endpoint, try(var.env.defaults.health_check_http_endpoint, null)) health_check_invocation_timeout = try(each.value.health_check_invocation_timeout, try(var.env.defaults.health_check_invocation_timeout, 5)) health_check_timeout = try(each.value.health_check_timeout, try(var.env.defaults.health_check_timeout, 180)) health_check_type = try(each.value.health_check_type, try(var.env.defaults.health_check_type, "port")) @@ -104,8 +109,7 @@ resource "cloudfoundry_app" "this" { dynamic "service_binding" { for_each = { for svc_key, svc_value in try(var.env.services, {}) : svc_key => svc_value - if contains(svc_value.applications, each.key) && - svc_value.service_type != "user-provided" + if contains(svc_value.applications, each.key) && svc_value.service_type != "user-provided" } content { service_instance = var.services.instance[service_binding.key].id @@ -127,6 +131,16 @@ resource "cloudfoundry_app" "this" { } } + ## Bind any external services, not deployed by the root code calling this module. + dynamic "service_binding" { + for_each = try(local.services_external, []) + content { + service_instance = data.cloudfoundry_service_instance.this[service_binding.value].id + params_json = try(var.env.services[service_binding.value].params_json, null) + params = try(var.env.services[service_binding.value].params, {}) + } + } + depends_on = [ data.archive_file.this, ] diff --git a/infra/modules/application/networking.tf b/terraform/modules/application/networking.tf similarity index 81% rename from infra/modules/application/networking.tf rename to terraform/modules/application/networking.tf index a23094aa1..b10803fe4 100755 --- a/infra/modules/application/networking.tf +++ b/terraform/modules/application/networking.tf @@ -3,7 +3,8 @@ locals { } resource "cloudfoundry_network_policy" "ingress_proxy" { - for_each = {for key, value in try(var.env.apps, {}) : key => value + for_each = { + for key, value in try(var.env.apps, []) : value.name => value if try(value.network_policy, null) != null && try(var.cloudfoundry.external_applications[value.network_policy.name].id, null) != null } @@ -16,14 +17,15 @@ resource "cloudfoundry_network_policy" "ingress_proxy" { } resource "cloudfoundry_network_policy" "egress_proxy" { - for_each = { for key, value in try(var.env.apps, {}) : key => value + for_each = { + for key, value in try(var.env.apps, []) : value.name => value if try(var.cloudfoundry.egress_app.id, null) != null && terraform.workspace != try(var.env.egress.workspace, null) } policy { source_app = cloudfoundry_app.this[each.key].id - destination_app = var.cloudfoundry.egress_app.id + destination_app = try(var.cloudfoundry.egress_app.id, null) port = try(var.env.egress.mtls_port, 61443) protocol = try(var.env.egress.protocol, "tcp") } diff --git a/infra/modules/application/output.tf b/terraform/modules/application/outputs.tf similarity index 100% rename from infra/modules/application/output.tf rename to terraform/modules/application/outputs.tf diff --git a/infra/modules/application/providers.tf b/terraform/modules/application/providers.tf similarity index 69% rename from infra/modules/application/providers.tf rename to terraform/modules/application/providers.tf index 2be1ef144..d106004d3 100644 --- a/infra/modules/application/providers.tf +++ b/terraform/modules/application/providers.tf @@ -2,8 +2,8 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.51.2" + version = "~> 0.5" } } - required_version = "> 1.4" + required_version = "> 1.7" } diff --git a/infra/modules/application/routes.tf b/terraform/modules/application/routes.tf similarity index 59% rename from infra/modules/application/routes.tf rename to terraform/modules/application/routes.tf index 8c8116890..f240b6284 100755 --- a/infra/modules/application/routes.tf +++ b/terraform/modules/application/routes.tf @@ -1,10 +1,11 @@ resource "cloudfoundry_route" "external" { for_each = { for key, value in try(var.env.apps, {}) : key => value - if value.public_route + if value.public_route && try(value.port, -1) != -1 } domain = var.cloudfoundry.domain_external.id - space = var.cloudfoundry.space.id + #space = var.cloudfoundry.space.id + space = try(var.cloudfoundry.spaces[each.value.space].id, var.cloudfoundry.space.id) hostname = format(var.env.name_pattern, each.key) port = try(cloudfoundry_app.this[each.key].port, null) @@ -15,12 +16,14 @@ resource "cloudfoundry_route" "external" { } resource "cloudfoundry_route" "internal" { - for_each = { for key, value in try(var.env.apps, {}) : key => value - if !value.public_route + for_each = { + for key, value in try(var.env.apps, {}) : key => value + if !value.public_route && try(value.port, -1) != -1 } domain = var.cloudfoundry.domain_internal.id - space = var.cloudfoundry.space.id + #space = var.cloudfoundry.space.id + space = try(var.cloudfoundry.spaces[each.value.space].id, var.cloudfoundry.space.id) hostname = format(var.env.name_pattern, each.key) port = try(cloudfoundry_app.this[each.key].port, null) diff --git a/terraform/modules/application/variables.tf b/terraform/modules/application/variables.tf new file mode 100755 index 000000000..e3747b8b0 --- /dev/null +++ b/terraform/modules/application/variables.tf @@ -0,0 +1,235 @@ +variable "cloudfoundry" { + description = "Cloudfoundry settings." + type = object( + { + domain_external = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + domain_internal = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + external_applications = optional( + map( + object( + { + name = string + environement = string + port = optional(number, 61443) + } + ) + ),{} + ) + organization = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + } + ) + services = map( + object( + { + id = string + name = string + service_broker_guid = string + service_broker_name = string + service_plans = map(string) + space = string + } + ) + ) + space = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + org = string + org_name = string + quota = string + } + ) + } + ) +} + +variable "env" { + description = "The settings object for this environment." + type = object({ + api_url = optional(string, "https://api.fr.cloud.gov") + apps = optional( + map( + object({ + allow_egress = optional(bool, true) + buildpacks = list(string) + command = optional(string, "entrypoint.sh") + disk_quota = optional(number, 1024) + enable_ssh = optional(bool, false) + environment = optional(map(string), {}) + health_check_timeout = optional(number, 180) + health_check_type = optional(string, "port") + instances = optional(number, 1) + labels = optional(map(string), {}) + memory = optional(number, 96) + network_policies = optional(map(number),{}) + port = optional(number, -1) + public_route = optional(bool, false) + services_external = optional(list(string), []) + space = optional(string ,null) + source = optional(string, null) + templates = list(map(string)) + }) + ), {} + ) + bootstrap_workspace = optional(string, "bootstrap") + defaults = object( + { + disk_quota = optional(number, 2048) + enable_ssh = optional(bool, true) + health_check_timeout = optional(number, 60) + health_check_type = optional(string, "port") + instances = optional(number, 1) + memory = optional(number, 64) + port = optional(number, 8080) + stack = optional(string, "cflinuxfs4") + stopped = optional(bool, false) + strategy = optional(string, "none") + timeout = optional(number, 300) + } + ) + external_applications = optional( + map( + object({ + enable_ssh = optional(bool, false) + instances = optional(number, 1) + memory = optional(number, 96) + port = optional(number, 61443) + }) + ), {} + ) + external_domain = optional(string, "app.cloud.gov") + internal_domain = optional(string, "apps.internal") + name_pattern = string + organization = optional(string, "gsa-tts-usagov") + passwords = optional( + map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ), {} + ) + project = string + secrets = optional( + map( + object( + { + encrypted = bool + key = string + } + ) + ), {} + ) + services = optional( + map( + object( + { + applications = optional(list(string), []) + environement = optional(string, "dev") + service_key = optional(bool, true) + service_plan = optional(string, "basic") + service_type = optional(string, "s3") + tags = optional(list(string), []) + } + ) + ), {} + ) + space = string + }) +} + +variable "secrets" { + description = "Sensitive credentials to be used to set application environmental variables." + type = map(string) + default = {} +} + +variable "services" { + description = "Services generated from the service module." + type = object( + { + instance = map( + object( + { + annotations = optional(string, null) + id = optional(string, null) + json_params = optional(string, null) + labels = optional(map(string), {}) + name = optional(string, null) + recursive_delete = optional(bool, null) + replace_on_params_change = optional(bool, false) + replace_on_service_plan_change = optional(bool, false) + service_plan = optional(string, null) + space = optional(string, null) + tags = optional(list(string), null) + } + ) + ) + user_provided = map( + object( + { + annotations = optional(string, null) + id = optional(string, null) + json_params = optional(string, null) + labels = optional(map(string), {}) + name = optional(string, null) + recursive_delete = optional(bool, null) + replace_on_params_change = optional(bool, false) + replace_on_service_plan_change = optional(bool, false) + service_plan = optional(string, null) + space = optional(string, null) + tags = optional(list(string), null) + } + ) + ) + service_key = map( + object( + { + name = optional(string, null) + service_instance = optional(string, null) + params = optional(map(string), null) + params_json = optional(string, null) + credentials = optional(map(string), {}) + } + ) + ) + } + ) + default = null +} \ No newline at end of file diff --git a/infra/modules/certificate/.terraform-docs.yaml b/terraform/modules/circleci/.terraform-docs.yaml similarity index 100% rename from infra/modules/certificate/.terraform-docs.yaml rename to terraform/modules/circleci/.terraform-docs.yaml diff --git a/infra/modules/circleci/.terraform-docs/footer.md b/terraform/modules/circleci/.terraform-docs/footer.md similarity index 100% rename from infra/modules/circleci/.terraform-docs/footer.md rename to terraform/modules/circleci/.terraform-docs/footer.md diff --git a/infra/modules/circleci/.terraform-docs/header.md b/terraform/modules/circleci/.terraform-docs/header.md similarity index 100% rename from infra/modules/circleci/.terraform-docs/header.md rename to terraform/modules/circleci/.terraform-docs/header.md diff --git a/infra/modules/circleci/README.md b/terraform/modules/circleci/README.md similarity index 100% rename from infra/modules/circleci/README.md rename to terraform/modules/circleci/README.md diff --git a/infra/modules/circleci/main.tf b/terraform/modules/circleci/main.tf similarity index 100% rename from infra/modules/circleci/main.tf rename to terraform/modules/circleci/main.tf diff --git a/infra/modules/circleci/providers.tf b/terraform/modules/circleci/providers.tf similarity index 80% rename from infra/modules/circleci/providers.tf rename to terraform/modules/circleci/providers.tf index e8acaedf6..3c44cb3ed 100644 --- a/infra/modules/circleci/providers.tf +++ b/terraform/modules/circleci/providers.tf @@ -5,5 +5,5 @@ terraform { version = "0.8.2" } } - required_version = "> 1.4" + required_version = "> 1.7" } diff --git a/infra/modules/circleci/variables.tf b/terraform/modules/circleci/variables.tf similarity index 100% rename from infra/modules/circleci/variables.tf rename to terraform/modules/circleci/variables.tf diff --git a/infra/modules/circleci/.terraform-docs.yaml b/terraform/modules/github/.terraform-docs.yaml similarity index 100% rename from infra/modules/circleci/.terraform-docs.yaml rename to terraform/modules/github/.terraform-docs.yaml diff --git a/terraform/modules/github/.terraform-docs/footer.md b/terraform/modules/github/.terraform-docs/footer.md new file mode 100755 index 000000000..e69de29bb diff --git a/terraform/modules/github/.terraform-docs/header.md b/terraform/modules/github/.terraform-docs/header.md new file mode 100755 index 000000000..70ad68afb --- /dev/null +++ b/terraform/modules/github/.terraform-docs/header.md @@ -0,0 +1 @@ +# Github Secrets and Variables diff --git a/terraform/modules/github/README.md b/terraform/modules/github/README.md new file mode 100644 index 000000000..82bd599ae --- /dev/null +++ b/terraform/modules/github/README.md @@ -0,0 +1,42 @@ + +# Github Secrets and Variables + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | > 1.7 | +| [github](#requirement\_github) | ~> 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | ~> 6.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_actions_secret.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_secret) | resource | +| [github_actions_variable.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_variable) | resource | +| [github_repository.this](https://registry.terraform.io/providers/integrations/github/latest/docs/data-sources/repository) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [env](#input\_env) | The settings object for this environment. |
{
instance = map(
object(
{
annotations = optional(string, null)
id = optional(string, null)
json_params = optional(string, null)
labels = optional(map(string), {})
name = optional(string, null)
recursive_delete = optional(bool, null)
replace_on_params_change = optional(bool, false)
replace_on_service_plan_change = optional(bool, false)
service_plan = optional(string, null)
space = optional(string, null)
tags = optional(list(string), null)
}
)
)
user_provided = map(
object(
{
annotations = optional(string, null)
id = optional(string, null)
json_params = optional(string, null)
labels = optional(map(string), {})
name = optional(string, null)
recursive_delete = optional(bool, null)
replace_on_params_change = optional(bool, false)
replace_on_service_plan_change = optional(bool, false)
service_plan = optional(string, null)
space = optional(string, null)
tags = optional(list(string), null)
}
)
)
service_key = map(
object(
{
name = optional(string, null)
service_instance = optional(string, null)
params = optional(map(string), null)
params_json = optional(string, null)
credentials = optional(map(string), {})
}
)
)
}
)
object({| n/a | yes | +| [github\_organization](#input\_github\_organization) | The organization to use with GitHub. | `string` | `"GSA"` | no | +| [github\_token](#input\_github\_token) | The token used authenticate with GitHub. | `string` | n/a | yes | +| [repository](#input\_repository) | The GitHub respository. | `string` | n/a | yes | +| [secrets](#input\_secrets) | Secrets to create in the respository. | `map(string)` | `{}` | no | + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/terraform/modules/github/main.tf b/terraform/modules/github/main.tf new file mode 100644 index 000000000..57722f76f --- /dev/null +++ b/terraform/modules/github/main.tf @@ -0,0 +1,18 @@ +data "github_repository" "this" { + full_name = var.repository +} + +resource "github_actions_secret" "this" { + for_each = { for key, value in try(var.env.secrets, []) : key => value } + repository = data.github_repository.this.name + secret_name = each.key + plaintext_value = !try(each.value.encrypted, false) ? try(var.secrets[each.value.key], null) : null + encrypted_value = try(each.value.encrypted, false) ? try(var.secrets[each.value.key], null) : null +} + +resource "github_actions_variable" "this" { + for_each = { for key, value in try(var.env.variables, []) : key => value } + repository = data.github_repository.this.name + variable_name = each.key + value = each.value.value +} diff --git a/terraform/modules/github/provider.tf b/terraform/modules/github/provider.tf new file mode 100644 index 000000000..a4f1bf27c --- /dev/null +++ b/terraform/modules/github/provider.tf @@ -0,0 +1,15 @@ +terraform { + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } + required_version = "> 1.7" +} + +# Configure the GitHub Provider +provider "github" { + owner = var.github_organization + token = var.github_token +} \ No newline at end of file diff --git a/terraform/modules/github/variables.tf b/terraform/modules/github/variables.tf new file mode 100644 index 000000000..fcac3b27e --- /dev/null +++ b/terraform/modules/github/variables.tf @@ -0,0 +1,126 @@ +variable "env" { + description = "The settings object for this environment." + type = object({ + api_url = optional(string, "https://api.fr.cloud.gov") + apps = optional( + map( + object({ + allow_egress = optional(bool, true) + buildpacks = list(string) + command = optional(string, "entrypoint.sh") + disk_quota = optional(number, 1024) + enable_ssh = optional(bool, false) + environment = optional(map(string), {}) + health_check_timeout = optional(number, 180) + health_check_type = optional(string, "port") + instances = optional(number, 1) + labels = optional(map(string), {}) + memory = optional(number, 96) + network_policies = optional(map(number),{}) + port = optional(number, 80) + public_route = optional(bool, false) + source = optional(string, null) + templates = list(map(string)) + }) + ), {} + ) + bootstrap_workspace = optional(string, "bootstrap") + defaults = object( + { + disk_quota = optional(number, 2048) + enable_ssh = optional(bool, true) + health_check_timeout = optional(number, 60) + health_check_type = optional(string, "port") + instances = optional(number, 1) + memory = optional(number, 64) + port = optional(number, 8080) + stack = optional(string, "cflinuxfs4") + stopped = optional(bool, false) + strategy = optional(string, "none") + timeout = optional(number, 300) + } + ) + external_applications = optional( + map( + object( + { + name = string + environement = string + port = optional(number, 61443) + } + ) + ),{} + ) + external_domain = optional(string, "app.cloud.gov") + internal_domain = optional(string, "apps.internal") + name_pattern = string + organization = optional(string, "gsa-tts-usagov") + passwords = optional( + map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ), {} + ) + project = string + secrets = optional( + map( + object( + { + encrypted = bool + key = string + } + ) + ), {} + ) + services = optional( + map( + object( + { + applications = optional(list(string), []) + environement = optional(string, "dev") + service_key = optional(bool, true) + service_plan = optional(string, "basic") + service_type = optional(string, "s3") + tags = optional(list(string), []) + } + ) + ), {} + ) + space = string + }) +} + +variable "github_organization" { + description = "The organization to use with GitHub." + type = string + default = "GSA" +} +variable "github_token" { + description = "The token used authenticate with GitHub." + type = string + sensitive = true +} + +variable "repository" { + description = "The GitHub respository." + type = string +} + +variable "secrets" { + default = {} + description = "Secrets to create in the respository." + type = map(string) +} diff --git a/infra/modules/random/.terraform-docs.yaml b/terraform/modules/random/.terraform-docs.yaml similarity index 100% rename from infra/modules/random/.terraform-docs.yaml rename to terraform/modules/random/.terraform-docs.yaml diff --git a/infra/modules/random/.terraform-docs/footer.md b/terraform/modules/random/.terraform-docs/footer.md similarity index 100% rename from infra/modules/random/.terraform-docs/footer.md rename to terraform/modules/random/.terraform-docs/footer.md diff --git a/infra/modules/random/.terraform-docs/header.md b/terraform/modules/random/.terraform-docs/header.md similarity index 100% rename from infra/modules/random/.terraform-docs/header.md rename to terraform/modules/random/.terraform-docs/header.md diff --git a/infra/modules/random/README.md b/terraform/modules/random/README.md similarity index 67% rename from infra/modules/random/README.md rename to terraform/modules/random/README.md index 2ef777eae..2f99e8a5e 100644 --- a/infra/modules/random/README.md +++ b/terraform/modules/random/README.md @@ -7,17 +7,14 @@ This module generates random credentials and hashes that can be used in various ## Requirements -| Name | Version | -|------|---------| -| [random](#requirement\_random) | 3.5.0 | -| [time](#requirement\_time) | 0.9.1 | +No requirements. ## Providers | Name | Version | |------|---------| -| [random](#provider\_random) | 3.5.0 | -| [time](#provider\_time) | 0.9.1 | +| [random](#provider\_random) | n/a | +| [time](#provider\_time) | n/a | ## Modules @@ -27,20 +24,19 @@ No modules. | Name | Type | |------|------| -| [random_password.multiple](https://registry.terraform.io/providers/hashicorp/random/3.5.0/docs/resources/password) | resource | -| [random_password.single](https://registry.terraform.io/providers/hashicorp/random/3.5.0/docs/resources/password) | resource | -| [time_rotating.multiple](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/rotating) | resource | -| [time_rotating.single](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/rotating) | resource | -| [time_static.multiple](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/static) | resource | -| [time_static.single](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/static) | resource | +| [random_password.multiple](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [random_password.single](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [time_rotating.multiple](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating) | resource | +| [time_rotating.single](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating) | resource | +| [time_static.multiple](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/static) | resource | +| [time_static.single](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/static) | resource | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [experation](#input\_experation) | A number, in days, when the password should be rotated. If none is set, the password will not rotate. | `number` | `0` | no | | [names](#input\_names) | List of unique names for the multiple resources. | `list(string)` | `[]` | no | -| [passwords](#input\_passwords) | A map of objects with password settings. | `any` | n/a | yes | +| [passwords](#input\_passwords) | A map of objects with password settings. |
api_url = optional(string, "https://api.fr.cloud.gov")
apps = optional(
map(
object({
allow_egress = optional(bool, true)
buildpacks = list(string)
command = optional(string, "entrypoint.sh")
disk_quota = optional(number, 1024)
enable_ssh = optional(bool, false)
environment = optional(map(string), {})
health_check_timeout = optional(number, 180)
health_check_type = optional(string, "port")
instances = optional(number, 1)
labels = optional(map(string), {})
memory = optional(number, 96)
network_policies = optional(map(number),{})
port = optional(number, 80)
public_route = optional(bool, false)
source = optional(string, null)
templates = list(map(string))
})
), {}
)
bootstrap_workspace = optional(string, "bootstrap")
defaults = object(
{
disk_quota = optional(number, 2048)
enable_ssh = optional(bool, true)
health_check_timeout = optional(number, 60)
health_check_type = optional(string, "port")
instances = optional(number, 1)
memory = optional(number, 64)
port = optional(number, 8080)
stack = optional(string, "cflinuxfs4")
stopped = optional(bool, false)
strategy = optional(string, "none")
timeout = optional(number, 300)
}
)
external_applications = optional(
map(
object(
{
name = string
environement = string
port = optional(number, 61443)
}
)
),{}
)
external_domain = optional(string, "app.cloud.gov")
internal_domain = optional(string, "apps.internal")
name_pattern = string
organization = optional(string, "gsa-tts-usagov")
passwords = optional(
list(
object(
{
length = optional(number, 32)
}
)
), []
)
project = string
secrets = optional(
map(
object(
{
encrypted = bool
key = string
}
)
), {}
)
services = optional(
map(
object(
{
applications = optional(list(string), [])
environement = optional(string, "dev")
service_key = optional(bool, true)
service_plan = optional(string, "basic")
service_type = optional(string, "s3")
tags = optional(list(string), [])
}
)
), {}
)
space = string
})
map(| n/a | yes | | [per\_workspace](#input\_per\_workspace) | Generate a password for each workspace. | `bool` | `false` | no | ## Outputs diff --git a/infra/modules/random/main.tf b/terraform/modules/random/main.tf similarity index 91% rename from infra/modules/random/main.tf rename to terraform/modules/random/main.tf index 997d6d074..0c63e2417 100755 --- a/infra/modules/random/main.tf +++ b/terraform/modules/random/main.tf @@ -13,7 +13,7 @@ locals { resource "time_rotating" "single" { for_each = { for key, value in var.passwords : key => value - if var.expiration > 0 + if try(value.expiration, 0) > 0 } rotation_days = each.value.expiration_days } @@ -21,7 +21,7 @@ resource "time_rotating" "single" { resource "time_static" "single" { for_each = { for key, value in var.passwords : key => value - if !var.per_workspace && var.expiration == 0 + if !var.per_workspace && try(value.expiration, 0) == 0 } } @@ -50,7 +50,7 @@ resource "random_password" "single" { resource "time_rotating" "multiple" { for_each = { for key, value in local.passwords : key => value - if var.per_workspace && var.expiration > 0 + if var.per_workspace && try(value.expiration, 0) > 0 } rotation_days = each.value.expiration_days } @@ -58,7 +58,7 @@ resource "time_rotating" "multiple" { resource "time_static" "multiple" { for_each = { for key, value in local.passwords : key => value - if var.per_workspace && var.expiration == 0 + if var.per_workspace && try(value.expiration, 0) == 0 } } diff --git a/infra/modules/random/outputs.tf b/terraform/modules/random/outputs.tf similarity index 100% rename from infra/modules/random/outputs.tf rename to terraform/modules/random/outputs.tf diff --git a/terraform/modules/random/variables.tf b/terraform/modules/random/variables.tf new file mode 100755 index 000000000..337e6a84f --- /dev/null +++ b/terraform/modules/random/variables.tf @@ -0,0 +1,32 @@ +variable "names" { + type = list(string) + description = "List of unique names for the multiple resources." + default = [] +} + +variable "passwords" { + description = "A map of objects with password settings." + type = map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ) +} + +variable "per_workspace" { + type = bool + description = "Generate a password for each workspace." + default = false +} \ No newline at end of file diff --git a/infra/modules/service/.terraform-docs.yaml b/terraform/modules/service/.terraform-docs.yaml similarity index 100% rename from infra/modules/service/.terraform-docs.yaml rename to terraform/modules/service/.terraform-docs.yaml diff --git a/infra/modules/service/.terraform-docs/footer.md b/terraform/modules/service/.terraform-docs/footer.md similarity index 100% rename from infra/modules/service/.terraform-docs/footer.md rename to terraform/modules/service/.terraform-docs/footer.md diff --git a/infra/modules/service/.terraform-docs/header.md b/terraform/modules/service/.terraform-docs/header.md similarity index 100% rename from infra/modules/service/.terraform-docs/header.md rename to terraform/modules/service/.terraform-docs/header.md diff --git a/terraform/modules/service/README.md b/terraform/modules/service/README.md new file mode 100644 index 000000000..a212a6c4e --- /dev/null +++ b/terraform/modules/service/README.md @@ -0,0 +1,87 @@ + +# CloudFoundry Service Module + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | > 1.7 | +| [cloudfoundry](#requirement\_cloudfoundry) | ~> 0.5 | + +## Providers + +| Name | Version | +|------|---------| +| [cloudfoundry](#provider\_cloudfoundry) | ~> 0.5 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [cloudfoundry_service_instance.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/service_instance) | resource | +| [cloudfoundry_service_key.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/service_key) | resource | +| [cloudfoundry_user_provided_service.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/user_provided_service) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cloudfoundry](#input\_cloudfoundry) | Cloudfoundry settings. |
object(
{
experation_days = optional(number, 0)
length = number
lower = optional(bool, false)
min_lower = optional(number, 0)
min_numeric = optional(number, 0)
min_special = optional(number, 0)
min_upper = optional(number, 0)
numeric = optional(bool, true)
override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?")
special = optional(bool, true)
upper = optional(bool, true)
}
)
)
object(| n/a | yes | +| [env](#input\_env) | The settings object for this environment. |
{
domain_external = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
domain_internal = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
external_applications = map(string)
organization = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
}
)
services = map(
object(
{
id = string
name = string
service_broker_guid = string
service_broker_name = string
service_plans = map(string)
space = string
}
)
)
space = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
org = string
org_name = string
quota = string
}
)
}
)
object({| n/a | yes | +| [secrets](#input\_secrets) | Sensitive strings to be added to the apps environmental variables. | `map` | `{}` | no | +| [skip\_service\_instances](#input\_skip\_service\_instances) | Allows the skipping of service instances. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | +| [skip\_user\_provided\_services](#input\_skip\_user\_provided\_services) | Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [results](#output\_results) | n/a | + +## Examples + +### Basic +```terraform +module "services" { + source = "./modules/service" + + cloudfoundry = local.cloudfoundry + env = local.env +} +``` + +### Advanced + +This advanced example will first generate service instances, such as RDS, along with other defined services, except for the `user defined` services. `User defined` services are useful for providing variables at runtime to applications. The issue is that until a service, such as RDS is deployed, their isn't a username and password created for that instance. + +The first step is to initalize any services that are not `user defined`, but setting `skip_user_provided_services` to `true`. + +```terraform +module "services" { + source = "./modules/service" + + cloudfoundry = local.cloudfoundry + env = local.env + + skip_user_provided_services = true +} +``` + +After the services are generated, another module block can be defined, which will pass a merged `map(string)` called `secrets`, that have the various information that is to be added to the `user defined` service. Setting the `skip_service_instances` to `true` will prevent the module from trying to redploy any non `user defined` service. + +```terraform +module "secrets" { + source = "./modules/service" + + cloudfoundry = local.cloudfoundry + env = local.env + + secrets = local.secrets + skip_service_instances = true +} +``` + \ No newline at end of file diff --git a/infra/modules/service/main.tf b/terraform/modules/service/main.tf similarity index 85% rename from infra/modules/service/main.tf rename to terraform/modules/service/main.tf index 31b5d3f06..ea18b7c88 100755 --- a/infra/modules/service/main.tf +++ b/terraform/modules/service/main.tf @@ -1,15 +1,15 @@ locals { credentials = merge( flatten([ - for key, value in try(var.env.services, {}) : { + for key, value in try(var.env.services,{}) : { "${key}" = { applications = value.applications service_type = value.service_type tags = value.tags credentials = merge( [ - for name in try(value.credentials, []) : { - try(nonsensitive("${name}"),"${name}") = try(try(nonsensitive(var.secrets[name]), var.secrets[name]), null) + for name in try(value.credentials, {}) : { + name = try(var.secrets[name], null) } ] ...) @@ -20,9 +20,6 @@ locals { ...) } -output "name" { - value = local.credentials -} resource "cloudfoundry_service_key" "this" { for_each = { @@ -53,7 +50,9 @@ resource "cloudfoundry_service_instance" "this" { } resource "cloudfoundry_user_provided_service" "this" { - for_each = local.credentials + for_each = { + for key, value in local.credentials : key => value + } name = format(var.env.name_pattern, each.key) space = var.cloudfoundry.space.id diff --git a/infra/modules/service/output.tf b/terraform/modules/service/output.tf similarity index 100% rename from infra/modules/service/output.tf rename to terraform/modules/service/output.tf diff --git a/infra/modules/service/providers.tf b/terraform/modules/service/providers.tf similarity index 69% rename from infra/modules/service/providers.tf rename to terraform/modules/service/providers.tf index 2be1ef144..d106004d3 100644 --- a/infra/modules/service/providers.tf +++ b/terraform/modules/service/providers.tf @@ -2,8 +2,8 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.51.2" + version = "~> 0.5" } } - required_version = "> 1.4" + required_version = "> 1.7" } diff --git a/terraform/modules/service/variables.tf b/terraform/modules/service/variables.tf new file mode 100755 index 000000000..4c633c9bb --- /dev/null +++ b/terraform/modules/service/variables.tf @@ -0,0 +1,182 @@ +variable "cloudfoundry" { + description = "Cloudfoundry settings." + type = object( + { + domain_external = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + domain_internal = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + external_applications = map(string) + organization = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + } + ) + services = map( + object( + { + id = string + name = string + service_broker_guid = string + service_broker_name = string + service_plans = map(string) + space = string + } + ) + ) + space = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + org = string + org_name = string + quota = string + } + ) + } + ) +} + +variable "env" { + description = "The settings object for this environment." + type = object({ + api_url = optional(string, "https://api.fr.cloud.gov") + apps = optional( + map( + object({ + allow_egress = optional(bool, true) + buildpacks = list(string) + command = optional(string, "entrypoint.sh") + disk_quota = optional(number, 1024) + enable_ssh = optional(bool, false) + environment = optional(map(string), {}) + health_check_timeout = optional(number, 180) + health_check_type = optional(string, "port") + instances = optional(number, 1) + labels = optional(map(string), {}) + memory = optional(number, 96) + network_policies = optional(map(number),{}) + port = optional(number, 80) + public_route = optional(bool, false) + source = optional(string, null) + templates = list(map(string)) + }) + ), {} + ) + bootstrap_workspace = optional(string, "bootstrap") + defaults = object( + { + disk_quota = optional(number, 2048) + enable_ssh = optional(bool, true) + health_check_timeout = optional(number, 60) + health_check_type = optional(string, "port") + instances = optional(number, 1) + memory = optional(number, 64) + port = optional(number, 8080) + stack = optional(string, "cflinuxfs4") + stopped = optional(bool, false) + strategy = optional(string, "none") + timeout = optional(number, 300) + } + ) + external_applications = optional( + map( + object( + { + environement = string + port = optional(number, 61443) + } + ) + ),{} + ) + external_domain = optional(string, "app.cloud.gov") + internal_domain = optional(string, "apps.internal") + name_pattern = string + organization = optional(string, "gsa-tts-usagov") + passwords = optional( + map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ), {} + ) + project = string + secrets = optional( + map( + object( + { + encrypted = bool + key = string + } + ) + ), {} + ) + services = optional( + map( + object( + { + applications = optional(list(string), []) + environement = optional(string, "dev") + service_key = optional(bool, true) + service_plan = optional(string, "basic") + service_type = optional(string, "s3") + tags = optional(list(string), []) + } + ) + ), {} + ) + space = string + }) +} + +variable "skip_service_instances" { + description = "Allows the skipping of service instances. Useful to inject service secrets into a user provided secret." + type = bool + default = false +} + +variable "skip_user_provided_services" { + description = "Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret." + type = bool + default = false +} + +variable "secrets" { + default = {} + description = "Sensitive strings to be added to the apps environmental variables." + type = map + sensitive = true +} \ No newline at end of file
api_url = optional(string, "https://api.fr.cloud.gov")
apps = optional(
map(
object({
allow_egress = optional(bool, true)
buildpacks = list(string)
command = optional(string, "entrypoint.sh")
disk_quota = optional(number, 1024)
enable_ssh = optional(bool, false)
environment = optional(map(string), {})
health_check_timeout = optional(number, 180)
health_check_type = optional(string, "port")
instances = optional(number, 1)
labels = optional(map(string), {})
memory = optional(number, 96)
network_policies = optional(map(number),{})
port = optional(number, 80)
public_route = optional(bool, false)
source = optional(string, null)
templates = list(map(string))
})
), {}
)
bootstrap_workspace = optional(string, "bootstrap")
defaults = object(
{
disk_quota = optional(number, 2048)
enable_ssh = optional(bool, true)
health_check_timeout = optional(number, 60)
health_check_type = optional(string, "port")
instances = optional(number, 1)
memory = optional(number, 64)
port = optional(number, 8080)
stack = optional(string, "cflinuxfs4")
stopped = optional(bool, false)
strategy = optional(string, "none")
timeout = optional(number, 300)
}
)
external_applications = optional(
map(
object(
{
environement = string
port = optional(number, 61443)
}
)
),{}
)
external_domain = optional(string, "app.cloud.gov")
internal_domain = optional(string, "apps.internal")
name_pattern = string
organization = optional(string, "gsa-tts-usagov")
passwords = optional(
list(
object(
{
length = optional(number, 32)
}
)
), []
)
project = string
secrets = optional(
map(
object(
{
encrypted = bool
key = string
}
)
), {}
)
services = optional(
map(
object(
{
applications = optional(list(string), [])
environement = optional(string, "dev")
service_key = optional(bool, true)
service_plan = optional(string, "basic")
service_type = optional(string, "s3")
tags = optional(list(string), [])
}
)
), {}
)
space = string
})