${ssh_passwd}
+
+if [ -d "${LOCAL}" ]; then
+ echo "Uploading folder '${LOCAL}'..."
+ sshpass -f "${ssh_passwd}" scp -F ${ssh_config} -r "${LOCAL}" "ssh.fr.cloud.gov:${REMOTE}"
+else
+ echo "Uploading file '${LOCAL}'..."
+ sshpass -f "${ssh_passwd}" scp -F ${ssh_config} "${LOCAL}" "ssh.fr.cloud.gov:${REMOTE}"
+fi
+
+echo "Upload complete."
\ No newline at end of file
diff --git a/scripts/pipeline/cloud-gov-waf-version.sh b/scripts/pipeline/cloud-gov-waf-version.sh
index fa29249f8..5015bc147 100755
--- a/scripts/pipeline/cloud-gov-waf-version.sh
+++ b/scripts/pipeline/cloud-gov-waf-version.sh
@@ -4,36 +4,49 @@ function version {
echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
}
-if [ "$(whoami)" != "root" ] ; then
- sudo wget -q --show-progress https://github.com/ericchiang/pup/releases/download/v0.4.0/pup_v0.4.0_linux_amd64.zip
- sudo unzip pup_v0.4.0_linux_amd64.zip -d /usr/local/bin
-else
- wget -q --show-progress https://github.com/ericchiang/pup/releases/download/v0.4.0/pup_v0.4.0_linux_amd64.zip
- unzip pup_v0.4.0_linux_amd64.zip -d /usr/local/bin
-fi
+# if [ "$(whoami)" != "root" ] ; then
+# sudo wget -q --show-progress https://github.com/ericchiang/pup/releases/download/v0.4.0/pup_v0.4.0_linux_amd64.zip
+# sudo unzip pup_v0.4.0_linux_amd64.zip -d /usr/local/bin
+# else
+# wget -q --show-progress https://github.com/ericchiang/pup/releases/download/v0.4.0/pup_v0.4.0_linux_amd64.zip
+# unzip pup_v0.4.0_linux_amd64.zip -d /usr/local/bin
+# fi
-declare CLOUDGOV_WF_VERSION
-CLOUDGOV_WF_VERSION=$(cf app "${PROJECT}-waf-${BRANCH}" | grep nginx_buildpack | xargs | awk '{print $2}')
+declare CURRENT_BP_VERSION
+CURRENT_BP_VERSION=$(cf app "${PROJECT}-waf-${BRANCH}" | grep nginx_buildpack | xargs | awk '{print $2}')
-declare CLOUDGOV_BP_VERSION
-CLOUDGOV_BP_VERSION=$(cf buildpacks | grep nginx | grep cflinuxfs4 | awk '{print $NF}' | grep -Eo '[0-9]\.[0-9]?(.[0-9]+)')
+declare NEW_BP_VERSION
+NEW_BP_VERSION=$(cf buildpacks | grep nginx | grep cflinuxfs4 | awk '{print $NF}' | grep -Eo '[0-9]\.[0-9]?(.[0-9]+)')
-bp_version=$(version "${CLOUDGOV_BP_VERSION}")
-waf_version=$(version "${CLOUDGOV_WF_VERSION}")
+new_bp_version=$(version "${NEW_BP_VERSION}")
+current_bp_version=$(version "${CURRENT_BP_VERSION}")
-if [ "${bp_version}" -ne "${waf_version}" ]; then
+if [ "${new_bp_version}" -ne "${current_bp_version}" ]; then
echo "New version of buildpack found!"
+
+ curl -Ls "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${CURRENT_BP_VERSION}" > /tmp/current_bp_version
declare current_nginx_version
- current_nginx_version=$(curl -s "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${CLOUDGOV_WF_VERSION}" | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text == "cflinuxfs4") | .children[].text' | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}')
+ current_nginx_version=$(cat /tmp/current_bp_version | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text == "cflinuxfs4") | .children[].text' | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}')
- declare nginx_version
- nginx_version=$(curl -s "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${CLOUDGOV_BP_VERSION}" | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text == "cflinuxfs4") | .children[].text' | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}')
+ curl -Ls "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${NEW_BP_VERSION}" > /tmp/new_nginx_version
+
+ declare default_nginx_binary_version
+ default_nginx_binary_version=$(cat /tmp/new_nginx_version | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text | contains(".x")) | .children[].text' | grep -v nginx | sed 's/.\{1\}$//')
+
+ declare new_nginx_version
+ new_nginx_version=$(cat /tmp/new_nginx_version | pup 'table json{}' | jq -r ".[].children[].children[] | select(.children[].text == \"nginx\") | select(.children[].text == \"cflinuxfs4\") | select(.children[].text | contains(\"${default_nginx_binary_version}\")) | .children[].text" | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}')
- echo "nginx_version=${nginx_version}" >> "${GITHUB_OUTPUT}"
+ echo "new_nginx_version=${new_nginx_version}" >> "${GITHUB_OUTPUT}"
echo "current_nginx_version=${current_nginx_version}" >> "${GITHUB_OUTPUT}"
- echo "cloudgov_wf_version=${CLOUDGOV_WF_VERSION}" >> "${GITHUB_OUTPUT}"
- echo "cloudgov_bp_version=${CLOUDGOV_BP_VERSION}" >> "${GITHUB_OUTPUT}"
+ echo "CURRENT_BP_VERSION=${CURRENT_BP_VERSION}" >> "${GITHUB_OUTPUT}"
+ echo "NEW_BP_VERSION=${NEW_BP_VERSION}" >> "${GITHUB_OUTPUT}"
echo "update=true" >> "${GITHUB_OUTPUT}"
else
echo "Running latest version of the buildpack!"
-fi
\ No newline at end of file
+fi
+
+ echo "new_nginx_version=${new_nginx_version}"
+ echo "current_nginx_version=${current_nginx_version}"
+ echo "CURRENT_BP_VERSION=${CURRENT_BP_VERSION}"
+ echo "NEW_BP_VERSION=${NEW_BP_VERSION}"
+ echo "update=true"
\ No newline at end of file
diff --git a/scripts/pipeline/cloud-gov-wait-for-app-start.sh b/scripts/pipeline/cloud-gov-wait-for-app-start.sh
new file mode 100755
index 000000000..176a7f81a
--- /dev/null
+++ b/scripts/pipeline/cloud-gov-wait-for-app-start.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+app_name=$1
+
+while : ; do
+ app_status=$( cf app "${app_name}" | grep "#0" | awk '{print $2}' )
+ if [ "${app_status}" == "running" ]; then
+ break
+ else
+ echo "waiting for application to start..."
+ sleep 2
+ fi
+done
\ No newline at end of file
diff --git a/scripts/pipeline/deb-basic-deps.sh b/scripts/pipeline/deb-basic-deps.sh
index 51e5c9346..4824710a2 100755
--- a/scripts/pipeline/deb-basic-deps.sh
+++ b/scripts/pipeline/deb-basic-deps.sh
@@ -1,10 +1,10 @@
#!/bin/bash
-apps="ca-certificates coreutils curl gnupg gettext jq wget"
+apps="ca-certificates coreutils curl gnupg gettext jq sshpass wget"
## To work for rootless and root images.
echo "Installing basic dependencies..."
-{
+#{
if [ "$(whoami)" != "root" ]; then
sudo apt-get update
sudo apt-get install -y $apps
@@ -12,4 +12,4 @@ echo "Installing basic dependencies..."
apt-get update
apt-get install -y $apps
fi
-} >/dev/null 2>&1
\ No newline at end of file
+#} >/dev/null 2>&1
\ No newline at end of file
diff --git a/scripts/pipeline/terraform-build-waf-plugin.sh b/scripts/pipeline/terraform-build-waf-plugin.sh
new file mode 100755
index 000000000..117e66758
--- /dev/null
+++ b/scripts/pipeline/terraform-build-waf-plugin.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+## The current root path.
+CWD=$(pwd)
+
+## Path to the WAF application.
+APP_PATH=terraform/applications/nginx-waf
+
+## Change directory to the Dockerfile path.
+cd "${APP_PATH}/.docker/" || exit 1
+
+## Run make, which builds the module and moves it to '../modules/'.
+make
+
+## Change directory back to the root path.
+cd "${CWD}" || exit 1
+
+## Make sure the module directory on the bastion exists.
+./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "mkdir -p px-benefit-finder/${APP_PATH}/modules/"
+
+## SCP the module to the bastion.
+./scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "${APP_PATH}/modules/" "px-benefit-finder/${APP_PATH}/"
\ No newline at end of file
diff --git a/scripts/pipeline/terraform-scp-tfvars.sh b/scripts/pipeline/terraform-scp-tfvars.sh
new file mode 100755
index 000000000..ba8ffa4e0
--- /dev/null
+++ b/scripts/pipeline/terraform-scp-tfvars.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+TF_PATH="terraform/infra"
+
+cd "./${TF_PATH}" || exit 1
+
+## Update terraform.tfvars with pipeline user/password.
+envsubst < terraform.tfvars.tmpl > terraform.tfvars
+
+cd "${CWD}" || exit 1
+
+## SCP the file to the bastion.
+./scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "./${TF_PATH}/terraform.tfvars" "px-benefit-finder/${TF_PATH}"
\ No newline at end of file
diff --git a/infra/.gitignore b/terraform/.gitignore
similarity index 98%
rename from infra/.gitignore
rename to terraform/.gitignore
index 419387d71..864371117 100755
--- a/infra/.gitignore
+++ b/terraform/.gitignore
@@ -3,7 +3,6 @@ terraform.tfstate.d
**terraform*
**.tfvars*
**tfplan*
-provider.tf
## Other files/folders
*.bak
diff --git a/infra/README.md b/terraform/README.md
similarity index 73%
rename from infra/README.md
rename to terraform/README.md
index a02d508d4..00b6356ef 100644
--- a/infra/README.md
+++ b/terraform/README.md
@@ -3,12 +3,13 @@
- Applications
- [Caddy Proxy](applications/caddy-proxy/README.md)
- [Web Application Firewall](applications/nginx-waf/README.md)
+ - [Terraform Bastion](applications/tf-bastion/README.md)
- [Scripts](docs/scripts.MD)
- Terraform
- modules
- [application](modules/application/README.md)
- - [certificate](modules/certificate/README.md)
- - [circleci](modules/circleci/README.md)
+ - [certificate](modules/circleci/README.md)
+ - [circleci](modules/github/README.md)
- [random](modules/random/README.md)
- [service](modules/service/README.md)
- [locals.tf](docs/locals.tf.MD)
diff --git a/infra/applications/caddy-proxy/.docker/Dockerfile b/terraform/applications/caddy-proxy/.docker/Dockerfile
similarity index 100%
rename from infra/applications/caddy-proxy/.docker/Dockerfile
rename to terraform/applications/caddy-proxy/.docker/Dockerfile
diff --git a/infra/applications/caddy-proxy/.docker/Makefile b/terraform/applications/caddy-proxy/.docker/Makefile
similarity index 100%
rename from infra/applications/caddy-proxy/.docker/Makefile
rename to terraform/applications/caddy-proxy/.docker/Makefile
diff --git a/infra/applications/caddy-proxy/Caddyfile.tmpl b/terraform/applications/caddy-proxy/Caddyfile.tmpl
similarity index 100%
rename from infra/applications/caddy-proxy/Caddyfile.tmpl
rename to terraform/applications/caddy-proxy/Caddyfile.tmpl
diff --git a/infra/applications/caddy-proxy/README.md b/terraform/applications/caddy-proxy/README.md
similarity index 100%
rename from infra/applications/caddy-proxy/README.md
rename to terraform/applications/caddy-proxy/README.md
diff --git a/infra/applications/caddy-proxy/start b/terraform/applications/caddy-proxy/start
similarity index 100%
rename from infra/applications/caddy-proxy/start
rename to terraform/applications/caddy-proxy/start
diff --git a/infra/applications/nginx-waf/.docker/Dockerfile b/terraform/applications/nginx-waf/.docker/Dockerfile
similarity index 100%
rename from infra/applications/nginx-waf/.docker/Dockerfile
rename to terraform/applications/nginx-waf/.docker/Dockerfile
diff --git a/infra/applications/nginx-waf/.docker/Makefile b/terraform/applications/nginx-waf/.docker/Makefile
similarity index 100%
rename from infra/applications/nginx-waf/.docker/Makefile
rename to terraform/applications/nginx-waf/.docker/Makefile
diff --git a/infra/applications/nginx-waf/README.md b/terraform/applications/nginx-waf/README.md
similarity index 100%
rename from infra/applications/nginx-waf/README.md
rename to terraform/applications/nginx-waf/README.md
diff --git a/infra/applications/nginx-waf/apt.yml b/terraform/applications/nginx-waf/apt.yml
similarity index 100%
rename from infra/applications/nginx-waf/apt.yml
rename to terraform/applications/nginx-waf/apt.yml
diff --git a/infra/applications/nginx-waf/entrypoint b/terraform/applications/nginx-waf/entrypoint
similarity index 100%
rename from infra/applications/nginx-waf/entrypoint
rename to terraform/applications/nginx-waf/entrypoint
diff --git a/infra/applications/nginx-waf/init b/terraform/applications/nginx-waf/init
similarity index 99%
rename from infra/applications/nginx-waf/init
rename to terraform/applications/nginx-waf/init
index d02f680fb..b8a9b955a 100755
--- a/infra/applications/nginx-waf/init
+++ b/terraform/applications/nginx-waf/init
@@ -54,7 +54,7 @@ rm -rf /tmp/owasp-crs
if [ -n "${MODSECURITY_UPDATE}" ]; then
modsecurity_version=$(echo "${MODSECURITY_UPDATE}" | cut -d '_' -f2 | cut -d '-' -f1)
echo "Updating libmodsecurity..."
- current_path=$(pwd)
+ #current_path=$(pwd)
dpkg-deb -R "${app_path}/packages/${MODSECURITY_UPDATE}" ${home}/deps/0/apt/
ln -s "${home}/deps/0/apt/usr/lib/x86_64-linux-gnu/libmodsecurity.so.${modsecurity_version}" "libmodsecurity.so.${modsecurity_version}"
diff --git a/infra/applications/nginx-waf/mime.types b/terraform/applications/nginx-waf/mime.types
similarity index 100%
rename from infra/applications/nginx-waf/mime.types
rename to terraform/applications/nginx-waf/mime.types
diff --git a/infra/applications/nginx-waf/modsecurity.conf b/terraform/applications/nginx-waf/modsecurity.conf
similarity index 100%
rename from infra/applications/nginx-waf/modsecurity.conf
rename to terraform/applications/nginx-waf/modsecurity.conf
diff --git a/infra/applications/nginx-waf/modsecurity/crs-setup.conf b/terraform/applications/nginx-waf/modsecurity/crs-setup.conf
similarity index 100%
rename from infra/applications/nginx-waf/modsecurity/crs-setup.conf
rename to terraform/applications/nginx-waf/modsecurity/crs-setup.conf
diff --git a/infra/applications/nginx-waf/modsecurity/mod-sec-rules.conf b/terraform/applications/nginx-waf/modsecurity/mod-sec-rules.conf
similarity index 100%
rename from infra/applications/nginx-waf/modsecurity/mod-sec-rules.conf
rename to terraform/applications/nginx-waf/modsecurity/mod-sec-rules.conf
diff --git a/infra/applications/nginx-waf/modsecurity/modsecurity-override.conf b/terraform/applications/nginx-waf/modsecurity/modsecurity-override.conf
similarity index 100%
rename from infra/applications/nginx-waf/modsecurity/modsecurity-override.conf
rename to terraform/applications/nginx-waf/modsecurity/modsecurity-override.conf
diff --git a/infra/applications/nginx-waf/modsecurity/modsecurity.conf b/terraform/applications/nginx-waf/modsecurity/modsecurity.conf
similarity index 100%
rename from infra/applications/nginx-waf/modsecurity/modsecurity.conf
rename to terraform/applications/nginx-waf/modsecurity/modsecurity.conf
diff --git a/infra/applications/nginx-waf/modsecurity/modsecurity.conf-recommended b/terraform/applications/nginx-waf/modsecurity/modsecurity.conf-recommended
similarity index 100%
rename from infra/applications/nginx-waf/modsecurity/modsecurity.conf-recommended
rename to terraform/applications/nginx-waf/modsecurity/modsecurity.conf-recommended
diff --git a/infra/applications/nginx-waf/modsecurity/unicode.mapping b/terraform/applications/nginx-waf/modsecurity/unicode.mapping
similarity index 100%
rename from infra/applications/nginx-waf/modsecurity/unicode.mapping
rename to terraform/applications/nginx-waf/modsecurity/unicode.mapping
diff --git a/infra/applications/nginx-waf/nginx.conf b/terraform/applications/nginx-waf/nginx.conf
similarity index 100%
rename from infra/applications/nginx-waf/nginx.conf
rename to terraform/applications/nginx-waf/nginx.conf
diff --git a/infra/applications/nginx-waf/nginx/conf.d/default.conf b/terraform/applications/nginx-waf/nginx/conf.d/default.conf
similarity index 100%
rename from infra/applications/nginx-waf/nginx/conf.d/default.conf
rename to terraform/applications/nginx-waf/nginx/conf.d/default.conf
diff --git a/infra/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf b/terraform/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf
similarity index 100%
rename from infra/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf
rename to terraform/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf
diff --git a/infra/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh b/terraform/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh
similarity index 100%
rename from infra/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh
rename to terraform/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh
diff --git a/infra/applications/nginx-waf/nginx/dynamic/domains-deny.list b/terraform/applications/nginx-waf/nginx/dynamic/domains-deny.list
similarity index 100%
rename from infra/applications/nginx-waf/nginx/dynamic/domains-deny.list
rename to terraform/applications/nginx-waf/nginx/dynamic/domains-deny.list
diff --git a/infra/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl
similarity index 100%
rename from infra/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl
rename to terraform/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl
diff --git a/infra/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl
similarity index 100%
rename from infra/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl
rename to terraform/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl
diff --git a/infra/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf b/terraform/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf
similarity index 100%
rename from infra/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf
rename to terraform/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf
diff --git a/infra/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl
similarity index 92%
rename from infra/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl
rename to terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl
index 8d515425e..b62bb7bd9 100644
--- a/infra/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl
+++ b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl
@@ -1,7 +1,7 @@
set $cf_forwarded_host "$host";
set $cf_forwarded_uri "$request_uri";
-set $cf_destination_host "${drupal_internal_endpoint}";
+set $cf_destination_host "${cms_internal_endpoint}";
set $cf_destination_port "61443";
set $base_host "$cf_forwarded_host";
diff --git a/infra/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl
similarity index 100%
rename from infra/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl
rename to terraform/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl
diff --git a/infra/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl
similarity index 100%
rename from infra/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl
rename to terraform/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl
diff --git a/infra/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz b/terraform/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz
similarity index 100%
rename from infra/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz
rename to terraform/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz
diff --git a/infra/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb b/terraform/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb
similarity index 100%
rename from infra/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb
rename to terraform/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb
diff --git a/infra/applications/nginx-waf/public/index.html b/terraform/applications/nginx-waf/public/index.html
similarity index 100%
rename from infra/applications/nginx-waf/public/index.html
rename to terraform/applications/nginx-waf/public/index.html
diff --git a/infra/applications/nginx-waf/start b/terraform/applications/nginx-waf/start
similarity index 100%
rename from infra/applications/nginx-waf/start
rename to terraform/applications/nginx-waf/start
diff --git a/terraform/applications/tf-bastion/apt.yml b/terraform/applications/tf-bastion/apt.yml
new file mode 100755
index 000000000..5f6bd5ca8
--- /dev/null
+++ b/terraform/applications/tf-bastion/apt.yml
@@ -0,0 +1,6 @@
+---
+packages:
+ - curl
+ - gettext
+ - git
+ - wget
diff --git a/terraform/applications/tf-bastion/start b/terraform/applications/tf-bastion/start
new file mode 100755
index 000000000..4a8365fa8
--- /dev/null
+++ b/terraform/applications/tf-bastion/start
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+home="/home/vcap"
+#app_path="${home}/app"
+
+rm /home/vcap/deps/0/bin/tofu
+wget -q "https://github.com/opentofu/opentofu/releases/download/v${OPENTOFU_VERSION}/tofu_${OPENTOFU_VERSION}_amd64.deb"
+dpkg-deb -R "tofu_${OPENTOFU_VERSION}_amd64.deb" ${home}/deps/0/apt/
+ln -s "${home}/deps/0/apt/usr/bin/tofu" "${home}/deps/0/bin/tofu"
+rm -f "tofu_${OPENTOFU_VERSION}_amd64.deb"
+
+PG_CONN_STR=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.uri')
+PGDATABASE=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.db_name')
+PGHOST=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.host')
+PGPASSWORD=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.password')
+PGPORT=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.port')
+PGUSER=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.username')
+
+echo "export PATH=${PATH}:${home}/deps/0/bin" > "${home}/exports.sh"
+echo "alias terraform=tofu" >> "${home}/exports.sh"
+echo "alias tf=tofu" >> "${home}/exports.sh"
+
+{
+ echo "export PG_CONN_STR=${PG_CONN_STR}"
+ echo "export PGDATABASE=${PGDATABASE}"
+ echo "export PGHOST=${PGHOST}"
+ echo "export PGPASSWORD=${PGPASSWORD}"
+ echo "export PGPORT=${PGPORT}"
+ echo "export PGUSER=${PGUSER}"
+} > "${home}/exports.sh"
+
+sed -i '1s/^/source exports.sh\n/' "${home}/.bashrc"
+
+while : ; do sleep 500 ; done
+
diff --git a/terraform/bootstrap/.terraform.lock.hcl b/terraform/bootstrap/.terraform.lock.hcl
new file mode 100644
index 000000000..03908252d
--- /dev/null
+++ b/terraform/bootstrap/.terraform.lock.hcl
@@ -0,0 +1,136 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/cloudfoundry-community/cloudfoundry" {
+ version = "0.53.1"
+ constraints = "~> 0.5"
+ hashes = [
+ "h1:o6nGtINonmkgsX810QianzlX+y+aJ7WzYRwAvhQu5qE=",
+ "zh:017a55cdbd444ccf8fe45a3c7cdbc08ddf4f0f13550fcd457c31df9b2cfdb767",
+ "zh:100e9bd10868547193134082427abebad9db6359f6139a882192232e8e6911e3",
+ "zh:34467f6504e8527bd3e18e372d5386a43f2bffd88abf54bb72d51f04ab3e4e23",
+ "zh:3a278f5f71e39d29c7db999e2a34e8135b79cee4f36510b0f2c2dfec47997cf1",
+ "zh:3be1fbe17382c91561b1985d372606d802513d94bae6368e1bafd8dd49494737",
+ "zh:3f12bd7a629d547c706c380d9499ff39eab7b8824a14662aa446f230304bdd3a",
+ "zh:404acaa9ad7f95e83baf2332be54c065c21053bf304e80ac41ae49719462b184",
+ "zh:5ac5f6159d1e0c989e739cf16aa8dede6cee3562a6262bf9f2c6b53f4da866fe",
+ "zh:7a440ee173e69fa153ea4baea47adfca34d7171ffc83e7a1c0ec319d28998cbc",
+ "zh:87e2200bf66443671e249108d1cfa4aa13a31b9fdf445cec88364db8ea6be623",
+ "zh:b1b20b2b751df7765225cee5b01290b06e245e50faa8053495c2ef5ebe316998",
+ "zh:c8ddda9cf7dff40d762ea4dc22941c993ae8e9b2388c8d421f43254a56c98482",
+ "zh:d6ce83f0077a9f6262ffa1f7d777e2b72feac7ea7c8735aa39a5f86b4f3f7084",
+ "zh:d74126b9189ab4ca137ca634eaa25c571491bdd2456ccd0f3276a6d49163e412",
+ "zh:db5d415346e03eac0c5e025f9c10afdebfff35487e8a8383b3c4cd867c422fe2",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/archive" {
+ version = "2.4.2"
+ hashes = [
+ "h1:tZcueUOGqjDRRzW9b6BMwV++XRqABodQjgC/K3bRoXM=",
+ "zh:0fee4f61bc999b5174a1268295e04c91c3f6be0160022cb53943b6ec0a3f1055",
+ "zh:10a895ee751beec68727d3dc6bf8e670f499618bb4b02649544be2c73e89603e",
+ "zh:1118373dfc03cf524273573e3aff9c99e0bb7128ab3ce0be211fd30e3928dfb8",
+ "zh:19c1b4c785f1d864e4fcaec7d96045437494efc333f1e661ea9994cd5c969cdb",
+ "zh:23f0aa399394ce8aa918a6a16ca9f5451d9d5b021e1b08929eb7972f65cb27da",
+ "zh:27d5daeec1819019a4b94c4980c09626e9cf71de3f54128a621fddb1b94b9ece",
+ "zh:56244088a96ff9e3a04b23de0ce2fcfa92c1a5fe6c91c6357cceda4d6d441c17",
+ "zh:578fcb23e8ebde3c5be6c5c67377b5e0c404cc807a74d7087e70c8fb3bb59b92",
+ "zh:9709d108559da5066f24a6d28be661b65a02e908f89b91fe42fc493962a5f466",
+ "zh:ff2a6df5d22bda78ca284756801ba7c86504e4bf0b48b31c8f5af44eefd9d0e8",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/github" {
+ version = "6.2.1"
+ hashes = [
+ "h1:uDerb9YJo3vAO+wKw+Z064InX5aXom+nKLDry2eGf14=",
+ "zh:172aa5141c525174f38504a0d2e69d0d16c0a0b941191b7170fe6ae4d7282e30",
+ "zh:1a098b731fa658c808b591d030cc17cc7dfca1bf001c3c32e596f8c1bf980e9f",
+ "zh:245d6a1c7e632d8ae4bdd2da2516610c50051e81505cf420a140aa5fa076ea90",
+ "zh:43c61c230fb4ed26ff1b04b857778e65be3d8f80292759abbe2a9eb3c95f6d97",
+ "zh:59bb7dd509004921e4322a196be476a2f70471b462802f09d03d6ce96f959860",
+ "zh:5cb2ab8035d015c0732107c109210243650b6eb115e872091b0f7b98c2763777",
+ "zh:69d2a6acfcd686f7e859673d1c8a07fc1fc1598a881493f19d0401eb74c0f325",
+ "zh:77f36d3f46911ace5c50dee892076fddfd64a289999a5099f8d524c0143456d1",
+ "zh:87df41097dfcde72a1fbe89caca882af257a4763c2e1af669c74dcb8530f9932",
+ "zh:899dbe621f32d58cb7c6674073a6db8328a9db66eecfb0cc3fc13299fd4e62e7",
+ "zh:ad2eb7987f02f7dd002076f65a685730705d04435313b5cf44d3a6923629fb29",
+ "zh:b2145ae7134dba893c7f74ad7dfdc65fdddf6c7b1d0ce7e2f3baa96212322fd8",
+ "zh:bd6bae3ac5c3f96ad9219d3404aa006ef1480e9041d4c95df1808737e37d911b",
+ "zh:e89758b20ae59f1b9a6d32c107b17846ddca9634b868cf8f5c927cbb894b1b1f",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/local" {
+ version = "2.5.1"
+ hashes = [
+ "h1:87L+rpGao062xifb1VuG9YVFwp9vbDP6G2fgfYxUkQs=",
+ "zh:031c2c2070672b7e78e0aa15560839278dc57fe7cf1e58a617ac13c67b31d5fb",
+ "zh:1ef64ea4f8382cd538a76f3d319f405d18130dc3280f1c16d6aaa52a188ecaa4",
+ "zh:422ce45691b2f384dbd4596fdc8209d95cb43d85a82aaa0173089d38976d6e96",
+ "zh:7415fbd8da72d9363ba55dd8115837714f9534f5a9a518ec42268c2da1b9ed2f",
+ "zh:92aa22d071339c8ef595f18a9f9245c287266c80689f5746b26e10eaed04d542",
+ "zh:9cd0d99f5d3be835d6336c19c4057af6274e193e677ecf6370e5b0de12b4aafe",
+ "zh:a8c1525b389be5809a97f02aa7126e491ba518f97f57ed3095a3992f2134bb8f",
+ "zh:b336fa75f72643154b07c09b3968e417a41293358a54fe03efc0db715c5451e6",
+ "zh:c66529133599a419123ad2e42874afbd9aba82bd1de2b15cc68d2a1e665d4c8e",
+ "zh:c7568f75ba6cb7c3660b69eaab8b0e4278533bd9a7a4c33ee6590cc7e69743ea",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/random" {
+ version = "3.6.2"
+ hashes = [
+ "h1:cwVnVdQqyli6MhRE74KtT70s6MepGHFFQu+oKcbETP4=",
+ "zh:1f27612f7099441526d8af59f5b4bdcc35f46915df5d243043d7337ea5a3e38a",
+ "zh:2a58e66502825db8b4b96116c04bd0323bca1cf1f5752bdd8f9c26feb84d3b1e",
+ "zh:4f0a4fa479e29de0c3c90146fd58799c097f7a55401cb00560dd4e9b1e6fad9d",
+ "zh:9c93c0fe6ef685513734527e0c8078636b2cc07591427502a7260f4744b1af1d",
+ "zh:a466ff5219beb77fb3b18a3d7e7fe30e7edd4d95c8e5c87f4f4e3fe3eeb8c2d7",
+ "zh:ab33e6176d0c757ddb31e40e01a941e6918ad10f7a786c8e8e4f35e5cff81c96",
+ "zh:b6eabf377a1c12cb3f9ddd97aacdd5b49c1646dc959074124f81d40fcd216d7e",
+ "zh:ccec5d03d0d1c0f354be299cdd6a417b2700f1a6781df36bcce77246b2f57e50",
+ "zh:d2a7945eeb691fdd2b1474da76ddc2d1655e2aedbb14b57f06d4f5123d47adf9",
+ "zh:ed62351f4ad9d1469c6798b77dee5f63b18b29c473620a0046ba3d4f111b621d",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/time" {
+ version = "0.11.2"
+ hashes = [
+ "h1:qxivXg2too+UTcA8O3ZOdajC1lpTfKHXA/CCcVOgpsc=",
+ "zh:534d56fac2daa5c15737fa1907b7afe2d91474197a0ec272d3f864d2a0a87743",
+ "zh:67c7765dc3b5ec19b6df32c29620de4b3e7846f5aa1a6b1b0e15394d87d0f875",
+ "zh:9502412f028d17051ea0550f5015d0515da78bc938f415191d9171742481330c",
+ "zh:982984971fe6cb87600f118fbf754e7f60f7ad343735a4f99d632bf148319cee",
+ "zh:9b0bedf8781be6c414f314989246a3b9a79302845db57dbe497d768cd56ee73e",
+ "zh:a35244d464dcad8940060c49b66a2f653d2dfc4e471c2dd59e2118a17a279427",
+ "zh:c8e3534b8cdff62f88a9882aed8c6dc4484639662bff2ea4e89499f1209b9ba9",
+ "zh:e78c88613bb6eab52d94bb7592d4df3a79bf69b9f1e50a86f9d9174db7e5251c",
+ "zh:fb28ffd2b641a449bd5526bef6e547894b4c5ddac0cef05ee03881a6c53eac39",
+ "zh:fcfcd35ff1178cbf11034e2eaf4254f9aadd8531f75d1b15b5e245715183d49b",
+ ]
+}
+
+provider "registry.opentofu.org/integrations/github" {
+ version = "6.2.1"
+ constraints = "~> 6.0"
+ hashes = [
+ "h1:uDerb9YJo3vAO+wKw+Z064InX5aXom+nKLDry2eGf14=",
+ "zh:172aa5141c525174f38504a0d2e69d0d16c0a0b941191b7170fe6ae4d7282e30",
+ "zh:1a098b731fa658c808b591d030cc17cc7dfca1bf001c3c32e596f8c1bf980e9f",
+ "zh:245d6a1c7e632d8ae4bdd2da2516610c50051e81505cf420a140aa5fa076ea90",
+ "zh:43c61c230fb4ed26ff1b04b857778e65be3d8f80292759abbe2a9eb3c95f6d97",
+ "zh:59bb7dd509004921e4322a196be476a2f70471b462802f09d03d6ce96f959860",
+ "zh:5cb2ab8035d015c0732107c109210243650b6eb115e872091b0f7b98c2763777",
+ "zh:69d2a6acfcd686f7e859673d1c8a07fc1fc1598a881493f19d0401eb74c0f325",
+ "zh:77f36d3f46911ace5c50dee892076fddfd64a289999a5099f8d524c0143456d1",
+ "zh:87df41097dfcde72a1fbe89caca882af257a4763c2e1af669c74dcb8530f9932",
+ "zh:899dbe621f32d58cb7c6674073a6db8328a9db66eecfb0cc3fc13299fd4e62e7",
+ "zh:ad2eb7987f02f7dd002076f65a685730705d04435313b5cf44d3a6923629fb29",
+ "zh:b2145ae7134dba893c7f74ad7dfdc65fdddf6c7b1d0ce7e2f3baa96212322fd8",
+ "zh:bd6bae3ac5c3f96ad9219d3404aa006ef1480e9041d4c95df1808737e37d911b",
+ "zh:e89758b20ae59f1b9a6d32c107b17846ddca9634b868cf8f5c927cbb894b1b1f",
+ ]
+}
diff --git a/terraform/bootstrap/README.md b/terraform/bootstrap/README.md
new file mode 100644
index 000000000..c309a92dd
--- /dev/null
+++ b/terraform/bootstrap/README.md
@@ -0,0 +1,52 @@
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | > 1.7 |
+| [cloudfoundry](#requirement\_cloudfoundry) | ~> 0.5 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [cloudfoundry](#provider\_cloudfoundry) | 0.53.1 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [applications](#module\_applications) | ../modules/application | n/a |
+| [github](#module\_github) | ../modules/github | n/a |
+| [random](#module\_random) | ../modules/random | n/a |
+| [services](#module\_services) | ../modules/service | n/a |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [cloudfoundry_app.external_applications](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/app) | data source |
+| [cloudfoundry_domain.external](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/domain) | data source |
+| [cloudfoundry_domain.internal](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/domain) | data source |
+| [cloudfoundry_org.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/org) | data source |
+| [cloudfoundry_service.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/service) | data source |
+| [cloudfoundry_space.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/space) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [cloudgov\_organization](#input\_cloudgov\_organization) | The organization for the cloud.gov account. | `string` | n/a | yes |
+| [cloudgov\_password](#input\_cloudgov\_password) | The password for the cloud.gov account. | `string` | n/a | yes |
+| [cloudgov\_space](#input\_cloudgov\_space) | The organization for the cloud.gov account. | `string` | n/a | yes |
+| [cloudgov\_username](#input\_cloudgov\_username) | The username for the cloudfoundry account. | `string` | 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 |
+| [mtls\_port](#input\_mtls\_port) | The default port to direct traffic to. Envoy proxy listens on 61443 and redirects to 8080, which the application should listen on. | `number` | `61443` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [name](#output\_name) | n/a |
+
\ No newline at end of file
diff --git a/terraform/bootstrap/data.tf b/terraform/bootstrap/data.tf
new file mode 100755
index 000000000..ddac76697
--- /dev/null
+++ b/terraform/bootstrap/data.tf
@@ -0,0 +1,51 @@
+locals {
+ cloudfoundry = {
+ external_applications = try(data.cloudfoundry_app.external_applications, null)
+ domain_external = try(data.cloudfoundry_domain.external, null)
+ domain_internal = try(data.cloudfoundry_domain.internal, null)
+ organization = try(data.cloudfoundry_org.this, null)
+ services = try(data.cloudfoundry_service.this, null)
+ space = try(data.cloudfoundry_space.this, null)
+ }
+}
+
+data "cloudfoundry_app" "external_applications" {
+ for_each = {
+ for key, value in try(local.env.external_applications, []) : value.name => value
+ if try(value.deployed, false) &&
+ try(data.cloudfoundry_space.this.id, null) != null
+ }
+ name_or_id = format(local.env.name_pattern, each.key)
+ space = try(data.cloudfoundry_space.this.id, null)
+}
+
+data "cloudfoundry_domain" "external" {
+ //domain = "${split(".", local.env.external_domain)[1]}.${split(".", local.env.external_domain)[2]}"
+ domain = join(",", slice(split(".", local.env.external_domain), 0, 0))
+ sub_domain = split(".", local.env.external_domain)[0]
+}
+
+data "cloudfoundry_domain" "internal" {
+ domain = join(",", slice(split(".", local.env.external_domain), 0, 0))
+ sub_domain = split(".", local.env.internal_domain)[0]
+}
+
+data "cloudfoundry_org" "this" {
+ name = local.env.organization
+}
+
+data "cloudfoundry_space" "this" {
+ name = try(format(local.space_pattern, local.production_space), terraform.workspace)
+ org = data.cloudfoundry_org.this.id
+}
+
+
+data "cloudfoundry_service" "this" {
+ for_each = {
+ for key, value in try(local.env.services, {}) : key => value
+ if value.service_type != "user-provided" && try(data.cloudfoundry_space.this.id, null) != null
+ }
+
+ name = each.value.service_type
+ space = try(data.cloudfoundry_space.this.id, null)
+}
\ No newline at end of file
diff --git a/terraform/bootstrap/locals.tf b/terraform/bootstrap/locals.tf
new file mode 100644
index 000000000..488c7d13e
--- /dev/null
+++ b/terraform/bootstrap/locals.tf
@@ -0,0 +1,231 @@
+locals {
+
+ ## The name of the project. Used to name most applications and services.
+ ## Default naming convention: ${local.project}-application-name-${terraform.workspace}
+ project = "benefit-finder"
+
+ ## The full name of the project. If their isn't a longer name, this can be set to
+ ## local.project.
+ project_full = "${local.project}"
+
+ production_space = "main"
+
+ repository = "GSA/px-benefit-finder"
+
+ space_pattern = "${local.project}-%s"
+
+## The various environment settings to be deployed.
+ envs = {
+
+ ## Every environment gets settings in 'all'.
+ all = {
+
+ ## The API URL for cloud.gov.
+ api_url = "https://api.fr.cloud.gov"
+
+ ## These values are defaults values when options aren't configured in the application block.
+ defaults = {
+
+ ## The default size of the containers ephemeral disk.
+ disk_quota = 2048
+
+ ## Is SSH enabled on the container by default?
+ enable_ssh = true
+
+ ## The default health check timeout.
+ health_check_timeout = 60
+
+ ## Default method of performing a health check.
+ ## Valid options: "port", "process", or "http"
+ ## https://docs.cloudfoundry.org/devguide/deploy-apps/healthchecks.html
+ health_check_type = "port"
+
+ ## Default number of application instances to deploy.
+ instances = 1
+
+ ## Default amount of memory to use memory to use for an application.
+ memory = 64
+
+ port = 8080
+
+ ## The default cloudfoundry stack to deploy.
+ ## https://docs.cloudfoundry.org/devguide/deploy-apps/stacks.html
+ stack = "cflinuxfs4"
+
+ ## Is the application stopped by default?
+ stopped = false
+
+ ## Default CloudFoundry deployment strategy.
+ ## Valid optons: "none", "standard", or "blue-green".
+ ## https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html
+ strategy = "none"
+
+ ## Default wait time for an application to start.
+ timeout = 300
+ }
+
+ ## Configuration settings for the egress proxy application.
+ # egress = local.egress
+
+ ## External application based on the Terraform workspace being used.
+ external_applications = {}
+
+ ## The domain name for applications accessable external of cloud.gov.
+ external_domain = "app.cloud.gov"
+
+ ## The domain name for applications accessable inside of cloud.gov.
+ internal_domain = "apps.internal"
+
+ ## The naming convention/pattern for deployed systems and subsystems.
+ ## %s is replaced with the name of the system.
+ name_pattern = "${local.project}-%s-${terraform.workspace}"
+
+ ## The name of the cloud.gov organization.
+ organization = "gsa-tts-usagov"
+
+ ## Passwords that are generated for workspaces. By default, it's an empty map.
+ ## If one is defined below in a workspace's settings, it will supersed this one.
+ passwords = {
+ test = {length = 32}
+ }
+
+ ## A copy of the project name, so it gets added to this setting object.
+ project = local.project
+
+ ## The name of the current Cloud.gov space.
+ space = "${local.project}-${terraform.workspace}"
+ }
+
+ ##
+ ##
+ ## The bootstrap workspace.
+ ## Used to initialize gobal/project wide applications/services.
+ ##
+ ##
+
+ bootstrap = {
+ secrets = {
+ PGDATABASE = {
+ encrypted = false
+ key = "db_name"
+ }
+ PGHOST = {
+ encrypted = false
+ key = "host"
+ }
+ PGPASSWORD = {
+ encrypted = false
+ key = "password"
+ }
+ PGPORT = {
+ encrypted = false
+ key = "port"
+ }
+ PG_CONN_STR = {
+ encrypted = false
+ key = "uri"
+ }
+ PGUSER = {
+ encrypted = false
+ key = "username"
+ }
+ }
+ services = {
+ terraform-backend = {
+ ## Applications to bind to this service.
+ applications = [ "tf-bastion" ]
+
+ ## The size of the instance to deploy.
+ service_plan = "micro-psql"
+
+ ## The type of service to be deployed.
+ service_type = "aws-rds"
+
+ ## Tags to add to the service.
+ tags = [
+ terraform.workspace
+ ]
+ }
+ }
+ space = "${local.project}-main"
+ }
+
+ main = {
+ apps = {
+ tf-bastion = {
+
+ ## Should the application have access to the internet?
+ allow_egress = true
+
+ ## Buildpacks to use with this application.
+ ## List buildpacks avalible with: cf buildpacks
+ buildpacks = [
+ "https://github.com/cloudfoundry/apt-buildpack",
+ "binary_buildpack"
+ ]
+
+ ## Command to run when container starts.
+ command = "./start"
+
+ ## Ephemeral disk storage.
+ disk_quota = 1024
+
+ ## Should SSH be enabled?
+ enable_ssh = true
+
+ ## Environmental variables. Avoid sensitive variables.
+ environment = {
+ CF_ORG = var.cloudgov_organization
+ CF_PASSWORD = var.cloudgov_password
+ CF_SPACE = var.cloudgov_space
+ CF_USER = var.cloudgov_username
+ OPENTOFU_VERSION = "1.7.1"
+ }
+
+ ## Timeout for health checks, in seconds.
+ health_check_timeout = 180
+
+ ## Type of health check.
+ ## Options: port, process, http
+ health_check_type = "process"
+
+ ## Number of instances of application to deploy.
+ instances = 1
+
+ ## Labels to add to the application.
+ labels = {
+ environment = "main"
+ }
+
+ ## Maximum amount of memory the application can use.
+ memory = 64
+
+ ## Addional network policies to add to the application.
+ ## Format: name of the application and the port it is listening on.
+ network_policies = {}
+
+ ## Port the application uses.
+ #port = 0
+
+ ## Can the application be accessed outside of cloud.gov?
+ public_route = false
+
+ ## The source file should be a directory or a zip file.
+ source = "../applications/tf-bastion"
+
+ space = "main"
+
+ ## Templates take templated files and fill them in with sensitive data.
+ templates = []
+ }
+ }
+ }
+ }
+
+ ## Map of the 'all' environement and the current workspace settings.
+ env = merge(try(local.envs.all, {}), try(local.envs.bootstrap, {}))
+}
+
+output "name" {
+ value = local.env.passwords
+}
\ No newline at end of file
diff --git a/terraform/bootstrap/main.tf b/terraform/bootstrap/main.tf
new file mode 100755
index 000000000..47d5ba8e0
--- /dev/null
+++ b/terraform/bootstrap/main.tf
@@ -0,0 +1,59 @@
+locals {
+ ## Merging of the various credentials and environmental variables.
+ secrets = merge(
+ flatten(
+ [
+ for service_key, service_value in try(local.env.services, {}) : [
+ for key, value in try(module.services.results.service_key[service_key].credentials, {}) : {
+ "${key}" = value
+ }
+ ] if try(module.services.results.service_key[service_key].credentials, null) != null
+ ]
+ )
+ ...)
+}
+
+module "random" {
+ source = "../modules/random"
+ names = [ "dev" ]
+ passwords = local.env.passwords
+}
+
+## The instanced services (i.e. RDS, S3, etc.) get created first.
+## This allows their credentials to be injected into "user-provided" services (JSON blobs), if needed.
+module "services" {
+ source = "../modules/service"
+
+ cloudfoundry = local.cloudfoundry
+ env = local.env
+ skip_user_provided_services = true
+}
+
+# module "secrets" {
+# source = "../modules/service"
+
+# cloudfoundry = local.cloudfoundry
+# env = local.env
+# skip_service_instances = true
+# secrets = local.secrets
+# }
+
+module "applications" {
+ #for_each = local.cloudfoundry.spaces
+ source = "../modules/application"
+
+ cloudfoundry = local.cloudfoundry
+ env = merge(local.envs.all, local.envs.bootstrap, local.envs[local.production_space])
+ secrets = local.secrets
+ services = module.services.results
+}
+
+module "github" {
+ source = "../modules/github"
+
+ env = local.env
+ github_organization = var.github_organization
+ github_token = var.github_token
+ repository = local.repository
+ secrets = local.secrets
+}
diff --git a/terraform/bootstrap/provider.tf b/terraform/bootstrap/provider.tf
new file mode 100644
index 000000000..e27de2fa1
--- /dev/null
+++ b/terraform/bootstrap/provider.tf
@@ -0,0 +1,21 @@
+terraform {
+ required_providers {
+ cloudfoundry = {
+ source = "cloudfoundry-community/cloudfoundry"
+ version = "~> 0.5"
+ }
+ }
+ required_version = "> 1.7"
+}
+
+provider "cloudfoundry" {
+ api_url = local.env.api_url
+ user = var.cloudgov_username
+ password = var.cloudgov_password
+}
+
+# Configure the GitHub Provider
+provider "github" {
+ owner = var.github_organization
+ token = var.github_token
+}
\ No newline at end of file
diff --git a/terraform/bootstrap/variables.tf b/terraform/bootstrap/variables.tf
new file mode 100644
index 000000000..69420fc97
--- /dev/null
+++ b/terraform/bootstrap/variables.tf
@@ -0,0 +1,40 @@
+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 "cloudgov_organization" {
+ description = "The organization for the cloud.gov account."
+ type = string
+ sensitive = true
+}
+
+variable "cloudgov_space" {
+ description = "The organization for the cloud.gov account."
+ type = string
+ sensitive = true
+}
+
+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 "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
+}
\ No newline at end of file
diff --git a/infra/docs/locals.tf.MD b/terraform/docs/locals.tf.MD
similarity index 100%
rename from infra/docs/locals.tf.MD
rename to terraform/docs/locals.tf.MD
diff --git a/infra/docs/scripts.MD b/terraform/docs/scripts.MD
similarity index 100%
rename from infra/docs/scripts.MD
rename to terraform/docs/scripts.MD
diff --git a/infra/.terraform-docs.yaml b/terraform/infra/.terraform-docs.yaml
similarity index 100%
rename from infra/.terraform-docs.yaml
rename to terraform/infra/.terraform-docs.yaml
diff --git a/infra/.terraform-docs/footer.md b/terraform/infra/.terraform-docs/footer.md
similarity index 100%
rename from infra/.terraform-docs/footer.md
rename to terraform/infra/.terraform-docs/footer.md
diff --git a/infra/.terraform-docs/header.md b/terraform/infra/.terraform-docs/header.md
similarity index 100%
rename from infra/.terraform-docs/header.md
rename to terraform/infra/.terraform-docs/header.md
diff --git a/terraform/infra/.terraform.lock.hcl b/terraform/infra/.terraform.lock.hcl
new file mode 100644
index 000000000..913e3ce15
--- /dev/null
+++ b/terraform/infra/.terraform.lock.hcl
@@ -0,0 +1,93 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/cloudfoundry-community/cloudfoundry" {
+ version = "0.51.2"
+ constraints = "~> 0.5, 0.51.2"
+ hashes = [
+ "h1:DAAWn0QmE75d6agoavWvchV6Ec5yOsxprPMMU7Q+xfM=",
+ "zh:2c15c7fbc8f15f6c21935d21c1eb8bab3e1454aec3476bc6fcda2d59bbd235a5",
+ "zh:3efe88cd4c40f1e90d71ceb94088d3ec2260ff01e4a4d722182c042b958c61f0",
+ "zh:41ea39daf091516f08cf6a5bc1efb88f19ac17ab8c146bc503d5a44c3c0fdd5a",
+ "zh:5287f2aade8821211426c8eed0a9dacaf41ab0be5a39ecf1526be2f5b71ab5a2",
+ "zh:7438d2dca479ace7720125c02c24660d4e928ee8b0ebd1514e2841b95d3563ef",
+ "zh:7583edf26c3160c4271c5ea473e799bca1f65da249d2e2e96dca69f2dce82c40",
+ "zh:8003fd57163a259d8005c7efa79d1d4d1cd3d98c26f82c58fa84f1e27c0f50d1",
+ "zh:8a5c05e59f4078193db1ceabd5350863cea869791e8ee5765472c0f36579cdcd",
+ "zh:8c0ccf62206c242b116ade68fee48c425b897c53f3da40d233c9c9a4a5fb514f",
+ "zh:9cc6ba428f1cd8c9a2d9bb3333dd16944cafcd2d2ca5af6fc040e4207cef4ea6",
+ "zh:a0fd393db027f03bde2b0056bfb04ce54827394eb31498b20e9beb3cb8e198b3",
+ "zh:a3d5cce15f8f611494c510f3a2dde2bd2841ffb2351541ed7f3177ea17f47a35",
+ "zh:bcf6edbebeabb36bf9254a2b6cdd3ea5e2f7d26836bd1392e6867a901d34c1ee",
+ "zh:e5a909abf388aa15af09ea1c9b6ec6fea5dd27ba4cefdf247b3afa90bb97cd3c",
+ "zh:fc2a42a8dbc41e216f63359087bfdaa02fe98717f4b793436e356013097c907a",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/archive" {
+ version = "2.4.2"
+ hashes = [
+ "h1:tZcueUOGqjDRRzW9b6BMwV++XRqABodQjgC/K3bRoXM=",
+ "zh:0fee4f61bc999b5174a1268295e04c91c3f6be0160022cb53943b6ec0a3f1055",
+ "zh:10a895ee751beec68727d3dc6bf8e670f499618bb4b02649544be2c73e89603e",
+ "zh:1118373dfc03cf524273573e3aff9c99e0bb7128ab3ce0be211fd30e3928dfb8",
+ "zh:19c1b4c785f1d864e4fcaec7d96045437494efc333f1e661ea9994cd5c969cdb",
+ "zh:23f0aa399394ce8aa918a6a16ca9f5451d9d5b021e1b08929eb7972f65cb27da",
+ "zh:27d5daeec1819019a4b94c4980c09626e9cf71de3f54128a621fddb1b94b9ece",
+ "zh:56244088a96ff9e3a04b23de0ce2fcfa92c1a5fe6c91c6357cceda4d6d441c17",
+ "zh:578fcb23e8ebde3c5be6c5c67377b5e0c404cc807a74d7087e70c8fb3bb59b92",
+ "zh:9709d108559da5066f24a6d28be661b65a02e908f89b91fe42fc493962a5f466",
+ "zh:ff2a6df5d22bda78ca284756801ba7c86504e4bf0b48b31c8f5af44eefd9d0e8",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/local" {
+ version = "2.5.1"
+ hashes = [
+ "h1:87L+rpGao062xifb1VuG9YVFwp9vbDP6G2fgfYxUkQs=",
+ "zh:031c2c2070672b7e78e0aa15560839278dc57fe7cf1e58a617ac13c67b31d5fb",
+ "zh:1ef64ea4f8382cd538a76f3d319f405d18130dc3280f1c16d6aaa52a188ecaa4",
+ "zh:422ce45691b2f384dbd4596fdc8209d95cb43d85a82aaa0173089d38976d6e96",
+ "zh:7415fbd8da72d9363ba55dd8115837714f9534f5a9a518ec42268c2da1b9ed2f",
+ "zh:92aa22d071339c8ef595f18a9f9245c287266c80689f5746b26e10eaed04d542",
+ "zh:9cd0d99f5d3be835d6336c19c4057af6274e193e677ecf6370e5b0de12b4aafe",
+ "zh:a8c1525b389be5809a97f02aa7126e491ba518f97f57ed3095a3992f2134bb8f",
+ "zh:b336fa75f72643154b07c09b3968e417a41293358a54fe03efc0db715c5451e6",
+ "zh:c66529133599a419123ad2e42874afbd9aba82bd1de2b15cc68d2a1e665d4c8e",
+ "zh:c7568f75ba6cb7c3660b69eaab8b0e4278533bd9a7a4c33ee6590cc7e69743ea",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/random" {
+ version = "3.6.1"
+ hashes = [
+ "h1:egGGMQ18ihxoFBTgL/6aRL2N5/0bTI738Mg+TTsvBHA=",
+ "zh:1208af24d1f66e858740812dd5da12e8951b1ca75cc6edb1975ba22bfdeefb1b",
+ "zh:19137e9b4d3c15e1d99d2352888b98ec0e69bd5b2e89049150379d7bbd115063",
+ "zh:26613834a1a8ac60390c7a4cbd4cb794b01dfe237d2b0c10f132f3e434a21e03",
+ "zh:2cbe4425918f3f401609d89e6381f7d120493d637a3d103d827f0c0fd00b1600",
+ "zh:44ef27a972540435efa88f323280f96d6ac77934079225e7fcc3560cc28aae59",
+ "zh:8c5d4ca7d1ce007f7c055807cde77aad4685eb807ff802c93ffbec8589068f17",
+ "zh:9a4fa908d6af48805c862cd4f3a1031d552b96d863a94263e390ac92915d74a9",
+ "zh:ba396849f0f6d488784f6039095634e1c84e67e31375f3d17218fcf8ce952cb8",
+ "zh:cb695db8798957bd64ce411f061307e39cb2baa69668b4d42ccf010db47d2e39",
+ "zh:d02704bf99a93dc0b1ca00bd6051df9c431fbe17cd662a1ab58db1b96264a26f",
+ ]
+}
+
+provider "registry.opentofu.org/hashicorp/time" {
+ version = "0.11.1"
+ hashes = [
+ "h1:+S9YvR/HeCxFGMS3ITjOFqlWrR6DdarWWowT9Cz18/M=",
+ "zh:048c56f9f810f67a7460363a26bf3ef939d64f0d7b7342b9e7f24cc85ee1491b",
+ "zh:49f949cc5cb50fbb65f7b4578b79fbe02b6bafe9e3f5f1c2936114dd445b84b3",
+ "zh:553174a4fa88f6e186800d7ee155a6b5b4c6c81793643f1a20eab26becc7f823",
+ "zh:5cae304e21f77091d4b50389c655afd5e4e2e8d4cd9c06de139a31b8e7d343a9",
+ "zh:7aae20832bd9885f034831aa44db3a6ffcec034a2d5a2815d92c42c40c14ca1d",
+ "zh:93d715610dce777474b5eff1d7dbe797e72ca0b679cd8636efb3aa45d1cb589e",
+ "zh:bd29e04645775851eb10e7f3b39104ae57ca3632dec4ae07328d33d4182e7fb5",
+ "zh:d6ad6a4d52a6989b8452466f2ec3dbcdb00cc44a96bd1ca618d91a5d74895f49",
+ "zh:e68cfad3ec526631410fa9406938d624fd56b9ab065c76525cb3f731d106fbfe",
+ "zh:ffee8aa6b7ce56f4b8fdc0c492404be0041137a278388eb1d1180b637fb5b3de",
+ ]
+}
diff --git a/infra/.tflint.hcl b/terraform/infra/.tflint.hcl
similarity index 100%
rename from infra/.tflint.hcl
rename to terraform/infra/.tflint.hcl
diff --git a/infra/TERRAFORM.md b/terraform/infra/TERRAFORM.md
similarity index 100%
rename from infra/TERRAFORM.md
rename to terraform/infra/TERRAFORM.md
diff --git a/terraform/infra/data.tf b/terraform/infra/data.tf
new file mode 100755
index 000000000..15b30a745
--- /dev/null
+++ b/terraform/infra/data.tf
@@ -0,0 +1,52 @@
+locals {
+ cloudfoundry = {
+ external_applications = try(data.cloudfoundry_app.external_applications, null)
+ domain_external = try(data.cloudfoundry_domain.external, null)
+ domain_internal = try(data.cloudfoundry_domain.internal, null)
+ organization = try(data.cloudfoundry_org.this, null)
+ services = try(data.cloudfoundry_service.this, null)
+ space = try(data.cloudfoundry_space.this, null)
+ }
+}
+
+data "cloudfoundry_app" "external_applications" {
+ for_each = {
+ for key, value in try(local.env.external_applications, []) : key => value
+ if try(value.deployed, false) &&
+ try(data.cloudfoundry_space.this.id, null) != null
+ }
+ name_or_id = format(local.env.name_pattern, each.key)
+ space = try(data.cloudfoundry_space.this.id, null)
+}
+
+data "cloudfoundry_domain" "external" {
+ //domain = "${split(".", local.env.external_domain)[1]}.${split(".", local.env.external_domain)[2]}"
+ domain = join(",", slice(split(".", local.env.external_domain), 0, 0))
+ sub_domain = split(".", local.env.external_domain)[0]
+}
+
+data "cloudfoundry_domain" "internal" {
+ domain = join(",", slice(split(".", local.env.external_domain), 0, 0))
+ sub_domain = split(".", local.env.internal_domain)[0]
+}
+
+data "cloudfoundry_org" "this" {
+ name = local.env.organization
+}
+
+data "cloudfoundry_space" "this" {
+ name = try(local.env.space, terraform.workspace)
+ org = data.cloudfoundry_org.this.id
+}
+
+
+data "cloudfoundry_service" "this" {
+ for_each = {
+ for key, value in try(local.env.services, {}) : key => value
+ if value.service_type != "user-provided" &&
+ try(data.cloudfoundry_space.this.id, null) != null
+ }
+
+ name = each.value.service_type
+ space = try(data.cloudfoundry_space.this.id, null)
+}
diff --git a/terraform/infra/dynamic.tf b/terraform/infra/dynamic.tf
new file mode 100644
index 000000000..6404b22ad
--- /dev/null
+++ b/terraform/infra/dynamic.tf
@@ -0,0 +1,45 @@
+locals {
+
+ ## Map of service instances and secrets merged together.
+ services = {
+ instance = merge(
+ module.services.results.instance,
+ module.secrets.results.instance
+ )
+ user_provided = merge(
+ module.services.results.user_provided,
+ module.secrets.results.user_provided
+ )
+ service_key = merge(
+ module.services.results.service_key,
+ module.secrets.results.service_key
+ )
+ }
+
+ ## Merging of the various credentials and environmental variables.
+ secrets = merge(
+ merge(
+ flatten([
+ for app in try(local.env.services, []) : [
+ for key, value in try(module.services.results.service_key[app.name].credentials, {}) : {
+ "${app.name}_${key}" = value
+ }
+ ] if try(module.services.results.service_key[app.name].credentials, null) != null
+ ])
+ ...),
+ merge(
+ flatten([
+ for key, value in try(module.random.results, {}) : {
+ "${key}" = value.result
+ }
+ ])
+ ...)
+ )
+
+ ## List of the workspaces defined in the configuration above.
+ workspaces = flatten([
+ for key, value in local.envs : [
+ key
+ ]
+ ])
+}
diff --git a/terraform/infra/locals.tf b/terraform/infra/locals.tf
new file mode 100755
index 000000000..cdd047297
--- /dev/null
+++ b/terraform/infra/locals.tf
@@ -0,0 +1,442 @@
+locals {
+
+ ## The name of the project. Used to name most applications and services.
+ ## Default naming convention: ${local.project}-application-name-${terraform.workspace}
+ project = "benefit-finder"
+
+ ## The full name of the project. If their isn't a longer name, this can be set to
+ ## local.project.
+ project_full = "${local.project}"
+
+ ## The names of the project's production workspaces. This is used to adjust
+ ## settings dynamically throughout this configuration file.
+ production_workspaces = ["main", "dev"]
+
+ cms_fqdn = "https://bf-cms-${terraform.workspace}.bxdev.net"
+ static_fqdn = "https://bf-static-${terraform.workspace}.bxdev.net"
+
+ tf_backend = {
+ type = "pg"
+ name_pattern_psql = "${local.project}-terraform-backend-bootstrap"
+ name_pattern_secrets = "${local.project}--pg-secrets-bootstrap "
+ }
+
+ ## "Common" applications and services that are deployed to every space.
+ globals = {
+ apps = {
+ ## Nginx Web Application Firewall (WAF).
+ waf = {
+
+ ## Should the application have access to the internet?
+ allow_egress = true
+
+ ## Buildpacks to use with this application.
+ ## List buildpacks avalible with: cf buildpacks
+ buildpacks = [
+ "https://github.com/cloudfoundry/apt-buildpack",
+ "nginx_buildpack"
+ ]
+
+ ## Command to run when container starts.
+ command = "./start"
+
+ ## Ephemeral disk storage.
+ disk_quota = 1024
+
+ ## Should SSH be enabled?
+ enable_ssh = true
+
+ ## Environmental variables. Avoid sensitive variables.
+ environment = {
+
+ ## IP addresses allowed to connected to the CMS.
+ ALLOWED_IPS_CMS = base64encode(
+ jsonencode([
+ "allow 0.0.0.0/0;"
+ ])
+ )
+
+ ## The OWASP CRS rules for modsecurity.
+ CRS_RULES = "coreruleset-4.0.0.tar.gz"
+
+ ## IP address that are denied access from the static website.
+ DENYED_IPS_STATIC = base64encode(jsonencode([]))
+
+ ## The current environment the application is running in.
+ ENV = terraform.workspace
+
+ ## Linux "Load Library Path", where system libraries are located. (i.e. libzip, gd, etc)
+ LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/"
+
+ ## Ubuntu patch for newer version of mod security.
+ MODSECURITY_UPDATE = "libmodsecurity3_3.0.9-1_amd64.deb"
+
+ ## Domains that shouldn't be passed to the egress proxy server (i.e. apps.internal).
+ #no_proxy = var.no_proxy
+ }
+
+ ## Timeout for health checks, in seconds.
+ health_check_timeout = 180
+
+ ## Type of health check.
+ ## Options: port, process, http
+ health_check_type = "port"
+
+ ## Number of instances of application to deploy.
+ instances = 1
+
+ ## Labels to add to the application.
+ labels = {
+ environment = terraform.workspace
+ }
+
+ ## Maximum amount of memory the application can use.
+ memory = 96
+
+ ## Addional network policies to add to the application.
+ ## Format: name of the application and the port it is listening on.
+ network_policies = {
+ drupal = 61443
+ }
+
+ ## Port the application uses.
+ port = 80
+
+ ## Can the application be accessed outside of cloud.gov?
+ public_route = true
+
+ ## The source file should be a directory or a zip file.
+ source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf"
+
+ ## Templates take templated files and fill them in with sensitive data.
+ ## The proxy-to-static.conf has the S3 bucket written to it during
+ ## the 'terraform apply' command, before it the files are zipped up and
+ ## uploaded to cloud.gov.
+ templates = [
+ {
+ source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl"
+ destination = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf"
+ },
+ {
+ source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl"
+ destination = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-static.conf"
+ },
+ {
+ source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl"
+ destination = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-app.conf"
+ }
+ ]
+ }
+ }
+
+ ## Services to deploy in this environment.
+ services = {
+
+ ## S3 storage for backups.
+ "backup" = {
+
+ ## Applications to bind to this service.
+ applications = []
+
+ ## Should a service key be generated for other applications to use?
+ service_key = true
+
+ ## The size of the instance to deploy.
+ service_plan = "basic"
+
+ ## The type of service to be deployed.
+ service_type = "s3"
+
+ ## Tags to add to the service.
+ tags = [
+ terraform.workspace
+ ]
+ },
+
+ ## MySQL RDS database.
+ "mysql" = {
+
+ ## Applications to bind to this service.
+ applications = ["cms"]
+
+ ## The size of the instance to deploy.
+ service_plan = contains(local.production_workspaces, terraform.workspace) ? "micro-mysql" : "micro-mysql"
+
+ ## The type of service to be deployed.
+ service_type = "aws-rds"
+
+ ## Tags to add to the service.
+ tags = [
+ terraform.workspace
+ ]
+ },
+
+ ## Credentials and other sensitive variables.
+ "secrets" = {
+
+ ## Applications to bind to this service.
+ applications = ["cms", "waf"]
+
+ ## Credentials that should be added to the json blob.
+ credentials = [
+ "cron_key",
+ "hash_salt",
+ "static_bucket",
+ "static_fips_endpoint",
+ "static_access_key_id",
+ "static_secret_access_key",
+ "storage_bucket",
+ "storage_fips_endpoint",
+ "storage_access_key_id",
+ "storage_secret_access_key"
+ ]
+
+ ## The type of service to be deployed.
+ service_type = "user-provided"
+
+ ## Tags to add to the service.
+ tags = [
+ terraform.workspace
+ ]
+ },
+
+ ## S3 storage for public files for Drupal.
+ ## Typically "sites/default/files/"
+ "storage" = {
+
+ ## Applications to bind to this service.
+ applications = ["cms", "waf"]
+
+ ## Should a service key be generated for other applications to use?
+ service_key = true
+
+ ## The size of the instance to deploy.
+ service_plan = "basic-public-sandbox"
+
+ ## The type of service to be deployed.
+ service_type = "s3"
+
+ ## Tags to add to the service.
+ tags = [
+ terraform.workspace
+ ]
+ },
+
+ # S3 storage for the statically generated site.
+ "static" = {
+
+ ## Applications to bind to this service.
+ applications = ["waf", "cms"]
+
+ ## Should a service key be generated for other applications to use?
+ service_key = true
+
+ ## The size of the instance to deploy.
+ service_plan = "basic-public-sandbox"
+
+ ## The type of service to be deployed.
+ service_type = "s3"
+
+ ## Tags to add to the service.
+ tags = [
+ terraform.workspace
+ ]
+ }
+ }
+ }
+
+ ## The mTLS port the proxy application uses.
+ ## Cloudfoundry will automatically redirect connections on this port to local port 8080.
+ mtls_port = var.mtls_port
+
+ ## Any applications that are external to this Terraform infrastucture.
+ ## In this case, the Drupal application is deployed via a manifest.yml in the Drupal
+ ## Github repostitory.
+ external_applications = {
+ cms = {
+
+ environement = "dev"
+
+ ## Port is the application listening on.
+ port = var.mtls_port
+ },
+ cms = {
+
+ environement = "main"
+
+ ## Port is the application listening on.
+ port = var.mtls_port
+ }
+ }
+
+ ## The various environment settings to be deployed.
+ envs = {
+
+ ## Every environment gets settings in 'all'.
+ all = {
+
+ ## The API URL for cloud.gov.
+ api_url = "https://api.fr.cloud.gov"
+
+ ## These values are defaults values when options aren't configured in the application block.
+ defaults = {
+
+ ## The default size of the containers ephemeral disk.
+ disk_quota = 2048
+
+ ## Is SSH enabled on the container by default?
+ enable_ssh = true
+
+ ## The default health check timeout.
+ health_check_timeout = 60
+
+ ## Default method of performing a health check.
+ ## Valid options: "port", "process", or "http"
+ ## https://docs.cloudfoundry.org/devguide/deploy-apps/healthchecks.html
+ health_check_type = "port"
+
+ ## Default number of application instances to deploy.
+ instances = 1
+
+ ## Default amount of memory to use memory to use for an application.
+ memory = 64
+
+ port = 8080
+
+ ## The default cloudfoundry stack to deploy.
+ ## https://docs.cloudfoundry.org/devguide/deploy-apps/stacks.html
+ stack = "cflinuxfs4"
+
+ ## Is the application stopped by default?
+ stopped = false
+
+ ## Default CloudFoundry deployment strategy.
+ ## Valid optons: "none", "standard", or "blue-green".
+ ## https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html
+ strategy = "none"
+
+ ## Default wait time for an application to start.
+ timeout = 300
+ }
+
+ ## Configuration settings for the egress proxy application.
+ # egress = local.egress
+
+ ## External application based on the Terraform workspace being used.
+ external_applications = try(local.external_applications, [])
+
+ ## The domain name for applications accessable external of cloud.gov.
+ external_domain = "app.cloud.gov"
+
+ ## The domain name for applications accessable inside of cloud.gov.
+ internal_domain = "apps.internal"
+
+ ## The naming convention/pattern for deployed systems and subsystems.
+ ## %s is replaced with the name of the system.
+ name_pattern = "${local.project}-%s-${terraform.workspace}"
+
+ ## The name of the cloud.gov organization.
+ organization = "gsa-tts-usagov"
+
+ ## Passwords that are generated for workspaces. By default, it's an empty map.
+ ## If one is defined below in a workspace's settings, it will supersed this one.
+ passwords = {
+ hash_salt = {
+ length = 32
+ }
+ cron_key = {
+ length = 32
+ }
+ }
+
+ ## A copy of the project name, so it gets added to this setting object.
+ project = local.project
+
+ ## The name of the current Cloud.gov space.
+ space = "${local.project}-${terraform.workspace}"
+ }
+
+ #################################
+ ##
+ ## ____
+ ## | _ \ _____ __
+ ## | | | |/ _ \ \ / /
+ ## | |_| | __/\ V /
+ ## |____/ \___| \_/
+ ##
+ #################################
+
+ dev = merge(
+ {
+ ## Applications to deploy.
+ apps = local.globals.apps
+ services = local.globals.services
+ },
+ {
+ ## The space to deploy to the application to.
+ space = "${local.project}-dev"
+
+ ## Passwords that need to be generated for this environment.
+ ## These will actually use the sha256 result from the random module.
+ passwords = {
+ hash_salt = {
+ length = 32
+ }
+ cron_key = {
+ length = 32
+ }
+ }
+ }
+ )
+
+ #################################
+ ##
+ ## ____ _
+ ## | _ \ _ __ ___ __| |
+ ## | |_) | '__/ _ \ / _` |
+ ## | __/| | | (_) | (_| |
+ ## |_| |_| \___/ \__,_|
+ ##
+ #################################
+
+
+ main = merge(
+ {
+ ## Applications to deploy.
+ apps = local.globals.apps
+ services = local.globals.services
+ },
+ {
+ ## The space to deploy to the application to.
+ space = "${local.project}-${terraform.workspace}"
+
+ ## Passwords that need to be generated for this environment.
+ ## These will actually use the sha256 result from the random module.
+ passwords = [
+ {
+ name = "hash_salt"
+ length = 32
+ },
+ {
+ name = "cron_key"
+ length = 32
+ }
+ ]
+ }
+ )
+ }
+
+ ## Map of the 'all' environement and the current workspace settings.
+ env = merge(try(local.envs.all, {}), try(local.envs[terraform.workspace], {}))
+
+ service_bindings = merge(
+ flatten(
+ [
+ for key, value in try(local.env.services, {}) : {
+ #svc_value.name => svc_value
+ "${key}" = value
+ }
+ ]
+ )
+ ...)
+}
diff --git a/infra/main.tf b/terraform/infra/main.tf
similarity index 63%
rename from infra/main.tf
rename to terraform/infra/main.tf
index f7d75d31b..a43f11cd7 100755
--- a/infra/main.tf
+++ b/terraform/infra/main.tf
@@ -1,12 +1,5 @@
-# module "certificates" {
-# source = "./modules/certificate"
-# project_name = local.env.project
-# workspace = local.env.bootstrap_workspace
-# env = local.env
-# }
-
module "random" {
- source = "./modules/random"
+ source = "../modules/random"
names = local.workspaces
passwords = local.env.passwords
}
@@ -14,7 +7,7 @@ module "random" {
## The instanced services (i.e. RDS, S3, etc.) get created first.
## This allows their credentials to be injected into "user-provided" services (JSON blobs), if needed.
module "services" {
- source = "./modules/service"
+ source = "../modules/service"
cloudfoundry = local.cloudfoundry
env = local.env
@@ -26,7 +19,7 @@ module "services" {
}
module "secrets" {
- source = "./modules/service"
+ source = "../modules/service"
cloudfoundry = local.cloudfoundry
env = local.env
@@ -39,7 +32,7 @@ module "secrets" {
}
module "applications" {
- source = "./modules/application"
+ source = "../modules/application"
cloudfoundry = local.cloudfoundry
env = local.env
@@ -47,10 +40,14 @@ module "applications" {
services = local.services
}
-# module "circleci" {
-# source = "./modules/circleci"
-
-# env = local.env
-# secrets = local.secrets
-# schedules = local.env.circleci.schedules
-# }
+# output "name" {
+# value = merge(
+# flatten(
+# [
+# for service in try(local.env.services, {}) : {
+# "${service.name}" = service
+# }
+# ]
+# )
+# ...)
+# }
\ No newline at end of file
diff --git a/infra/provider.tf.tmpl b/terraform/infra/provider.tf
old mode 100755
new mode 100644
similarity index 57%
rename from infra/provider.tf.tmpl
rename to terraform/infra/provider.tf
index fe787c702..cc8f39a48
--- a/infra/provider.tf.tmpl
+++ b/terraform/infra/provider.tf
@@ -5,17 +5,11 @@ terraform {
version = "~> 0.5"
}
}
- required_version = "1.8.2"
+ required_version = "> 1.7"
}
terraform {
- backend "s3" {
- bucket = "$AWS_BUCKET"
- key = "terraform"
- region = "$AWS_DEFAULT_REGION"
- access_key = "$AWS_ACCESS_KEY_ID"
- secret_key = "$AWS_SECRET_ACCESS_KEY"
- }
+ backend "pg" { }
}
provider "cloudfoundry" {
diff --git a/infra/scripts/cloudgov-aws-creds.sh b/terraform/infra/scripts/cloudgov-aws-creds.sh
similarity index 100%
rename from infra/scripts/cloudgov-aws-creds.sh
rename to terraform/infra/scripts/cloudgov-aws-creds.sh
diff --git a/infra/scripts/cloudgov-create-service-account.sh b/terraform/infra/scripts/cloudgov-create-service-account.sh
similarity index 100%
rename from infra/scripts/cloudgov-create-service-account.sh
rename to terraform/infra/scripts/cloudgov-create-service-account.sh
diff --git a/infra/scripts/egress-network-policy.sh b/terraform/infra/scripts/egress-network-policy.sh
similarity index 100%
rename from infra/scripts/egress-network-policy.sh
rename to terraform/infra/scripts/egress-network-policy.sh
diff --git a/infra/scripts/init.sh b/terraform/infra/scripts/init.sh
similarity index 100%
rename from infra/scripts/init.sh
rename to terraform/infra/scripts/init.sh
diff --git a/terraform/infra/terraform.tfvars.tmpl b/terraform/infra/terraform.tfvars.tmpl
new file mode 100644
index 000000000..4b0a21f22
--- /dev/null
+++ b/terraform/infra/terraform.tfvars.tmpl
@@ -0,0 +1,2 @@
+cloudgov_password="$CF_PASSWORD"
+cloudgov_username="$CF_USER"
\ No newline at end of file
diff --git a/terraform/infra/variables.tf b/terraform/infra/variables.tf
new file mode 100755
index 000000000..cf03767a5
--- /dev/null
+++ b/terraform/infra/variables.tf
@@ -0,0 +1,23 @@
+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 "terraform_working_dir" {
+ description = "Working directory for Terraform."
+ type = string
+ default = "px-benefit-finder/terraform"
+}
+
+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
+}
\ No newline at end of file
diff --git a/infra/modules/application/.terraform-docs.yaml b/terraform/modules/application/.terraform-docs.yaml
similarity index 100%
rename from infra/modules/application/.terraform-docs.yaml
rename to terraform/modules/application/.terraform-docs.yaml
diff --git a/infra/modules/application/.terraform-docs/footer.md b/terraform/modules/application/.terraform-docs/footer.md
similarity index 100%
rename from infra/modules/application/.terraform-docs/footer.md
rename to terraform/modules/application/.terraform-docs/footer.md
diff --git a/infra/modules/application/.terraform-docs/header.md b/terraform/modules/application/.terraform-docs/header.md
similarity index 100%
rename from infra/modules/application/.terraform-docs/header.md
rename to terraform/modules/application/.terraform-docs/header.md
diff --git a/terraform/modules/application/README.md b/terraform/modules/application/README.md
new file mode 100644
index 000000000..22607fe81
--- /dev/null
+++ b/terraform/modules/application/README.md
@@ -0,0 +1,137 @@
+
+# CloudFoundry Application Module
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | > 1.7 |
+| [cloudfoundry](#requirement\_cloudfoundry) | ~> 0.5 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [archive](#provider\_archive) | n/a |
+| [cloudfoundry](#provider\_cloudfoundry) | ~> 0.5 |
+| [local](#provider\_local) | n/a |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [cloudfoundry_app.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) | resource |
+| [cloudfoundry_network_policy.egress_proxy](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/network_policy) | resource |
+| [cloudfoundry_network_policy.ingress_proxy](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/network_policy) | resource |
+| [cloudfoundry_route.external](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/route) | resource |
+| [cloudfoundry_route.internal](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/route) | resource |
+| [local_sensitive_file.this](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/sensitive_file) | resource |
+| [archive_file.this](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [cloudfoundry](#input\_cloudfoundry) | Cloudfoundry settings. | 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
}
)
}
)
| n/a | yes |
+| [env](#input\_env) | The settings object for this environment. | 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)
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
})
| 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. | 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), {})
}
)
)
}
)
| `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. | 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(
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
})
| 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. | 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)
}
)
)
| 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(
{
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
}
)
}
)
| n/a | yes |
+| [env](#input\_env) | The settings object for this environment. | 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(
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
})
| 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