├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── Bug_Report.md │ ├── Feature_Request.md │ └── config.yaml ├── pull_request_template.md └── workflows │ ├── auto.yml │ ├── call-create-jira-issue.yml │ └── create-jira-issue.yml ├── .gitignore ├── .tflint.hcl ├── LICENSE ├── README.md ├── fixtures ├── test_bastion_init │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── templates │ │ ├── setup-ssh.sh.tpl │ │ └── ssh-config.tpl │ ├── variables.tf │ └── versions.tf ├── test_hcp_vault │ ├── main.tf │ ├── outputs.tf │ ├── provider.tf │ ├── variables.tf │ └── versions.tf └── test_proxy_init │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── templates │ ├── get_base64_secrets.func │ ├── install_packages.func │ ├── mitmproxy.sh.tpl │ └── squid.sh.tpl │ ├── variables.tf │ └── versions.tf ├── modules ├── runtime_container_engine_config │ ├── database_config.tf │ ├── main.tf │ ├── outputs.tf │ ├── redis_config.tf │ ├── storage_config.tf │ ├── variables.tf │ ├── vault_config.tf │ └── versions.tf ├── settings │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── replicated_config.tf │ ├── tfe_base_config.tf │ ├── tfe_base_external_config.tf │ ├── tfe_external_aws_config.tf │ ├── tfe_external_azure_config.tf │ ├── tfe_external_google_config.tf │ ├── tfe_external_vault_config.tf │ ├── tfe_redis_config.tf │ ├── variables.tf │ └── versions.tf ├── tfe_init │ ├── README.md │ ├── files │ │ └── daemon.json │ ├── functions.tf │ ├── main.tf │ ├── outputs.tf │ ├── templates │ │ ├── aws.rhel.docker.tfe.sh.tpl │ │ ├── aws.rhel.podman.tfe.sh.tpl │ │ ├── aws.ubuntu.docker.tfe.sh.tpl │ │ ├── azurerm.rhel.docker.tfe.sh.tpl │ │ ├── azurerm.rhel.podman.tfe.sh.tpl │ │ ├── azurerm.ubuntu.docker.tfe.sh.tpl │ │ ├── get_base64_secrets.func │ │ ├── google.rhel.docker.tfe.sh.tpl │ │ ├── google.rhel.podman.tfe.sh.tpl │ │ ├── google.ubuntu.docker.tfe.sh.tpl │ │ ├── install_monitoring_agents.func │ │ ├── install_packages.func │ │ ├── retry.func │ │ ├── terraform-enterprise.kube.tpl │ │ └── tfe.sh.tpl │ ├── variables.tf │ └── versions.tf └── tfe_init_replicated │ ├── README.md │ ├── files │ └── daemon.json │ ├── functions.tf │ ├── main.tf │ ├── outputs.tf │ ├── templates │ ├── get_base64_secrets.func │ ├── install_monitoring_agents.func │ ├── install_packages.func │ ├── retry.func │ └── tfe_replicated.sh.tpl │ ├── variables.tf │ └── versions.tf └── templates └── template.README.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @hashicorp/ptfe-review -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_Report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: If something isn't working as expected 🤔. 4 | labels: bug 5 | 6 | --- 7 | 8 | ### Expected Behavior 9 | 10 | 11 | 12 | ### Actual Behavior 13 | 14 | 15 | 16 | ### Steps to Reproduce 17 | 18 | 19 | 20 | 1. `terraform apply` 21 | 22 | ### Important Factoids 23 | 24 | 25 | 26 | ### References 27 | 28 | 33 | 34 | * #0000 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_Request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | 7 | --- 8 | 9 | **What problem would you like to address?** 10 | 11 | 12 | **Who is the audience for this request?** 13 | 14 | 15 | **What would the ideal solution look like?** 16 | 17 | 18 | **What alternatives have you considered?** 19 | 20 | 21 | **How does this impact you?** 22 | 23 | 24 | **Are there any deadlines?** 25 | 26 | 27 | **Is there any additional context?** 28 | 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | blank_issues_enabled: false 5 | contact_links: 6 | - name: Security Vulnerability 7 | url: https://www.hashicorp.com/security.html 8 | about: Please report security vulnerabilities responsibly. -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Background 2 | 3 | Please include a one or two sentence description of what you're changing and why. 4 | 5 | Relates OR Closes #0000 6 | 7 | ## How has this been tested? 8 | 9 | ## TFE Modules 10 | 11 | ### Did you add a new setting? 12 | 13 | If no, you may delete these tasks. 14 | If yes, please check each box after you have have added an issue in the TFE modules to add this setting: 15 | 16 | - [ ] [AWS](https://github.com/hashicorp/terraform-aws-terraform-enterprise) 17 | - [ ] [Azure](https://github.com/hashicorp/terraform-azurerm-terraform-enterprise) 18 | - [ ] [GCP](https://github.com/hashicorp/terraform-google-terraform-enterprise) 19 | 20 | ## This PR makes me feel 21 | 22 | ![optional gif describing your feelings about this pr]() 23 | -------------------------------------------------------------------------------- /.github/workflows/auto.yml: -------------------------------------------------------------------------------- 1 | name: Automatic CI for Utility Modules 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | defaults: 9 | run: 10 | shell: bash 11 | 12 | jobs: 13 | terraform_format: 14 | 15 | name: Run terraform fmt 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | 20 | steps: 21 | - uses: hashicorp/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 22 | with: 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | persist-credentials: false 25 | 26 | - name: Setup Terraform 27 | uses: hashicorp/setup-terraform@v3 28 | with: 29 | terraform_version: 1.1.5 30 | 31 | - name: Format all .tf files recursively 32 | run: | 33 | terraform fmt -check -diff -recursive ${{ github.workspace }} 34 | 35 | terraform_lint: 36 | 37 | name: Run terraform-lint 38 | runs-on: ubuntu-latest 39 | permissions: 40 | contents: read 41 | 42 | steps: 43 | - uses: hashicorp/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 44 | with: 45 | token: ${{ secrets.GITHUB_TOKEN }} 46 | persist-credentials: false 47 | 48 | - name: Setup Terraform Lint 49 | uses: terraform-linters/setup-tflint@19a52fbac37dacb22a09518e4ef6ee234f2d4987 # v4.0.0 50 | with: 51 | tflint_version: v0.26.0 52 | 53 | - name: Lint modules directory in a loop 54 | run: | 55 | for m in $(ls -1d modules/*/) 56 | do 57 | tflint \ 58 | --config ${{ github.workspace }}/.tflint.hcl \ 59 | ${{ github.workspace }}/${m} 60 | done 61 | 62 | - name: Lint fixtures directory in a loop 63 | run: | 64 | for m in $(ls -1d fixtures/*/) 65 | do 66 | tflint \ 67 | --config ${{ github.workspace }}/.tflint.hcl \ 68 | ${{ github.workspace }}/${m} 69 | done 70 | -------------------------------------------------------------------------------- /.github/workflows/call-create-jira-issue.yml: -------------------------------------------------------------------------------- 1 | name: Create Jira Issue 2 | 3 | on: 4 | issues: 5 | types: [opened, closed, deleted, reopened] 6 | pull_request_target: 7 | types: [opened, closed, reopened] 8 | issue_comment: 9 | types: [created] 10 | 11 | jobs: 12 | call-workflow: 13 | uses: ./.github/workflows/create-jira-issue.yml 14 | with: 15 | github-team: Terraform-Enterprise 16 | project: TFE 17 | secrets: inherit 18 | -------------------------------------------------------------------------------- /.github/workflows/create-jira-issue.yml: -------------------------------------------------------------------------------- 1 | name: Create Jira Issue 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | github-team: 7 | required: true 8 | type: string 9 | project: 10 | required: true 11 | type: string 12 | issue-extra-fields: 13 | type: string 14 | default: "{}" 15 | required: false 16 | jobs: 17 | sync: 18 | runs-on: ubuntu-latest 19 | name: Jira sync 20 | steps: 21 | - name: Login 22 | uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 23 | env: 24 | JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} 25 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} 26 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} 27 | 28 | - name: Set ticket type 29 | id: set-ticket-type 30 | run: | 31 | if [[ "${{ contains(github.event.issue.labels.*.name, 'bug') }}" == "true" ]]; then 32 | echo "::set-output name=type::GH Issue" 33 | elif [[ "${{ contains(github.event.issue.labels.*.name, 'enhancement') }}" == "true" ]]; then 34 | echo "::set-output name=type::Feature Request" 35 | else 36 | echo "::set-output name=type::Task" 37 | fi 38 | 39 | - name: Set ticket labels 40 | if: github.event.action == 'opened' 41 | id: set-ticket-labels 42 | run: | 43 | LABELS="[" 44 | if [[ "${{ contains(github.event.issue.labels.*.name, 'bug') }}" == "true" ]]; then LABELS+="\"bug\", "; fi 45 | if [[ "${{ contains(github.event.issue.labels.*.name, 'enhancement') }}" == "true" ]]; then LABELS+="\"enhancement\", "; fi 46 | if [[ ${#LABELS} != 1 ]]; then LABELS=${LABELS::-2}"]"; else LABELS+="]"; fi 47 | echo "::set-output name=labels::${LABELS}" 48 | 49 | - name: Check if team member 50 | if: github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' 51 | id: is-team-member 52 | run: | 53 | TEAM="${{ inputs.github-team }}" 54 | ROLE="$(hub api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" 55 | if [[ -n ${ROLE} ]]; then 56 | echo "Actor ${{ github.actor }} is a ${TEAM} team member" 57 | echo "::set-output name=message::true" 58 | else 59 | echo "Actor ${{ github.actor }} is NOT a ${TEAM} team member" 60 | echo "::set-output name=message::false" 61 | fi 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.GH_DISPATCH_TOKEN }} 64 | 65 | - name: Build Extra fields 66 | id: build-extra-fields 67 | env: 68 | # customfield_10089 is "Issue Link", customfield_10371 is "Source" (use JIRA API to retrieve) 69 | EXTRA_FIELDS: | 70 | { "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", 71 | "customfield_10371": { "value": "GitHub" }, 72 | "components": [{ "name": "${{ github.event.repository.name }}" }], 73 | "labels": ${{ steps.set-ticket-labels.outputs.labels }} 74 | } 75 | run: | 76 | echo "::set-output name=extra::$(echo '${{ env.EXTRA_FIELDS }}' '${{ inputs.issue-extra-fields }}' | jq -rcs '.[0] * .[1]')" 77 | 78 | - name: Create ticket 79 | if: ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type != 'Task' ) || ( github.event.action == 'opened' && steps.set-ticket-type.outputs.type == 'Task' && steps.is-team-member.outputs.message == 'false' ) 80 | uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1 81 | with: 82 | project: "${{ inputs.project }}" 83 | issuetype: "${{ steps.set-ticket-type.outputs.type }}" 84 | summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.type }} #${{ github.event.issue.number || github.event.pull_request.number }}]: ${{ github.event.issue.title || github.event.pull_request.title }}" 85 | description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created in GitHub by ${{ github.actor }}._" 86 | extraFields: ${{ steps.build-extra-fields.outputs.extra }} 87 | 88 | - name: Search 89 | if: github.event.action != 'opened' 90 | id: search 91 | uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2 92 | with: 93 | # cf[10089] is Issue Link (use JIRA API to retrieve) 94 | jql: 'issuetype = "${{ steps.set-ticket-type.outputs.type }}" and cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' 95 | 96 | - name: Sync comment 97 | if: github.event.action == 'created' && steps.search.outputs.issue 98 | uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0 99 | with: 100 | issue: ${{ steps.search.outputs.issue }} 101 | comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" 102 | 103 | - name: Close ticket 104 | if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue 105 | uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 106 | with: 107 | issue: ${{ steps.search.outputs.issue }} 108 | transition: "Closed" 109 | 110 | - name: Reopen ticket 111 | if: github.event.action == 'reopened' && steps.search.outputs.issue 112 | uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 113 | with: 114 | issue: ${{ steps.search.outputs.issue }} 115 | transition: "To Do" 116 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | /.terraform/ 3 | **/.terraform/* 4 | 5 | # .tfstate files 6 | *.tfstate 7 | *.tfstate.* 8 | 9 | # .tfvars files 10 | *.tfvars 11 | 12 | # Terraform lock file 13 | .terraform.lock.hcl 14 | 15 | .DS_Store 16 | *.bk 17 | 18 | # Editor files 19 | .vscode 20 | 21 | # TFE license 22 | *.rli 23 | 24 | # Crash log files 25 | crash.log 26 | 27 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 28 | # .tfvars files are managed as part of configuration and so should be included in 29 | # version control. 30 | # 31 | # example.tfvars 32 | 33 | # Ignore override files as they are usually used to override resources locally and so 34 | # are not checked in 35 | override.tf 36 | override.tf.json 37 | *_override.tf 38 | *_override.tf.json 39 | 40 | # Include override files you do wish to add to version control using negated pattern 41 | # 42 | # !example_override.tf 43 | 44 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 45 | # example: *tfplan* 46 | 47 | # Working directory 48 | /work -------------------------------------------------------------------------------- /.tflint.hcl: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | config { 5 | module = false 6 | force = false 7 | disabled_by_default = false 8 | } 9 | 10 | rule "terraform_deprecated_index" { 11 | enabled = true 12 | } 13 | 14 | rule "terraform_unused_declarations" { 15 | enabled = true 16 | } 17 | 18 | rule "terraform_comment_syntax" { 19 | enabled = true 20 | } 21 | 22 | rule "terraform_documented_outputs" { 23 | enabled = true 24 | } 25 | 26 | rule "terraform_documented_variables" { 27 | enabled = true 28 | } 29 | 30 | rule "terraform_typed_variables" { 31 | enabled = false 32 | } 33 | 34 | rule "terraform_naming_convention" { 35 | enabled = true 36 | } 37 | 38 | rule "terraform_required_version" { 39 | enabled = true 40 | } 41 | 42 | rule "terraform_required_providers" { 43 | enabled = true 44 | } 45 | 46 | rule "terraform_unused_required_providers" { 47 | enabled = true 48 | } 49 | 50 | rule "terraform_standard_module_structure" { 51 | enabled = true 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-random-tfe-utility 2 | 3 | **IMPORTANT**: You are viewing a **beta version** of the utility 4 | modules used to support installations of Terraform Enterprise. This 5 | is not currently meant for production use. Please contact your Customer 6 | Success Manager for details before using. 7 | 8 | This is an open-source repository that houses modules that centralize 9 | logic for creating utilities that are common to all Terraform Enterprise 10 | modules. 11 | 12 | ## About This Repository 13 | 14 | This repository contains both modules that are necessary for TFE installations 15 | as well as fixture modules that set up utilities that are common to TFE 16 | installations but not required. Please see the READMEs of each module for 17 | further details. 18 | 19 | ### Terraform version >= 0.13 20 | 21 | * This module requires Terraform version `0.13` or greater to be installed on 22 | the running machine. 23 | -------------------------------------------------------------------------------- /fixtures/test_bastion_init/README.md: -------------------------------------------------------------------------------- 1 | # Test Bastion Init 2 | 3 | This fixture provides initialization scripts for bastion servers which 4 | are used in tests. This module only generates the contents of a 5 | cloud-init scripts. The script will: 6 | - add the TFE private key to the bastion virtual machine 7 | - add `ssh-config` files for the IP addresses that are passed in 8 | 9 | *NOTE: THIS IS NOT RECOMMENDED FOR PRODUCTION.* 10 | Our TFE modules already provide cloud specific ways of connecting to the 11 | TFE instances, therefore this configuration is only necessary when you are 12 | creating a bastion _virtual machine_ for testing purposes. 13 | 14 | ## Example Usage 15 | 16 | ```tf 17 | 18 | # Create the init script from the template 19 | module "test_bastion_init" { 20 | source = "github.com/hashicorp/terraform-random-tfe-utility//fixtures/test_bastion_init" 21 | 22 | tfe_instance_ip_addresses = ["10.0.32.4", "10.0.32.5"] 23 | tfe_instance_user_name = "tfeuser" 24 | tfe_private_key_path = "~/.ssh/private-key.pem" 25 | tfe_private_key_data_base64 = base64encode(module.azure_public_active_active.instance_private_key) 26 | tfe_ssh_config_path = "~/.ssh/ssh_config_" 27 | } 28 | 29 | # Use the init script in the custom data of the bastion virtual machine 30 | module "azurerm_bastion_vm" { 31 | source = "git::https://git@github.com/hashicorp/terraform-azurerm-terraform-enterprise.git//fixtures/bastion_vm" 32 | friendly_name_prefix = random_string.azure_public_active_active.id 33 | 34 | location = "Central US" 35 | resource_group_name = "my_rg" 36 | virtual_network_name = "my-vnet" 37 | network_allow_range = "*" 38 | bastion_subnet_cidr = "10.0.16.0/20" 39 | ssh_public_key = tls_private_key.bastion_ssh.public_key_openssh 40 | bastion_user = "bastionuser" 41 | bastion_custom_data = module.test_bastion_init.custom_data_setup_ssh 42 | 43 | tags = local.common_tags 44 | } 45 | 46 | resource "tls_private_key" "bastion_ssh" { 47 | algorithm = "RSA" 48 | rsa_bits = 4096 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /fixtures/test_bastion_init/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | tfe_ssh_config = [for addr in toset(var.tfe_instance_ip_addresses) : templatefile("${path.module}/templates/ssh-config.tpl", { 6 | instance_ip_address = addr 7 | instance_user_name = var.tfe_instance_user_name 8 | private_key_path = var.tfe_private_key_path 9 | })] 10 | 11 | setup_ssh = templatefile("${path.module}/templates/setup-ssh.sh.tpl", { 12 | ssh_config = local.tfe_ssh_config 13 | private_key_data = var.tfe_private_key_data_base64 14 | private_key_path = var.tfe_private_key_path 15 | ssh_config_path = var.tfe_ssh_config_path 16 | }) 17 | } -------------------------------------------------------------------------------- /fixtures/test_bastion_init/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | output "custom_data_setup_ssh" { 5 | value = base64encode(local.setup_ssh) 6 | description = "A Base64 encoded, rendered template of the cloud-init script that will allow for SSH to the TFE instance." 7 | } -------------------------------------------------------------------------------- /fixtures/test_bastion_init/templates/setup-ssh.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | i=0 6 | %{ for config in ssh_config ~} 7 | ((i=i+1)) 8 | echo "${config}" > ${ssh_config_path}$i 9 | %{ endfor ~} 10 | 11 | echo "${private_key_data}" | base64 -d > ${private_key_path} 12 | chmod 400 ${private_key_path} -------------------------------------------------------------------------------- /fixtures/test_bastion_init/templates/ssh-config.tpl: -------------------------------------------------------------------------------- 1 | Host default 2 | Hostname ${instance_ip_address} 3 | User ${instance_user_name} 4 | Port 22 5 | UserKnownHostsFile /dev/null 6 | StrictHostKeyChecking no 7 | PasswordAuthentication no 8 | IdentityFile ${private_key_path} 9 | IdentitiesOnly yes 10 | LogLevel FATAL -------------------------------------------------------------------------------- /fixtures/test_bastion_init/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # SSH Config 5 | # ---------- 6 | variable "tfe_instance_ip_addresses" { 7 | default = null 8 | type = list(string) 9 | description = "The internal IP address of the TFE instance." 10 | } 11 | 12 | variable "tfe_instance_user_name" { 13 | default = null 14 | type = string 15 | description = "The username of the TFE instance." 16 | } 17 | 18 | variable "tfe_private_key_path" { 19 | default = null 20 | type = string 21 | description = "The private SSH key." 22 | } 23 | 24 | variable "tfe_private_key_data_base64" { 25 | default = null 26 | type = string 27 | description = "The SSH private key data to use on the bastion VM in order to SSH to the TFE instance." 28 | } 29 | 30 | variable "tfe_ssh_config_path" { 31 | default = null 32 | type = string 33 | description = "The path to put the ssh-config file for the TFE instance. This will be iterated based on how many instances there are." 34 | } 35 | -------------------------------------------------------------------------------- /fixtures/test_bastion_init/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_version = ">= 0.13" 6 | 7 | required_providers {} 8 | } 9 | -------------------------------------------------------------------------------- /fixtures/test_hcp_vault/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # Vault AppRole 5 | # ------------- 6 | resource "hcp_vault_cluster" "test" { 7 | cluster_id = var.hcp_vault_cluster_id 8 | hvn_id = var.hcp_vault_cluster_hvn_id 9 | public_endpoint = var.hcp_vault_cluster_public_endpoint 10 | tier = var.hcp_vault_cluster_tier 11 | } 12 | 13 | resource "hcp_vault_cluster_admin_token" "test" { 14 | cluster_id = hcp_vault_cluster.test.cluster_id 15 | } 16 | 17 | # Vault Policy 18 | # ------------- 19 | resource "vault_policy" "ptfe" { 20 | name = var.vault_policy_name 21 | 22 | policy = <$service 50 | [Unit] 51 | Description=mitmproxy 52 | ConditionPathExists=$confdir 53 | [Service] 54 | ExecStart=/usr/local/bin/mitmdump --listen-port ${http_port} --set confdir=$confdir 55 | Restart=always 56 | [Install] 57 | WantedBy=multi-user.target 58 | EOF 59 | 60 | echo "[$(date +"%FT%T")] Starting mitmproxy service" | tee --append $log_pathname 61 | systemctl daemon-reload 62 | systemctl start mitmproxy 63 | systemctl enable mitmproxy 64 | 65 | echo "[$(date +"%FT%T")] Finished mitmproxy startup script" | tee --append $log_pathname 66 | -------------------------------------------------------------------------------- /fixtures/test_proxy_init/templates/squid.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u -o pipefail 4 | 5 | echo "[$(date +"%FT%T")] Starting Squid startup script" | tee --append /var/log/ptfe.log 6 | 7 | echo "[$(date +"%FT%T")] Sleeping 30 seconds to let the network settle" | tee --append /var/log/ptfe.log 8 | sleep 30 9 | 10 | apt-get --yes --option "Acquire::Retries=5" update 11 | 12 | echo "[$(date +"%FT%T")] Installing Squid" | tee --append /var/log/ptfe.log 13 | apt-get --assume-yes update 14 | apt-get --assume-yes install squid 15 | 16 | echo "[$(date +"%FT%T")] Configuring Squid" | tee --append /var/log/ptfe.log 17 | cat < /etc/squid/squid.conf 18 | # https://wiki.squid-cache.org/SquidFaq/ConfiguringSquid#Squid-3.5_default_config 19 | 20 | http_port ${http_port} 21 | 22 | acl localnet src 10.0.0.0/8 # RFC1918 possible internal network 23 | acl localnet src 172.16.0.0/12 # RFC1918 possible internal network 24 | acl localnet src 192.168.0.0/16 # RFC1918 possible internal network 25 | acl localnet src fc00::/7 # RFC 4193 local private network range 26 | acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines 27 | 28 | acl SSL_ports port 443 29 | 30 | acl Safe_ports port 80 # http 31 | acl Safe_ports port 21 # ftp 32 | acl Safe_ports port 443 # https 33 | acl Safe_ports port 70 # gopher 34 | acl Safe_ports port 210 # wais 35 | acl Safe_ports port 280 # http-mgmt 36 | acl Safe_ports port 488 # gss-http 37 | acl Safe_ports port 591 # filemaker 38 | acl Safe_ports port 777 # multiling http 39 | acl Safe_ports port 1025-65535 # unregistered ports 40 | 41 | acl CONNECT method CONNECT 42 | 43 | http_access deny !Safe_ports 44 | http_access deny CONNECT !SSL_ports 45 | http_access allow localhost manager 46 | http_access deny manager 47 | 48 | http_access allow localnet 49 | http_access allow localhost 50 | http_access deny all 51 | 52 | coredump_dir /squid/var/cache/squid 53 | 54 | refresh_pattern ^ftp: 1440 20% 10080 55 | refresh_pattern ^gopher: 1440 0% 1440 56 | refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 57 | refresh_pattern . 0 20% 4320 58 | EOF 59 | 60 | echo "[$(date +"%FT%T")] Restarting Squid" | tee --append /var/log/ptfe.log 61 | systemctl restart squid 62 | 63 | echo "[$(date +"%FT%T")] Finished Squid startup script" | tee --append /var/log/ptfe.log 64 | -------------------------------------------------------------------------------- /fixtures/test_proxy_init/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # General 5 | # ------- 6 | variable "cloud" { 7 | type = string 8 | description = "(Required) On which cloud is this Terraoform Enterprise installation being deployed?" 9 | validation { 10 | condition = ( 11 | var.cloud == "aws" || 12 | var.cloud == "azurerm" || 13 | var.cloud == "google" 14 | ) 15 | 16 | error_message = "Supported values for cloud are 'aws', 'azurerm', or 'google'." 17 | } 18 | } 19 | 20 | variable "mitmproxy_ca_certificate_secret" { 21 | default = null 22 | description = <<-EOD 23 | The identifier of a secret comprising a Base64 encoded PEM certificate file for the mitmproxy Certificate Authority. 24 | For GCP, the Terraform provider calls this value the secret_id and the GCP UI calls it the name. 25 | EOD 26 | type = string 27 | } 28 | 29 | variable "mitmproxy_ca_private_key_secret" { 30 | default = null 31 | description = <<-EOD 32 | The identifier of a secret comprising a Base64 encoded PEM private key file for the mitmproxy Certificate Authority. 33 | For GCP, the Terraform provider calls this value the secret_id and the GCP UI calls it the name. 34 | EOD 35 | type = string 36 | } 37 | -------------------------------------------------------------------------------- /fixtures/test_proxy_init/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_version = ">= 0.13" 6 | 7 | required_providers {} 8 | } 9 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/database_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | database = { 6 | TFE_DATABASE_USER = var.database_user 7 | TFE_DATABASE_PASSWORD = var.database_password 8 | TFE_DATABASE_HOST = var.database_host 9 | TFE_DATABASE_NAME = var.database_name 10 | TFE_DATABASE_PARAMETERS = var.database_parameters 11 | } 12 | database_configuration = local.disk ? {} : local.database 13 | } 14 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | 6 | active_active = var.operational_mode == "active-active" 7 | disk = var.operational_mode == "disk" 8 | env = merge( 9 | local.database_configuration, 10 | local.redis_configuration, 11 | local.storage_configuration, 12 | local.vault_configuration, 13 | { 14 | http_proxy = var.http_proxy != null ? "http://${var.http_proxy}" : null 15 | HTTP_PROXY = var.http_proxy != null ? "http://${var.http_proxy}" : null 16 | https_proxy = var.https_proxy != null ? "http://${var.https_proxy}" : null 17 | HTTPS_PROXY = var.https_proxy != null ? "http://${var.https_proxy}" : null 18 | no_proxy = var.no_proxy != null ? join(",", var.no_proxy) : null 19 | NO_PROXY = var.no_proxy != null ? join(",", var.no_proxy) : null 20 | TFE_HOSTNAME = var.hostname 21 | TFE_HTTP_PORT = var.http_port 22 | TFE_HTTPS_PORT = var.https_port 23 | TFE_ADMIN_HTTPS_PORT = var.admin_api_https_port 24 | TFE_OPERATIONAL_MODE = var.operational_mode 25 | TFE_ENCRYPTION_PASSWORD = random_password.enc_password.result 26 | TFE_DISK_CACHE_VOLUME_NAME = "terraform-enterprise_terraform-enterprise-cache" 27 | TFE_LICENSE_REPORTING_OPT_OUT = var.license_reporting_opt_out 28 | TFE_USAGE_REPORTING_OPT_OUT = var.usage_reporting_opt_out 29 | TFE_LICENSE = var.tfe_license 30 | TFE_TLS_CA_BUNDLE_FILE = var.tls_ca_bundle_file != null ? var.tls_ca_bundle_file : null 31 | TFE_TLS_CERT_FILE = var.cert_file 32 | TFE_TLS_CIPHERS = var.tls_ciphers 33 | TFE_TLS_KEY_FILE = var.key_file 34 | TFE_TLS_VERSION = var.tls_version != null ? var.tls_version : "" 35 | TFE_RUN_PIPELINE_IMAGE = var.run_pipeline_image 36 | TFE_CAPACITY_CONCURRENCY = var.capacity_concurrency 37 | TFE_CAPACITY_CPU = var.capacity_cpu 38 | TFE_CAPACITY_MEMORY = var.capacity_memory 39 | TFE_IACT_SUBNETS = var.iact_subnets 40 | TFE_IACT_TIME_LIMIT = var.iact_time_limit 41 | TFE_IACT_TRUSTED_PROXIES = join(",", var.trusted_proxies) 42 | } 43 | ) 44 | # compose files allow for $ deliminated variable injection. $$ is the appropriate escape. 45 | sensitive_fields = ["TFE_ENCRYPTION_PASSWORD", "TFE_DATABASE_PASSWORD", "TFE_REDIS_PASSWORD"] 46 | compose_escaped_env = { 47 | for k, v in local.env : 48 | k => (contains(local.sensitive_fields, k) ? replace((v == null ? "" : v), "$", "$$") : v) 49 | } 50 | compose = { 51 | version = "3.9" 52 | name = "terraform-enterprise" 53 | services = { 54 | tfe = { 55 | image = var.tfe_image 56 | environment = local.compose_escaped_env 57 | cap_add = [ 58 | "IPC_LOCK" 59 | ] 60 | read_only = true 61 | tmpfs = [ 62 | "/tmp:mode=01777", 63 | "/run:${var.enable_run_exec_tmpfs ? "exec" : "noexec"}", 64 | "/var/log/terraform-enterprise", 65 | ] 66 | ports = flatten([ 67 | "80:${var.http_port}", 68 | "443:${var.https_port}", 69 | "${var.admin_api_https_port}:${var.admin_api_https_port}", 70 | local.active_active ? ["8201:8201"] : [], 71 | var.metrics_endpoint_enabled ? [ 72 | "${var.metrics_endpoint_port_http}:9090", 73 | "${var.metrics_endpoint_port_https}:9091" 74 | ] : [] 75 | ]) 76 | 77 | volumes = flatten([ 78 | { 79 | type = "bind" 80 | source = "/var/run/docker.sock" 81 | target = "/run/docker.sock" 82 | }, 83 | { 84 | type = "bind" 85 | source = "/etc/tfe/ssl" 86 | target = "/etc/ssl/private/terraform-enterprise" 87 | }, 88 | { 89 | type = "volume" 90 | source = "terraform-enterprise-cache" 91 | target = "/var/cache/tfe-task-worker/terraform" 92 | }, 93 | local.disk ? [{ 94 | type = "bind" 95 | source = var.disk_path 96 | target = "/var/lib/terraform-enterprise" 97 | }] : [], 98 | ]) 99 | } 100 | } 101 | volumes = merge( 102 | { terraform-enterprise-cache = {} }, 103 | local.disk ? { terraform-enterprise = {} } : {} 104 | ) 105 | } 106 | kube = { 107 | apiVersion = "v1" 108 | kind = "Pod" 109 | metadata = { 110 | labels = { 111 | app = "terraform-enterprise" 112 | } 113 | name = "terraform-enterprise" 114 | } 115 | spec = { 116 | restartPolicy = "Never" 117 | containers = [{ 118 | env = [ 119 | for k, v in local.env : { 120 | name = k, 121 | value = v 122 | } 123 | ] 124 | image = var.tfe_image 125 | name = "terraform-enterprise" 126 | ports = flatten([ 127 | { 128 | containerPort = var.http_port 129 | hostPort = 80 130 | }, 131 | { 132 | containerPort = var.https_port 133 | hostPort = 443 134 | }, 135 | { 136 | containerPort = var.admin_api_https_port 137 | hostPort = var.admin_api_https_port 138 | }, 139 | local.active_active ? [{ containerPort = 8201, hostPort = 8201 }] : [], 140 | var.metrics_endpoint_enabled ? [ 141 | { containerPort = 9090, hostPort = var.metrics_endpoint_port_http }, 142 | { containerPort = 9091, hostPort = var.metrics_endpoint_port_https } 143 | ] : [] 144 | ]) 145 | securityContext = { 146 | capabilities = { 147 | add = [ 148 | "CAP_IPC_LOCK" 149 | ] 150 | } 151 | readOnlyRootFilesystem = true 152 | seLinuxOptions = { 153 | type = "spc_t" 154 | } 155 | } 156 | volumeMounts = flatten([ 157 | { 158 | mountPath = "/etc/ssl/private/terraform-enterprise" 159 | name = "certs" 160 | }, 161 | { 162 | mountPath = "/var/log/terraform-enterprise" 163 | name = "log" 164 | }, 165 | { 166 | mountPath = "/run" 167 | name = "run" 168 | }, 169 | { 170 | mountPath = "/tmp" 171 | name = "tmp" 172 | }, 173 | { 174 | mountPath = "/run/docker.sock" 175 | name = "docker-sock" 176 | }, 177 | { 178 | mountPath = "/var/cache/tfe-task-worker/terraform" 179 | name = "terraform-enterprise_terraform-enterprise-cache-pvc" 180 | }, 181 | local.disk ? [{ 182 | mountPath = "/var/lib/terraform-enterprise" 183 | name = "data" 184 | }] : [] 185 | ]) 186 | }, 187 | ] 188 | volumes = flatten([ 189 | { 190 | hostPath = { 191 | path = "/etc/tfe/ssl" 192 | type = "Directory" 193 | } 194 | name = "certs" 195 | }, 196 | { 197 | emptyDir = { 198 | medium = "Memory" 199 | } 200 | name = "log" 201 | }, 202 | { 203 | emptyDir = { 204 | medium = "Memory" 205 | } 206 | name = "run" 207 | }, 208 | { 209 | emptyDir = { 210 | medium = "Memory" 211 | } 212 | name = "tmp" 213 | }, 214 | { 215 | hostPath = { 216 | path = "/var/run/docker.sock" 217 | type = "File" 218 | } 219 | name = "docker-sock" 220 | }, 221 | { 222 | name = "terraform-enterprise_terraform-enterprise-cache-pvc" 223 | persistentVolumeClaim = { 224 | claimName = "terraform-enterprise_terraform-enterprise-cache" 225 | } 226 | }, 227 | local.disk ? [{ 228 | hostPath = { 229 | path = var.disk_path 230 | type = "Directory" 231 | } 232 | name = "data" 233 | }] : [], 234 | ]) 235 | } 236 | } 237 | } 238 | 239 | resource "random_password" "enc_password" { 240 | length = 32 241 | special = true 242 | override_special = "!#$%&*()-_=+[]{}<>:?" 243 | } 244 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | output "docker_compose_yaml" { 5 | value = base64encode(yamlencode(local.compose)) 6 | description = "A base 64 encoded yaml object that will be used as the Docker Compose file for TFE deployment." 7 | } 8 | 9 | output "podman_kube_yaml" { 10 | value = base64encode(yamlencode(local.kube)) 11 | description = "A base 64 encoded yaml object that will be used as the Podman kube.yaml file for TFE deployment" 12 | } 13 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/redis_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | redis = { 6 | TFE_REDIS_HOST = var.redis_use_tls != null ? var.redis_use_tls ? "${var.redis_host}:6380" : var.redis_host : null 7 | TFE_REDIS_USER = var.redis_user 8 | TFE_REDIS_PASSWORD = var.redis_password 9 | TFE_REDIS_USE_TLS = var.redis_use_tls 10 | TFE_REDIS_USE_AUTH = var.redis_use_auth 11 | TFE_REDIS_SENTINEL_ENABLED = var.redis_use_sentinel 12 | TFE_REDIS_SENTINEL_HOSTS = join(",", var.redis_sentinel_hosts) 13 | TFE_REDIS_SENTINEL_LEADER_NAME = var.redis_sentinel_leader_name 14 | TFE_REDIS_SENTINEL_PASSWORD = var.redis_sentinel_password 15 | TFE_REDIS_SENTINEL_USERNAME = var.redis_sentinel_user 16 | } 17 | redis_configuration = local.active_active ? local.redis : {} 18 | } 19 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/storage_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | storage_options = { 6 | azure = { 7 | TFE_OBJECT_STORAGE_TYPE = var.storage_type 8 | TFE_OBJECT_STORAGE_AZURE_ACCOUNT_KEY = var.azure_account_key 9 | TFE_OBJECT_STORAGE_AZURE_ACCOUNT_NAME = var.azure_account_name 10 | TFE_OBJECT_STORAGE_AZURE_CONTAINER = var.azure_container 11 | TFE_OBJECT_STORAGE_AZURE_ENDPOINT = var.azure_endpoint 12 | } 13 | google = { 14 | TFE_OBJECT_STORAGE_TYPE = var.storage_type 15 | TFE_OBJECT_STORAGE_GOOGLE_BUCKET = var.google_bucket 16 | TFE_OBJECT_STORAGE_GOOGLE_CREDENTIALS = var.google_credentials 17 | TFE_OBJECT_STORAGE_GOOGLE_PROJECT = var.google_project 18 | } 19 | s3 = { 20 | TFE_OBJECT_STORAGE_TYPE = var.storage_type 21 | TFE_OBJECT_STORAGE_S3_ACCESS_KEY_ID = var.s3_access_key_id 22 | TFE_OBJECT_STORAGE_S3_SECRET_ACCESS_KEY = var.s3_secret_access_key 23 | TFE_OBJECT_STORAGE_S3_REGION = var.s3_region 24 | TFE_OBJECT_STORAGE_S3_BUCKET = var.s3_bucket 25 | TFE_OBJECT_STORAGE_S3_ENDPOINT = var.s3_endpoint 26 | TFE_OBJECT_STORAGE_S3_SERVER_SIDE_ENCRYPTION = var.s3_server_side_encryption 27 | TFE_OBJECT_STORAGE_S3_SERVER_SIDE_ENCRYPTION_KMS_KEY_ID = var.s3_server_side_encryption_kms_key_id 28 | TFE_OBJECT_STORAGE_S3_USE_INSTANCE_PROFILE = var.s3_use_instance_profile 29 | } 30 | } 31 | 32 | storage_configuration = var.storage_type != null && !local.disk ? local.storage_options[var.storage_type] : {} 33 | } 34 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "azure_account_key" { 5 | default = null 6 | type = string 7 | description = "Azure Blob Storage access key. Required when TFE_OBJECT_STORAGE_TYPE is azure and TFE_OBJECT_STORAGE_AZURE_USE_MSI is false." 8 | } 9 | 10 | variable "azure_account_name" { 11 | default = null 12 | type = string 13 | description = "Azure Blob Storage account name. Required when TFE_OBJECT_STORAGE_TYPE is azure." 14 | } 15 | 16 | variable "azure_container" { 17 | default = null 18 | type = string 19 | description = "Azure Blob Storage container name. Required when TFE_OBJECT_STORAGE_TYPE is azure." 20 | } 21 | 22 | variable "azure_endpoint" { 23 | default = null 24 | type = string 25 | description = "Azure Storage endpoint. Useful if using a private endpoint for Azure Stoage. Leave blank to use the default Azure Storage endpoint. Defaults to \"\" if no value is given. " 26 | } 27 | 28 | variable "capacity_concurrency" { 29 | type = number 30 | description = "Maximum number of Terraform runs that can execute concurrently on each Terraform Enterprise node. Defaults to 10 if no value is given." 31 | } 32 | 33 | variable "capacity_cpu" { 34 | type = number 35 | description = "Maximum number of CPU cores a Terraform run is allowed to use. Set to 0 for no limit. Defaults to 0." 36 | } 37 | 38 | variable "capacity_memory" { 39 | type = number 40 | description = "Maximum amount of memory (MiB) a Terraform run is allowed to use. Defaults to 2048 if no value is given." 41 | } 42 | 43 | variable "cert_file" { 44 | type = string 45 | description = "Path to a file containing the TLS certificate Terraform Enterprise will use when serving TLS connections to clients." 46 | } 47 | 48 | variable "database_host" { 49 | type = string 50 | description = "The PostgreSQL server to connect to in the format HOST[:PORT] (e.g. db.example.com or db.example.com:5432). If only HOST is provided then the :PORT defaults to :5432 if no value is given. Required when TFE_OPERATIONAL_MODE is external or active-active." 51 | } 52 | 53 | variable "database_name" { 54 | type = string 55 | description = "Name of the PostgreSQL database to store application data in. Required when TFE_OPERATIONAL_MODE is external or active-active." 56 | } 57 | 58 | variable "database_parameters" { 59 | type = string 60 | description = "PostgreSQL server parameters for the connection URI. Used to configure the PostgreSQL connection (e.g. sslmode=require)." 61 | } 62 | 63 | variable "database_password" { 64 | type = string 65 | description = "PostgreSQL password. Required when TFE_OPERATIONAL_MODE is external or active-active." 66 | } 67 | 68 | variable "database_user" { 69 | type = string 70 | description = "PostgreSQL user. Required when TFE_OPERATIONAL_MODE is external or active-active." 71 | } 72 | 73 | variable "disk_path" { 74 | default = null 75 | description = "The pathname of the directory in which Terraform Enterprise will store data in Mounted Disk mode. Required when var.operational_mode is 'disk'." 76 | type = string 77 | } 78 | 79 | variable "http_port" { 80 | default = null 81 | type = number 82 | description = "Port application listens on for HTTP. Default is 80." 83 | } 84 | 85 | variable "https_port" { 86 | default = null 87 | type = number 88 | description = "Port application listens on for HTTPS. Default is 443." 89 | } 90 | 91 | variable "admin_api_https_port" { 92 | default = 8443 93 | type = number 94 | description = "Port application listens on for Admin API. Default is 8443." 95 | } 96 | 97 | variable "iact_subnets" { 98 | type = string 99 | description = "Comma-separated list of subnets in CIDR notation that are allowed to retrieve the initial admin creation token via the API (e.g. 10.0.0.0/8,192.168.0.0/24). Leave blank to disable retrieving the initial admin creation token via the API from outside the host. Defaults to \"\" if no value is given." 100 | } 101 | 102 | variable "iact_time_limit" { 103 | type = number 104 | description = "Number of minutes that the initial admin creation token can be retrieved via the API after the application starts. Defaults to 60 if no value is given." 105 | } 106 | 107 | variable "google_bucket" { 108 | default = null 109 | type = string 110 | description = "Google Cloud Storage bucket name. Required when TFE_OBJECT_STORAGE_TYPE is google." 111 | } 112 | 113 | variable "google_credentials" { 114 | default = null 115 | type = string 116 | description = "Google Cloud Storage JSON credentials. Must be given as an escaped string of JSON or Base64 encoded JSON. Leave blank to use the attached service account. Defaults to \"\" if no value is given." 117 | } 118 | 119 | variable "google_project" { 120 | default = null 121 | type = string 122 | description = "Google Cloud Storage project name. Required when TFE_OBJECT_STORAGE_TYPE is google." 123 | } 124 | 125 | variable "hostname" { 126 | type = string 127 | description = "Hostname where Terraform Enterprise is accessed (e.g. terraform.example.com)." 128 | } 129 | 130 | variable "http_proxy" { 131 | type = string 132 | description = "(Optional) The IP address and port of existing web proxy to route TFE http traffic through." 133 | default = null 134 | } 135 | 136 | variable "https_proxy" { 137 | type = string 138 | description = "(Optional) The IP address and port of existing web proxy to route TFE https traffic through." 139 | default = null 140 | } 141 | 142 | variable "license_reporting_opt_out" { 143 | type = bool 144 | default = false 145 | description = "Whether to opt out of reporting licensing information to HashiCorp. Defaults to false if no value is given." 146 | } 147 | 148 | variable "usage_reporting_opt_out" { 149 | type = bool 150 | default = false 151 | description = "Whether to opt out of TFE usage reporting to HashiCorp. Defaults to false if no value is given." 152 | } 153 | 154 | variable "key_file" { 155 | type = string 156 | description = "Path to a file containing the TLS private key Terraform Enterprise will use when serving TLS connections to clients." 157 | } 158 | 159 | variable "metrics_endpoint_enabled" { 160 | default = false 161 | type = bool 162 | description = "(Optional) Metrics are used to understand the behavior of Terraform Enterprise and to troubleshoot and tune performance. Enable an endpoint to expose container metrics. Defaults to false." 163 | } 164 | 165 | variable "metrics_endpoint_port_http" { 166 | default = null 167 | type = number 168 | description = "(Optional when metrics_endpoint_enabled is true.) Defines the TCP port on which HTTP metrics requests will be handled. Defaults to 9090." 169 | } 170 | 171 | variable "metrics_endpoint_port_https" { 172 | default = null 173 | type = string 174 | description = "(Optional when metrics_endpoint_enabled is true.) Defines the TCP port on which HTTPS metrics requests will be handled. Defaults to 9091." 175 | } 176 | 177 | 178 | variable "no_proxy" { 179 | type = list(string) 180 | description = "(Optional) List of IP addresses to not proxy" 181 | default = [] 182 | } 183 | 184 | variable "operational_mode" { 185 | type = string 186 | description = "Terraform Enterprise operational mode." 187 | validation { 188 | condition = ( 189 | var.operational_mode == "disk" || 190 | var.operational_mode == "external" || 191 | var.operational_mode == "active-active" 192 | ) 193 | 194 | error_message = "Supported values for operational_mode are 'disk', 'external', and 'active-active'." 195 | } 196 | } 197 | 198 | variable "redis_host" { 199 | type = string 200 | description = "The Redis server to connect to in the format HOST[:PORT] (e.g. redis.example.com or redis.example.com:). If only HOST is provided then the :PORT defaults to :6379 if no value is given. Required when TFE_OPERATIONAL_MODE is active-active." 201 | } 202 | 203 | variable "redis_password" { 204 | type = string 205 | description = "Redis server password. Required when TFE_REDIS_USE_AUTH is true." 206 | } 207 | 208 | variable "redis_use_auth" { 209 | type = bool 210 | description = "Whether or not to use authentication to access Redis. Defaults to false if no value is given." 211 | } 212 | 213 | variable "redis_use_tls" { 214 | type = bool 215 | description = "Whether or not to use TLS to access Redis. Defaults to false if no value is given." 216 | } 217 | 218 | variable "redis_user" { 219 | type = string 220 | description = "Redis server user. Leave blank to not use a user when authenticating. Defaults to \"\" if no value is given." 221 | } 222 | 223 | variable "redis_use_sentinel" { 224 | type = bool 225 | description = "Will connections to redis use the sentinel protocol?" 226 | default = false 227 | } 228 | 229 | variable "redis_sentinel_hosts" { 230 | type = list(string) 231 | description = "A list of sentinel host/port combinations in the form of 'host:port', eg: sentinel-leader.terraform.io:26379" 232 | default = [] 233 | } 234 | 235 | variable "redis_sentinel_leader_name" { 236 | type = string 237 | description = "The name of the sentinel leader." 238 | default = null 239 | } 240 | 241 | variable "redis_sentinel_user" { 242 | type = string 243 | description = "Redis sentinel user. Leave blank to not use a user when authenticating to redis sentinel. Defaults to \"\" if no value is given." 244 | default = null 245 | } 246 | 247 | variable "redis_sentinel_password" { 248 | type = string 249 | description = "Redis senitnel password." 250 | default = null 251 | } 252 | 253 | variable "run_pipeline_image" { 254 | type = string 255 | description = "Container image used to execute Terraform runs. Leave blank to use the default image that comes with Terraform Enterprise. Defaults to \"\" if no value is given." 256 | } 257 | 258 | variable "s3_access_key_id" { 259 | default = null 260 | type = string 261 | description = "S3 access key ID. Required when TFE_OBJECT_STORAGE_TYPE is s3 and TFE_OBJECT_STORAGE_S3_USE_INSTANCE_PROFILE is false." 262 | } 263 | 264 | variable "s3_secret_access_key" { 265 | default = null 266 | type = string 267 | description = "S3 secret access key. Required when TFE_OBJECT_STORAGE_TYPE is s3 and TFE_OBJECT_STORAGE_S3_USE_INSTANCE_PROFILE is false." 268 | 269 | } 270 | 271 | variable "s3_region" { 272 | default = null 273 | type = string 274 | description = "S3 region. Required when TFE_OBJECT_STORAGE_TYPE is s3." 275 | } 276 | 277 | variable "s3_bucket" { 278 | default = null 279 | type = string 280 | description = "S3 bucket name. Required when TFE_OBJECT_STORAGE_TYPE is s3." 281 | } 282 | 283 | variable "s3_endpoint" { 284 | default = null 285 | type = string 286 | description = "S3 endpoint. Useful when using a private S3 endpoint. Leave blank to use the default AWS S3 endpoint. Defaults to \"\" if no value is given." 287 | } 288 | 289 | variable "s3_server_side_encryption" { 290 | default = null 291 | type = string 292 | description = "Server-side encryption algorithm to use. Set to aws:kms to use AWS KMS. Leave blank to disable server-side encryption. Defaults to \"\" if no value is given." 293 | } 294 | 295 | variable "s3_server_side_encryption_kms_key_id" { 296 | default = null 297 | type = string 298 | description = "KMS key ID to use for server-side encryption. Leave blank to use AWS-managed keys. Defaults to \"\" if no value is given." 299 | } 300 | 301 | variable "s3_use_instance_profile" { 302 | default = null 303 | type = string 304 | description = "Whether to use the instance profile for authentication. Defaults to false if no value is given." 305 | } 306 | 307 | variable "storage_type" { 308 | type = string 309 | description = "Type of object storage to use. Must be one of s3, azure, or google. Required when TFE_OPERATIONAL_MODE is external or active-active." 310 | validation { 311 | condition = contains(["s3", "google", "azure"], var.storage_type) 312 | error_message = "The storage_type value must be one of: \"s3\"; \"google\"; \"azure\"." 313 | } 314 | } 315 | 316 | variable "tfe_image" { 317 | type = string 318 | description = "The registry path, image name, and image version (e.g. \"quay.io/hashicorp/terraform-enterprise:1234567\")" 319 | } 320 | 321 | variable "tfe_license" { 322 | type = string 323 | description = "The HashiCorp license. Defaults to \"\" if no value is given. Required when TFE_LICENSE_PATH is unset." 324 | } 325 | 326 | variable "tls_ca_bundle_file" { 327 | default = null 328 | type = string 329 | description = "Path to a file containing TLS CA certificates to be added to the OS CA certificates bundle. Leave blank to not add CA certificates to the OS CA certificates bundle. Defaults to \"\" if no value is given." 330 | } 331 | 332 | variable "tls_ciphers" { 333 | type = string 334 | description = "TLS ciphers to use for TLS. Must be valid OpenSSL format. Leave blank to use the default ciphers. Defaults to \"\" if no value is given." 335 | } 336 | 337 | variable "tls_version" { 338 | default = null 339 | type = string 340 | description = "(Not needed if is_replicated_deployment is true) TLS version to use. Leave blank to use both TLS v1.2 and TLS v1.3. Defaults to `\"\"` if no value is given." 341 | validation { 342 | condition = ( 343 | var.tls_version == null || 344 | var.tls_version == "tls_1_2" || 345 | var.tls_version == "tls_1_3" || 346 | var.tls_version == "tls_1_2_tls_1_3" 347 | ) 348 | error_message = "The tls_version value must be 'tls_1_2', 'tls_1_3', or null." 349 | } 350 | } 351 | 352 | variable "trusted_proxies" { 353 | default = [] 354 | description = "A list of IP address ranges which will be considered safe to ignore when evaluating the IP addresses of requests like those made to the IACT endpoint." 355 | type = list(string) 356 | } 357 | 358 | variable "vault_address" { 359 | type = string 360 | description = "Address of the external Vault server (e.g. https://vault.example.com:8200). Defaults to \"\" if no value is given. Required when TFE_VAULT_USE_EXTERNAL is true." 361 | } 362 | 363 | variable "vault_namespace" { 364 | type = string 365 | description = "Vault namespace. External Vault only. Leave blank to use the default namespace. Defaults to \"\" if no value is given." 366 | } 367 | 368 | variable "vault_path" { 369 | type = string 370 | description = "Vault path when AppRole is mounted. External Vault only. Defaults to auth/approle if no value is given." 371 | } 372 | 373 | variable "vault_role_id" { 374 | type = string 375 | description = "Vault role ID. External Vault only. Required when TFE_VAULT_USE_EXTERNAL is true." 376 | } 377 | 378 | variable "vault_secret_id" { 379 | type = string 380 | description = "Vault secret ID. External Vault only. Required when TFE_VAULT_USE_EXTERNAL is true." 381 | } 382 | 383 | variable "vault_token_renew" { 384 | type = number 385 | description = "Vault token renewal period in seconds. Required when TFE_VAULT_USE_EXTERNAL is true." 386 | } 387 | 388 | variable "enable_run_exec_tmpfs" { 389 | default = false 390 | type = bool 391 | description = "Enable the use of executables in the tmpfs for the /run directory. Defaults to false." 392 | } 393 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/vault_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | 6 | vault_enable_external = var.vault_address != null ? true : false 7 | 8 | external_vault_config = { 9 | TFE_VAULT_USE_EXTERNAL = true 10 | TFE_VAULT_ADDRESS = var.vault_address 11 | TFE_VAULT_NAMESPACE = var.vault_namespace 12 | TFE_VAULT_PATH = var.vault_path 13 | TFE_VAULT_ROLE_ID = var.vault_role_id 14 | TFE_VAULT_SECRET_ID = var.vault_secret_id 15 | TFE_VAULT_TOKEN_RENEW = var.vault_token_renew 16 | } 17 | 18 | vault_cluster_address = { 19 | TFE_VAULT_CLUSTER_ADDRESS = join("", ["https://", "$HOST_IP", ":8201"]) 20 | } 21 | 22 | vault_configuration = local.vault_enable_external ? local.external_vault_config : local.active_active && !local.vault_enable_external ? local.vault_cluster_address : {} 23 | } 24 | -------------------------------------------------------------------------------- /modules/runtime_container_engine_config/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_version = ">= 0.14" 6 | required_providers { 7 | random = { 8 | source = "hashicorp/random" 9 | version = "~> 3.1" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /modules/settings/README.md: -------------------------------------------------------------------------------- 1 | # TFE Settings Module 2 | 3 | This module is used to create the settings that are required for installing Terraform Enterprise (TFE) on a virtual machine. 4 | 5 | ## Required variables 6 | 7 | None of the variables in this module are required, however, if you are using this module to provide the input 8 | variables for the [`tfe_init`](../tfe_init) module, then please review both the variables file in this module 9 | as well as the `tfe_init` module to see what you will need. 10 | 11 | ## Example usage 12 | 13 | This example illustrates how this module may be used by a Terraform Enterprise module, consuming outputs from other submodules. 14 | 15 | ```hcl 16 | module "settings" { 17 | source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/settings?ref=main" 18 | 19 | # TFE Base Configuration 20 | production_type = var.production_type 21 | iact_subnet_list = var.iact_subnet_list 22 | trusted_proxies = local.trusted_proxies 23 | release_sequence = var.release_sequence 24 | pg_extra_params = var.pg_extra_params 25 | 26 | # Replicated Base Configuration 27 | hostname = module.load_balancer.fqdn 28 | tfe_license_file_location = var.tfe_license_file_location 29 | tls_bootstrap_cert_pathname = var.tls_bootstrap_cert_pathname 30 | tls_bootstrap_key_pathname = var.tls_bootstrap_key_pathname 31 | bypass_preflight_checks = var.bypass_preflight_checks 32 | 33 | # Database 34 | pg_dbname = local.database.name 35 | pg_netloc = local.database.address 36 | pg_user = local.database.server.administrator_login 37 | pg_password = local.database.server.administrator_password 38 | 39 | # Redis 40 | redis_host = local.redis.host 41 | redis_pass = local.redis.pass 42 | redis_use_tls = local.redis.use_tls 43 | redis_use_password_auth = local.redis.use_password_auth 44 | 45 | # Azure 46 | azure_account_key = local.object_storage.storage_account_key 47 | azure_account_name = local.object_storage.storage_account_name 48 | azure_container = local.object_storage.storage_account_container_name 49 | } 50 | ``` 51 | 52 | ## Resources 53 | 54 | - [random_id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) 55 | 56 | In addition to `random_id` resources, this module uses `local` variables to form objects that will 57 | be output with the intention of becoming input variables for the [`tfe_init`](../tfe_init) module. 58 | -------------------------------------------------------------------------------- /modules/settings/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | replicated_configuration = { for k, v in local.replicated_base_config : k => v if v != tostring(null) } 6 | 7 | tfe_merged_configuration = merge( 8 | local.base_configs, 9 | local.base_external_configs, 10 | local.external_aws_configs, 11 | local.external_azure_configs, 12 | local.external_google_configs, 13 | local.redis_configuration, 14 | local.external_vault_configs 15 | ) 16 | 17 | tfe_configuration_remove_null = { for k, v in flatten([local.tfe_merged_configuration]).0 : k => v if v.value != tostring(null) } 18 | tfe_configuration = { for k, v in local.tfe_configuration_remove_null : k => v if v.value != "" } 19 | } 20 | 21 | resource "random_id" "archivist_token" { 22 | byte_length = 16 23 | } 24 | 25 | resource "random_id" "cookie_hash" { 26 | byte_length = 16 27 | } 28 | 29 | resource "random_password" "enc_password" { 30 | length = 32 31 | special = true 32 | override_special = "!#$%&*()-_=+[]{}<>:?" 33 | } 34 | 35 | 36 | resource "random_id" "install_id" { 37 | byte_length = 16 38 | } 39 | 40 | resource "random_id" "internal_api_token" { 41 | byte_length = 16 42 | } 43 | 44 | resource "random_id" "root_secret" { 45 | byte_length = 16 46 | } 47 | 48 | resource "random_id" "registry_session_secret_key" { 49 | byte_length = 16 50 | } 51 | 52 | resource "random_id" "registry_session_encryption_key" { 53 | byte_length = 16 54 | } 55 | 56 | resource "random_id" "user_token" { 57 | byte_length = 16 58 | } 59 | -------------------------------------------------------------------------------- /modules/settings/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | output "replicated_configuration" { 5 | value = local.replicated_configuration 6 | description = "The settings that will be used to configure Replicated." 7 | } 8 | 9 | output "tfe_configuration" { 10 | value = local.tfe_configuration 11 | description = "The settings that will be used to configure Terraform Enterprise." 12 | } -------------------------------------------------------------------------------- /modules/settings/replicated_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | resource "random_string" "password" { 5 | length = 16 6 | special = false 7 | } 8 | 9 | locals { 10 | replicated_base_config = { 11 | BypassPreflightChecks = var.bypass_preflight_checks 12 | DaemonAuthenticationType = "password" 13 | DaemonAuthenticationPassword = random_string.password.result 14 | ImportSettingsFrom = "/etc/ptfe-settings.json" 15 | LicenseFileLocation = var.tfe_license_file_location 16 | LicenseBootstrapAirgapPackagePath = var.tfe_license_bootstrap_airgap_package_path 17 | LicenseBootstrapChannelID = var.tfe_license_bootstrap_channel_id 18 | LogLevel = var.log_level 19 | TlsBootstrapHostname = var.hostname 20 | TlsBootstrapCert = var.tls_bootstrap_cert_pathname 21 | TlsBootstrapKey = var.tls_bootstrap_key_pathname 22 | TlsBootstrapType = var.tls_bootstrap_cert_pathname != null ? "server-path" : "self-signed" 23 | ReleaseSequence = var.tfe_license_bootstrap_airgap_package_path != null ? null : var.release_sequence 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /modules/settings/tfe_base_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | has_external_storage = var.production_type == "active-active" || var.production_type == "external" 6 | base_configs = { 7 | hostname = { 8 | value = var.hostname 9 | } 10 | 11 | production_type = { 12 | value = var.production_type 13 | } 14 | 15 | # Alphabetical starting here 16 | archivist_token = { 17 | value = random_id.archivist_token.hex 18 | } 19 | 20 | backup_token = { 21 | value = var.backup_token 22 | } 23 | 24 | capacity_concurrency = { 25 | value = var.capacity_concurrency != null ? tostring(var.capacity_concurrency) : null 26 | } 27 | 28 | capacity_memory = { 29 | value = var.capacity_memory != null ? tostring(var.capacity_memory) : null 30 | } 31 | 32 | capacity_cpus = { 33 | value = var.capacity_cpus != null ? tostring(var.capacity_cpus) : null 34 | } 35 | 36 | cookie_hash = { 37 | value = random_id.cookie_hash.hex 38 | } 39 | 40 | custom_image_tag = { 41 | value = var.custom_agent_image_tag != null ? null : var.custom_image_tag 42 | } 43 | 44 | custom_agent_image_tag = { 45 | value = var.custom_agent_image_tag 46 | } 47 | 48 | disk_path = { 49 | value = var.disk_path 50 | } 51 | 52 | enc_password = { 53 | value = var.extern_vault_enable != null ? var.extern_vault_enable ? null : random_password.enc_password.result : random_password.enc_password.result 54 | } 55 | 56 | extra_no_proxy = { 57 | value = var.extra_no_proxy != null ? join(",", var.extra_no_proxy) : null 58 | } 59 | 60 | hairpin_addressing = { 61 | value = var.hairpin_addressing != null ? var.hairpin_addressing ? "1" : "0" : null 62 | } 63 | 64 | force_tls = { 65 | value = var.force_tls != null ? var.force_tls ? "1" : "0" : null 66 | } 67 | 68 | iact_subnet_list = { 69 | value = var.iact_subnet_list != null ? join(",", var.iact_subnet_list) : null 70 | } 71 | 72 | iact_subnet_time_limit = { 73 | value = var.iact_subnet_time_limit 74 | } 75 | 76 | install_id = { 77 | value = random_id.install_id.hex 78 | } 79 | 80 | internal_api_token = { 81 | value = random_id.internal_api_token.hex 82 | } 83 | 84 | metrics_endpoint_enabled = { 85 | value = var.metrics_endpoint_enabled != null ? var.metrics_endpoint_enabled ? "1" : "0" : null 86 | } 87 | 88 | metrics_endpoint_port_http = { 89 | value = var.metrics_endpoint_port_http != null ? tostring(var.metrics_endpoint_port_http) : null 90 | } 91 | 92 | metrics_endpoint_port_https = { 93 | value = var.metrics_endpoint_port_https != null ? tostring(var.metrics_endpoint_port_https) : null 94 | } 95 | 96 | placement = { 97 | value = (local.has_external_storage && var.s3_bucket != null) ? "placement_s3" : (local.has_external_storage && var.azure_account_name != null) ? "placement_azure" : (local.has_external_storage && var.gcs_bucket != null) ? "placement_gcs" : null 98 | } 99 | 100 | registry_session_encryption_key = { 101 | value = random_id.registry_session_encryption_key.hex 102 | } 103 | 104 | registry_session_secret_key = { 105 | value = random_id.registry_session_secret_key.hex 106 | } 107 | 108 | restrict_worker_metadata_access = { 109 | value = var.restrict_worker_metadata_access != null ? var.restrict_worker_metadata_access ? "1" : "0" : null 110 | } 111 | 112 | root_secret = { 113 | value = random_id.root_secret.hex 114 | } 115 | 116 | run_pipeline_mode = { 117 | value = var.custom_image_tag != null ? "legacy" : "agent" 118 | } 119 | 120 | tbw_image = { 121 | value = var.custom_image_tag != null ? "custom_image" : null 122 | } 123 | 124 | tls_ciphers = { 125 | value = var.tls_ciphers 126 | } 127 | 128 | tls_vers = { 129 | value = var.tls_vers 130 | } 131 | 132 | trusted_proxies = { 133 | value = var.trusted_proxies != null ? join(",", var.trusted_proxies) : null 134 | } 135 | 136 | user_token = { 137 | value = random_id.user_token.hex 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /modules/settings/tfe_base_external_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | pg_configs = { 6 | enable_active_active = { 7 | value = var.production_type == "active-active" ? "1" : "0" 8 | } 9 | 10 | pg_dbname = { 11 | value = var.pg_dbname 12 | } 13 | 14 | pg_netloc = { 15 | value = var.pg_netloc 16 | } 17 | 18 | pg_password = { 19 | value = var.pg_password 20 | } 21 | 22 | pg_user = { 23 | value = var.pg_user 24 | } 25 | 26 | log_forwarding_config = { 27 | value = var.log_forwarding_config 28 | } 29 | 30 | log_forwarding_enabled = { 31 | value = var.log_forwarding_enabled != null ? var.log_forwarding_enabled ? "1" : "0" : null 32 | } 33 | 34 | } 35 | 36 | pg_optional_configs = { 37 | pg_extra_params = { 38 | value = var.pg_extra_params 39 | } 40 | } 41 | 42 | base_external_configs = local.pg_optional_configs != null && (var.production_type == "active-active" || var.production_type == "external") ? (merge(local.pg_configs, local.pg_optional_configs)) : local.pg_configs 43 | } 44 | -------------------------------------------------------------------------------- /modules/settings/tfe_external_aws_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | external_aws_configs = { 6 | 7 | aws_instance_profile = { 8 | value = var.aws_access_key_id == null ? "1" : "0" 9 | } 10 | 11 | aws_access_key_id = { 12 | value = var.aws_access_key_id 13 | } 14 | 15 | aws_secret_access_key = { 16 | value = var.aws_secret_access_key 17 | } 18 | 19 | s3_endpoint = { 20 | value = var.s3_endpoint 21 | } 22 | 23 | s3_bucket = { 24 | value = var.s3_bucket 25 | } 26 | 27 | s3_region = { 28 | value = var.s3_region 29 | } 30 | 31 | s3_sse = { 32 | value = var.s3_sse 33 | } 34 | 35 | s3_sse_kms_key_id = { 36 | value = var.s3_sse_kms_key_id 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /modules/settings/tfe_external_azure_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | external_azure_configs = { 6 | azure_account_name = { 7 | value = var.azure_account_name 8 | } 9 | 10 | azure_account_key = { 11 | value = var.azure_account_key 12 | } 13 | 14 | azure_container = { 15 | value = var.azure_container 16 | } 17 | 18 | azure_endpoint = { 19 | value = var.azure_endpoint 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /modules/settings/tfe_external_google_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | external_google_configs = { 6 | gcs_bucket = { 7 | value = var.gcs_bucket 8 | } 9 | 10 | gcs_credentials = { 11 | value = var.gcs_credentials 12 | } 13 | 14 | gcs_project = { 15 | value = var.gcs_project 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /modules/settings/tfe_external_vault_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | extern_vault_configs = { 6 | extern_vault_enable = { 7 | value = var.extern_vault_enable != null ? var.extern_vault_enable ? "1" : "0" : null 8 | } 9 | 10 | extern_vault_addr = { 11 | value = var.extern_vault_addr 12 | } 13 | 14 | extern_vault_role_id = { 15 | value = var.extern_vault_role_id 16 | } 17 | 18 | extern_vault_secret_id = { 19 | value = var.extern_vault_secret_id 20 | } 21 | 22 | extern_vault_path = { 23 | value = var.extern_vault_path 24 | } 25 | 26 | extern_vault_token_renew = { 27 | value = var.extern_vault_token_renew != null ? tostring(var.extern_vault_token_renew) : null 28 | } 29 | 30 | extern_vault_namespace = { 31 | value = var.extern_vault_namespace 32 | } 33 | 34 | extern_vault_propagate = { 35 | value = var.extern_vault_propagate != null ? var.extern_vault_propagate ? "1" : "0" : null 36 | } 37 | } 38 | 39 | external_vault_configs = var.extern_vault_enable != null ? var.extern_vault_enable ? local.extern_vault_configs : {} : {} 40 | } -------------------------------------------------------------------------------- /modules/settings/tfe_redis_config.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | redis_configs = { 6 | redis_host = { 7 | value = var.redis_host 8 | } 9 | 10 | redis_port = { 11 | value = var.redis_use_tls != null ? var.redis_use_tls ? "6380" : "6379" : null 12 | } 13 | 14 | redis_use_password_auth = { 15 | value = var.redis_use_password_auth != null ? var.redis_use_password_auth ? "1" : "0" : null 16 | } 17 | 18 | redis_pass = { 19 | value = var.redis_pass 20 | } 21 | 22 | redis_use_tls = { 23 | value = var.redis_use_tls != null ? var.redis_use_tls ? "1" : "0" : null 24 | } 25 | } 26 | 27 | redis_configuration = var.production_type == "active-active" ? local.redis_configs : {} 28 | } 29 | -------------------------------------------------------------------------------- /modules/settings/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_version = ">= 0.13" 6 | required_providers { 7 | random = { 8 | source = "hashicorp/random" 9 | version = "~> 3.0" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /modules/tfe_init/README.md: -------------------------------------------------------------------------------- 1 | # TFE Init Module 2 | 3 | This module is used to create the script that will install Terraform Enterprise (TFE) with [Flexible Deployments Options](https://developer.hashicorp.com/terraform/enterprise/flexible-deployments) on a virtual machine. 4 | 5 | ## Required variables 6 | 7 | * `cloud` - the cloud you are deploying to; `aws`, `azurerm`, or `google` 8 | * `distribution` - the OS distribution on which TFE will be deployed; `rhel` or `ubuntu` 9 | * `registry_username` - the username for the docker registry from which to pull the terraform_enterprise container images 10 | * `registry_password` - the password for the docker registry from which to pull the terraform_enterprise container images 11 | * `docker_compose_yaml` - the yaml encoded contents of what make up a docker compose file, to be run with docker compose in the user data script 12 | * `operational_mode` - `disk`, `external`, or `active-active` 13 | 14 | ## Example usage 15 | 16 | This example illustrates how it may be used by a Terraform Enterprise module, consuming outputs from other submodules. 17 | 18 | ```hcl 19 | module "tfe_init_fdo" { 20 | source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init?ref=main" 21 | 22 | cloud = "azurerm" 23 | distribution = "ubuntu" 24 | disk_path = "/opt/hashicorp/data" 25 | disk_device_name = "disk/azure/scsi1/lun${var.vm_data_disk_lun}" 26 | operational_mode = "disk" 27 | enable_monitoring = true 28 | 29 | ca_certificate_secret_id = var.ca_certificate_secret 30 | certificate_secret_id = var.vm_certificate_secret 31 | key_secret_id = var.vm_key_secret 32 | 33 | registry_username = "myusername" 34 | registry_password = "mypassword" 35 | docker_compose_yaml = module.docker_compose_config.docker_compose_yaml 36 | } 37 | ``` 38 | 39 | ## Resources 40 | 41 | This module does not create any Terraform resources, but rather uses the [`templatefile` function](https://www.terraform.io/language/functions/templatefile) 42 | to render a template of the Terraform Enterprise installation script. The module will then output the 43 | rendered script so that it can be used in a TFE installation. 44 | -------------------------------------------------------------------------------- /modules/tfe_init/files/daemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "storage-driver": "overlay2", 3 | "mtu": 1460 4 | } -------------------------------------------------------------------------------- /modules/tfe_init/functions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | get_base64_secrets = templatefile("${path.module}/templates/get_base64_secrets.func", { 6 | cloud = var.cloud 7 | }) 8 | 9 | install_packages = templatefile("${path.module}/templates/install_packages.func", { 10 | cloud = var.cloud 11 | distribution = var.distribution 12 | }) 13 | 14 | install_monitoring_agents = templatefile("${path.module}/templates/install_monitoring_agents.func", { 15 | cloud = var.cloud 16 | distribution = var.distribution 17 | enable_monitoring = var.enable_monitoring != null ? var.enable_monitoring : false 18 | }) 19 | 20 | quadlet_unit = templatefile("${path.module}/templates/terraform-enterprise.kube.tpl", {}) 21 | 22 | retry = templatefile("${path.module}/templates/retry.func", { 23 | cloud = var.cloud 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /modules/tfe_init/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | 6 | tls_bootstrap_path = "/etc/tfe/ssl" 7 | tls_bootstrap_cert_pathname = "${local.tls_bootstrap_path}/cert.pem" 8 | tls_bootstrap_key_pathname = "${local.tls_bootstrap_path}/key.pem" 9 | tls_bootstrap_ca_pathname = "${local.tls_bootstrap_path}/bundle.pem" 10 | user_data_template = { 11 | aws = { 12 | ubuntu = { 13 | docker = "${path.module}/templates/aws.ubuntu.docker.tfe.sh.tpl", 14 | podman = null 15 | }, 16 | rhel = { 17 | docker = "${path.module}/templates/aws.rhel.docker.tfe.sh.tpl", 18 | podman = "${path.module}/templates/aws.rhel.podman.tfe.sh.tpl", 19 | } 20 | }, 21 | azurerm = { 22 | ubuntu = { 23 | docker = "${path.module}/templates/azurerm.ubuntu.docker.tfe.sh.tpl", 24 | podman = null 25 | }, 26 | rhel = { 27 | docker = "${path.module}/templates/azurerm.rhel.docker.tfe.sh.tpl", 28 | podman = "${path.module}/templates/azurerm.rhel.podman.tfe.sh.tpl", 29 | } 30 | } 31 | google = { 32 | ubuntu = { 33 | docker = "${path.module}/templates/google.ubuntu.docker.tfe.sh.tpl", 34 | podman = null 35 | }, 36 | rhel = { 37 | docker = "${path.module}/templates/google.rhel.docker.tfe.sh.tpl", 38 | podman = "${path.module}/templates/google.rhel.podman.tfe.sh.tpl", 39 | } 40 | } 41 | } 42 | tfe_user_data = templatefile( 43 | local.user_data_template[var.cloud][var.distribution][var.container_runtime_engine], 44 | { 45 | get_base64_secrets = local.get_base64_secrets 46 | install_packages = local.install_packages 47 | install_monitoring_agents = local.install_monitoring_agents 48 | retry = local.retry 49 | quadlet_unit = local.quadlet_unit 50 | 51 | active_active = var.operational_mode == "active-active" 52 | cloud = var.cloud 53 | custom_image_tag = try(var.custom_image_tag, null) 54 | disk_path = var.disk_path 55 | disk_device_name = var.disk_device_name 56 | distribution = var.distribution 57 | docker_config = filebase64("${path.module}/files/daemon.json") 58 | docker_version = var.distribution == "rhel" ? var.docker_version_rhel : null 59 | enable_monitoring = var.enable_monitoring != null ? var.enable_monitoring : false 60 | tls_bootstrap_cert_pathname = local.tls_bootstrap_cert_pathname 61 | tls_bootstrap_key_pathname = local.tls_bootstrap_key_pathname 62 | tls_bootstrap_ca_pathname = local.tls_bootstrap_ca_pathname 63 | docker_compose = var.docker_compose_yaml 64 | podman_kube_config = var.podman_kube_yaml 65 | 66 | ca_certificate_secret_id = var.ca_certificate_secret_id 67 | certificate_secret_id = var.certificate_secret_id 68 | key_secret_id = var.key_secret_id 69 | 70 | proxy_ip = var.proxy_ip 71 | proxy_port = var.proxy_port 72 | no_proxy = var.extra_no_proxy != null ? join(",", var.extra_no_proxy) : null 73 | 74 | registry = var.registry 75 | registry_password = var.registry_password 76 | registry_username = var.registry_username 77 | registry_credential = base64encode("${var.registry_username}:${var.registry_password}") 78 | 79 | tfe_image = var.tfe_image 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /modules/tfe_init/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | output "tfe_userdata_base64_encoded" { 5 | value = base64encode(local.tfe_user_data) 6 | description = "The Base64 encoded TFE init script built from modules/tfe_init/templates/tfe.sh.tpl" 7 | } 8 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/aws.rhel.docker.tfe.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu pipefail 3 | 4 | ${get_base64_secrets} 5 | ${install_packages} 6 | %{ if enable_monitoring ~} 7 | ${install_monitoring_agents} 8 | %{ endif ~} 9 | 10 | log_pathname="/var/log/startup.log" 11 | 12 | install_packages $log_pathname 13 | 14 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 15 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 16 | sudo chmod +x /bin/jq 17 | 18 | %{ if proxy_ip != null ~} 19 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 20 | proxy_ip="${proxy_ip}" 21 | proxy_port="${proxy_port}" 22 | /bin/cat <>/etc/environment 23 | http_proxy="${proxy_ip}:${proxy_port}" 24 | https_proxy="${proxy_ip}:${proxy_port}" 25 | no_proxy="${no_proxy}" 26 | EOF 27 | 28 | /bin/cat </etc/profile.d/proxy.sh 29 | http_proxy="${proxy_ip}:${proxy_port}" 30 | https_proxy="${proxy_ip}:${proxy_port}" 31 | no_proxy="${no_proxy}" 32 | EOF 33 | 34 | export http_proxy="${proxy_ip}:${proxy_port}" 35 | export https_proxy="${proxy_ip}:${proxy_port}" 36 | export no_proxy="${no_proxy}" 37 | %{ else ~} 38 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 39 | %{ endif ~} 40 | 41 | %{ if certificate_secret_id != null ~} 42 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 43 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 44 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 45 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 46 | %{ else ~} 47 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 48 | %{ endif ~} 49 | 50 | %{ if key_secret_id != null ~} 51 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 52 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 53 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 54 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 55 | chmod 0600 ${tls_bootstrap_key_pathname} 56 | %{ else ~} 57 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 58 | %{ endif ~} 59 | ca_certificate_directory="/dev/null" 60 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 61 | 62 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 63 | %{ if ca_certificate_secret_id != null ~} 64 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 65 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 66 | mkdir -p $ca_certificate_directory 67 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 68 | %{ else ~} 69 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 70 | %{ endif ~} 71 | 72 | if [ -f "$ca_cert_filepath" ] 73 | then 74 | update-ca-trust 75 | system_ca_certificate_file="/etc/pki/tls/certs/ca-bundle.crt" 76 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 77 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 78 | fi 79 | 80 | %{ if disk_path != null ~} 81 | device="/dev/${disk_device_name}" 82 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 83 | if lsblk --fs $device | grep ext4 84 | then 85 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 86 | else 87 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 88 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 89 | fi 90 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 91 | mkdir --parents ${disk_path} 92 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 93 | mount --options discard,defaults $device ${disk_path} 94 | chmod og+rw ${disk_path} 95 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 96 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 97 | %{ endif ~} 98 | 99 | %{ if enable_monitoring ~} 100 | install_monitoring_agents $log_pathname 101 | %{ endif ~} 102 | 103 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Docker Engine from Repository" | tee -a $log_pathname 104 | /bin/cat < /etc/yum/pluginconf.d/subscription-manager.conf 105 | [main] 106 | enabled=0 107 | EOF 108 | yum install --assumeyes yum-utils 109 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 110 | os_release=$(cat /etc/os-release | grep VERSION_ID | sed "s/VERSION_ID=\"\(.*\)\"/\1/g") 111 | if (( $(echo "$os_release < 8.0" | bc -l ) )); then 112 | /bin/cat <>/etc/yum.repos.d/docker-ce.repo 113 | [centos-extras] 114 | name=Centos extras - \$basearch 115 | baseurl=http://mirror.centos.org/centos/7/extras/x86_64 116 | enabled=1 117 | gpgcheck=1 118 | gpgkey=http://centos.org/keys/RPM-GPG-KEY-CentOS-7 119 | EOF 120 | fi 121 | yum install --assumeyes docker-ce-${docker_version} docker-ce-cli-${docker_version} containerd.io docker-buildx-plugin docker-compose-plugin 122 | systemctl start docker 123 | 124 | 125 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 126 | hostname > /var/log/tfe-fdo.log 127 | docker login -u="${registry_username}" -p="${registry_password}" ${registry} 128 | 129 | export HOST_IP=$(hostname -i) 130 | 131 | tfe_dir="/etc/tfe" 132 | mkdir -p $tfe_dir 133 | 134 | echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml 135 | 136 | docker compose -f /etc/tfe/compose.yaml up -d 137 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/aws.rhel.podman.tfe.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu pipefail 3 | 4 | ${retry} 5 | ${get_base64_secrets} 6 | ${install_packages} 7 | %{ if enable_monitoring ~} 8 | ${install_monitoring_agents} 9 | %{ endif ~} 10 | 11 | log_pathname="/var/log/startup.log" 12 | 13 | install_packages $log_pathname 14 | 15 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 16 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 17 | sudo chmod +x /bin/jq 18 | 19 | %{ if proxy_ip != null ~} 20 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 21 | proxy_ip="${proxy_ip}" 22 | proxy_port="${proxy_port}" 23 | /bin/cat <>/etc/environment 24 | http_proxy="${proxy_ip}:${proxy_port}" 25 | https_proxy="${proxy_ip}:${proxy_port}" 26 | no_proxy="${no_proxy}" 27 | EOF 28 | 29 | /bin/cat </etc/profile.d/proxy.sh 30 | http_proxy="${proxy_ip}:${proxy_port}" 31 | https_proxy="${proxy_ip}:${proxy_port}" 32 | no_proxy="${no_proxy}" 33 | EOF 34 | 35 | export http_proxy="${proxy_ip}:${proxy_port}" 36 | export https_proxy="${proxy_ip}:${proxy_port}" 37 | export no_proxy="${no_proxy}" 38 | %{ else ~} 39 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 40 | %{ endif ~} 41 | 42 | %{ if certificate_secret_id != null ~} 43 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 44 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 45 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 46 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 47 | %{ else ~} 48 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 49 | %{ endif ~} 50 | 51 | %{ if key_secret_id != null ~} 52 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 53 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 54 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 55 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 56 | chmod 0600 ${tls_bootstrap_key_pathname} 57 | %{ else ~} 58 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 59 | %{ endif ~} 60 | ca_certificate_directory="/dev/null" 61 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 62 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 63 | %{ if ca_certificate_secret_id != null ~} 64 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 65 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 66 | mkdir -p $ca_certificate_directory 67 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 68 | %{ else ~} 69 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 70 | %{ endif ~} 71 | 72 | if [ -f "$ca_cert_filepath" ] 73 | then 74 | update-ca-trust 75 | system_ca_certificate_file="/etc/pki/tls/certs/ca-bundle.crt" 76 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 77 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 78 | fi 79 | 80 | %{ if disk_path != null ~} 81 | device="/dev/${disk_device_name}" 82 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 83 | if lsblk --fs $device | grep ext4 84 | then 85 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 86 | else 87 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 88 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 89 | fi 90 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 91 | mkdir --parents ${disk_path} 92 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 93 | mount --options discard,defaults $device ${disk_path} 94 | chmod og+rw ${disk_path} 95 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 96 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 97 | %{ endif ~} 98 | 99 | %{ if enable_monitoring ~} 100 | install_monitoring_agents $log_pathname 101 | %{ endif ~} 102 | 103 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Podman" | tee -a $log_pathname 104 | 105 | if grep -q -i "release 9" /etc/redhat-release 106 | then 107 | dnf install -y container-tools 108 | elif grep -q -i "release 8" /etc/redhat-release 109 | then 110 | dnf module install -y container-tools 111 | dnf install -y podman-docker 112 | else 113 | dnf module install -y container-tools 114 | dnf install -y podman-docker 115 | fi 116 | systemctl enable --now podman.socket 117 | 118 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 119 | hostname > /var/log/tfe-fdo.log 120 | export HOST_IP=$(hostname -i) 121 | tfe_dir="/etc/tfe" 122 | mkdir -p $tfe_dir 123 | 124 | echo ${podman_kube_config} | base64 -d > $tfe_dir/tfe.yaml 125 | 126 | cat > $tfe_dir/auth.json < $tfe_dir/terraform-enterprise.kube <>/etc/environment 24 | http_proxy="${proxy_ip}:${proxy_port}" 25 | https_proxy="${proxy_ip}:${proxy_port}" 26 | no_proxy="${no_proxy}" 27 | EOF 28 | 29 | /bin/cat </etc/profile.d/proxy.sh 30 | http_proxy="${proxy_ip}:${proxy_port}" 31 | https_proxy="${proxy_ip}:${proxy_port}" 32 | no_proxy="${no_proxy}" 33 | EOF 34 | 35 | /bin/cat </etc/apt/apt.conf 36 | Acquire::http::Proxy "http://${proxy_ip}:${proxy_port}"; 37 | Acquire::https::Proxy "http://${proxy_ip}:${proxy_port}"; 38 | EOF 39 | 40 | export http_proxy="${proxy_ip}:${proxy_port}" 41 | export https_proxy="${proxy_ip}:${proxy_port}" 42 | export no_proxy="${no_proxy}" 43 | %{ else ~} 44 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 45 | %{ endif ~} 46 | 47 | %{ if certificate_secret_id != null ~} 48 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 49 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 50 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 51 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 52 | %{ else ~} 53 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 54 | %{ endif ~} 55 | 56 | %{ if key_secret_id != null ~} 57 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 58 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 59 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 60 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 61 | chmod 0600 ${tls_bootstrap_key_pathname} 62 | %{ else ~} 63 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 64 | %{ endif ~} 65 | ca_certificate_directory="/dev/null" 66 | ca_certificate_directory=/usr/local/share/ca-certificates/extra 67 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 68 | %{ if ca_certificate_secret_id != null ~} 69 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 70 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 71 | mkdir -p $ca_certificate_directory 72 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 73 | %{ else ~} 74 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 75 | %{ endif ~} 76 | 77 | if [ -f "$ca_cert_filepath" ] 78 | then 79 | update-ca-certificates 80 | system_ca_certificate_file="/etc/ssl/certs/ca-certificates.crt" 81 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 82 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 83 | fi 84 | 85 | %{ if disk_path != null ~} 86 | device="/dev/${disk_device_name}" 87 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 88 | if lsblk --fs $device | grep ext4 89 | then 90 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 91 | else 92 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 93 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 94 | fi 95 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 96 | mkdir --parents ${disk_path} 97 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 98 | mount --options discard,defaults $device ${disk_path} 99 | chmod og+rw ${disk_path} 100 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 101 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 102 | %{ endif ~} 103 | 104 | %{ if enable_monitoring ~} 105 | install_monitoring_agents $log_pathname 106 | %{ endif ~} 107 | 108 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Docker Engine from Repository" | tee -a $log_pathname 109 | curl --noproxy '*' --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg \ 110 | | gpg --dearmor --output /usr/share/keyrings/docker-archive-keyring.gpg 111 | echo \ 112 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \ 113 | https://download.docker.com/linux/ubuntu $(lsb_release --codename --short) stable" \ 114 | | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 115 | retry 10 apt-get --assume-yes update 116 | retry 10 apt-get --assume-yes install docker-ce docker-ce-cli containerd.io 117 | retry 10 apt-get --assume-yes autoremove 118 | 119 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 120 | hostname > /var/log/tfe-fdo.log 121 | docker login -u="${registry_username}" -p="${registry_password}" ${registry} 122 | 123 | export HOST_IP=$(hostname -i) 124 | 125 | tfe_dir="/etc/tfe" 126 | mkdir -p $tfe_dir 127 | 128 | echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml 129 | 130 | docker compose -f /etc/tfe/compose.yaml up -d 131 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/azurerm.rhel.docker.tfe.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu pipefail 3 | 4 | ${get_base64_secrets} 5 | ${install_packages} 6 | %{ if enable_monitoring ~} 7 | ${install_monitoring_agents} 8 | %{ endif ~} 9 | 10 | log_pathname="/var/log/startup.log" 11 | 12 | install_packages $log_pathname 13 | 14 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 15 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 16 | sudo chmod +x /bin/jq 17 | 18 | %{ if proxy_ip != null ~} 19 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 20 | proxy_ip="${proxy_ip}" 21 | proxy_port="${proxy_port}" 22 | /bin/cat <>/etc/environment 23 | http_proxy="${proxy_ip}:${proxy_port}" 24 | https_proxy="${proxy_ip}:${proxy_port}" 25 | no_proxy="${no_proxy}" 26 | EOF 27 | 28 | /bin/cat </etc/profile.d/proxy.sh 29 | http_proxy="${proxy_ip}:${proxy_port}" 30 | https_proxy="${proxy_ip}:${proxy_port}" 31 | no_proxy="${no_proxy}" 32 | EOF 33 | 34 | export http_proxy="${proxy_ip}:${proxy_port}" 35 | export https_proxy="${proxy_ip}:${proxy_port}" 36 | export no_proxy="${no_proxy}" 37 | %{ else ~} 38 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 39 | %{ endif ~} 40 | 41 | %{ if certificate_secret_id != null ~} 42 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 43 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 44 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 45 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 46 | %{ else ~} 47 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 48 | %{ endif ~} 49 | 50 | %{ if key_secret_id != null ~} 51 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 52 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 53 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 54 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 55 | chmod 0600 ${tls_bootstrap_key_pathname} 56 | %{ else ~} 57 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 58 | %{ endif ~} 59 | ca_certificate_directory="/dev/null" 60 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 61 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 62 | %{ if ca_certificate_secret_id != null ~} 63 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 64 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 65 | mkdir -p $ca_certificate_directory 66 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 67 | %{ else ~} 68 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 69 | %{ endif ~} 70 | 71 | if [ -f "$ca_cert_filepath" ] 72 | then 73 | update-ca-trust 74 | system_ca_certificate_file="/etc/pki/tls/certs/ca-bundle.crt" 75 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 76 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 77 | fi 78 | 79 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Resize RHEL logical volume" | tee -a $log_pathname 80 | terminal_partition=$(parted --script /dev/disk/cloud/azure_root u s p | tail -2 | head -n 1) 81 | terminal_partition_number=$(echo $${terminal_partition:0:3} | xargs) 82 | terminal_partition_link=/dev/disk/cloud/azure_root-part$terminal_partition_number 83 | # Because Microsoft is publishing only LVM-partitioned images, it is necessary to partition it to the specs that TFE requires. 84 | # First, extend the partition to fill available space 85 | growpart /dev/disk/cloud/azure_root $terminal_partition_number 86 | # Resize the physical volume 87 | pvresize $terminal_partition_link 88 | # Then resize the logical volumes to meet TFE specs 89 | lvresize -r -L 10G /dev/mapper/rootvg-rootlv 90 | lvresize -r -L 40G /dev/mapper/rootvg-varlv 91 | 92 | 93 | %{ if disk_path != null ~} 94 | device="/dev/${disk_device_name}" 95 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 96 | if lsblk --fs $device | grep ext4 97 | then 98 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 99 | else 100 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 101 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 102 | fi 103 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 104 | mkdir --parents ${disk_path} 105 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 106 | mount --options discard,defaults $device ${disk_path} 107 | chmod og+rw ${disk_path} 108 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 109 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 110 | %{ endif ~} 111 | 112 | %{ if enable_monitoring ~} 113 | install_monitoring_agents $log_pathname 114 | %{ endif ~} 115 | 116 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Docker Engine from Repository" | tee -a $log_pathname 117 | 118 | /bin/cat < /etc/yum/pluginconf.d/subscription-manager.conf 119 | [main] 120 | enabled=0 121 | EOF 122 | yum install --assumeyes yum-utils 123 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 124 | os_release=$(cat /etc/os-release | grep VERSION_ID | sed "s/VERSION_ID=\"\(.*\)\"/\1/g") 125 | if (( $(echo "$os_release < 8.0" | bc -l ) )); then 126 | /bin/cat <>/etc/yum.repos.d/docker-ce.repo 127 | [centos-extras] 128 | name=Centos extras - \$basearch 129 | baseurl=http://mirror.centos.org/centos/7/extras/x86_64 130 | enabled=1 131 | gpgcheck=1 132 | gpgkey=http://centos.org/keys/RPM-GPG-KEY-CentOS-7 133 | EOF 134 | fi 135 | yum install --assumeyes docker-ce-${docker_version} docker-ce-cli-${docker_version} containerd.io docker-buildx-plugin docker-compose-plugin 136 | systemctl start docker 137 | 138 | 139 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 140 | hostname > /var/log/tfe-fdo.log 141 | docker login -u="${registry_username}" -p="${registry_password}" ${registry} 142 | 143 | export HOST_IP=$(hostname -i) 144 | 145 | tfe_dir="/etc/tfe" 146 | mkdir -p $tfe_dir 147 | 148 | echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml 149 | 150 | docker compose -f /etc/tfe/compose.yaml up -d 151 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/azurerm.rhel.podman.tfe.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu pipefail 3 | 4 | ${get_base64_secrets} 5 | ${install_packages} 6 | %{ if enable_monitoring ~} 7 | ${install_monitoring_agents} 8 | %{ endif ~} 9 | 10 | log_pathname="/var/log/startup.log" 11 | 12 | install_packages $log_pathname 13 | 14 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 15 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 16 | sudo chmod +x /bin/jq 17 | 18 | %{ if proxy_ip != null ~} 19 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 20 | proxy_ip="${proxy_ip}" 21 | proxy_port="${proxy_port}" 22 | /bin/cat <>/etc/environment 23 | http_proxy="${proxy_ip}:${proxy_port}" 24 | https_proxy="${proxy_ip}:${proxy_port}" 25 | no_proxy="${no_proxy}" 26 | EOF 27 | 28 | /bin/cat </etc/profile.d/proxy.sh 29 | http_proxy="${proxy_ip}:${proxy_port}" 30 | https_proxy="${proxy_ip}:${proxy_port}" 31 | no_proxy="${no_proxy}" 32 | EOF 33 | 34 | export http_proxy="${proxy_ip}:${proxy_port}" 35 | export https_proxy="${proxy_ip}:${proxy_port}" 36 | export no_proxy="${no_proxy}" 37 | %{ else ~} 38 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 39 | %{ endif ~} 40 | 41 | %{ if certificate_secret_id != null ~} 42 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 43 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 44 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 45 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 46 | %{ else ~} 47 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 48 | %{ endif ~} 49 | 50 | %{ if key_secret_id != null ~} 51 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 52 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 53 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 54 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 55 | chmod 0600 ${tls_bootstrap_key_pathname} 56 | %{ else ~} 57 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 58 | %{ endif ~} 59 | ca_certificate_directory="/dev/null" 60 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 61 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 62 | %{ if ca_certificate_secret_id != null ~} 63 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 64 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 65 | mkdir -p $ca_certificate_directory 66 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 67 | %{ else ~} 68 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 69 | %{ endif ~} 70 | 71 | if [ -f "$ca_cert_filepath" ] 72 | then 73 | update-ca-trust 74 | system_ca_certificate_file="/etc/pki/tls/certs/ca-bundle.crt" 75 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 76 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 77 | fi 78 | 79 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Resize RHEL logical volume" | tee -a $log_pathname 80 | terminal_partition=$(parted --script /dev/disk/cloud/azure_root u s p | tail -2 | head -n 1) 81 | terminal_partition_number=$(echo $${terminal_partition:0:3} | xargs) 82 | terminal_partition_link=/dev/disk/cloud/azure_root-part$terminal_partition_number 83 | # Because Microsoft is publishing only LVM-partitioned images, it is necessary to partition it to the specs that TFE requires. 84 | # First, extend the partition to fill available space 85 | growpart /dev/disk/cloud/azure_root $terminal_partition_number 86 | # Resize the physical volume 87 | pvresize $terminal_partition_link 88 | # Then resize the logical volumes to meet TFE specs 89 | lvresize -r -L 10G /dev/mapper/rootvg-rootlv 90 | lvresize -r -L 40G /dev/mapper/rootvg-varlv 91 | 92 | 93 | %{ if disk_path != null ~} 94 | device="/dev/${disk_device_name}" 95 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 96 | if lsblk --fs $device | grep ext4 97 | then 98 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 99 | else 100 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 101 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 102 | fi 103 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 104 | mkdir --parents ${disk_path} 105 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 106 | mount --options discard,defaults $device ${disk_path} 107 | chmod og+rw ${disk_path} 108 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 109 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 110 | %{ endif ~} 111 | 112 | %{ if enable_monitoring ~} 113 | install_monitoring_agents $log_pathname 114 | %{ endif ~} 115 | 116 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Podman" | tee -a $log_pathname 117 | 118 | if grep -q -i "release 9" /etc/redhat-release 119 | then 120 | dnf install -y container-tools 121 | elif grep -q -i "release 8" /etc/redhat-release 122 | then 123 | dnf module install -y container-tools 124 | dnf install -y podman-docker 125 | else 126 | dnf module install -y container-tools 127 | dnf install -y podman-docker 128 | fi 129 | systemctl enable --now podman.socket 130 | 131 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 132 | hostname > /var/log/tfe-fdo.log 133 | export HOST_IP=$(hostname -i) 134 | tfe_dir="/etc/tfe" 135 | mkdir -p $tfe_dir 136 | 137 | echo ${podman_kube_config} | base64 -d > $tfe_dir/tfe.yaml 138 | 139 | cat > $tfe_dir/auth.json < $tfe_dir/terraform-enterprise.kube <>/etc/environment 23 | http_proxy="${proxy_ip}:${proxy_port}" 24 | https_proxy="${proxy_ip}:${proxy_port}" 25 | no_proxy="${no_proxy}" 26 | EOF 27 | 28 | /bin/cat </etc/profile.d/proxy.sh 29 | http_proxy="${proxy_ip}:${proxy_port}" 30 | https_proxy="${proxy_ip}:${proxy_port}" 31 | no_proxy="${no_proxy}" 32 | EOF 33 | 34 | /bin/cat </etc/apt/apt.conf 35 | Acquire::http::Proxy "http://${proxy_ip}:${proxy_port}"; 36 | Acquire::https::Proxy "http://${proxy_ip}:${proxy_port}"; 37 | EOF 38 | 39 | export http_proxy="${proxy_ip}:${proxy_port}" 40 | export https_proxy="${proxy_ip}:${proxy_port}" 41 | export no_proxy="${no_proxy}" 42 | %{ else ~} 43 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 44 | %{ endif ~} 45 | 46 | %{ if certificate_secret_id != null ~} 47 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 48 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 49 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 50 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 51 | %{ else ~} 52 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 53 | %{ endif ~} 54 | 55 | %{ if key_secret_id != null ~} 56 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 57 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 58 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 59 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 60 | chmod 0600 ${tls_bootstrap_key_pathname} 61 | %{ else ~} 62 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 63 | %{ endif ~} 64 | ca_certificate_directory="/dev/null" 65 | ca_certificate_directory=/usr/local/share/ca-certificates/extra 66 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 67 | %{ if ca_certificate_secret_id != null ~} 68 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 69 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 70 | mkdir -p $ca_certificate_directory 71 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 72 | %{ else ~} 73 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 74 | %{ endif ~} 75 | 76 | if [ -f "$ca_cert_filepath" ] 77 | then 78 | update-ca-certificates 79 | system_ca_certificate_file="/etc/ssl/certs/ca-certificates.crt" 80 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 81 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 82 | fi 83 | 84 | %{ if disk_path != null ~} 85 | device="/dev/${disk_device_name}" 86 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 87 | if lsblk --fs $device | grep ext4 88 | then 89 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 90 | else 91 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 92 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 93 | fi 94 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 95 | mkdir --parents ${disk_path} 96 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 97 | mount --options discard,defaults $device ${disk_path} 98 | chmod og+rw ${disk_path} 99 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 100 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 101 | %{ endif ~} 102 | 103 | %{ if enable_monitoring ~} 104 | install_monitoring_agents $log_pathname 105 | %{ endif ~} 106 | 107 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Docker Engine from Repository" | tee -a $log_pathname 108 | curl --noproxy '*' --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg \ 109 | | gpg --dearmor --output /usr/share/keyrings/docker-archive-keyring.gpg 110 | echo \ 111 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \ 112 | https://download.docker.com/linux/ubuntu $(lsb_release --codename --short) stable" \ 113 | | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 114 | apt-get --assume-yes update 115 | apt-get --assume-yes install docker-ce docker-ce-cli containerd.io 116 | apt-get --assume-yes autoremove 117 | 118 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 119 | hostname > /var/log/tfe-fdo.log 120 | docker login -u="${registry_username}" -p="${registry_password}" ${registry} 121 | 122 | export HOST_IP=$(hostname -i) 123 | 124 | tfe_dir="/etc/tfe" 125 | mkdir -p $tfe_dir 126 | 127 | echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml 128 | 129 | docker compose -f /etc/tfe/compose.yaml up -d 130 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/get_base64_secrets.func: -------------------------------------------------------------------------------- 1 | %{ if cloud == "azurerm" ~} 2 | function get_base64_secrets { 3 | local secret_id=$1 4 | # OS: Agnostic 5 | # Description: Pull the Base 64 encoded secrets from Azure Key Vault 6 | 7 | access_token=$(curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net' -H Metadata:true | jq -r .access_token) 8 | curl -s --noproxy '*' $secret_id?api-version=2016-10-01 -H "x-ms-version: 2017-11-09" -H "Authorization: Bearer $access_token" | jq -r .value 9 | } 10 | %{ endif ~} 11 | 12 | %{ if cloud == "aws" ~} 13 | function get_base64_secrets { 14 | local secret_id=$1 15 | # OS: Agnostic 16 | # Description: Pull the Base 64 encoded secrets from AWS Secrets Manager 17 | 18 | /usr/local/bin/aws secretsmanager get-secret-value --secret-id $secret_id | jq --raw-output '.SecretBinary,.SecretString | select(. != null)' 19 | } 20 | %{ endif ~} 21 | 22 | %{ if cloud == "google" ~} 23 | get_base64_secrets () { 24 | local secret_id=$1 25 | # OS: Agnostic 26 | # Description: Pull the Base 64 encoded secrets from Google Secrets Manager 27 | 28 | http_proxy="" https_proxy="" gcloud secrets versions access latest --secret="$secret_id" 29 | } 30 | %{ endif ~} -------------------------------------------------------------------------------- /modules/tfe_init/templates/google.rhel.docker.tfe.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu pipefail 3 | 4 | ${get_base64_secrets} 5 | ${install_packages} 6 | %{ if enable_monitoring ~} 7 | ${install_monitoring_agents} 8 | %{ endif ~} 9 | 10 | log_pathname="/var/log/startup.log" 11 | 12 | echo "[Terraform Enterprise] Patching GCP Yum repo configuration" | tee -a $log_pathname 13 | # workaround for GCP RHEL 7 known issue 14 | # https://cloud.google.com/compute/docs/troubleshooting/known-issues#keyexpired 15 | sed -i 's/repo_gpgcheck=1/repo_gpgcheck=0/g' /etc/yum.repos.d/google-cloud.repo 16 | 17 | 18 | install_packages $log_pathname 19 | 20 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 21 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 22 | sudo chmod +x /bin/jq 23 | 24 | docker_directory="/etc/docker" 25 | echo "[Terraform Enterprise] Creating Docker directory at '$docker_directory'" | tee -a $log_pathname 26 | mkdir -p $docker_directory 27 | docker_daemon_pathname="$docker_directory/daemon.json" 28 | echo "[Terraform Enterprise] Writing Docker daemon to '$docker_daemon_pathname'" | tee -a $log_pathname 29 | echo "${docker_config}" | base64 --decode > $docker_daemon_pathname 30 | 31 | %{ if proxy_ip != null ~} 32 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 33 | proxy_ip="${proxy_ip}" 34 | proxy_port="${proxy_port}" 35 | /bin/cat <>/etc/environment 36 | http_proxy="${proxy_ip}:${proxy_port}" 37 | https_proxy="${proxy_ip}:${proxy_port}" 38 | no_proxy="${no_proxy}" 39 | EOF 40 | 41 | /bin/cat </etc/profile.d/proxy.sh 42 | http_proxy="${proxy_ip}:${proxy_port}" 43 | https_proxy="${proxy_ip}:${proxy_port}" 44 | no_proxy="${no_proxy}" 45 | EOF 46 | 47 | export http_proxy="${proxy_ip}:${proxy_port}" 48 | export https_proxy="${proxy_ip}:${proxy_port}" 49 | export no_proxy="${no_proxy}" 50 | %{ else ~} 51 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 52 | %{ endif ~} 53 | 54 | %{ if certificate_secret_id != null ~} 55 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 56 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 57 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 58 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 59 | %{ else ~} 60 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 61 | %{ endif ~} 62 | 63 | %{ if key_secret_id != null ~} 64 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 65 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 66 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 67 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 68 | chmod 0600 ${tls_bootstrap_key_pathname} 69 | %{ else ~} 70 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 71 | %{ endif ~} 72 | ca_certificate_directory="/dev/null" 73 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 74 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 75 | %{ if ca_certificate_secret_id != null ~} 76 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 77 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 78 | mkdir -p $ca_certificate_directory 79 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 80 | %{ else ~} 81 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 82 | %{ endif ~} 83 | 84 | if [ -f "$ca_cert_filepath" ] 85 | then 86 | update-ca-trust 87 | system_ca_certificate_file="/etc/pki/tls/certs/ca-bundle.crt" 88 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 89 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 90 | fi 91 | 92 | %{ if disk_path != null ~} 93 | device="/dev/${disk_device_name}" 94 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 95 | if lsblk --fs $device | grep ext4 96 | then 97 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 98 | else 99 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 100 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 101 | fi 102 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 103 | mkdir --parents ${disk_path} 104 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 105 | mount --options discard,defaults $device ${disk_path} 106 | chmod og+rw ${disk_path} 107 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 108 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 109 | %{ endif ~} 110 | 111 | %{ if enable_monitoring ~} 112 | install_monitoring_agents $log_pathname 113 | %{ endif ~} 114 | 115 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Docker Engine from Repository" | tee -a $log_pathname 116 | /bin/cat < /etc/yum/pluginconf.d/subscription-manager.conf 117 | [main] 118 | enabled=0 119 | EOF 120 | yum install --assumeyes yum-utils 121 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 122 | os_release=$(cat /etc/os-release | grep VERSION_ID | sed "s/VERSION_ID=\"\(.*\)\"/\1/g") 123 | if (( $(echo "$os_release < 8.0" | bc -l ) )); then 124 | /bin/cat <>/etc/yum.repos.d/docker-ce.repo 125 | [centos-extras] 126 | name=Centos extras - \$basearch 127 | baseurl=http://mirror.centos.org/centos/7/extras/x86_64 128 | enabled=1 129 | gpgcheck=1 130 | gpgkey=http://centos.org/keys/RPM-GPG-KEY-CentOS-7 131 | EOF 132 | fi 133 | yum install --assumeyes docker-ce-${docker_version} docker-ce-cli-${docker_version} containerd.io docker-buildx-plugin docker-compose-plugin 134 | systemctl start docker 135 | 136 | 137 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 138 | hostname > /var/log/tfe-fdo.log 139 | docker login -u="${registry_username}" -p="${registry_password}" ${registry} 140 | 141 | export HOST_IP=$(hostname -i) 142 | 143 | tfe_dir="/etc/tfe" 144 | mkdir -p $tfe_dir 145 | 146 | echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml 147 | 148 | docker compose -f /etc/tfe/compose.yaml up -d 149 | 150 | if (( $(echo "$os_release < 8.0" | bc -l ) )); then 151 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Disable SELinux (temporary)" | tee -a $log_pathname 152 | setenforce 0 153 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Add docker0 to firewalld" | tee -a $log_pathname 154 | firewall-cmd --permanent --zone=trusted --change-interface=docker0 155 | firewall-cmd --reload 156 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Enable SELinux" | tee -a $log_pathname 157 | setenforce 1 158 | fi 159 | 160 | 161 | %{ if custom_image_tag != null ~} 162 | %{ if length(regexall("^.+-docker\\.pkg\\.dev|^.*\\.?gcr\\.io", custom_image_tag)) > 0 ~} 163 | echo "[Terraform Enterprise] Registering gcloud as a Docker credential helper" | tee -a 164 | gcloud auth configure-docker --quiet ${split("/", custom_image_tag)[0]} 165 | 166 | %{ endif ~} 167 | echo "[Terraform Enterprise] Pulling custom worker image '${custom_image_tag}'" | tee -a 168 | docker pull ${custom_image_tag} 169 | %{ endif ~} 170 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/google.rhel.podman.tfe.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu pipefail 3 | 4 | ${get_base64_secrets} 5 | ${install_packages} 6 | %{ if enable_monitoring ~} 7 | ${install_monitoring_agents} 8 | %{ endif ~} 9 | 10 | log_pathname="/var/log/startup.log" 11 | 12 | echo "[Terraform Enterprise] Patching GCP Yum repo configuration" | tee -a $log_pathname 13 | # workaround for GCP RHEL 7 known issue 14 | # https://cloud.google.com/compute/docs/troubleshooting/known-issues#keyexpired 15 | sed -i 's/repo_gpgcheck=1/repo_gpgcheck=0/g' /etc/yum.repos.d/google-cloud.repo 16 | 17 | 18 | install_packages $log_pathname 19 | 20 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 21 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 22 | sudo chmod +x /bin/jq 23 | 24 | docker_directory="/etc/docker" 25 | echo "[Terraform Enterprise] Creating Docker directory at '$docker_directory'" | tee -a $log_pathname 26 | mkdir -p $docker_directory 27 | docker_daemon_pathname="$docker_directory/daemon.json" 28 | echo "[Terraform Enterprise] Writing Docker daemon to '$docker_daemon_pathname'" | tee -a $log_pathname 29 | echo "${docker_config}" | base64 --decode > $docker_daemon_pathname 30 | 31 | %{ if proxy_ip != null ~} 32 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 33 | proxy_ip="${proxy_ip}" 34 | proxy_port="${proxy_port}" 35 | /bin/cat <>/etc/environment 36 | http_proxy="${proxy_ip}:${proxy_port}" 37 | https_proxy="${proxy_ip}:${proxy_port}" 38 | no_proxy="${no_proxy}" 39 | EOF 40 | 41 | /bin/cat </etc/profile.d/proxy.sh 42 | http_proxy="${proxy_ip}:${proxy_port}" 43 | https_proxy="${proxy_ip}:${proxy_port}" 44 | no_proxy="${no_proxy}" 45 | EOF 46 | 47 | export http_proxy="${proxy_ip}:${proxy_port}" 48 | export https_proxy="${proxy_ip}:${proxy_port}" 49 | export no_proxy="${no_proxy}" 50 | %{ else ~} 51 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 52 | %{ endif ~} 53 | 54 | %{ if certificate_secret_id != null ~} 55 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 56 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 57 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 58 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 59 | %{ else ~} 60 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 61 | %{ endif ~} 62 | 63 | %{ if key_secret_id != null ~} 64 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 65 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 66 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 67 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 68 | chmod 0600 ${tls_bootstrap_key_pathname} 69 | %{ else ~} 70 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 71 | %{ endif ~} 72 | ca_certificate_directory="/dev/null" 73 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 74 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 75 | %{ if ca_certificate_secret_id != null ~} 76 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 77 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 78 | mkdir -p $ca_certificate_directory 79 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 80 | %{ else ~} 81 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 82 | %{ endif ~} 83 | 84 | if [ -f "$ca_cert_filepath" ] 85 | then 86 | update-ca-trust 87 | system_ca_certificate_file="/etc/pki/tls/certs/ca-bundle.crt" 88 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 89 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 90 | fi 91 | 92 | %{ if disk_path != null ~} 93 | device="/dev/${disk_device_name}" 94 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 95 | if lsblk --fs $device | grep ext4 96 | then 97 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 98 | else 99 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 100 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 101 | fi 102 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 103 | mkdir --parents ${disk_path} 104 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 105 | mount --options discard,defaults $device ${disk_path} 106 | chmod og+rw ${disk_path} 107 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 108 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 109 | %{ endif ~} 110 | 111 | %{ if enable_monitoring ~} 112 | install_monitoring_agents $log_pathname 113 | %{ endif ~} 114 | 115 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Podman" | tee -a $log_pathname 116 | 117 | if grep -q -i "release 9" /etc/redhat-release 118 | then 119 | dnf install -y container-tools 120 | elif grep -q -i "release 8" /etc/redhat-release 121 | then 122 | dnf module install -y container-tools 123 | dnf install -y podman-docker 124 | else 125 | dnf module install -y container-tools 126 | dnf install -y podman-docker 127 | fi 128 | systemctl enable --now podman.socket 129 | 130 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 131 | hostname > /var/log/tfe-fdo.log 132 | export HOST_IP=$(hostname -i) 133 | tfe_dir="/etc/tfe" 134 | mkdir -p $tfe_dir 135 | 136 | echo ${podman_kube_config} | base64 -d > $tfe_dir/tfe.yaml 137 | 138 | cat > $tfe_dir/auth.json < $tfe_dir/terraform-enterprise.kube < $docker_daemon_pathname 31 | 32 | %{ if proxy_ip != null ~} 33 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 34 | proxy_ip="${proxy_ip}" 35 | proxy_port="${proxy_port}" 36 | /bin/cat <>/etc/environment 37 | http_proxy="${proxy_ip}:${proxy_port}" 38 | https_proxy="${proxy_ip}:${proxy_port}" 39 | no_proxy="${no_proxy}" 40 | EOF 41 | 42 | /bin/cat </etc/profile.d/proxy.sh 43 | http_proxy="${proxy_ip}:${proxy_port}" 44 | https_proxy="${proxy_ip}:${proxy_port}" 45 | no_proxy="${no_proxy}" 46 | EOF 47 | 48 | /bin/cat </etc/apt/apt.conf 49 | Acquire::http::Proxy "http://${proxy_ip}:${proxy_port}"; 50 | Acquire::https::Proxy "http://${proxy_ip}:${proxy_port}"; 51 | EOF 52 | 53 | export http_proxy="${proxy_ip}:${proxy_port}" 54 | export https_proxy="${proxy_ip}:${proxy_port}" 55 | export no_proxy="${no_proxy}" 56 | %{ else ~} 57 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 58 | %{ endif ~} 59 | 60 | %{ if certificate_secret_id != null ~} 61 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 62 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 63 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 64 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 65 | %{ else ~} 66 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 67 | %{ endif ~} 68 | 69 | %{ if key_secret_id != null ~} 70 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 71 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 72 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 73 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 74 | chmod 0600 ${tls_bootstrap_key_pathname} 75 | %{ else ~} 76 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 77 | %{ endif ~} 78 | ca_certificate_directory="/dev/null" 79 | ca_certificate_directory=/usr/local/share/ca-certificates/extra 80 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 81 | %{ if ca_certificate_secret_id != null ~} 82 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 83 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 84 | mkdir -p $ca_certificate_directory 85 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 86 | %{ else ~} 87 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 88 | %{ endif ~} 89 | 90 | if [ -f "$ca_cert_filepath" ] 91 | then 92 | update-ca-certificates 93 | system_ca_certificate_file="/etc/ssl/certs/ca-certificates.crt" 94 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 95 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 96 | fi 97 | 98 | %{ if disk_path != null ~} 99 | device="/dev/${disk_device_name}" 100 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 101 | if lsblk --fs $device | grep ext4 102 | then 103 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 104 | else 105 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 106 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 107 | fi 108 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 109 | mkdir --parents ${disk_path} 110 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 111 | mount --options discard,defaults $device ${disk_path} 112 | chmod og+rw ${disk_path} 113 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 114 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 115 | %{ endif ~} 116 | 117 | %{ if enable_monitoring ~} 118 | install_monitoring_agents $log_pathname 119 | %{ endif ~} 120 | 121 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Docker Engine from Repository" | tee -a $log_pathname 122 | curl --noproxy '*' --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg \ 123 | | gpg --dearmor --output /usr/share/keyrings/docker-archive-keyring.gpg 124 | echo \ 125 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \ 126 | https://download.docker.com/linux/ubuntu $(lsb_release --codename --short) stable" \ 127 | | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 128 | apt-get --assume-yes update 129 | apt-get --assume-yes install docker-ce docker-ce-cli containerd.io 130 | apt-get --assume-yes autoremove 131 | 132 | 133 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 134 | hostname > /var/log/tfe-fdo.log 135 | docker login -u="${registry_username}" -p="${registry_password}" ${registry} 136 | 137 | export HOST_IP=$(hostname -i) 138 | 139 | tfe_dir="/etc/tfe" 140 | mkdir -p $tfe_dir 141 | 142 | echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml 143 | 144 | docker compose -f /etc/tfe/compose.yaml up -d 145 | 146 | 147 | %{ if custom_image_tag != null ~} 148 | %{ if length(regexall("^.+-docker\\.pkg\\.dev|^.*\\.?gcr\\.io", custom_image_tag)) > 0 ~} 149 | echo "[Terraform Enterprise] Registering gcloud as a Docker credential helper" | tee -a 150 | gcloud auth configure-docker --quiet ${split("/", custom_image_tag)[0]} 151 | 152 | %{ endif ~} 153 | echo "[Terraform Enterprise] Pulling custom worker image '${custom_image_tag}'" | tee -a 154 | docker pull ${custom_image_tag} 155 | %{ endif ~} 156 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/install_monitoring_agents.func: -------------------------------------------------------------------------------- 1 | %{ if enable_monitoring && cloud == "azurerm" ~} 2 | function install_monitoring_agents { 3 | local log_pathname=$1 4 | : 5 | } 6 | %{ endif ~} 7 | 8 | %{ if enable_monitoring && cloud == "aws" ~} 9 | function install_monitoring_agents { 10 | local log_pathname=$1 11 | : 12 | } 13 | %{ endif ~} 14 | 15 | %{ if enable_monitoring && cloud == "google" ~} 16 | function install_monitoring_agents { 17 | local log_pathname=$1 18 | 19 | monitoring_agent_url="https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh" 20 | monitoring_agent_pathname="/tmp/add-monitoring-agent-repo.sh" 21 | 22 | echo "[Terraform Enterprise] Downloading Cloud Monitoring agent script from '$monitoring_agent_url' to '$monitoring_agent_pathname'" | tee -a $log_pathname 23 | monitoring_agent_url="https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh" 24 | monitoring_agent_pathname="/tmp/add-monitoring-agent-repo.sh" 25 | 26 | echo "[Terraform Enterprise] Downloading Cloud Monitoring agent script from '$monitoring_agent_url' to '$monitoring_agent_pathname'" | tee -a $log_pathname 27 | curl -sS -o $monitoring_agent_pathname $monitoring_agent_url 28 | 29 | echo "[Terraform Enterprise] Executing Cloud Monitoring agent script at '$monitoring_agent_pathname'" | tee -a $log_pathname 30 | chmod +x $monitoring_agent_pathname 31 | bash $monitoring_agent_pathname 32 | 33 | echo "[Terraform Enterprise] Installing Cloud Monitoring agent" | tee -a $log_pathname 34 | %{ if distribution == "ubuntu" ~} 35 | apt-get --assume-yes update 36 | apt-get --assume-yes install stackdriver-agent 37 | apt-get --assume-yes autoremove 38 | %{ else ~} 39 | yum install --assumeyes stackdriver-agent 40 | %{ endif ~} 41 | 42 | echo "[Terraform Enterprise] Starting Cloud Monitoring agent service" | tee -a $log_pathname 43 | service stackdriver-agent start 44 | } 45 | %{ endif ~} -------------------------------------------------------------------------------- /modules/tfe_init/templates/install_packages.func: -------------------------------------------------------------------------------- 1 | %{ if cloud == "aws" ~} 2 | function install_packages { 3 | local log_pathname=$1 4 | # OS: Agnostic 5 | # Description: Install AWS packages 6 | 7 | %{ if distribution == "rhel" ~} 8 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install unzip and SSMAgent with yum" | tee -a $log_pathname 9 | yum install -y \ 10 | firewalld \ 11 | unzip \ 12 | https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm 13 | systemctl enable amazon-ssm-agent 14 | systemctl start amazon-ssm-agent 15 | systemctl enable firewalld 16 | systemctl start firewalld 17 | %{ else ~} 18 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install unzip with apt-get" | tee -a $log_pathname 19 | retry 10 apt-get update -y 20 | retry 10 apt-get install -y unzip 21 | %{ endif ~} 22 | 23 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install AWS CLI" | tee -a $log_pathname 24 | curl --noproxy '*' "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m | grep -q "arm\|aarch" && echo "aarch64" || echo "x86_64").zip" -o "awscliv2.zip" 25 | unzip awscliv2.zip 26 | ./aws/install 27 | rm -f ./awscliv2.zip 28 | rm -rf ./aws 29 | } 30 | %{ endif ~} 31 | 32 | %{ if cloud == "azurerm" ~} 33 | function install_packages { 34 | : 35 | } 36 | %{ endif ~} 37 | 38 | %{ if cloud == "google" ~} 39 | install_packages () { 40 | : 41 | } 42 | %{ endif ~} 43 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/retry.func: -------------------------------------------------------------------------------- 1 | # Retry a command up to a specific numer of times until it exits successfully, 2 | # with exponential back off. 3 | # 4 | # $ retry 5 echo Hello 5 | 6 | function retry { 7 | local retries=$1 8 | shift 9 | 10 | local count=0 11 | until "$@"; do 12 | exit=$? 13 | wait=$((2 ** $count)) 14 | count=$(($count + 1)) 15 | if [ $count -lt $retries ]; then 16 | echo "Retry $count/$retries exited $exit, retrying in $wait seconds..." 17 | sleep $wait 18 | else 19 | echo "Retry $count/$retries exited $exit, no more retries left." 20 | return $exit 21 | fi 22 | done 23 | return 0 24 | 25 | } 26 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/terraform-enterprise.kube.tpl: -------------------------------------------------------------------------------- 1 | [Install] 2 | WantedBy=default.target 3 | 4 | [Service] 5 | Restart=always 6 | 7 | [Kube] 8 | Yaml=tfe.yaml 9 | -------------------------------------------------------------------------------- /modules/tfe_init/templates/tfe.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu pipefail 3 | 4 | ${retry} 5 | ${get_base64_secrets} 6 | ${install_packages} 7 | %{ if enable_monitoring ~} 8 | ${install_monitoring_agents} 9 | %{ endif ~} 10 | 11 | log_pathname="/var/log/startup.log" 12 | 13 | %{ if cloud == "google" && distribution == "rhel" ~} 14 | echo "[Terraform Enterprise] Patching GCP Yum repo configuration" | tee -a $log_pathname 15 | # workaround for GCP RHEL 7 known issue 16 | # https://cloud.google.com/compute/docs/troubleshooting/known-issues#keyexpired 17 | sed -i 's/repo_gpgcheck=1/repo_gpgcheck=0/g' /etc/yum.repos.d/google-cloud.repo 18 | %{ endif ~} 19 | 20 | install_packages $log_pathname 21 | 22 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 23 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 24 | sudo chmod +x /bin/jq 25 | 26 | %{ if cloud == "google" ~} 27 | docker_directory="/etc/docker" 28 | echo "[Terraform Enterprise] Creating Docker directory at '$docker_directory'" | tee -a $log_pathname 29 | mkdir -p $docker_directory 30 | docker_daemon_pathname="$docker_directory/daemon.json" 31 | echo "[Terraform Enterprise] Writing Docker daemon to '$docker_daemon_pathname'" | tee -a $log_pathname 32 | echo "${docker_config}" | base64 --decode > $docker_daemon_pathname 33 | %{ endif ~} 34 | 35 | %{ if proxy_ip != null ~} 36 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 37 | proxy_ip="${proxy_ip}" 38 | proxy_port="${proxy_port}" 39 | /bin/cat <>/etc/environment 40 | http_proxy="${proxy_ip}:${proxy_port}" 41 | https_proxy="${proxy_ip}:${proxy_port}" 42 | no_proxy="${no_proxy}" 43 | EOF 44 | 45 | /bin/cat </etc/profile.d/proxy.sh 46 | http_proxy="${proxy_ip}:${proxy_port}" 47 | https_proxy="${proxy_ip}:${proxy_port}" 48 | no_proxy="${no_proxy}" 49 | EOF 50 | 51 | /bin/cat </etc/apt/apt.conf 52 | Acquire::http::Proxy "http://${proxy_ip}:${proxy_port}"; 53 | Acquire::https::Proxy "http://${proxy_ip}:${proxy_port}"; 54 | EOF 55 | 56 | export http_proxy="${proxy_ip}:${proxy_port}" 57 | export https_proxy="${proxy_ip}:${proxy_port}" 58 | export no_proxy="${no_proxy}" 59 | %{ else ~} 60 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 61 | %{ endif ~} 62 | 63 | %{ if certificate_secret_id != null ~} 64 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 65 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 66 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 67 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 68 | %{ else ~} 69 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 70 | %{ endif ~} 71 | 72 | %{ if key_secret_id != null ~} 73 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 74 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 75 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 76 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 77 | chmod 0600 ${tls_bootstrap_key_pathname} 78 | %{ else ~} 79 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 80 | %{ endif ~} 81 | ca_certificate_directory="/dev/null" 82 | %{ if distribution == "rhel" ~} 83 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 84 | %{ else ~} 85 | ca_certificate_directory=/usr/local/share/ca-certificates/extra 86 | %{ endif ~} 87 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 88 | %{ if ca_certificate_secret_id != null ~} 89 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 90 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 91 | mkdir -p $ca_certificate_directory 92 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 93 | %{ else ~} 94 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 95 | %{ endif ~} 96 | 97 | if [ -f "$ca_cert_filepath" ] 98 | then 99 | %{ if distribution == "rhel" ~} 100 | update-ca-trust 101 | system_ca_certificate_file="/etc/pki/tls/certs/ca-bundle.crt" 102 | %{ else ~} 103 | update-ca-certificates 104 | system_ca_certificate_file="/etc/ssl/certs/ca-certificates.crt" 105 | %{ endif ~} 106 | cp $ca_cert_filepath ${tls_bootstrap_ca_pathname} 107 | tr -d "\\r" < "$ca_cert_filepath" >> "$system_ca_certificate_file" 108 | fi 109 | 110 | %{ if cloud == "azurerm" && distribution == "rhel" ~} 111 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Resize RHEL logical volume" | tee -a $log_pathname 112 | terminal_partition=$(parted --script /dev/disk/cloud/azure_root u s p | tail -2 | head -n 1) 113 | terminal_partition_number=$(echo $${terminal_partition:0:3} | xargs) 114 | terminal_partition_link=/dev/disk/cloud/azure_root-part$terminal_partition_number 115 | # Because Microsoft is publishing only LVM-partitioned images, it is necessary to partition it to the specs that TFE requires. 116 | # First, extend the partition to fill available space 117 | growpart /dev/disk/cloud/azure_root $terminal_partition_number 118 | # Resize the physical volume 119 | pvresize $terminal_partition_link 120 | # Then resize the logical volumes to meet TFE specs 121 | lvresize -r -L 10G /dev/mapper/rootvg-rootlv 122 | lvresize -r -L 40G /dev/mapper/rootvg-varlv 123 | %{ endif ~} 124 | 125 | %{ if disk_path != null ~} 126 | device="/dev/${disk_device_name}" 127 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 128 | if lsblk --fs $device | grep ext4 129 | then 130 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 131 | else 132 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 133 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 134 | fi 135 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 136 | mkdir --parents ${disk_path} 137 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 138 | mount --options discard,defaults $device ${disk_path} 139 | chmod og+rw ${disk_path} 140 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 141 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 142 | %{ endif ~} 143 | 144 | %{ if enable_monitoring ~} 145 | install_monitoring_agents $log_pathname 146 | %{ endif ~} 147 | 148 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing Docker Engine from Repository" | tee -a $log_pathname 149 | %{ if distribution == "rhel" ~} 150 | /bin/cat < /etc/yum/pluginconf.d/subscription-manager.conf 151 | [main] 152 | enabled=0 153 | EOF 154 | yum install --assumeyes yum-utils 155 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 156 | os_release=$(cat /etc/os-release | grep VERSION_ID | sed "s/VERSION_ID=\"\(.*\)\"/\1/g") 157 | if (( $(echo "$os_release < 8.0" | bc -l ) )); then 158 | /bin/cat <>/etc/yum.repos.d/docker-ce.repo 159 | [centos-extras] 160 | name=Centos extras - \$basearch 161 | baseurl=http://mirror.centos.org/centos/7/extras/x86_64 162 | enabled=1 163 | gpgcheck=1 164 | gpgkey=http://centos.org/keys/RPM-GPG-KEY-CentOS-7 165 | EOF 166 | fi 167 | yum install --assumeyes docker-ce-${docker_version} docker-ce-cli-${docker_version} containerd.io docker-buildx-plugin docker-compose-plugin 168 | systemctl start docker 169 | %{ else ~} 170 | curl --noproxy '*' --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg \ 171 | | gpg --dearmor --output /usr/share/keyrings/docker-archive-keyring.gpg 172 | echo \ 173 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \ 174 | https://download.docker.com/linux/ubuntu $(lsb_release --codename --short) stable" \ 175 | | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 176 | apt-get --assume-yes update 177 | apt-get --assume-yes install docker-ce docker-ce-cli containerd.io 178 | apt-get --assume-yes autoremove 179 | %{ endif ~} 180 | 181 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Installing TFE FDO" | tee -a $log_pathname 182 | hostname > /var/log/tfe-fdo.log 183 | docker login -u="${registry_username}" -p="${registry_password}" ${registry} 184 | 185 | export HOST_IP=$(hostname -i) 186 | 187 | tfe_dir="/etc/tfe" 188 | mkdir -p $tfe_dir 189 | 190 | echo ${docker_compose} | base64 -d > $tfe_dir/compose.yaml 191 | 192 | docker compose -f /etc/tfe/compose.yaml up -d 193 | 194 | %{ if distribution == "rhel" && cloud != "google" ~} 195 | if (( $(echo "$os_release < 8.0" | bc -l ) )); then 196 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Disable SELinux (temporary)" | tee -a $log_pathname 197 | setenforce 0 198 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Add docker0 to firewalld" | tee -a $log_pathname 199 | firewall-cmd --permanent --zone=trusted --change-interface=docker0 200 | firewall-cmd --reload 201 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Enable SELinux" | tee -a $log_pathname 202 | setenforce 1 203 | fi 204 | %{ endif ~} 205 | 206 | %{ if custom_image_tag != null && cloud == "google" ~} 207 | %{ if length(regexall("^.+-docker\\.pkg\\.dev|^.*\\.?gcr\\.io", custom_image_tag)) > 0 ~} 208 | echo "[Terraform Enterprise] Registering gcloud as a Docker credential helper" | tee -a 209 | gcloud auth configure-docker --quiet ${split("/", custom_image_tag)[0]} 210 | 211 | %{ endif ~} 212 | echo "[Terraform Enterprise] Pulling custom worker image '${custom_image_tag}'" | tee -a 213 | docker pull ${custom_image_tag} 214 | %{ endif ~} 215 | -------------------------------------------------------------------------------- /modules/tfe_init/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | variable "ca_certificate_secret_id" { 5 | default = null 6 | type = string 7 | description = "A secret ID which contains the Base64 encoded version of a PEM encoded public certificate of a certificate authority (CA) to be trusted by the TFE instance(s)." 8 | } 9 | 10 | variable "certificate_secret_id" { 11 | default = null 12 | type = string 13 | description = "A secret ID which contains the Base64 encoded version of a PEM encoded public certificate for the TFE instance(s)." 14 | } 15 | 16 | variable "cloud" { 17 | default = null 18 | type = string 19 | description = "(Required) On which cloud is this Terraform Enterprise installation being deployed?" 20 | validation { 21 | condition = contains(["aws", "azurerm", "google"], var.cloud) 22 | error_message = "Supported values for cloud are 'aws', 'azurerm', or 'google'." 23 | } 24 | } 25 | 26 | variable "container_runtime_engine" { 27 | default = "docker" 28 | type = string 29 | description = "The container runtime engine to run the FDO container on. Default is docker." 30 | validation { 31 | condition = contains(["docker", "podman"], var.container_runtime_engine) 32 | error_message = "Supported values for container_runtime_enginer are docker and podman." 33 | } 34 | } 35 | 36 | variable "custom_image_tag" { 37 | default = null 38 | type = string 39 | description = "(Required if tbw_image is 'custom_image'.) The name and tag for your alternative Terraform build worker image in the format :. Default is 'hashicorp/build-worker:now'. If this variable is used, the 'tbw_image' variable must be 'custom_image'." 40 | } 41 | 42 | variable "disk_device_name" { 43 | default = null 44 | description = "The name of the disk device on which Terraform Enterprise will store data in Mounted Disk mode." 45 | type = string 46 | } 47 | 48 | variable "disk_path" { 49 | default = null 50 | description = "The pathname of the directory in which Terraform Enterprise will store data in Mounted Disk mode." 51 | type = string 52 | } 53 | 54 | variable "distribution" { 55 | default = null 56 | type = string 57 | description = "(Required) What is the OS distribution of the instance on which Terraoform Enterprise will be deployed?" 58 | validation { 59 | condition = contains(["rhel", "ubuntu"], var.distribution) 60 | error_message = "Supported values for distribution are 'rhel', or 'ubuntu'." 61 | } 62 | } 63 | 64 | variable "docker_compose_yaml" { 65 | default = null 66 | description = "The yaml encoded contents of what make up a docker compose file, to be run with docker compose in the user data script" 67 | } 68 | 69 | variable "docker_version_rhel" { 70 | default = "24.0.2" 71 | description = "When you run `yum list docker-ce --showduplicates | sort -r`, the version comes from the center column. All you need is the format major.minor.patch format." 72 | } 73 | 74 | variable "enable_monitoring" { 75 | default = null 76 | type = bool 77 | description = "Should cloud appropriate monitoring agents be installed as a part of the TFE installation script?" 78 | } 79 | 80 | variable "extra_no_proxy" { 81 | default = null 82 | type = list(string) 83 | description = "When configured to use a proxy, a list of hosts to exclude from proxying. Please note that this list does not support whitespace characters." 84 | } 85 | 86 | variable "key_secret_id" { 87 | default = null 88 | type = string 89 | description = "A secret ID which contains the Base64 encoded version of a PEM encoded private key for the TFE instance(s)." 90 | } 91 | 92 | 93 | variable "operational_mode" { 94 | default = null 95 | description = "A special string to control the operational mode of Terraform Enterprise. Valid values are: 'external' for External Services mode; 'disk' for Mounted Disk mode; 'active-active' for Active/Active mode." 96 | } 97 | 98 | variable "podman_kube_yaml" { 99 | default = null 100 | description = "The yaml encoded contents of what makes up a podman kube yaml file, to be run with podman play kube in the user data script" 101 | } 102 | 103 | variable "proxy_ip" { 104 | default = null 105 | type = string 106 | description = "IP Address of the proxy server" 107 | } 108 | 109 | variable "proxy_port" { 110 | default = null 111 | type = string 112 | description = "Port that the proxy server will use" 113 | } 114 | 115 | variable "registry" { 116 | default = null 117 | type = string 118 | description = "The docker registry from which to source the terraform_enterprise container images." 119 | } 120 | 121 | variable "registry_password" { 122 | default = null 123 | description = "The password for the docker registry from which to pull the terraform_enterprise container images." 124 | type = string 125 | } 126 | 127 | variable "registry_username" { 128 | default = null 129 | description = "The username for the docker registry from which to pull the terraform_enterprise container images." 130 | type = string 131 | } 132 | 133 | variable "tfe_image" { 134 | type = string 135 | description = "The registry path, image name, and image version (e.g. \"quay.io/hashicorp/terraform-enterprise:1234567\")" 136 | } 137 | -------------------------------------------------------------------------------- /modules/tfe_init/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_version = ">= 0.13" 6 | 7 | required_providers {} 8 | } 9 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/README.md: -------------------------------------------------------------------------------- 1 | # TFE Init Module (Replicated) 2 | 3 | This module is used to create the script that will install Terraform Enterprise (TFE) via Replicated on a virtual machine. 4 | 5 | ## Required variables 6 | 7 | * `tfe_license_secret_id` - string value for the TFE license secret ID 8 | * `replicated_configuration` - output object from the [`settings` module](../settings) of the Replicated configuration 9 | * `tfe_configuration` - output object from the [`settings` module](../settings) of the TFE configuration 10 | 11 | ## Example usage 12 | 13 | This example illustrates how it may be used by a Terraform Enterprise module, consuming outputs from other submodules. 14 | 15 | ```hcl 16 | module "tfe_init_replicated" { 17 | source = "git::https://github.com/hashicorp/terraform-random-tfe-utility//modules/tfe_init?ref=main" 18 | 19 | # Replicated Configuration data 20 | enable_active_active = true 21 | 22 | tfe_configuration = module.settings.tfe_configuration 23 | replicated_configuration = module.settings.replicated_configuration 24 | 25 | # Secrets 26 | ca_certificate_secret_id = var.ca_certificate_secret_id 27 | certificate_secret_id = var.vm_certificate_secret_id 28 | key_secret_id = var.vm_key_secret_id 29 | tfe_license_secret_id = var.tfe_license_secret_id 30 | } 31 | ``` 32 | 33 | ## Resources 34 | 35 | This module does not create any Terraform resources, but rather uses the [`templatefile` function](https://www.terraform.io/language/functions/templatefile) 36 | to render a template of the Terraform Enterprise installation script. The module will then output the 37 | rendered script so that it can be used in a TFE installation. 38 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/files/daemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "storage-driver": "overlay2", 3 | "mtu": 1460 4 | } -------------------------------------------------------------------------------- /modules/tfe_init_replicated/functions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | get_base64_secrets = templatefile("${path.module}/templates/get_base64_secrets.func", { 6 | cloud = var.cloud 7 | }) 8 | 9 | install_packages = templatefile("${path.module}/templates/install_packages.func", { 10 | cloud = var.cloud 11 | distribution = var.distribution 12 | }) 13 | 14 | install_monitoring_agents = templatefile("${path.module}/templates/install_monitoring_agents.func", { 15 | cloud = var.cloud 16 | distribution = var.distribution 17 | enable_monitoring = var.enable_monitoring != null ? var.enable_monitoring : false 18 | }) 19 | 20 | retry = templatefile("${path.module}/templates/retry.func", { 21 | cloud = var.cloud 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/main.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | locals { 5 | 6 | # Build TFE user data / custom data / cloud init 7 | tfe_replicated_user_data = templatefile( 8 | "${path.module}/templates/tfe_replicated.sh.tpl", 9 | { 10 | # Functions 11 | get_base64_secrets = local.get_base64_secrets 12 | install_packages = local.install_packages 13 | install_monitoring_agents = local.install_monitoring_agents 14 | retry = local.retry 15 | 16 | # Configuration data 17 | active_active = var.tfe_configuration != null ? var.tfe_configuration.enable_active_active.value == "1" ? true : false : null 18 | airgap_url = var.airgap_url 19 | airgap_pathname = try(var.replicated_configuration.LicenseBootstrapAirgapPackagePath, null) 20 | cloud = var.cloud 21 | custom_image_tag = try(var.tfe_configuration.custom_image_tag.value, null) 22 | disk_path = var.disk_path 23 | disk_device_name = var.disk_device_name 24 | distribution = var.distribution 25 | docker_config = filebase64("${path.module}/files/daemon.json") 26 | enable_monitoring = var.enable_monitoring != null ? var.enable_monitoring : false 27 | replicated = base64encode(jsonencode(var.replicated_configuration)) 28 | settings = base64encode(jsonencode(var.tfe_configuration)) 29 | tls_bootstrap_cert_pathname = try(var.replicated_configuration.TlsBootstrapCert, null) 30 | tls_bootstrap_key_pathname = try(var.replicated_configuration.TlsBootstrapKey, null) 31 | 32 | # Secrets 33 | ca_certificate_secret_id = var.ca_certificate_secret_id 34 | certificate_secret_id = var.certificate_secret_id 35 | key_secret_id = var.key_secret_id 36 | tfe_license_file_location = var.replicated_configuration != null ? var.replicated_configuration.LicenseFileLocation : null 37 | tfe_license_secret_id = var.tfe_license_secret_id 38 | 39 | # Proxy information 40 | proxy_ip = var.proxy_ip 41 | proxy_port = var.proxy_port 42 | no_proxy = var.tfe_configuration != null ? var.tfe_configuration.extra_no_proxy.value : null 43 | } 44 | ) 45 | } 46 | 47 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | output "tfe_userdata_base64_encoded" { 5 | value = base64encode(local.tfe_replicated_user_data) 6 | description = "The Base64 encoded TFE init script built from modules/tfe_init/templates/tfe.sh.tpl" 7 | } 8 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/templates/get_base64_secrets.func: -------------------------------------------------------------------------------- 1 | %{ if cloud == "azurerm" ~} 2 | function get_base64_secrets { 3 | local secret_id=$1 4 | # OS: Agnostic 5 | # Description: Pull the Base 64 encoded secrets from Azure Key Vault 6 | 7 | access_token=$(curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net' -H Metadata:true | jq -r .access_token) 8 | curl -s --noproxy '*' $secret_id?api-version=2016-10-01 -H "x-ms-version: 2017-11-09" -H "Authorization: Bearer $access_token" | jq -r .value 9 | } 10 | %{ endif ~} 11 | 12 | %{ if cloud == "aws" ~} 13 | function get_base64_secrets { 14 | local secret_id=$1 15 | # OS: Agnostic 16 | # Description: Pull the Base 64 encoded secrets from AWS Secrets Manager 17 | 18 | /usr/local/bin/aws secretsmanager get-secret-value --secret-id $secret_id | jq --raw-output '.SecretBinary,.SecretString | select(. != null)' 19 | } 20 | %{ endif ~} 21 | 22 | %{ if cloud == "google" ~} 23 | get_base64_secrets () { 24 | local secret_id=$1 25 | # OS: Agnostic 26 | # Description: Pull the Base 64 encoded secrets from Google Secrets Manager 27 | 28 | http_proxy="" https_proxy="" gcloud secrets versions access latest --secret="$secret_id" 29 | } 30 | %{ endif ~} 31 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/templates/install_monitoring_agents.func: -------------------------------------------------------------------------------- 1 | %{ if enable_monitoring && cloud == "azurerm" ~} 2 | function install_monitoring_agents { 3 | local log_pathname=$1 4 | : 5 | } 6 | %{ endif ~} 7 | 8 | %{ if enable_monitoring && cloud == "aws" ~} 9 | function install_monitoring_agents { 10 | local log_pathname=$1 11 | : 12 | } 13 | %{ endif ~} 14 | 15 | %{ if enable_monitoring && cloud == "google" ~} 16 | function install_monitoring_agents { 17 | local log_pathname=$1 18 | 19 | monitoring_agent_url="https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh" 20 | monitoring_agent_pathname="/tmp/add-monitoring-agent-repo.sh" 21 | 22 | echo "[Terraform Enterprise] Downloading Cloud Monitoring agent script from '$monitoring_agent_url' to '$monitoring_agent_pathname'" | tee -a $log_pathname 23 | monitoring_agent_url="https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh" 24 | monitoring_agent_pathname="/tmp/add-monitoring-agent-repo.sh" 25 | 26 | echo "[Terraform Enterprise] Downloading Cloud Monitoring agent script from '$monitoring_agent_url' to '$monitoring_agent_pathname'" | tee -a $log_pathname 27 | curl -sS -o $monitoring_agent_pathname $monitoring_agent_url 28 | 29 | echo "[Terraform Enterprise] Executing Cloud Monitoring agent script at '$monitoring_agent_pathname'" | tee -a $log_pathname 30 | chmod +x $monitoring_agent_pathname 31 | bash $monitoring_agent_pathname 32 | 33 | echo "[Terraform Enterprise] Installing Cloud Monitoring agent" | tee -a $log_pathname 34 | %{ if distribution == "ubuntu" ~} 35 | apt-get --assume-yes update 36 | apt-get --assume-yes install stackdriver-agent 37 | apt-get --assume-yes autoremove 38 | %{ else ~} 39 | yum install --assumeyes stackdriver-agent 40 | %{ endif ~} 41 | 42 | echo "[Terraform Enterprise] Starting Cloud Monitoring agent service" | tee -a $log_pathname 43 | service stackdriver-agent start 44 | } 45 | %{ endif ~} -------------------------------------------------------------------------------- /modules/tfe_init_replicated/templates/install_packages.func: -------------------------------------------------------------------------------- 1 | %{ if cloud == "aws" ~} 2 | function install_packages { 3 | local log_pathname=$1 4 | # OS: Agnostic 5 | # Description: Install AWS packages 6 | 7 | %{ if distribution == "rhel" || distribution == "amazon-linux-2023" ~} 8 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install unzip with yum" | tee -a $log_pathname 9 | yum install -y unzip 10 | 11 | %{ if distribution == "rhel" ~} 12 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install firewalld with yum" | tee -a $log_pathname 13 | yum install -y firewalld 14 | 15 | # Amazon Linux 2023 already has amazon-ssm-agent so install on rhel only 16 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install ssm-agent with yum" | tee -a $log_pathname 17 | yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm 18 | 19 | systemctl enable firewalld 20 | systemctl start firewalld 21 | %{ endif ~} 22 | 23 | %{ if distribution == "amazon-linux-2023" ~} 24 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Remove existing docker and install v24 with yum" | tee -a $log_pathname 25 | yum -y remove docker 26 | yum clean packages 27 | yum -y install docker-24.0.5-1.amzn2023.0.3 bc 28 | 29 | systemctl start docker 30 | systemctl enable docker 31 | %{ endif ~} 32 | 33 | systemctl enable amazon-ssm-agent 34 | systemctl start amazon-ssm-agent 35 | %{ else ~} 36 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install unzip with apt-get" | tee -a $log_pathname 37 | retry 10 apt-get update -y 38 | retry 10 apt-get install -y unzip 39 | %{ endif ~} 40 | 41 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install AWS CLI" | tee -a $log_pathname 42 | curl --noproxy '*' "https://awscli.amazonaws.com/awscli-exe-linux-$(uname -m | grep -q "arm\|aarch" && echo "aarch64" || echo "x86_64").zip" -o "awscliv2.zip" 43 | unzip awscliv2.zip 44 | ./aws/install 45 | rm -f ./awscliv2.zip 46 | rm -rf ./aws 47 | } 48 | %{ endif ~} 49 | 50 | %{ if cloud == "azurerm" ~} 51 | function install_packages { 52 | : 53 | } 54 | %{ endif ~} 55 | 56 | %{ if cloud == "google" ~} 57 | install_packages () { 58 | : 59 | } 60 | %{ endif ~} 61 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/templates/retry.func: -------------------------------------------------------------------------------- 1 | # Retry a command up to a specific numer of times until it exits successfully, 2 | # with exponential back off. 3 | # 4 | # $ retry 5 echo Hello 5 | 6 | function retry { 7 | local retries=$1 8 | shift 9 | 10 | local count=0 11 | until "$@"; do 12 | exit=$? 13 | wait=$((2 ** $count)) 14 | count=$(($count + 1)) 15 | if [ $count -lt $retries ]; then 16 | echo "Retry $count/$retries exited $exit, retrying in $wait seconds..." 17 | sleep $wait 18 | else 19 | echo "Retry $count/$retries exited $exit, no more retries left." 20 | return $exit 21 | fi 22 | done 23 | return 0 24 | 25 | } 26 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/templates/tfe_replicated.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | ${retry} 5 | ${get_base64_secrets} 6 | ${install_packages} 7 | ${install_monitoring_agents} 8 | 9 | log_pathname="/var/log/ptfe.log" 10 | tfe_settings_file="ptfe-settings.json" 11 | tfe_settings_path="/etc/$tfe_settings_file" 12 | 13 | # ----------------------------------------------------------------------------- 14 | # Patching GCP Yum repo configuration (if GCP environment) 15 | # ----------------------------------------------------------------------------- 16 | %{ if cloud == "google" && distribution == "rhel" ~} 17 | echo "[Terraform Enterprise] Patching GCP Yum repo configuration" | tee -a $log_pathname 18 | # workaround for GCP RHEL 7 known issue 19 | # https://cloud.google.com/compute/docs/troubleshooting/known-issues#keyexpired 20 | sed -i 's/repo_gpgcheck=1/repo_gpgcheck=0/g' /etc/yum.repos.d/google-cloud.repo 21 | %{ endif ~} 22 | 23 | # ----------------------------------------------------------------------------- 24 | # Install jq and cloud specific packages (if not an airgapped environment) 25 | # ----------------------------------------------------------------------------- 26 | %{ if (airgap_url == null && airgap_pathname == null) || (airgap_url != null && airgap_pathname != null) ~} 27 | install_packages $log_pathname 28 | 29 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install JQ" | tee -a $log_pathname 30 | sudo curl --noproxy '*' -Lo /bin/jq https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$(uname -m | grep -q "arm\|aarch" && echo "arm64" || echo "amd64") 31 | sudo chmod +x /bin/jq 32 | 33 | %{ endif ~} 34 | 35 | # ----------------------------------------------------------------------------- 36 | # Create TFE & Replicated Settings Files 37 | # ----------------------------------------------------------------------------- 38 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Create configuration files" | tee -a $log_pathname 39 | sudo echo "${settings}" | sudo base64 -d > $tfe_settings_path 40 | echo "${replicated}" | base64 -d > /etc/replicated.conf 41 | 42 | # ----------------------------------------------------------------------------- 43 | # Configure Docker (if GCP environment) 44 | # ----------------------------------------------------------------------------- 45 | %{ if cloud == "google" ~} 46 | docker_directory="/etc/docker" 47 | echo "[Terraform Enterprise] Creating Docker directory at '$docker_directory'" | tee -a $log_pathname 48 | mkdir -p $docker_directory 49 | docker_daemon_pathname="$docker_directory/daemon.json" 50 | echo "[Terraform Enterprise] Writing Docker daemon to '$docker_daemon_pathname'" | tee -a $log_pathname 51 | echo "${docker_config}" | base64 --decode > $docker_daemon_pathname 52 | %{ endif ~} 53 | 54 | # ----------------------------------------------------------------------------- 55 | # Configure the proxy (if applicable) 56 | # ----------------------------------------------------------------------------- 57 | %{ if proxy_ip != null ~} 58 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure proxy" | tee -a $log_pathname 59 | proxy_ip="${proxy_ip}" 60 | proxy_port="${proxy_port}" 61 | /bin/cat <>/etc/environment 62 | http_proxy="${proxy_ip}:${proxy_port}" 63 | https_proxy="${proxy_ip}:${proxy_port}" 64 | no_proxy="${no_proxy}" 65 | EOF 66 | 67 | /bin/cat </etc/profile.d/proxy.sh 68 | http_proxy="${proxy_ip}:${proxy_port}" 69 | https_proxy="${proxy_ip}:${proxy_port}" 70 | no_proxy="${no_proxy}" 71 | EOF 72 | 73 | export http_proxy="${proxy_ip}:${proxy_port}" 74 | export https_proxy="${proxy_ip}:${proxy_port}" 75 | export no_proxy="${no_proxy}" 76 | %{ else ~} 77 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping proxy configuration" | tee -a $log_pathname 78 | %{ endif ~} 79 | 80 | # ----------------------------------------------------------------------------- 81 | # Configure TLS (if not an airgapped environment) 82 | # ----------------------------------------------------------------------------- 83 | %{ if certificate_secret_id != null ~} 84 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapCert" | tee -a $log_pathname 85 | certificate_data_b64=$(get_base64_secrets ${certificate_secret_id}) 86 | mkdir -p $(dirname ${tls_bootstrap_cert_pathname}) 87 | echo $certificate_data_b64 | base64 --decode > ${tls_bootstrap_cert_pathname} 88 | %{ else ~} 89 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapCert configuration" | tee -a $log_pathname 90 | %{ endif ~} 91 | 92 | %{ if key_secret_id != null ~} 93 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure TlsBootstrapKey" | tee -a $log_pathname 94 | key_data_b64=$(get_base64_secrets ${key_secret_id}) 95 | mkdir -p $(dirname ${tls_bootstrap_key_pathname}) 96 | echo $key_data_b64 | base64 --decode > ${tls_bootstrap_key_pathname} 97 | chmod 0600 ${tls_bootstrap_key_pathname} 98 | 99 | %{ else ~} 100 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping TlsBootstrapKey configuration" | tee -a $log_pathname 101 | %{ endif ~} 102 | 103 | #------------------------------------------------------------------------------ 104 | # Configure CA Certificate (if not an airgapped environment) 105 | #------------------------------------------------------------------------------ 106 | ca_certificate_directory="/dev/null" 107 | 108 | %{ if distribution == "rhel" || distribution == "amazon-linux-2023" ~} 109 | ca_certificate_directory=/usr/share/pki/ca-trust-source/anchors 110 | %{ else ~} 111 | ca_certificate_directory=/usr/local/share/ca-certificates/extra 112 | %{ endif ~} 113 | ca_cert_filepath="$ca_certificate_directory/tfe-ca-certificate.crt" 114 | 115 | %{ if ca_certificate_secret_id != null ~} 116 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Configure CA cert" | tee -a $log_pathname 117 | ca_certificate_data_b64=$(get_base64_secrets ${ca_certificate_secret_id}) 118 | 119 | mkdir -p $ca_certificate_directory 120 | echo $ca_certificate_data_b64 | base64 --decode > $ca_cert_filepath 121 | %{ else ~} 122 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Skipping CA certificate configuration" | tee -a $log_pathname 123 | %{ endif ~} 124 | 125 | if [ -f "$ca_cert_filepath" ] 126 | then 127 | %{ if distribution == "rhel" || distribution == "amazon-linux-2023" ~} 128 | update-ca-trust 129 | 130 | %{ else ~} 131 | update-ca-certificates 132 | %{ endif ~} 133 | 134 | jq ". + { ca_certs: { value: \"$(/bin/cat $ca_cert_filepath)\" } }" -- $tfe_settings_path > $tfe_settings_file.updated 135 | cp ./$tfe_settings_file.updated $tfe_settings_path 136 | fi 137 | 138 | # ----------------------------------------------------------------------------- 139 | # Resize RHEL logical volume (if Azure environment) 140 | # ----------------------------------------------------------------------------- 141 | %{ if cloud == "azurerm" && distribution == "rhel" ~} 142 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Resize RHEL logical volume" | tee -a $log_pathname 143 | 144 | terminal_partition=$(parted --script /dev/disk/cloud/azure_root u s p | tail -2 | head -n 1) 145 | terminal_partition_number=$(echo $${terminal_partition:0:3} | xargs) 146 | terminal_partition_link=/dev/disk/cloud/azure_root-part$terminal_partition_number 147 | # Because Microsoft is publishing only LVM-partitioned images, it is necessary to partition it to the specs that TFE requires. 148 | # First, extend the partition to fill available space 149 | growpart /dev/disk/cloud/azure_root $terminal_partition_number 150 | # Resize the physical volume 151 | pvresize $terminal_partition_link 152 | # Then resize the logical volumes to meet TFE specs 153 | lvresize -r -L 10G /dev/mapper/rootvg-rootlv 154 | lvresize -r -L 40G /dev/mapper/rootvg-varlv 155 | %{ endif ~} 156 | 157 | # ----------------------------------------------------------------------------- 158 | # Configure Mounted Disk Installation 159 | # ----------------------------------------------------------------------------- 160 | %{ if disk_path != null ~} 161 | device="/dev/${disk_device_name}" 162 | echo "[Terraform Enterprise] Checking disk at '$device' for EXT4 filesystem" | tee -a $log_pathname 163 | 164 | if lsblk --fs $device | grep ext4 165 | then 166 | echo "[Terraform Enterprise] EXT4 filesystem detected on disk at '$device'" | tee -a $log_pathname 167 | else 168 | echo "[Terraform Enterprise] Creating EXT4 filesystem on disk at '$device'" | tee -a $log_pathname 169 | 170 | mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard $device -F 171 | fi 172 | 173 | echo "[Terraform Enterprise] Creating mounted disk directory at '${disk_path}'" | tee -a $log_pathname 174 | mkdir --parents ${disk_path} 175 | 176 | echo "[Terraform Enterprise] Mounting disk '$device' to directory at '${disk_path}'" | tee -a $log_pathname 177 | mount --options discard,defaults $device ${disk_path} 178 | chmod og+rw ${disk_path} 179 | 180 | echo "[Terraform Enterprise] Configuring automatic mounting of '$device' to directory at '${disk_path}' on reboot" | tee -a $log_pathname 181 | echo "UUID=$(lsblk --noheadings --output uuid $device) ${disk_path} ext4 discard,defaults 0 2" >> /etc/fstab 182 | 183 | %{ endif ~} 184 | 185 | # ----------------------------------------------------------------------------- 186 | # Install Monitoring Agents 187 | # ----------------------------------------------------------------------------- 188 | %{ if enable_monitoring ~} 189 | install_monitoring_agents $log_pathname 190 | %{ endif ~} 191 | 192 | # ----------------------------------------------------------------------------- 193 | # Retrieve TFE license (if not an airgapped environment) 194 | # ----------------------------------------------------------------------------- 195 | %{ if tfe_license_secret_id != null ~} 196 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Retrieve TFE license" | tee -a $log_pathname 197 | license=$(get_base64_secrets ${tfe_license_secret_id}) 198 | echo $license | base64 -d > ${tfe_license_file_location} 199 | %{ endif ~} 200 | 201 | # ----------------------------------------------------------------------------- 202 | # Download Replicated 203 | # ----------------------------------------------------------------------------- 204 | replicated_directory="/etc/replicated" 205 | 206 | %{ if airgap_url != null && airgap_pathname != null ~} 207 | # Bootstrap airgapped environment with prerequisites (for dev/test environments) 208 | echo "[Terraform Enterprise] Installing Docker Engine from Repository for Bootstrapping an Airgapped Installation" | tee -a $log_pathname 209 | 210 | %{ if distribution == "rhel" ~} 211 | yum install --assumeyes yum-utils 212 | yum-config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo 213 | yum install --assumeyes docker-ce docker-ce-cli containerd.io 214 | %{ else ~} 215 | apt-get --assume-yes update 216 | apt-get --assume-yes install \ 217 | ca-certificates \ 218 | curl \ 219 | gnupg \ 220 | lsb-release 221 | curl --noproxy '*' --fail --silent --show-error --location https://download.docker.com/linux/ubuntu/gpg \ 222 | | gpg --dearmor --output /usr/share/keyrings/docker-archive-keyring.gpg 223 | echo \ 224 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \ 225 | https://download.docker.com/linux/ubuntu $(lsb_release --codename --short) stable" \ 226 | | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 227 | apt-get --assume-yes update 228 | apt-get --assume-yes install docker-ce docker-ce-cli containerd.io 229 | apt-get --assume-yes autoremove 230 | %{ endif ~} 231 | 232 | replicated_filename="replicated.tar.gz" 233 | replicated_url="https://s3.amazonaws.com/replicated-airgap-work/$replicated_filename" 234 | replicated_pathname="$replicated_directory/$replicated_filename" 235 | 236 | echo "[Terraform Enterprise] Downloading Replicated from '$replicated_url' to '$replicated_pathname'" | tee -a $log_pathname 237 | curl --noproxy '*' --create-dirs --output "$replicated_pathname" "$replicated_url" 238 | echo "[Terraform Enterprise] Extracting Replicated in '$replicated_directory'" | tee -a $log_pathname 239 | tar --directory "$replicated_directory" --extract --file "$replicated_pathname" 240 | 241 | echo "[Terraform Enterprise] Copying airgap package '${airgap_url}' to '${airgap_pathname}'" | tee -a $log_pathname 242 | curl --noproxy '*' --create-dirs --output "${airgap_pathname}" "${airgap_url}" 243 | %{ else ~} 244 | echo "[Terraform Enterprise] Skipping Airgapped Replicated download" | tee -a $log_pathname 245 | %{ endif ~} 246 | 247 | # ----------------------------------------------------------------------------- 248 | # Install Terraform Enterprise 249 | # ----------------------------------------------------------------------------- 250 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Install TFE" | tee -a $log_pathname 251 | 252 | %{ if cloud == "azurerm" ~} 253 | instance_ip=$(curl -s -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq '.network.interface[0].ipv4.ipAddress[0].privateIpAddress' -r) 254 | %{ else ~} 255 | instance_ip=$(hostname -i) 256 | %{ endif ~} 257 | 258 | install_pathname="$replicated_directory/install.sh" 259 | 260 | %{ if airgap_pathname == null ~} 261 | curl --noproxy '*' --create-dirs --output $install_pathname https://get.replicated.com/docker/terraformenterprise/active-active 262 | %{ endif ~} 263 | 264 | chmod +x $install_pathname 265 | cd $replicated_directory 266 | $install_pathname \ 267 | fast-timeouts \ 268 | bypass-firewalld-warning \ 269 | %{ if proxy_ip != null ~} 270 | http-proxy="${proxy_ip}:${proxy_port}" \ 271 | additional-no-proxy="${no_proxy}" \ 272 | %{ else ~} 273 | no-proxy \ 274 | %{ endif ~} 275 | %{if active_active ~} 276 | disable-replicated-ui \ 277 | %{ endif ~} 278 | private-address="$instance_ip" \ 279 | public-address="$instance_ip" \ 280 | %{ if airgap_pathname != null ~} 281 | airgap \ 282 | %{ endif ~} 283 | %{ if distribution == "amazon-linux-2023" ~} 284 | no-docker \ 285 | %{ endif ~} 286 | | tee -a $log_pathname 287 | 288 | # ----------------------------------------------------------------------------- 289 | # Add docker0 to firewalld (for Red Hat instances only) 290 | # ----------------------------------------------------------------------------- 291 | %{ if distribution == "amazon-linux-2023" || distribution == "rhel" && cloud != "google" ~} 292 | os_release=$(cat /etc/os-release | grep VERSION_ID | sed "s/VERSION_ID=\"\(.*\)\"/\1/g") 293 | if (( $(echo "$os_release < 8.0" | bc -l ) )); then 294 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Disable SELinux (temporary)" | tee -a $log_pathname 295 | setenforce 0 296 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Add docker0 to firewalld" | tee -a $log_pathname 297 | firewall-cmd --permanent --zone=trusted --change-interface=docker0 298 | firewall-cmd --reload 299 | echo "[$(date +"%FT%T")] [Terraform Enterprise] Enable SELinux" | tee -a $log_pathname 300 | setenforce 1 301 | fi 302 | %{ endif ~} 303 | 304 | # ----------------------------------------------------------------------------- 305 | # Pulling custom worker image (currently for GCP environments only) 306 | # ----------------------------------------------------------------------------- 307 | %{ if custom_image_tag != null && cloud == "google" ~} 308 | %{ if length(regexall("^.+-docker\\.pkg\\.dev|^.*\\.?gcr\\.io", custom_image_tag)) > 0 ~} 309 | echo "[Terraform Enterprise] Registering gcloud as a Docker credential helper" | tee -a 310 | gcloud auth configure-docker --quiet ${split("/", custom_image_tag)[0]} 311 | 312 | %{ endif ~} 313 | echo "[Terraform Enterprise] Pulling custom worker image '${custom_image_tag}'" | tee -a 314 | docker pull ${custom_image_tag} 315 | %{ endif ~} 316 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # General 5 | # ------- 6 | variable "cloud" { 7 | default = null 8 | type = string 9 | description = "(Required) On which cloud is this Terraform Enterprise installation being deployed?" 10 | validation { 11 | condition = contains(["aws", "azurerm", "google"], var.cloud) 12 | error_message = "Supported values for cloud are 'aws', 'azurerm', or 'google'." 13 | } 14 | } 15 | 16 | variable "distribution" { 17 | default = null 18 | type = string 19 | description = "(Required) What is the OS distribution of the instance on which Terraoform Enterprise will be deployed?" 20 | validation { 21 | condition = contains(["rhel", "ubuntu", "amazon-linux-2023"], var.distribution) 22 | error_message = "Supported values for distribution are 'rhel', 'ubuntu' or amazon-linux-2023." 23 | } 24 | } 25 | 26 | variable "tfe_license_secret_id" { 27 | default = null 28 | type = string 29 | description = "The secrets manager secret ID under which the Base64 encoded Terraform Enterprise license is stored. NOTE: If this is an airgapped installation, then it is expected that the TFE license will be put on the path defined by tfe_license_file_location prior to running this module (i.e. on the virtual machine image)." 30 | } 31 | 32 | variable "airgap_url" { 33 | default = null 34 | description = "The URL of a Replicated airgap package for Terraform Enterprise. NOTE: If this value is given, then this script will install the airgap installation prerequisites. The airgap bundle should already be on the virtual machine image, and you would not use this variable if this were a truly airgapped environment." 35 | type = string 36 | } 37 | 38 | variable "ca_certificate_secret_id" { 39 | default = null 40 | type = string 41 | description = "A secret ID which contains the Base64 encoded version of a PEM encoded public certificate of a certificate authority (CA) to be trusted by the TFE instance(s)." 42 | } 43 | 44 | variable "certificate_secret_id" { 45 | default = null 46 | type = string 47 | description = "A secret ID which contains the Base64 encoded version of a PEM encoded public certificate for the TFE instance(s)." 48 | } 49 | 50 | variable "key_secret_id" { 51 | default = null 52 | type = string 53 | description = "A secret ID which contains the Base64 encoded version of a PEM encoded private key for the TFE instance(s)." 54 | } 55 | 56 | # Proxy 57 | # ----- 58 | variable "proxy_ip" { 59 | default = null 60 | type = string 61 | description = "IP Address of the proxy server" 62 | } 63 | 64 | variable "proxy_port" { 65 | default = null 66 | type = string 67 | description = "Port that the proxy server will use" 68 | } 69 | 70 | variable "tfe_configuration" { 71 | default = null 72 | description = "The settings that will be used to configure Terraform Enterprise." 73 | } 74 | 75 | variable "enable_monitoring" { 76 | default = null 77 | type = bool 78 | description = "Should cloud appropriate monitoring agents be installed as a part of the TFE installation script?" 79 | } 80 | 81 | # Mounted Disk 82 | # ------------ 83 | variable "disk_device_name" { 84 | default = null 85 | description = "The name of the disk device on which Terraform Enterprise will store data in Mounted Disk mode." 86 | type = string 87 | } 88 | 89 | variable "disk_path" { 90 | default = null 91 | description = "The pathname of the directory in which Terraform Enterprise will store data in Mounted Disk mode." 92 | type = string 93 | } 94 | 95 | variable "replicated_configuration" { 96 | default = null 97 | description = "The settings that will be used to configure Replicated." 98 | } 99 | -------------------------------------------------------------------------------- /modules/tfe_init_replicated/versions.tf: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | terraform { 5 | required_version = ">= 0.13" 6 | 7 | required_providers {} 8 | } 9 | -------------------------------------------------------------------------------- /templates/template.README.md: -------------------------------------------------------------------------------- 1 | # EXAMPLE: [Standalone / Active-Active], [operational type], [environment (optional)] Installation of Terraform Enterprise 2 | 3 | ## About This Example 4 | 5 | > *This section describes the high level traits of the TFE installation* 6 | 7 | > PLEASE UPDATE APPLICABLE TRAITS IN *[(XXXXXXX)]* 8 | 9 | This example for Terraform Enterprise creates a TFE installation with the following traits: 10 | 11 | - [(Standalone / [Active/Active](https://www.terraform.io/enterprise/install/automated/active-active))] architecture defined by `var.node_count` 12 | - [(External Services / Mounted Disk)] production type 13 | - [Air gapped (mocked)] 14 | - This example merely tests that the `airgap_url` package is able to install TFE. It does not, however, assume that the environment is air gapped, and it therefore installs the prerequisites for an air gapped installation, too. 15 | - [(n1-standard-4 / Standard_D4_v3 / m5.xlarge)] virtual machine type 16 | - [(Ubuntu 20.04, RHEL 7.9)] 17 | - A [(publicly / privately)] accessible [(HTTP / TCP)] load balancer with TLS [(termination / pass-through)] 18 | - An ubuntu based [(mitm / squid)]proxy server with TLS termination 19 | 20 | ## Prerequisites 21 | 22 | > *This section describes the prerequisites assumed by the installation modules.* 23 | > *Prerequisites are external resources that the module consumes like TFE license,certificates, vm-image, airgap package etc* 24 | 25 | > PLEASE INCLUDE ONLY APPLICABLE PREREQUISITES 26 | 27 | This example assumes that the following resources exist: 28 | 29 | - TFE license is on a file path defined by `var.license_file` 30 | - Air gap prerequisites : 31 | - The vm image is prepared according to the [documentation](https://www.terraform.io/enterprise/install/interactive/installer#prepare-the-instance) 32 | - Certificate and key data is present on the vm image at the following paths (when applicable): 33 | - The value of the secret represented by the root module's `key_secret_id` variable is present at the path defined by `var.tls_bootstrap_cert_pathname` (0600 access permissions). 34 | - The value of the secret represented by the root module's `certificate_secret_id` variable is present at the path defined by `var.tls_bootstrap_key_pathname` (0600 access permissions). 35 | - The value of the secret represented by the root module's `ca_certificate_secret_id` variable is present at the path: 36 | - RHEL - /usr/share/pki/ca-trust-source/anchors/tfe-ca-certificate.crt 37 | - Ubuntu - /usr/local/share/ca-certificates/extra/tfe-ca-certificate.crt 38 | - The air gap package is on a filepath defined by `var.tfe_license_bootstrap_airgap_package_path`. 39 | - The extracted Replicated package from https://install.terraform.io/airgap/latest.tar.gz is at `/tmp/replicated/replicated.tar.gz`. 40 | - The URL of an air gap package 41 | - A DNS zone 42 | - Valid managed SSL certificate to use with load balancer: 43 | - GCP - Create/Import a managed SSL Certificate in Network Services -> Load Balancing to serve as the certificate for the DNS A Record. 44 | - AWS - Create/Import a managed SSL Certificate using AWS ACM to serve as the certificate for the DNS A Record. 45 | - Azure - An Azure Key Vault in which the following are stored: 46 | - TFE CA certificate (certificate) 47 | - Key Vault secret which contains the Base64 encoded version of a PEM encoded public certificate for the Virtual Machine Scale Set 48 | - Key Vault secret which contains the Base64 encoded version of a PEM encoded private key for the Virtual Machine Scale Set. 49 | - A valid CA certificate and private key for the MITM proxy that are both stored in the [(Azure Key Vault / Secrets Manager)] as secrets. These should be Base64 encoded versions of a PEM encoded certificate and private key. *(applicable only if MITM proxy is used)* 50 | - Existing Virtual Network ([VPC/VNET]) 51 | - Existing Virtual Machine Image 52 | 53 | ## How to Use This Module 54 | 55 | > *This section provides step-by-step instructions on how to run the module for each cloud provider. This includes steps on how to set credentials, provider block, creating any local files such as terraform.auto.tfvars and commands that are triggered to init, plan and apply.* 56 | 57 | > PLEASE UPDATE APPLICABLE VALUES IN *[(XXXXXXX)]* 58 | 59 | ### Deployment 60 | 61 | 1. Read the entire [README.md](../../README.md) of the root module. 62 | 2. Ensure account meets module prerequisites from above. 63 | 3. Clone repository. 64 | 4. Change directory into desired example folder. 65 | 5. Create a local `terraform.auto.tfvars` file and instantiate the required inputs as required in the respective `./examples/[(example-name)]/variables.tf` including the path to the license under the `license_file` variable value. 66 | 6. Authenticate against the [(AWS/AzureRM/Google)] provider. See [instructions](replace with appropriate URL below). 67 | - 68 | - 69 | - 70 | 7. Initialize terraform and apply the module configurations using the commands below: 71 | 72 | NOTE: `terraform plan` will print out the execution plan which describes the actions Terraform will take in order to build your infrastructure to match the module configuration. If anything in the plan seems incorrect or dangerous, it is safe to abort here and not proceed to `terraform apply`. 73 | 74 | ``` 75 | terraform init 76 | terraform plan 77 | terraform apply 78 | ``` 79 | 80 | ## Post-deployment Tasks 81 | 82 | > *This section describes activities after deployment is triggered. If a proxy is deployed, provide instructions to set up local connection to the proxy as well.* 83 | 84 | The build should take approximately 10-15 minutes to deploy. Once the module has completed, give the platform another 10 minutes or so prior to attempting to interact with it in order for all containers to start up. 85 | 86 | Unless amended, this example will not create an initial admin user using the IACT, but it does output the URL for your convenience. Follow the advice in this document to create the initial admin user, and log into the system using this user in order to configure it for use. 87 | 88 | ### Connecting to [(proxy/bastion)] server 89 | 90 | > *Applicable only when connecting via proxy/bastion host.* 91 | > *In short, to set up a proxy server in Google Chrome, you're just doing the first few steps in the browser, and then completing the process in your machine's operating system* 92 | 93 | 1. To create a tunnel for Chrome: 94 | By default, Chrome uses your macOS or Windows proxy. To change your proxy settings from within Chrome, take the following steps: 95 | - Open the Chrome toolbar and select "Settings". 96 | - Scroll down to the bottom of the display. Click on "Show advanced settings". 97 | - Scroll down to “System” and choose "Open your computer’s proxy settings". 98 | - Set Chrome proxy server settings. 99 | 2. Next, follow the instructions for your operating system to set up your proxy server settings: 100 | - [macOS](https://support.apple.com/en-ca/guide/mac-help/mchlp2591/mac) 101 | - [Windows](https://www.dummies.com/article/technology/computers/operating-systems/windows/windows-10/how-to-set-up-a-proxy-in-windows-10-140262/#tab2) 102 | 3. SSH to [(proxy/bastion)] via: `$ ssh -N -p 22 -D localhost:5000 <[(bastionuser/proxyuser)]>@<[(bastionhost/proxyserver)]> -i ../path/to/id_rsa` 103 | 4. The TFE URL is now aacessible via [(proxy/bastion)]. 104 | 105 | ### Connecting to the TFE Console 106 | 107 | > *Applicable only for standalone environment* 108 | 109 | The TFE Console is only available in a standalone environment 110 | 111 | 1. Navigate to the URL supplied via `tfe_console_url` Terraform output 112 | 2. Copy the `tfe_console_password` Terraform output 113 | 3. Enter the console password 114 | 4. Click `Unlock` 115 | 116 | ### Connecting to the TFE Application 117 | 118 | 1. Navigate to the URL supplied via the `login_url` Terraform output. (It may take several minutes for this to be available after initial deployment. You may monitor the progress of cloud init if desired on one of the instances.) 119 | 2. Enter a `username`, `email`, and `password` for the initial user. 120 | 3. Click `Create an account`. 121 | 4. After the initial user is created you may access the TFE Application normally using the URL supplied via `login_url` Terraform output. 122 | --------------------------------------------------------------------------------