From ff8bbcf343c818994c24bf82b1d16f824e575fac Mon Sep 17 00:00:00 2001 From: Timo Furrer <tuxtimo@gmail.com> Date: Fri, 14 Mar 2025 12:47:14 +0100 Subject: [PATCH] Implement support for `id_tokens` setups This change set implements support to configure `id_tokens` in a somewhat user-friendly way. Due to the lacking support of map nodes as input values multiple inputs are needed to configure it's support. Limitation: https://gitlab.com/gitlab-org/gitlab/-/issues/452451 Closes https://gitlab.com/components/opentofu/-/issues/49 Changelog: added --- .gitlab/README.md.template | 54 +++++++++ .gitlab/scripts/update-opentofu-versions.sh | 2 +- README.md | 54 +++++++++ templates/__internal_id_tokens_base_job.yml | 21 ++++ templates/apply.yml | 39 ++++++ templates/destroy.yml | 39 ++++++ templates/full-pipeline.yml | 36 ++++++ templates/graph.yml | 47 ++++++++ templates/job-templates.yml | 36 ++++++ templates/plan.yml | 40 ++++++- templates/test.yml | 39 ++++++ templates/validate-plan-apply.yml | 30 +++++ templates/validate-plan-destroy.yml | 30 +++++ templates/validate-plan.yml | 27 +++++ templates/validate.yml | 42 ++++++- tests/iac-id-tokens/backend.tf | 3 + tests/iac-id-tokens/id-tokens-setup.sh | 1 + tests/iac-id-tokens/main.tf | 41 +++++++ .../iac-id-tokens/modules/random-pet/main.tf | 12 ++ .../modules/random-pet/outputs.tf | 3 + .../modules/random-pet/variables.tf | 4 + tests/iac-id-tokens/tests/main.tftest.hcl | 6 + .../varfile.integration-test.tfvars | 1 + .../integration-tests/IdTokens.gitlab-ci.yml | 112 ++++++++++++++++++ tests/integration.gitlab-ci.yml | 18 +++ 25 files changed, 734 insertions(+), 3 deletions(-) create mode 100644 templates/__internal_id_tokens_base_job.yml create mode 100644 tests/iac-id-tokens/backend.tf create mode 100644 tests/iac-id-tokens/id-tokens-setup.sh create mode 100644 tests/iac-id-tokens/main.tf create mode 100644 tests/iac-id-tokens/modules/random-pet/main.tf create mode 100644 tests/iac-id-tokens/modules/random-pet/outputs.tf create mode 100644 tests/iac-id-tokens/modules/random-pet/variables.tf create mode 100644 tests/iac-id-tokens/tests/main.tftest.hcl create mode 100644 tests/iac-id-tokens/varfile.integration-test.tfvars create mode 100644 tests/integration-tests/IdTokens.gitlab-ci.yml diff --git a/.gitlab/README.md.template b/.gitlab/README.md.template index 1793fc3..5c9e3a1 100644 --- a/.gitlab/README.md.template +++ b/.gitlab/README.md.template @@ -199,6 +199,60 @@ include: stages: [validate, build, deploy] ``` +### Configure `id_tokens` + +> [!note] +> Due to [lacking support](https://gitlab.com/gitlab-org/gitlab/-/issues/452451) +> of map nodes as input value types the configuration for `id_tokens` is somewhat special, +> but nonetheless super easy. Read along! + +To configure [`id_tokens`](https://docs.gitlab.com/ci/yaml/#id_tokens) support you need these +three things: + +1. set the `enable_id_tokens` input to `true`. +2. configure the `.gitlab-tofu:id_tokens` job with your desired `id_tokens` setup. +3. (optionally) provide the `.gitlab/ci/setup-id-tokens.sh` script to configure things, + like assuming IAM roles. + +An example setup may look like this: + +```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> + enable_id_tokens: true + +stages: [validate, build, deploy] + +.gitlab-tofu:id_tokens: + id_tokens: + GITLAB_OIDC_TOKEN: + aud: https://gitlab.com +``` + +Then in the `.gitlab/ci/setup-id-tokens.sh` script you might assume a AWS IAM role: + +```shell +apk add --no-cache aws-cli +export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \ + $(aws sts assume-role-with-web-identity \ + --role-arn ${GITLAB_CI_ROLE_ARN} \ + --role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}" \ + --web-identity-token ${GITLAB_OIDC_TOKEN} \ + --duration-seconds 3600 \ + --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \ + --output text)) +aws sts get-caller-identity +``` + +You might configure the name of the `id_tokens` job and the setup script location +with the `id_tokens_base_job_name` and `id_tokens_setup_script` inputs, respectively. + ### Access to Terraform Module Registry Similar to automatically configuring the [GitLab-managed Terraform state backend] diff --git a/.gitlab/scripts/update-opentofu-versions.sh b/.gitlab/scripts/update-opentofu-versions.sh index bfd5470..417a7a8 100755 --- a/.gitlab/scripts/update-opentofu-versions.sh +++ b/.gitlab/scripts/update-opentofu-versions.sh @@ -8,7 +8,7 @@ project_dir="$script_dir/../.." echo "Updating template files ..." templates="templates/*.yml" -templates_exclude="templates/delete-state.yml templates/module-release.yml" +templates_exclude="templates/delete-state.yml templates/module-release.yml templates/__internal_id_tokens_base_job.yml" for relative_template_file in $templates; do if echo "$templates_exclude" | grep -q "$relative_template_file"; then continue; fi diff --git a/README.md b/README.md index 6c89664..4f490c5 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,60 @@ include: stages: [validate, build, deploy] ``` +### Configure `id_tokens` + +> [!note] +> Due to [lacking support](https://gitlab.com/gitlab-org/gitlab/-/issues/452451) +> of map nodes as input value types the configuration for `id_tokens` is somewhat special, +> but nonetheless super easy. Read along! + +To configure [`id_tokens`](https://docs.gitlab.com/ci/yaml/#id_tokens) support you need these +three things: + +1. set the `enable_id_tokens` input to `true`. +2. configure the `.gitlab-tofu:id_tokens` job with your desired `id_tokens` setup. +3. (optionally) provide the `.gitlab/ci/setup-id-tokens.sh` script to configure things, + like assuming IAM roles. + +An example setup may look like this: + +```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> + enable_id_tokens: true + +stages: [validate, build, deploy] + +.gitlab-tofu:id_tokens: + id_tokens: + GITLAB_OIDC_TOKEN: + aud: https://gitlab.com +``` + +Then in the `.gitlab/ci/setup-id-tokens.sh` script you might assume a AWS IAM role: + +```shell +apk add --no-cache aws-cli +export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \ + $(aws sts assume-role-with-web-identity \ + --role-arn ${GITLAB_CI_ROLE_ARN} \ + --role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}" \ + --web-identity-token ${GITLAB_OIDC_TOKEN} \ + --duration-seconds 3600 \ + --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \ + --output text)) +aws sts get-caller-identity +``` + +You might configure the name of the `id_tokens` job and the setup script location +with the `id_tokens_base_job_name` and `id_tokens_setup_script` inputs, respectively. + ### Access to Terraform Module Registry Similar to automatically configuring the [GitLab-managed Terraform state backend] diff --git a/templates/__internal_id_tokens_base_job.yml b/templates/__internal_id_tokens_base_job.yml new file mode 100644 index 0000000..8775b65 --- /dev/null +++ b/templates/__internal_id_tokens_base_job.yml @@ -0,0 +1,21 @@ +spec: + inputs: + # NOTE: see the using templates for their description + as: {type: string} + id_tokens_base_job_name: {type: string} + id_tokens_setup_script: {type: string} + +--- + + +# NOTE: okay, this is bad, but it's what we got. +# We have to outsource this job (see the using templates for why we have it in the first place) +# because GitLab ALWAYS evaluates jobs, even if they are never used. +# This implies that all base jobs have to exist. +# And the only way for GitLab NOT to parse a job is to NOT include it. +# Thus, we simply do not include this if we don't have to. +'.$[[ inputs.as ]]:id_tokens-setup:true': + extends: $[[ inputs.id_tokens_base_job_name ]] + before_script: + - test -f "$[[ inputs.id_tokens_setup_script ]]" && . $[[ inputs.id_tokens_setup_script ]] + diff --git a/templates/apply.yml b/templates/apply.yml index be1c76c..2539887 100644 --- a/templates/apply.yml +++ b/templates/apply.yml @@ -99,10 +99,49 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the job. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend this job from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- +include: + # NOTE: see the note in that template file for what he heck is going on here. + - local: '/templates/__internal_id_tokens_base_job.yml' + rules: + - if: '"$[[ inputs.enable_id_tokens ]]" == "true"' + inputs: + as: $[[ inputs.as ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] + +# NOTE: due to the lacking support of providing map nodes as input values +# we need to hack support for id_tokens together. It's even worse that id_tokens +# do NOT have a consistent type - or rather it's child nodes - for example, the `aud` +# can be a literal string value, a variable or an array. Limitations, bear with me. +# See https://gitlab.com/gitlab-org/gitlab/-/issues/452451 +'.$[[ inputs.as ]]:id_tokens-setup:false': + extends: null + '$[[ inputs.as ]]': + extends: + # NOTE: see the comment above. This is to support the `id_tokens` setup. + - '.$[[ inputs.as ]]:id_tokens-setup:$[[ inputs.enable_id_tokens ]]' stage: $[[ inputs.stage ]] environment: name: $GITLAB_TOFU_STATE_NAME diff --git a/templates/destroy.yml b/templates/destroy.yml index 45e0997..f70df31 100644 --- a/templates/destroy.yml +++ b/templates/destroy.yml @@ -103,10 +103,49 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the job. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend this job from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- +include: + # NOTE: see the note in that template file for what he heck is going on here. + - local: '/templates/__internal_id_tokens_base_job.yml' + rules: + - if: '"$[[ inputs.enable_id_tokens ]]" == "true"' + inputs: + as: $[[ inputs.as ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] + +# NOTE: due to the lacking support of providing map nodes as input values +# we need to hack support for id_tokens together. It's even worse that id_tokens +# do NOT have a consistent type - or rather it's child nodes - for example, the `aud` +# can be a literal string value, a variable or an array. Limitations, bear with me. +# See https://gitlab.com/gitlab-org/gitlab/-/issues/452451 +'.$[[ inputs.as ]]:id_tokens-setup:false': + extends: null + '$[[ inputs.as ]]': + extends: + # NOTE: see the comment above. This is to support the `id_tokens` setup. + - '.$[[ inputs.as ]]:id_tokens-setup:$[[ inputs.enable_id_tokens ]]' stage: $[[ inputs.stage ]] environment: name: $GITLAB_TOFU_STATE_NAME diff --git a/templates/full-pipeline.yml b/templates/full-pipeline.yml index 290e81b..98403b1 100644 --- a/templates/full-pipeline.yml +++ b/templates/full-pipeline.yml @@ -202,6 +202,24 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the pipeline. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend the jobs from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- @@ -241,6 +259,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/test.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "true"' @@ -265,6 +286,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -289,6 +313,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -310,6 +337,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -330,6 +360,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -418,6 +451,9 @@ stages: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/graph.yml b/templates/graph.yml index c78cde0..ed881e5 100644 --- a/templates/graph.yml +++ b/templates/graph.yml @@ -80,6 +80,13 @@ spec: default: [] type: array description: 'Defines the `needs` of the job.' + rules: + # FIXME: eventually, we'll want to define `null` as the default, + # but this is NOT support yet, see + # https://gitlab.com/gitlab-org/gitlab/-/issues/440468 + default: [{when: on_success}] + type: array + description: 'Defines the `rules` of the job.' cache_policy: default: pull-push type: string @@ -100,12 +107,52 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the job. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend this job from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- +include: + # NOTE: see the note in that template file for what he heck is going on here. + - local: '/templates/__internal_id_tokens_base_job.yml' + rules: + - if: '"$[[ inputs.enable_id_tokens ]]" == "true"' + inputs: + as: $[[ inputs.as ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] + +# NOTE: due to the lacking support of providing map nodes as input values +# we need to hack support for id_tokens together. It's even worse that id_tokens +# do NOT have a consistent type - or rather it's child nodes - for example, the `aud` +# can be a literal string value, a variable or an array. Limitations, bear with me. +# See https://gitlab.com/gitlab-org/gitlab/-/issues/452451 +'.$[[ inputs.as ]]:id_tokens-setup:false': + extends: null + '$[[ inputs.as ]]': + extends: + # NOTE: see the comment above. This is to support the `id_tokens` setup. + - '.$[[ inputs.as ]]:id_tokens-setup:$[[ inputs.enable_id_tokens ]]' stage: $[[ inputs.stage ]] needs: $[[ inputs.needs ]] + rules: $[[ inputs.rules ]] cache: key: "$__CACHE_KEY_HACK" policy: $[[ inputs.cache_policy ]] diff --git a/templates/job-templates.yml b/templates/job-templates.yml index eaa2d30..6f69de6 100644 --- a/templates/job-templates.yml +++ b/templates/job-templates.yml @@ -107,6 +107,24 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the pipeline. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend the jobs from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- @@ -139,6 +157,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/graph.yml' inputs: as: '$[[ inputs.job_name_prefix ]]graph' @@ -154,6 +175,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/test.yml' inputs: as: '$[[ inputs.job_name_prefix ]]test' @@ -171,6 +195,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/plan.yml' inputs: as: '$[[ inputs.job_name_prefix ]]plan' @@ -191,6 +218,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/apply.yml' inputs: as: '$[[ inputs.job_name_prefix ]]apply' @@ -209,6 +239,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/destroy.yml' inputs: as: '$[[ inputs.job_name_prefix ]]destroy' @@ -227,6 +260,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/delete-state.yml' inputs: as: '$[[ inputs.job_name_prefix ]]delete-state' diff --git a/templates/plan.yml b/templates/plan.yml index 73b4a51..52b2186 100644 --- a/templates/plan.yml +++ b/templates/plan.yml @@ -113,9 +113,37 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the job. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend this job from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- +include: + # NOTE: see the note in that template file for what he heck is going on here. + - local: '/templates/__internal_id_tokens_base_job.yml' + rules: + - if: '"$[[ inputs.enable_id_tokens ]]" == "true"' + inputs: + as: $[[ inputs.as ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] + # NOTE: the two following jobs are necessary to implement the abstraction logic # required for the `warning_on_non_empty_plan` input. # Without any kind of flow control support for the GitLab CI YAML we cannot infer @@ -142,11 +170,21 @@ spec: # but we still want to upload all the artifacts. when: always +# NOTE: due to the lacking support of providing map nodes as input values +# we need to hack support for id_tokens together. It's even worse that id_tokens +# do NOT have a consistent type - or rather it's child nodes - for example, the `aud` +# can be a literal string value, a variable or an array. Limitations, bear with me. +# See https://gitlab.com/gitlab-org/gitlab/-/issues/452451 +'.$[[ inputs.as ]]:id_tokens-setup:false': + extends: null + '$[[ inputs.as ]]': - stage: $[[ inputs.stage ]] extends: # NOTE: see the comment above. This is to support the `warning_on_non_empty_plan` input. - '.$[[ inputs.as ]]:detailed_exitcode:warning:$[[ inputs.warning_on_non_empty_plan ]]' + # NOTE: see the comment above. This is to support the `id_tokens` setup. + - '.$[[ inputs.as ]]:id_tokens-setup:$[[ inputs.enable_id_tokens ]]' + stage: $[[ inputs.stage ]] environment: name: $[[ inputs.state_name ]] action: prepare diff --git a/templates/test.yml b/templates/test.yml index a76e329..124ff37 100644 --- a/templates/test.yml +++ b/templates/test.yml @@ -102,13 +102,52 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the job. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend this job from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- +include: + # NOTE: see the note in that template file for what he heck is going on here. + - local: '/templates/__internal_id_tokens_base_job.yml' + rules: + - if: '"$[[ inputs.enable_id_tokens ]]" == "true"' + inputs: + as: $[[ inputs.as ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] + +# NOTE: due to the lacking support of providing map nodes as input values +# we need to hack support for id_tokens together. It's even worse that id_tokens +# do NOT have a consistent type - or rather it's child nodes - for example, the `aud` +# can be a literal string value, a variable or an array. Limitations, bear with me. +# See https://gitlab.com/gitlab-org/gitlab/-/issues/452451 +'.$[[ inputs.as ]]:id_tokens-setup:false': + extends: null + '$[[ inputs.as ]]': stage: $[[ inputs.stage ]] needs: $[[ inputs.needs ]] rules: $[[ inputs.rules ]] + extends: + # NOTE: see the comment above. This is to support the `id_tokens` setup. + - '.$[[ inputs.as ]]:id_tokens-setup:$[[ inputs.enable_id_tokens ]]' cache: key: "$__CACHE_KEY_HACK" policy: $[[ inputs.cache_policy ]] diff --git a/templates/validate-plan-apply.yml b/templates/validate-plan-apply.yml index f1c5005..f72f9c9 100644 --- a/templates/validate-plan-apply.yml +++ b/templates/validate-plan-apply.yml @@ -166,6 +166,24 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the pipeline. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend the jobs from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- @@ -206,6 +224,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -231,6 +252,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/apply.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -253,6 +277,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -311,6 +338,9 @@ stages: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml index 1d303fd..b21f69b 100644 --- a/templates/validate-plan-destroy.yml +++ b/templates/validate-plan-destroy.yml @@ -172,6 +172,24 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the pipeline. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend the jobs from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- @@ -212,6 +230,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -238,6 +259,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/destroy.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -261,6 +285,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/delete-state.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -344,6 +371,9 @@ stages: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate-plan.yml b/templates/validate-plan.yml index ed976b6..136e9d7 100644 --- a/templates/validate-plan.yml +++ b/templates/validate-plan.yml @@ -150,6 +150,24 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the pipeline. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend the jobs from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- @@ -190,6 +208,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] - local: '/templates/plan.yml' rules: - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"' @@ -215,6 +236,9 @@ include: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled. @@ -270,6 +294,9 @@ stages: 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 ]] + enable_id_tokens: $[[ inputs.enable_id_tokens ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] trigger_in_child_pipeline: false forward: yaml_variables: true diff --git a/templates/validate.yml b/templates/validate.yml index 14154a1..ef0a042 100644 --- a/templates/validate.yml +++ b/templates/validate.yml @@ -99,10 +99,50 @@ spec: default: false type: boolean description: 'Whether to automatically define the HTTP backend configuration block.' - + enable_id_tokens: + default: false + type: boolean + description: | + Whether to enable `id_tokens` support for the job. + This works by extending a hidden base job configuration with the `id_tokens` field. + The job should only contain the `id_tokens` field, the rest may be overridden. + The job name can be configured with `id_tokens_base_job_name` if necessary. + If the script given in `id_tokens_setup_script` exists, it will be sourced so that setup actions can be performed, + like assuming an IAM role of your cloud provider. + id_tokens_base_job_name: + default: '.gitlab-tofu:id_tokens' + type: string + description: 'Name of the hidden base job containing the `id_tokens` configuration to extend this job from. Make sure to only configure `id_tokens`, everything else might be overridden.' + id_tokens_setup_script: + default: '.gitlab/ci/setup-id-tokens.sh' + type: string + description: 'Path to a shell script that is sourced when `enable_id_tokens` is `true`.' --- +include: + # NOTE: see the note in that template file for what he heck is going on here. + - local: '/templates/__internal_id_tokens_base_job.yml' + rules: + - if: '"$[[ inputs.enable_id_tokens ]]" == "true"' + inputs: + as: $[[ inputs.as ]] + id_tokens_base_job_name: $[[ inputs.id_tokens_base_job_name ]] + id_tokens_setup_script: $[[ inputs.id_tokens_setup_script ]] + +# NOTE: due to the lacking support of providing map nodes as input values +# we need to hack support for id_tokens together. It's even worse that id_tokens +# do NOT have a consistent type - or rather it's child nodes - for example, the `aud` +# can be a literal string value, a variable or an array. Limitations, bear with me. +# See https://gitlab.com/gitlab-org/gitlab/-/issues/452451 +# Oh and yes, it gets worse, the `true` counterpart for this job is in __internal_id_tokens_base_job.yml +# See the note there for why ... +'.$[[ inputs.as ]]:id_tokens-setup:false': + extends: null + '$[[ inputs.as ]]': + extends: + # NOTE: see the comment above. This is to support the `id_tokens` setup. + - '.$[[ inputs.as ]]:id_tokens-setup:$[[ inputs.enable_id_tokens ]]' stage: $[[ inputs.stage ]] rules: $[[ inputs.rules ]] cache: diff --git a/tests/iac-id-tokens/backend.tf b/tests/iac-id-tokens/backend.tf new file mode 100644 index 0000000..1736bf1 --- /dev/null +++ b/tests/iac-id-tokens/backend.tf @@ -0,0 +1,3 @@ +terraform { + backend "http" {} +} diff --git a/tests/iac-id-tokens/id-tokens-setup.sh b/tests/iac-id-tokens/id-tokens-setup.sh new file mode 100644 index 0000000..77f3959 --- /dev/null +++ b/tests/iac-id-tokens/id-tokens-setup.sh @@ -0,0 +1 @@ +touch id-tokens-setup-ran diff --git a/tests/iac-id-tokens/main.tf b/tests/iac-id-tokens/main.tf new file mode 100644 index 0000000..aa8705c --- /dev/null +++ b/tests/iac-id-tokens/main.tf @@ -0,0 +1,41 @@ +module "random_pet" { + source = "./modules/random-pet" +} + +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 + } +} + +variable "ci_project_name" { + type = string + default = "default" +} + +variable "test_variable" { + type = string + default = "default value" +} + +output "project_name" { + value = var.ci_project_name +} + +output "test_variable" { + value = var.test_variable +} + +output "this_always_changes" { + value = local.ts +} diff --git a/tests/iac-id-tokens/modules/random-pet/main.tf b/tests/iac-id-tokens/modules/random-pet/main.tf new file mode 100644 index 0000000..382c4e6 --- /dev/null +++ b/tests/iac-id-tokens/modules/random-pet/main.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + random = { + source = "hashicorp/random" + version = "3.1.2" + } + } +} + +resource "random_pet" "random_pet" { + length = var.length +} diff --git a/tests/iac-id-tokens/modules/random-pet/outputs.tf b/tests/iac-id-tokens/modules/random-pet/outputs.tf new file mode 100644 index 0000000..6a77100 --- /dev/null +++ b/tests/iac-id-tokens/modules/random-pet/outputs.tf @@ -0,0 +1,3 @@ +output "random_pet" { + value = random_pet.random_pet.id +} diff --git a/tests/iac-id-tokens/modules/random-pet/variables.tf b/tests/iac-id-tokens/modules/random-pet/variables.tf new file mode 100644 index 0000000..3e65d0a --- /dev/null +++ b/tests/iac-id-tokens/modules/random-pet/variables.tf @@ -0,0 +1,4 @@ +variable "length" { + default = 1 + type = number +} \ No newline at end of file diff --git a/tests/iac-id-tokens/tests/main.tftest.hcl b/tests/iac-id-tokens/tests/main.tftest.hcl new file mode 100644 index 0000000..fedf96e --- /dev/null +++ b/tests/iac-id-tokens/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/iac-id-tokens/varfile.integration-test.tfvars b/tests/iac-id-tokens/varfile.integration-test.tfvars new file mode 100644 index 0000000..a9e6ed5 --- /dev/null +++ b/tests/iac-id-tokens/varfile.integration-test.tfvars @@ -0,0 +1 @@ +test_variable = "varfile integration test" diff --git a/tests/integration-tests/IdTokens.gitlab-ci.yml b/tests/integration-tests/IdTokens.gitlab-ci.yml new file mode 100644 index 0000000..7d4078d --- /dev/null +++ b/tests/integration-tests/IdTokens.gitlab-ci.yml @@ -0,0 +1,112 @@ +include: + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/validate@$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 + stage: validate + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + enable_id_tokens: true + id_tokens_setup_script: $TEST_GITLAB_TOFU_ROOT_DIR/id-tokens-setup.sh + + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/test@$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 + stage: test + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + enable_id_tokens: true + id_tokens_setup_script: $TEST_GITLAB_TOFU_ROOT_DIR/id-tokens-setup.sh + + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/graph@$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 + stage: graph + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + enable_id_tokens: true + id_tokens_setup_script: $TEST_GITLAB_TOFU_ROOT_DIR/id-tokens-setup.sh + + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/plan@$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 + stage: plan + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + enable_id_tokens: true + id_tokens_setup_script: $TEST_GITLAB_TOFU_ROOT_DIR/id-tokens-setup.sh + + - 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 + root_dir: $TEST_GITLAB_TOFU_ROOT_DIR + stage: apply + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + enable_id_tokens: true + id_tokens_setup_script: $TEST_GITLAB_TOFU_ROOT_DIR/id-tokens-setup.sh + + - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/destroy@$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 + stage: destroy + state_name: $TEST_GITLAB_TOFU_STATE_NAME + rules: [{when: always}] + enable_id_tokens: true + id_tokens_setup_script: $TEST_GITLAB_TOFU_ROOT_DIR/id-tokens-setup.sh + + # 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: [validate, test, graph, plan, apply, destroy, cleanup] + +.gitlab-tofu:id_tokens: + id_tokens: + GITLAB_OIDC_TOKEN: + aud: https://gitlab.com + +.check-id-tokens-setup-ran: &check_id_tokens_setup_ran + - test -f id-tokens-setup-ran + +validate: + after_script: *check_id_tokens_setup_ran + +test: + after_script: *check_id_tokens_setup_ran + +graph: + after_script: *check_id_tokens_setup_ran + +plan: + after_script: *check_id_tokens_setup_ran + +apply: + after_script: *check_id_tokens_setup_ran + +destroy: + after_script: *check_id_tokens_setup_ran diff --git a/tests/integration.gitlab-ci.yml b/tests/integration.gitlab-ci.yml index f5ed0b8..6f3be6d 100644 --- a/tests/integration.gitlab-ci.yml +++ b/tests/integration.gitlab-ci.yml @@ -175,3 +175,21 @@ destroy-job-template: GITLAB_OPENTOFU_BASE_IMAGE_OS: - alpine - debian + +id-tokens: + 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-id-tokens + trigger: + include: tests/integration-tests/$PIPELINE_NAME.gitlab-ci.yml + strategy: depend + parallel: + matrix: + - PIPELINE_NAME: + - IdTokens + GITLAB_OPENTOFU_BASE_IMAGE_OS: + - alpine + - debian + -- GitLab