diff --git a/.gitlab/README.md.template b/.gitlab/README.md.template index 53feabfa637d99881edb8860458d0ab3dbab0151..523109be86f2e01a9b23b326eba7f20846026a44 100644 --- a/.gitlab/README.md.template +++ b/.gitlab/README.md.template @@ -125,9 +125,10 @@ The base image OS can be specified with the `base_os` input. ### GitLab-managed Terraform state backend This component - by leveraging the [`gitlab-tofu`](src/gitlab-tofu.sh) CLI internally - -automatically configures the +can automatically define and configure the [GitLab-managed Terraform state backend](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html). -The only thing required is that the Terraform configuration must specify an empty `http` backend block, like this: + +By default the HTTP backend must be defined manually using the following HCL: ```hcl terraform { @@ -135,8 +136,9 @@ terraform { } ``` -We recommend having a dedicated `backend.tf` file inside your `root_dir` -with the aforementioned block. +However, you may simply enable the `auto_define_backend` so that the component takes care of this step. + +**Note**: in future versions of this component we may enable `auto_define_backend` by default. ### State and Plan Encryption diff --git a/README.md b/README.md index 88d55c01c420caee75d7b77d8b369d36ad4cadda..82e05faff9fea8038dc36a938fe52eb3d74da7c4 100644 --- a/README.md +++ b/README.md @@ -127,9 +127,10 @@ The base image OS can be specified with the `base_os` input. ### GitLab-managed Terraform state backend This component - by leveraging the [`gitlab-tofu`](src/gitlab-tofu.sh) CLI internally - -automatically configures the +can automatically define and configure the [GitLab-managed Terraform state backend](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html). -The only thing required is that the Terraform configuration must specify an empty `http` backend block, like this: + +By default the HTTP backend must be defined manually using the following HCL: ```hcl terraform { @@ -137,8 +138,9 @@ terraform { } ``` -We recommend having a dedicated `backend.tf` file inside your `root_dir` -with the aforementioned block. +However, you may simply enable the `auto_define_backend` so that the component takes care of this step. + +**Note**: in future versions of this component we may enable `auto_define_backend` by default. ### State and Plan Encryption @@ -315,7 +317,8 @@ The following environment variables are respected by the `gitlab-tofu` script: - `GITLAB_TOFU_AUTO_ENCRYPTION`: if set to true, enables auto state and plan encryption. Defaults to `false`. - `GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE`: the passphrase to use for state and plan encryption. Required if `GITLAB_TOFU_AUTO_ENCRYPTION` is true. - `GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED_ENABLED`: if set to true, enables a fallback for state and plan encryption to migrate unencrypted plans and states to encrypted ones. Defaults to `false`. -- `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +- `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE`: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +- `GITLAB_TOFU_AUTO_DEFINE_BACKEND`: if set to true, automatically creates a file with a HTTP backend configuration block. #### Respected OpenTofu Environment Variables diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh index e5ecfc1505eaddb0480a84eaeeb77f61d24dae45..8a339e09122a14d3a0a37391ee1e6fb80b1ba855 100644 --- a/src/gitlab-tofu.sh +++ b/src/gitlab-tofu.sh @@ -30,7 +30,8 @@ # - `GITLAB_TOFU_AUTO_ENCRYPTION`: if set to true, enables auto state and plan encryption. Defaults to `false`. # - `GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE`: the passphrase to use for state and plan encryption. Required if `GITLAB_TOFU_AUTO_ENCRYPTION` is true. # - `GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED_ENABLED`: if set to true, enables a fallback for state and plan encryption to migrate unencrypted plans and states to encrypted ones. Defaults to `false`. -# - `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +# - `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE`: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan. +# - `GITLAB_TOFU_AUTO_DEFINE_BACKEND`: if set to true, automatically creates a file with a HTTP backend configuration block. # # #### Respected OpenTofu Environment Variables # @@ -163,6 +164,7 @@ fi # ============================ # Backend related variables +auto_define_backend=${GITLAB_TOFU_AUTO_DEFINE_BACKEND:-false} backend_username="gitlab-ci-token" backend_password="${CI_JOB_TOKEN}" backend_state_name="$(jq -rn --arg x "${GITLAB_TOFU_STATE_NAME:-default}" '$x|@uri')" @@ -177,6 +179,8 @@ if [ -n "${GITLAB_TOFU_ROOT_DIR}" ]; then default_tf_plan_cache="${abs_tf_root}/${base_plan_name}.cache" default_tf_plan_json="${abs_tf_root}/${base_plan_name}.json" +else + abs_tf_root=$(realpath "${CI_PROJECT_DIR}") fi # Init related variables @@ -227,6 +231,26 @@ fi # Helper functions # ================ +# define_http_backend defines the HTTP backend in a file called __gitlab-opentofu-backend.tf if no backend can be found. +# The backend configuration is attempted to be found with a simple grep. +define_http_backend() { + if ! $auto_define_backend; then + return + fi + + if ! grep -q '^[[:space:]]*backend[[:space:]]\+"http"[[:space:]]\+{.*$' "${abs_tf_root}" -r 2>/dev/null; then + echo "gitlab-tofu: automatically defining the HTTP backend in __gitlab-opentofu-backend.tf. If that is a mistake, please disable it with the auto_define_backend: false input." + + cat <<EOF > __gitlab-opentofu-backend.tf +terraform { + backend "http" {} +} +EOF + else + echo "gitlab-tofu: auto_define_backend is enabled, but found manually configured HTTP backend, doing nothing." + fi +} + # configure_variables_for_tofu sets and exports all relevant variables for subsequent `tofu` command invocations. configure_variables_for_tofu() { # Use terraform automation mode (will remove some verbose unneeded messages) @@ -352,6 +376,7 @@ EOF } # We always want to configure the tofu variables, even in source-mode. +define_http_backend configure_variables_for_tofu configure_encryption_for_tofu diff --git a/templates/apply.yml b/templates/apply.yml index 9ee6039a4643d35a65806a0ed4e9f9ad0bbba88e..3b98e55eeb8db936292b6a73e0ac272c2fd6b585 100644 --- a/templates/apply.yml +++ b/templates/apply.yml @@ -98,6 +98,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -124,6 +128,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/destroy.yml b/templates/destroy.yml index 90b1e55d43a753681b82c43d8df0636a6621bee5..fa350f8b0f2fb24c07358cd81c3cce32e1bee4da 100644 --- a/templates/destroy.yml +++ b/templates/destroy.yml @@ -102,6 +102,10 @@ spec: default: false type: boolean description: 'Whether to automatically delete the Terraform state. This only makes sense when using the GitLab-managed state backend. It is equivalent to running the delete-state job.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -128,6 +132,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/full-pipeline.yml b/templates/full-pipeline.yml index f4f22f14c7f4b0a1f17d87a7d9d4499563ccf52f..290e81bb2925a828d76275a7c2aa2b78ef390dfb 100644 --- a/templates/full-pipeline.yml +++ b/templates/full-pipeline.yml @@ -198,6 +198,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -236,6 +240,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/test.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "true"' @@ -259,6 +264,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -282,6 +288,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -302,6 +309,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -321,6 +329,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -408,6 +417,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/graph.yml b/templates/graph.yml index 02da5178c8b6ef647298a4e0261abe490b0c0562..c78cde0ae86b353bdd6d2a1719f1dd817cc31e6c 100644 --- a/templates/graph.yml +++ b/templates/graph.yml @@ -96,6 +96,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -116,6 +120,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/job-templates.yml b/templates/job-templates.yml index 387f02f11691b2372a24189596f314543df3deae..eaa2d306161fccc811d3318729eef823ccab3066 100644 --- a/templates/job-templates.yml +++ b/templates/job-templates.yml @@ -103,6 +103,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -134,6 +138,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/graph.yml' inputs: as: '$[[ inputs.job_name_prefix ]]graph' @@ -148,6 +153,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/test.yml' inputs: as: '$[[ inputs.job_name_prefix ]]test' @@ -164,6 +170,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' inputs: as: '$[[ inputs.job_name_prefix ]]plan' @@ -183,6 +190,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/apply.yml' inputs: as: '$[[ inputs.job_name_prefix ]]apply' @@ -200,6 +208,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/destroy.yml' inputs: as: '$[[ inputs.job_name_prefix ]]destroy' @@ -217,6 +226,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/delete-state.yml' inputs: as: '$[[ inputs.job_name_prefix ]]delete-state' diff --git a/templates/plan.yml b/templates/plan.yml index 752c37c99f7d1cebf2e4827a54f31c2e19c81bb2..73b4a5118416234e0b2bfe08d384d34baeda9690 100644 --- a/templates/plan.yml +++ b/templates/plan.yml @@ -109,6 +109,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -175,6 +179,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: '$[[ inputs.allow_developer_role ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/test.yml b/templates/test.yml index 79b2b0b717b1061ea8378be15023478ab0dde33d..a76e3293c24b73d6d05dc913cc4e1951152aa795 100644 --- a/templates/test.yml +++ b/templates/test.yml @@ -98,6 +98,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -119,6 +123,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/templates/validate-plan-apply.yml b/templates/validate-plan-apply.yml index 6924516558cabbc5ab96f5c8ca1c04ae4acd4f12..f1c50051c4fbb540644bcf1cbab08ec7602a38f6 100644 --- a/templates/validate-plan-apply.yml +++ b/templates/validate-plan-apply.yml @@ -162,6 +162,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -201,6 +205,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -225,6 +230,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -246,6 +252,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -303,6 +310,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml index c6a54b711f241431874d9ec7cff9bda8bcd69897..1d303fd6eaef9113cd84dadbc85c78b7b7614f63 100644 --- a/templates/validate-plan-destroy.yml +++ b/templates/validate-plan-destroy.yml @@ -168,6 +168,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -207,6 +211,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -232,6 +237,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -254,6 +260,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -336,6 +343,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan.yml b/templates/validate-plan.yml index 96d199e6fb0db2007e827cd7cbc99c7aa9b4320f..ed976b6f333474f60bed15a95a26b4bcec0b4559 100644 --- a/templates/validate-plan.yml +++ b/templates/validate-plan.yml @@ -146,6 +146,10 @@ spec: default: false type: boolean description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -185,6 +189,7 @@ include: auto_encryption: $[[ inputs.auto_encryption ]] auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -209,6 +214,7 @@ include: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -263,6 +269,7 @@ stages: auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]] auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]] allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]] + auto_define_backend: $[[ inputs.auto_define_backend ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate.yml b/templates/validate.yml index e4dcb8e17a20118fb06f8618b3fc01bb438f8ae0..14154a195b7b0b668e41a8ad6861c94f22c8d6e1 100644 --- a/templates/validate.yml +++ b/templates/validate.yml @@ -95,6 +95,10 @@ spec: default: false type: boolean description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.' + auto_define_backend: + default: false + type: boolean + description: 'Whether to automatically define the HTTP backend configuration block.' --- @@ -116,6 +120,7 @@ spec: GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]' GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]' GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]' + GITLAB_TOFU_AUTO_DEFINE_BACKEND: '$[[ inputs.auto_define_backend ]]' image: name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]' script: diff --git a/tests/iac-auto-define-backend/main.tf b/tests/iac-auto-define-backend/main.tf new file mode 100644 index 0000000000000000000000000000000000000000..6f610dcb023f602dc4d7920121c713d25e627fc7 --- /dev/null +++ b/tests/iac-auto-define-backend/main.tf @@ -0,0 +1,19 @@ +resource "local_file" "foo" { + content = "foo!" + filename = "${path.module}/foo.bar" +} + +locals { + ts = plantimestamp() +} + +// NOTE: always force a change. +resource "null_resource" "this" { + triggers = { + timestamp = local.ts + } +} + +output "this_always_changes" { + value = local.ts +} diff --git a/tests/iac-auto-define-backend/tests/main.tftest.hcl b/tests/iac-auto-define-backend/tests/main.tftest.hcl new file mode 100644 index 0000000000000000000000000000000000000000..fedf96e4c458eee084740b05980a52ffe64362a1 --- /dev/null +++ b/tests/iac-auto-define-backend/tests/main.tftest.hcl @@ -0,0 +1,6 @@ +run "test" { + assert { + condition = file(local_file.foo.filename) == "foo!" + error_message = "Incorrect content in ${local_file.foo.filename}" + } +} diff --git a/tests/integration-tests/AutoDefineBackend.gitlab-ci.yml b/tests/integration-tests/AutoDefineBackend.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..4a64e52cafecf9fc001f5a2dca1e185f76f875ba --- /dev/null +++ b/tests/integration-tests/AutoDefineBackend.gitlab-ci.yml @@ -0,0 +1,20 @@ +include: + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/full-pipeline@$CI_COMMIT_SHA + inputs: + image_registry_base: $GITLAB_OPENTOFU_IMAGE_BASE + version: $CI_COMMIT_SHA + base_os: $GITLAB_OPENTOFU_BASE_IMAGE_OS + opentofu_version: $OPENTOFU_VERSION + root_dir: $TEST_GITLAB_TOFU_ROOT_DIR + state_name: $TEST_GITLAB_TOFU_STATE_NAME + auto_define_backend: true + # Required to run everything immediately, instead of manually. + fmt_rules: [{when: always}] + validate_rules: [{when: always}] + test_rules: [{when: always}] + plan_rules: [{when: always}] + apply_rules: [{when: always}] + destroy_rules: [{when: always}] + delete_state_rules: [{when: always}] + +stages: [validate, test, build, deploy, cleanup] diff --git a/tests/integration.gitlab-ci.yml b/tests/integration.gitlab-ci.yml index c9298a573791c160dc6e1af085ba4bf823ea5ef4..f5ed0b8e88b23543c0a6c8f2a335c8457b73ae6d 100644 --- a/tests/integration.gitlab-ci.yml +++ b/tests/integration.gitlab-ci.yml @@ -17,6 +17,23 @@ full-pipeline: - alpine - debian +auto-define-backend: + stage: test-integration + variables: + OPENTOFU_VERSION: $LATEST_OPENTOFU_VERSION + TEST_GITLAB_TOFU_STATE_NAME: ci-integration-$CI_JOB_NAME_SLUG-$CI_PIPELINE_IID-$CI_NODE_INDEX + TEST_GITLAB_TOFU_ROOT_DIR: tests/iac-auto-define-backend + trigger: + include: tests/integration-tests/$PIPELINE_NAME.gitlab-ci.yml + strategy: depend + parallel: + matrix: + - PIPELINE_NAME: + - AutoDefineBackend + GITLAB_OPENTOFU_BASE_IMAGE_OS: + - alpine + - debian + validate-plan-apply: stage: test-integration variables: