diff --git a/.gitlab/README.md.template b/.gitlab/README.md.template index c2556569b66201891dee8ccc0b5ad1b5d33c8172..6addbf731d21190384674b2752e7f81451756b25 100644 --- a/.gitlab/README.md.template +++ b/.gitlab/README.md.template @@ -138,6 +138,50 @@ terraform { We recommend having a dedicated `backend.tf` file inside your `root_dir` with the aforementioned block. +### State and Plan Encryption + +We recommend that you configure the OpenTofu +[State and Plan Encryption](https://opentofu.org/docs/language/state/encryption). + +You may either do this manually by commit your `encryption` config and providing +it with the necessary secrets - for example defining a `sensitive` `variable` +and configure a GitLab CI/CD variable for it. + +Another option is to let this component auto-encrypt the state and plan for you. +The only thing you have to do is to provide a passphrase. + +All templates related to the state have the following inputs related to auto-encryption: + +- `auto_encrpytion` (`boolean`): if set to `true` will auto-encrypt your state and plan. +- `auto_encrpytion_passphrase` (`string`): is required if `auto_encrpytion` is `true` and + defines the passphrase for your state and plan files. Make sure to keep it secured. + You may use a protected and masked GitLab CI/CD variable for it. +- `auto_encryption_enable_migration_from_unencrypted` (`boolean`): if set to `true` will + migrate automatically migrate an unencrypted state and plan into an encrypted one. + This should only be set to `true` temporarily and disabled again afterwards. + Currently, a migration to an encrypted state requires actual changes to the + infrastructure. + See [this comment](https://gitlab.com/gitlab-org/gitlab/-/issues/450816#note_2228897756) + for details. + +The following snippet will auto-encrypt your state with a passphrase coming from the +`PASSPHRASE` CI/CD variable: + +```yaml +include: + - component: $CI_SERVER_FQDN/components/opentofu/validate-plan-apply@<VERSION> + inputs: + # The version must currently be specified explicitly as an input, + # to find the correctly associated images. # This can be removed + # once https://gitlab.com/gitlab-org/gitlab/-/issues/438275 is solved. + version: <VERSION> # component version + opentofu_version: <OPENTOFU_VERSION> + auto_encrpytion: true + auto_encrpytion_passphrase: $PASSPHRASE + +stages: [validate, build, deploy] +``` + ### Access to Terraform Module Registry Similar to automatically configuring the [GitLab-managed Terraform state backend] @@ -326,96 +370,6 @@ or `TF_CLI_ARGS_init` (handled by OpenTofu directly) to `-lockfile=readonly` to prevent any changes to the lockfile during the pipeline job and with that ensuring that OpenTofu really uses the locked dependencies. -#### State and Plan Encryption - -We recommend that you configure the OpenTofu -[State and Plan Encryption](https://opentofu.org/docs/language/state/encryption). - -You can easily do this by following the guide on the page linked above. - -Here is an example: - -**Tofu config at `<root-dir>/encryption.tf`**: - -```hcl -variable "passphrase" { - sensitive = true - description = "Passphrase to encrypt and decrypt state and plan" -} - -terraform { - encryption { - key_provider "pbkdf2" "this" { - passphrase = var.passphrase - } - - method "aes_gcm" "this" { - keys = key_provider.pbkdf2.this - } - - state { - method = method.aes_gcm.this - } - - plan { - method = method.aes_gcm.this - } - } -} -``` - -Then you only have to configure a passphrase as CI/CD variable with the name -`TF_VAR_passphrase`. - -Everything else will work out of the box. - -In case you want to migrate from an unencrypted state and plan you can -temporarily configure your encryption block with `fallback`s, like so: - -```hcl -variable "passphrase" { - sensitive = true - description = "Passphrase to ecnrypt and decrypt state and plan" -} - -terraform { - encryption { - method "unencrypted" "migrate" {} - - key_provider "pbkdf2" "this" { - passphrase = var.passphrase - } - - method "aes_gcm" "this" { - keys = key_provider.pbkdf2.this - } - - state { - method = method.aes_gcm.this - - fallback { - method = method.unencrypted.migrate - } - } - - plan { - method = method.aes_gcm.this - - fallback { - method = method.unencrypted.migrate - } - } - } -} -``` - -Then you can run the pipeline one time to migrate and then remove the -`unencrypted` `method` and the `fallback` blocks. - -> **Call for Action**: -> If you have a good proposal on how to make state and plan encryption -> easier with this component then let us know in an issue! - ### Examples Here are some example repositories to demonstrate how this component maybe used: diff --git a/README.md b/README.md index f3e7b37e3fff3579c9d05d85232025fdb2c66e6b..11123be4957ee20498f71fcc649aaffa10901c34 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,50 @@ terraform { We recommend having a dedicated `backend.tf` file inside your `root_dir` with the aforementioned block. +### State and Plan Encryption + +We recommend that you configure the OpenTofu +[State and Plan Encryption](https://opentofu.org/docs/language/state/encryption). + +You may either do this manually by commit your `encryption` config and providing +it with the necessary secrets - for example defining a `sensitive` `variable` +and configure a GitLab CI/CD variable for it. + +Another option is to let this component auto-encrypt the state and plan for you. +The only thing you have to do is to provide a passphrase. + +All templates related to the state have the following inputs related to auto-encryption: + +- `auto_encrpytion` (`boolean`): if set to `true` will auto-encrypt your state and plan. +- `auto_encrpytion_passphrase` (`string`): is required if `auto_encrpytion` is `true` and + defines the passphrase for your state and plan files. Make sure to keep it secured. + You may use a protected and masked GitLab CI/CD variable for it. +- `auto_encryption_enable_migration_from_unencrypted` (`boolean`): if set to `true` will + migrate automatically migrate an unencrypted state and plan into an encrypted one. + This should only be set to `true` temporarily and disabled again afterwards. + Currently, a migration to an encrypted state requires actual changes to the + infrastructure. + See [this comment](https://gitlab.com/gitlab-org/gitlab/-/issues/450816#note_2228897756) + for details. + +The following snippet will auto-encrypt your state with a passphrase coming from the +`PASSPHRASE` CI/CD variable: + +```yaml +include: + - component: $CI_SERVER_FQDN/components/opentofu/validate-plan-apply@<VERSION> + inputs: + # The version must currently be specified explicitly as an input, + # to find the correctly associated images. # This can be removed + # once https://gitlab.com/gitlab-org/gitlab/-/issues/438275 is solved. + version: <VERSION> # component version + opentofu_version: <OPENTOFU_VERSION> + auto_encrpytion: true + auto_encrpytion_passphrase: $PASSPHRASE + +stages: [validate, build, deploy] +``` + ### Access to Terraform Module Registry Similar to automatically configuring the [GitLab-managed Terraform state backend] @@ -274,6 +318,9 @@ The following environment variables are respected by the `gitlab-tofu` script: - `GITLAB_TOFU_USE_DETAILED_EXITCODE`: if set to true, `-detailed-exitcode` is supplied to `tofu plan`. Defaults to `false`. - `GITLAB_TOFU_PLAN_WITH_JSON`: if set to true, will directly generate a JSON plan file when running `gitlab-tofu plan`. Defaults to `false`. - `GITLAB_TOFU_VAR_FILE`: if set to a path it will pass `-var-file` to all `tofu` commands that support it. +- `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`. #### Respected OpenTofu Environment Variables @@ -402,96 +449,6 @@ or `TF_CLI_ARGS_init` (handled by OpenTofu directly) to `-lockfile=readonly` to prevent any changes to the lockfile during the pipeline job and with that ensuring that OpenTofu really uses the locked dependencies. -#### State and Plan Encryption - -We recommend that you configure the OpenTofu -[State and Plan Encryption](https://opentofu.org/docs/language/state/encryption). - -You can easily do this by following the guide on the page linked above. - -Here is an example: - -**Tofu config at `<root-dir>/encryption.tf`**: - -```hcl -variable "passphrase" { - sensitive = true - description = "Passphrase to encrypt and decrypt state and plan" -} - -terraform { - encryption { - key_provider "pbkdf2" "this" { - passphrase = var.passphrase - } - - method "aes_gcm" "this" { - keys = key_provider.pbkdf2.this - } - - state { - method = method.aes_gcm.this - } - - plan { - method = method.aes_gcm.this - } - } -} -``` - -Then you only have to configure a passphrase as CI/CD variable with the name -`TF_VAR_passphrase`. - -Everything else will work out of the box. - -In case you want to migrate from an unencrypted state and plan you can -temporarily configure your encryption block with `fallback`s, like so: - -```hcl -variable "passphrase" { - sensitive = true - description = "Passphrase to ecnrypt and decrypt state and plan" -} - -terraform { - encryption { - method "unencrypted" "migrate" {} - - key_provider "pbkdf2" "this" { - passphrase = var.passphrase - } - - method "aes_gcm" "this" { - keys = key_provider.pbkdf2.this - } - - state { - method = method.aes_gcm.this - - fallback { - method = method.unencrypted.migrate - } - } - - plan { - method = method.aes_gcm.this - - fallback { - method = method.unencrypted.migrate - } - } - } -} -``` - -Then you can run the pipeline one time to migrate and then remove the -`unencrypted` `method` and the `fallback` blocks. - -> **Call for Action**: -> If you have a good proposal on how to make state and plan encryption -> easier with this component then let us know in an issue! - ### Examples Here are some example repositories to demonstrate how this component maybe used: diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh index 5c33e2b6f3f91e29c4e592b1196b4391b9fc6080..4b98d618f4a3d9deba7cf0eaa8d0535797a34e18 100644 --- a/src/gitlab-tofu.sh +++ b/src/gitlab-tofu.sh @@ -27,6 +27,9 @@ # - `GITLAB_TOFU_USE_DETAILED_EXITCODE`: if set to true, `-detailed-exitcode` is supplied to `tofu plan`. Defaults to `false`. # - `GITLAB_TOFU_PLAN_WITH_JSON`: if set to true, will directly generate a JSON plan file when running `gitlab-tofu plan`. Defaults to `false`. # - `GITLAB_TOFU_VAR_FILE`: if set to a path it will pass `-var-file` to all `tofu` commands that support it. +# - `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`. # # #### Respected OpenTofu Environment Variables # @@ -197,6 +200,21 @@ plan_jq_filter=' } ' +# auto encryption related variables +auto_encryption_enabled=${GITLAB_TOFU_AUTO_ENCRYPTION:-false} +auto_encryption_passphrase="${GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE}" +auto_encryption_migration_from_unencrypted_enabled=${GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED:-false} +if $auto_encryption_enabled && [ -z "${auto_encryption_passphrase}" ]; then + # shellcheck disable=SC2016 # we actually want to print the variable names and not expand them. + echo 'ERROR: a passphrase ($GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE) must be provided when enabled auto encryption ($GITLAB_TOFU_AUTO_ENCRYPTION)' >&2 + exit 1 +fi +if ! $auto_encryption_enabled && $auto_encryption_migration_from_unencrypted_enabled; then + # shellcheck disable=SC2016 # we actually want to print the variable names and not expand them. + echo 'ERROR: you must enable auto encryption ($GITLAB_TOFU_AUTO_ENCRYPTION) to enable migration from an unencrypted state ($GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED)' >&2 + exit 1 +fi + # Misc variables var_file="${GITLAB_TOFU_VAR_FILE}" @@ -268,8 +286,69 @@ tofu_init() { 1>&2 || $should_ignore_init_errors } + +# configure_encryption_for_tofu configures automatic state and plan encryption +configure_encryption_for_tofu() { + if ! $auto_encryption_enabled; then + return + fi + + if $auto_encryption_migration_from_unencrypted_enabled; then + TF_ENCRYPTION=$(cat <<EOF +method "unencrypted" "gitlab_tofu_auto_encryption_migrate" {} + +key_provider "pbkdf2" "gitlab_tofu_auto_encryption" { + passphrase = "${auto_encryption_passphrase}" +} + +method "aes_gcm" "gitlab_tofu_auto_encryption" { + keys = key_provider.pbkdf2.gitlab_tofu_auto_encryption +} + +state { + method = method.aes_gcm.gitlab_tofu_auto_encryption + + fallback { + method = method.unencrypted.gitlab_tofu_auto_encryption_migrate + } +} + +plan { + method = method.aes_gcm.gitlab_tofu_auto_encryption + + fallback { + method = method.unencrypted.gitlab_tofu_auto_encryption_migrate + } +} +EOF +) + else + TF_ENCRYPTION=$(cat <<EOF +key_provider "pbkdf2" "gitlab_tofu_auto_encryption" { + passphrase = "${auto_encryption_passphrase}" +} + +method "aes_gcm" "gitlab_tofu_auto_encryption" { + keys = key_provider.pbkdf2.gitlab_tofu_auto_encryption +} + +state { + method = method.aes_gcm.gitlab_tofu_auto_encryption +} + +plan { + method = method.aes_gcm.gitlab_tofu_auto_encryption +} +EOF +) + fi + + export TF_ENCRYPTION +} + # We always want to configure the tofu variables, even in source-mode. configure_variables_for_tofu +configure_encryption_for_tofu # If this script is executed and not sourced, a tofu command is ran. # Otherwise, nothing happens and the sourced shell can use the defined variables diff --git a/templates/apply.yml b/templates/apply.yml index af617ec685761fc7416b1f67d9a76c740ae54019..cbc4df9a21d89a422869a07159aa454b0045e41e 100644 --- a/templates/apply.yml +++ b/templates/apply.yml @@ -92,6 +92,18 @@ spec: default: pull-push type: string description: 'Defines the cache policy of the job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -115,6 +127,9 @@ spec: GITLAB_TOFU_APPLY_NO_PLAN: $[[ inputs.no_plan ]] GITLAB_TOFU_PLAN_NAME: $[[ inputs.plan_name ]] GITLAB_TOFU_VAR_FILE: '$[[ inputs.var_file ]]' + 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 ]]' 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 0e1fb04ecc29d393793c1ed732ab150b4d2b1b77..7c159619fc7a309492713faa71d8fe41d4e619aa 100644 --- a/templates/destroy.yml +++ b/templates/destroy.yml @@ -92,6 +92,18 @@ spec: default: pull-push type: string description: 'Defines the cache policy of the job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -115,6 +127,9 @@ spec: GITLAB_TOFU_APPLY_NO_PLAN: $[[ inputs.no_plan ]] GITLAB_TOFU_PLAN_NAME: $[[ inputs.plan_name ]] GITLAB_TOFU_VAR_FILE: '$[[ inputs.var_file ]]' + 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 ]]' 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 9b83944d2525fd611117c0f6cc177ec51aaab2c0..815c43c8ec041b7b0f0d338b04abdaf8417e5bc4 100644 --- a/templates/full-pipeline.yml +++ b/templates/full-pipeline.yml @@ -188,6 +188,18 @@ spec: default: [{when: on_success}] type: array description: 'Defines the `rules` of the child pipeline bridge job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -223,6 +235,9 @@ include: state_name: $[[ inputs.state_name ]] var_file: $[[ inputs.var_file ]] rules: $[[ inputs.validate_rules ]] + 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 ]] - local: '/templates/test.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "true"' @@ -243,6 +258,9 @@ include: var_file: $[[ inputs.var_file ]] needs: [] rules: $[[ inputs.test_rules ]] + 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 ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -262,6 +280,9 @@ include: var_file: $[[ inputs.var_file ]] rules: $[[ inputs.plan_rules ]] warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -279,6 +300,9 @@ include: plan_name: $[[ inputs.plan_name ]] var_file: $[[ inputs.var_file ]] rules: $[[ inputs.apply_rules ]] + 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 ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -295,6 +319,9 @@ include: state_name: $[[ inputs.state_name ]] var_file: $[[ inputs.var_file ]] rules: $[[ inputs.destroy_rules ]] + 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 ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -378,6 +405,9 @@ stages: destroy_rules: $[[ inputs.destroy_rules ]] delete_state_rules: $[[ inputs.delete_state_rules ]] warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/graph.yml b/templates/graph.yml index 561d44ee074660c77a3b16ae474dd837aaa2f8d5..e8dd1ba0105dff37385d1582c9b461aa6fed905f 100644 --- a/templates/graph.yml +++ b/templates/graph.yml @@ -90,6 +90,18 @@ spec: default: pull-push type: string description: 'Defines the cache policy of the job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -107,6 +119,9 @@ spec: GITLAB_TOFU_ROOT_DIR: $[[ inputs.root_dir ]] GITLAB_TOFU_STATE_NAME: $[[ inputs.state_name ]] GITLAB_TOFU_VAR_FILE: '$[[ inputs.var_file ]]' + 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 ]]' 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 6e7aa58cb3401421d6f15b3c94cd17504c3008c8..eda3791a09397e1a2658675820bbf638cf623dec 100644 --- a/templates/job-templates.yml +++ b/templates/job-templates.yml @@ -93,6 +93,18 @@ spec: default: false type: boolean description: 'Whether to mark the job with a warning if the plan contains a diff.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -121,6 +133,9 @@ include: root_dir: $[[ inputs.root_dir ]] state_name: $[[ inputs.state_name ]] var_file: $[[ inputs.var_file ]] + 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 ]] - local: '/templates/graph.yml' inputs: as: '$[[ inputs.job_name_prefix ]]graph' @@ -132,6 +147,9 @@ include: image_name: $[[ inputs.image_name ]] root_dir: $[[ inputs.root_dir ]] var_file: $[[ inputs.var_file ]] + 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 ]] - local: '/templates/test.yml' inputs: as: '$[[ inputs.job_name_prefix ]]test' @@ -145,6 +163,9 @@ include: root_dir: $[[ inputs.root_dir ]] state_name: $[[ inputs.state_name ]] var_file: $[[ inputs.var_file ]] + 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 ]] - local: '/templates/plan.yml' inputs: as: '$[[ inputs.job_name_prefix ]]plan' @@ -160,6 +181,9 @@ include: plan_name: $[[ inputs.plan_name ]] var_file: $[[ inputs.var_file ]] warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] - local: '/templates/apply.yml' inputs: as: '$[[ inputs.job_name_prefix ]]apply' @@ -174,6 +198,9 @@ include: state_name: $[[ inputs.state_name ]] plan_name: $[[ inputs.plan_name ]] var_file: $[[ inputs.var_file ]] + 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 ]] - local: '/templates/destroy.yml' inputs: as: '$[[ inputs.job_name_prefix ]]destroy' @@ -188,6 +215,9 @@ include: state_name: $[[ inputs.state_name ]] plan_name: $[[ inputs.plan_name ]] var_file: $[[ inputs.var_file ]] + 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 ]] - local: '/templates/delete-state.yml' inputs: as: '$[[ inputs.job_name_prefix ]]delete-state' diff --git a/templates/plan.yml b/templates/plan.yml index 85710bec4e3a787999ea45a8f93f6162aad04b71..a0d3235b3504e3ce90a9a92c4fea4cda0831c175 100644 --- a/templates/plan.yml +++ b/templates/plan.yml @@ -99,6 +99,18 @@ spec: default: false type: boolean description: 'Whether to mark the job with a warning if the plan contains a diff.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -161,6 +173,9 @@ spec: GITLAB_TOFU_PLAN_NAME: $[[ inputs.plan_name ]] GITLAB_TOFU_PLAN_WITH_JSON: true GITLAB_TOFU_VAR_FILE: '$[[ inputs.var_file ]]' + 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 ]]' 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 7634385f54c2b360ed8c9032e240b400fd42f84a..2de8cee0787a2c77d1f8e9b060681518396ee196 100644 --- a/templates/test.yml +++ b/templates/test.yml @@ -92,6 +92,18 @@ spec: default: pull-push type: string description: 'Defines the cache policy of the job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -110,6 +122,9 @@ spec: GITLAB_TOFU_ROOT_DIR: $[[ inputs.root_dir ]] GITLAB_TOFU_STATE_NAME: $[[ inputs.state_name ]] GITLAB_TOFU_VAR_FILE: '$[[ inputs.var_file ]]' + 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 ]]' 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 1e8aad05cfe3042c4dab76baf06ba6cc0ec8df60..c74ee0a7154a03de36329465a251e7572ab14f96 100644 --- a/templates/validate-plan-apply.yml +++ b/templates/validate-plan-apply.yml @@ -152,6 +152,18 @@ spec: default: [{when: on_success}] type: array description: 'Defines the `rules` of the child pipeline bridge job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -188,6 +200,9 @@ include: var_file: $[[ inputs.var_file ]] rules: $[[ inputs.validate_rules ]] cache_policy: pull-push + 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 ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -208,6 +223,9 @@ include: rules: $[[ inputs.plan_rules ]] cache_policy: pull warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -226,6 +244,9 @@ include: var_file: $[[ inputs.var_file ]] rules: $[[ inputs.apply_rules ]] cache_policy: pull + 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 ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -279,6 +300,9 @@ stages: plan_rules: $[[ inputs.plan_rules ]] apply_rules: $[[ inputs.apply_rules ]] warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml index 15cc048c667288abde142016a4b3c079deb17b69..d533d5d438139dab4f4699da255ef7ab642a3248 100644 --- a/templates/validate-plan-destroy.yml +++ b/templates/validate-plan-destroy.yml @@ -158,6 +158,18 @@ spec: default: [{when: on_success}] type: array description: 'Defines the `rules` of the child pipeline bridge job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -194,6 +206,9 @@ include: var_file: $[[ inputs.var_file ]] rules: $[[ inputs.validate_rules ]] cache_policy: pull-push + 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 ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -215,6 +230,9 @@ include: rules: $[[ inputs.plan_rules ]] cache_policy: pull warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -234,6 +252,9 @@ include: var_file: $[[ inputs.var_file ]] rules: $[[ inputs.destroy_rules ]] cache_policy: pull + 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 ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -312,6 +333,9 @@ stages: destroy_rules: $[[ inputs.destroy_rules ]] delete_state_rules: $[[ inputs.delete_state_rules ]] warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan.yml b/templates/validate-plan.yml index e17200705769e050f050dfb9f38e55160c45b6de..0d7d4df249fd34363c62cf2d0d19520ab9e7038a 100644 --- a/templates/validate-plan.yml +++ b/templates/validate-plan.yml @@ -136,6 +136,18 @@ spec: default: [{when: on_success}] type: array description: 'Defines the `rules` of the child pipeline bridge job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -172,6 +184,9 @@ include: var_file: $[[ inputs.var_file ]] rules: $[[ inputs.validate_rules ]] cache_policy: pull-push + 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 ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -192,6 +207,9 @@ include: rules: $[[ inputs.plan_rules ]] cache_policy: pull warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -242,6 +260,9 @@ stages: validate_rules: $[[ inputs.validate_rules ]] plan_rules: $[[ inputs.plan_rules ]] warning_on_non_empty_plan: $[[ inputs.warning_on_non_empty_plan ]] + 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 ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate.yml b/templates/validate.yml index 072d953bd204ce7350f3bd11af0626d8dd55d553..f272bf6b3200cd3b28d93d2d026e4375d5efb57a 100644 --- a/templates/validate.yml +++ b/templates/validate.yml @@ -89,6 +89,18 @@ spec: default: pull-push type: string description: 'Defines the cache policy of the job.' + auto_encryption: + default: false + type: boolean + description: 'Whether to enable automatic state and plan encryption.' + auto_encryption_passphrase: + default: '' + type: string + description: 'Defines the passphrase to auto encrypt the state and plan. Only used if `auto_encryption` is `true`.' + auto_encryption_enable_migration_from_unencrypted: + 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.' --- @@ -107,6 +119,9 @@ spec: GITLAB_TOFU_STATE_NAME: $[[ inputs.state_name ]] GITLAB_TOFU_IGNORE_INIT_ERRORS: 'true' # Tofu can report errors which might be the reason init failed. GITLAB_TOFU_VAR_FILE: '$[[ inputs.var_file ]]' + 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 ]]' 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/main.tf b/tests/iac/main.tf index f65a2b5a7a0681119c15026fd645f1ab8a7f8501..aa8705c6f46153325b06b6113fdab94804fa7df4 100644 --- a/tests/iac/main.tf +++ b/tests/iac/main.tf @@ -7,6 +7,17 @@ resource "local_file" "foo" { filename = "${path.module}/foo.bar" } +locals { + ts = plantimestamp() +} + +// NOTE: always force a change. +resource "null_resource" "this" { + triggers = { + timestamp = local.ts + } +} + variable "ci_project_name" { type = string default = "default" @@ -24,3 +35,7 @@ output "project_name" { output "test_variable" { value = var.test_variable } + +output "this_always_changes" { + value = local.ts +} diff --git a/tests/integration-tests/AutoEncryption.gitlab-ci.yml b/tests/integration-tests/AutoEncryption.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..e25b09802223f78ddda089da1ba24a76a715f07b --- /dev/null +++ b/tests/integration-tests/AutoEncryption.gitlab-ci.yml @@ -0,0 +1,39 @@ +include: + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/apply@$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 + stage: test + root_dir: $TEST_GITLAB_TOFU_ROOT_DIR + state_name: $TEST_GITLAB_TOFU_STATE_NAME + no_plan: true + auto_encryption: true + auto_encryption_passphrase: '947F23E4-B9FC-4E76-B7B4-1D35ECBE9B09' + + # For CI Terraform state cleanup + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/delete-state@$CI_COMMIT_SHA + inputs: + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + +stages: [test, verify, cleanup] + +state-is-encrypted: + stage: verify + image: $GITLAB_OPENTOFU_IMAGE_BASE/gitlab-opentofu:$CI_COMMIT_SHA-opentofu$OPENTOFU_VERSION-alpine + variables: + GITLAB_TOFU_STATE_NAME: $TEST_GITLAB_TOFU_STATE_NAME + script: + - | + . $(which gitlab-tofu) + echo "Requesting state at $TF_HTTP_ADDRESS to check if it is encrypted ..." + success=$(curl --fail-with-body -u "${TF_HTTP_USERNAME}:${TF_HTTP_PASSWORD}" "${TF_HTTP_ADDRESS}" | jq -r '.encrypted_data != ""') + if [ "$success" != 'true' ]; then + echo 'Error: no encrypted data found in state.' + exit 1 + else + echo 'Success: the state is encrypted.' + fi + rules: [{when: on_success}] diff --git a/tests/integration-tests/AutoEncryptionMigrate.gitlab-ci.yml b/tests/integration-tests/AutoEncryptionMigrate.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..c2631ad2957c9667d6da2238045f262de19418d5 --- /dev/null +++ b/tests/integration-tests/AutoEncryptionMigrate.gitlab-ci.yml @@ -0,0 +1,49 @@ +include: + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/apply@$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 + as: 'apply:unencrypted' + stage: unencrypted + root_dir: $TEST_GITLAB_TOFU_ROOT_DIR + state_name: $TEST_GITLAB_TOFU_STATE_NAME + no_plan: true + + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/apply@$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 + as: 'apply:migrate' + stage: migrate + root_dir: $TEST_GITLAB_TOFU_ROOT_DIR + state_name: $TEST_GITLAB_TOFU_STATE_NAME + no_plan: true + auto_encryption: true + auto_encryption_passphrase: '947F23E4-B9FC-4E76-B7B4-1D35ECBE9B09' + auto_encryption_enable_migration_from_unencrypted: true + + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/apply@$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 + as: 'apply:encrypted' + stage: encrypted + root_dir: $TEST_GITLAB_TOFU_ROOT_DIR + state_name: $TEST_GITLAB_TOFU_STATE_NAME + no_plan: true + auto_encryption: true + auto_encryption_passphrase: '947F23E4-B9FC-4E76-B7B4-1D35ECBE9B09' + + # For CI Terraform state cleanup + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/delete-state@$CI_COMMIT_SHA + inputs: + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + +stages: [unencrypted, migrate, encrypted, cleanup] diff --git a/tests/integration-tests/WarningOnNonEmptyPlan.gitlab-ci.yml b/tests/integration-tests/WarningOnNonEmptyPlan.gitlab-ci.yml index 1edddd7d70364a78abb67d8b4381025e72d17b83..9ec1043ea260ef05f8db0f1f2bdf7e0920424904 100644 --- a/tests/integration-tests/WarningOnNonEmptyPlan.gitlab-ci.yml +++ b/tests/integration-tests/WarningOnNonEmptyPlan.gitlab-ci.yml @@ -26,6 +26,7 @@ verify:plan-job:has-warning-state: - apk add --update curl jq script: - | + backend_address="${GITLAB_TOFU_STATE_ADDRESS:-${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${backend_state_name}}" endpoint="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/jobs" is_warning_job=$(curl --silent "$endpoint" | jq -r '.[] | select(.name == "plan") | [.status == "failed", .allow_failure == true] | all') if [ "$is_warning_job" != 'true' ]; then diff --git a/tests/integration.gitlab-ci.yml b/tests/integration.gitlab-ci.yml index 8e8e0bc09db7699c0de703ee240f25833d3906cc..92e1012590b03abda8d1938f52addf228220f4c7 100644 --- a/tests/integration.gitlab-ci.yml +++ b/tests/integration.gitlab-ci.yml @@ -122,3 +122,21 @@ plan-job-template: GITLAB_OPENTOFU_BASE_IMAGE_OS: - alpine - debian + +apply-job-template: + 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 + trigger: + include: tests/integration-tests/$PIPELINE_NAME.gitlab-ci.yml + strategy: depend + parallel: + matrix: + - PIPELINE_NAME: + - AutoEncryption + - AutoEncryptionMigrate + GITLAB_OPENTOFU_BASE_IMAGE_OS: + - alpine + - debian