Skip to content
Snippets Groups Projects
README.md 29.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Timo Furrer's avatar
    Timo Furrer committed
    <!-- This document is generated by `make docs` from `.gitlab/README.md` -->
    
    
    # OpenTofu CI/CD Component
    
    Fabio Pitino's avatar
    Fabio Pitino committed
    
    
    This project is home to the **OpenTofu CI/CD component** and it's related assets,
    
    Timo Furrer's avatar
    Timo Furrer committed
    like the `gitlab-tofu` wrapper script and OCI images containing that script
    together with an OpenTofu version.
    
    
    > [!note]
    > Please make sure to use a released version of this CI/CD component.
    > You find all releases on the [Releases Overview Page](https://gitlab.com/components/opentofu/-/releases).
    
    > [!tip]
    > GitLab CI/CD components and the CI/CD catalog are fairly recent additions to GitLab.
    > You can learn more about them here:
    >
    > - [CI/CD components](https://docs.gitlab.com/ee/ci/components)
    > - [Development guide for GitLab CI/CD components](https://docs.gitlab.com/ee/development/cicd/components)
    > - [CI/CD Catalog](https://docs.gitlab.com/ee/ci/components/index.html#cicd-catalog)
    >
    >♻️ **Migrating from the Terraform CI/CD templates?** Check **[this](#migrating-from-the-terraform-cicd-templates)** out.
    
    Timo Furrer's avatar
    Timo Furrer committed
    ## Usage
    
    
    > [!tip]
    > The usage examples use `<...>` pattern for placeholders that you must replace with your desired values.
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    ```yaml
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/full-pipeline@<VERSION>
    
    Timo Furrer's avatar
    Timo Furrer committed
        inputs:
          opentofu_version: <OPENTOFU_VERSION>
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    
    stages: [validate, test, build, deploy, cleanup]
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    ---
    
    # ... or without the destroy jobs:
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/validate-plan-apply@<VERSION>
    
    Timo Furrer's avatar
    Timo Furrer committed
        inputs:
          opentofu_version: <OPENTOFU_VERSION>
    
    stages: [validate, build, deploy]
    
    
    # ... or in a child pipeline:
    include:
      - component: $CI_SERVER_FQDN/components/opentofu/validate-plan-apply@<VERSION>
        inputs:
          opentofu_version: <OPENTOFU_VERSION>
          trigger_in_child_pipeline: true
    
    Timo Furrer's avatar
    Timo Furrer committed
    ```
    
    A concrete example may look like this:
    
    ```yaml
    
    # Using version `0.10.0`:
    
    Timo Furrer's avatar
    Timo Furrer committed
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/full-pipeline@0.10.0
    
    Timo Furrer's avatar
    Timo Furrer committed
        inputs:
    
    Timo Furrer's avatar
    Timo Furrer committed
          opentofu_version: 1.6.1
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    
    stages: [validate, test, build, deploy, cleanup]
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    ---
    
    
    # ... in case you absolutely know what you are doing and are
    # aware that this may introduce breaking changes, you may use the latest release:
    
    Timo Furrer's avatar
    Timo Furrer committed
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/full-pipeline@~latest
    
    Timo Furrer's avatar
    Timo Furrer committed
        inputs:
          # The version must currently be specified explicitly as an input,
    
    Timo Furrer's avatar
    Timo Furrer committed
          # to find the correctly associated images. # This can be removed
    
    Timo Furrer's avatar
    Timo Furrer committed
          # once https://gitlab.com/gitlab-org/gitlab/-/issues/438275 is solved.
    
          version: latest # component version
    
    Timo Furrer's avatar
    Timo Furrer committed
          opentofu_version: 1.6.1
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    
    stages: [validate, test, build, deploy, cleanup]
    
    Timo Furrer's avatar
    Timo Furrer committed
    ```
    
    
    Or import all jobs as hidden templates ready to be extended:
    
    ```yaml
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/job-templates@<VERSION>
    
        inputs:
          opentofu_version: <OPENTOFU_VERSION>
    
    stages: [...]
    
    fmt:
      extends: [.opentofu:fmt]
    
    ...
    ```
    
    
    ### OpenTofu Version
    
    The OpenTofu version can be specified with the `opentofu_version` input.
    More details can be found [here](#available-opentofu-versions).
    
    ### Base Image OS
    
    The GitLab OpenTofu images come in multiple base image variants:
    
    - `alpine` (default)
    - `debian`
    
    The base image OS can be specified with the `base_os` input.
    
    
    ### GitLab-managed Terraform state backend
    
    
    > [!tip]
    > Consider using `auto_define_backend: true` to let the component automatically set up
    > the OpenTofu HTTP backend configuration block.
    
    
    This component - by leveraging the [`gitlab-tofu`](src/gitlab-tofu.sh) CLI internally -
    
    can automatically define and configure the
    
    [GitLab-managed Terraform state backend](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html).
    
    
    By default the HTTP backend must be defined manually using the following HCL:
    
    However, you may simply enable the `auto_define_backend` so that the component takes care of this step.
    
    **Note**: in future versions of this component we may enable `auto_define_backend` by default.
    
    ### State and Plan Encryption
    
    
    > [!tip]
    > State and Plan encryption is not enabled by default which may impact security negatively
    > for your use case. Please consider using it. The example below gives you a good sense
    > of how easy it is to enable.
    
    
    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:
    
    
    Jon Richter's avatar
    Jon Richter committed
    - `auto_encryption` (`boolean`): if set to `true` will auto-encrypt your state and plan.
    - `auto_encryption_passphrase` (`string`): is required if `auto_encryption` 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:
          opentofu_version: <OPENTOFU_VERSION>
    
    Jon Richter's avatar
    Jon Richter committed
          auto_encryption: true
          auto_encryption_passphrase: $PASSPHRASE
    
    
    stages: [validate, build, deploy]
    ```
    
    
    #### Working with encrypted states locally
    
    To locally work with encrypted states that have been auto encrypted by the component you can
    manually do what the component does:
    
    Copy the encryption setup from [the `configure_encryption_for_tofu` function](/src/gitlab-tofu.sh#L310)
    into a temporary file called `encryption.tf` or expose it in the `TF_ENCRYPTION` variable - make sure to
    correctly set your passphrase the match the value from GitLab CI. Then you can simply continue using your
    regular `tofu` tooling.
    
    
    ### 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:
          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]
    the component also sets up credentials to authenticate with the
    [Terraform Module Registry](https://docs.gitlab.com/ee/user/packages/terraform_module_registry/)
    of the project the pipeline runs in.
    It basically sets the `TF_TOKEN_<domain>` variable to the `$CI_JOB_TOKEN`, where `<domain>` is
    the GitLab instance domain, for example for GitLab.com this would set `TF_TOKEN_gitlab_com` to
    the `$CI_JOB_TOKEN`. However, it'll only do so if the variable is not already provided.
    Thus, if you want to authenticate differently or to another Terraform Module Registry,
    you may just provide the `TF_TOKEN_<domain>` variable yourself, e.g. via CI/CD variables.
    
    
    ### Access to GitLab via `glab` or GitLab Terraform Provider
    
    The GitLab CLI `glab` is pre-installed in all the images.
    If you want to use `glab` or the GitLab Terraform Provider we recommend configuring
    a CI/CD variable called `GITLAB_TOKEN`.
    This will automatically authenticate both tools.
    For `glab` you can just start using it, for the Terraform Provider you just need to define
    the provider requirement. Tofu will do the rest.
    
    
    This component repository also provides some templates that may often be used,
    
    for example one that only runs validation (`fmt` and `validate`), plan and an apply,
    but no destructive actions.
    
    
    - [`validate-plan`](templates/validate-plan.yml)
    
    - [`validate-plan-apply`](templates/validate-plan-apply.yml)
    
    - [`validate-plan-destroy`](templates/validate-plan-destroy.yml)
    
    These templates support the `trigger_in_child_pipeline` input which will
    include the component but run all its job in a child pipeline. This may be useful in cases
    where you want to run dedicated child pipeline for each of your environments.
    
    
    Instead of including the `full-pipeline` or another opinionated template,
    
    it's also possible to include individual jobs
    
    Timo Furrer's avatar
    Timo Furrer committed
    and compose your own pipeline, for example, to just run the `fmt` job you can do:
    
    ```yaml
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/fmt@<VERSION>
    
    Timo Furrer's avatar
    Timo Furrer committed
        inputs:
    
    Timo Furrer's avatar
    Timo Furrer committed
          opentofu_version: 1.6.1
    
    Timo Furrer's avatar
    Timo Furrer committed
          root_dir: tofu/
    ```
    
    
    Or you can also include the `job-templates` template, that will include
    all available OpenTofu jobs as hidden job templates prefixed with `.opentofu:`.
    Those are especially useful when you want to minimize your includes and
    you want to extend the jobs:
    
    ```yaml
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/job-templates@<VERSION>
    
        inputs:
          opentofu_version: 1.6.1
    
    plan:
      extends: [.opentofu:plan]
      parallel:
        matrix:
    
          - GITLAB_TOFU_ROOT_DIR: test/
          - GITLAB_TOFU_ROOT_DIR: prod/
    
    Have a look at the [`full-pipeline`](templates/job-templates.yml) for how it's constructed.
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    The following job components exist:
    
    
    - [`fmt`](templates/fmt.yml): Check formatting of configuration files.
    - [`validate`](templates/validate.yml): Validate configuration.
    - [`test`](templates/test.yml): Test configuration.
    - [`plan`](templates/plan.yml): Plan an apply or destroy.
    - [`apply`](templates/apply.yml): Apply a configuration.
    - [`destroy`](templates/destroy.yml): Destroy a configuration.
    - [`delete-state`](templates/delete-state.yml): Delete the GitLab-managed Terraform state.
    - [`custom-command`](templates/custom-command.yml): Run a custom OpenTofu command.
    - [`module-release`](templates/module-release.yml): Release an OpenTofu module to the GitLab Terraform Module Registry.
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    Have a look at the individual template spec to learn about the available inputs.
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    ### Inputs
    
    
    Please checkout the individual templates for the input definitions.
    The [catalog page](https://gitlab.com/explore/catalog/components/opentofu)
    beautifully renders the inputs for each templates - check it out!
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    
    ### Available OpenTofu Versions
    
    The following OpenTofu versions are available with this component via the `opentofu_version` input:
    
    
    - [`1.9.0`](https://github.com/opentofu/opentofu/releases/tag/v1.9.0)
    
    - [`1.8.8`](https://github.com/opentofu/opentofu/releases/tag/v1.8.8)
    
    - [`1.7.7`](https://github.com/opentofu/opentofu/releases/tag/v1.7.7)
    - [`1.6.3`](https://github.com/opentofu/opentofu/releases/tag/v1.6.3)
    
    ### Environment Variables
    
    The following environment variables are respected by the `gitlab-tofu` script:
    
    #### Respected Environment Variables
    
    - `GITLAB_TOFU_DEBUG`: if set to true will enable xtrace.
    - `GITLAB_TOFU_SOURCE`: forces this script in source-mode. Required when source auto-detection fails.
    - `GITLAB_TOFU_APPLY_NO_PLAN`: if set to true, the apply command does not use a plan cache file.
    - `GITLAB_TOFU_PLAN_NAME`: the name of the plan cache and json files. Defaults to `plan`.
    - `GITLAB_TOFU_PLAN_CACHE`: if set to the full path of the plan cache file. Defaults to `<root>/$GITLAB_TOFU_PLAN_NAME.cache`
    - `GITLAB_TOFU_PLAN_JSON`: if set to the full path of the plan json file. Defaults to `<root>/$GITLAB_TOFU_PLAN_NAME.json`
    - `GITLAB_TOFU_IMPLICIT_INIT`: if set to true will perform an implicit `tofu init` before any command that require it. Defaults to `true`.
    - `GITLAB_TOFU_IGNORE_INIT_ERRORS`: if set to true will ignore errors in the `tofu init` command.
    - `GITLAB_TOFU_INIT_NO_RECONFIGURE`: if set to true will not pass `-reconfigure` to the `tofu init` command. Defaults to `false`.
    - `GITLAB_TOFU_STATE_NAME`: the name of the GitLab-managed Terraform state backend endpoint.
    - `GITLAB_TOFU_STATE_ADDRESS`: the address of the GitLab-managed Terraform state backend. Defaults to `$CI_API_V4_URL/projects/$CI_PROJECT_ID/terraform/state/$GITLAB_TOFU_STATE_NAME`.
    - `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`.
    
    - `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE`: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.
    - `GITLAB_TOFU_AUTO_DEFINE_BACKEND`: if set to true, automatically creates a file with a HTTP backend configuration block.
    
    
    #### Respected OpenTofu Environment Variables
    
    > these are variables that are
    > respected if set and avoid using
    > the gitlab-tofu values for them.
    
    - `TF_HTTP_USERNAME`: username for the HTTP backend. Defaults to `gitlab-ci-token`.
    - `TF_HTTP_PASSWORD`: password for the HTTP backend. Defaults to `$CI_JOB_TOKEN`.
    - `TF_HTTP_ADDRESS`: address for the HTTP backend. Defaults to `$CI_API_V4_URL/projects/$CI_PROJECT_ID/terraform/state/<urlencode($GITLAB_TOFU_STATE_NAME)>`.
    - `TF_HTTP_LOCK_ADDRESS`: lock address for the HTTP backend. Defaults to `$TF_HTTP_ADDRESS/lock`.
    - `TF_HTTP_LOCK_METHOD`: lock method for the HTTP backend. Defaults to `POST`.
    - `TF_HTTP_UNLOCK_ADDRESS`: unlock address for the HTTP backend. Defaults to `lock`.
    - `TF_HTTP_UNLOCK_METHOD`: unlock address for the HTTP backend. Defaults to `unlock`.
    - `TF_HTTP_RETRY_WAIT_MIN`: retry minimum waiting time in seconds. Defaults to `5`.
    - `TF_CLI_CONFIG_FILE`: config file path. Defaults to `$HOME/.terraformrc` if it exists.
    
    #### Respected GitLab CI/CD Variables
    
    > these are variables exposed by
    > GitLab CI/CD and respected by
    > the gitlab-tofu script for
    > certain configurations.
    
    - `CI_JOB_TOKEN`:
      - used as default value for `TF_HTTP_PASSWORD`.
      - used as value for `TF_TOKEN_<host>` variable.
    - `CI_PROJECT_DIR`:
      - used as default value for root directory.
    - `CI_PROJECT_ID`:
      - used as default value in constructing the `GITLAB_TOFU_STATE_ADDRESS`.
    - `CI_API_V4_URL`:
      - used as default value in constructing the `GITLAB_TOFU_STATE_ADDRESS`.
    - `CI_SERVER_HOST`:
      - used to construct for `TF_TOKEN_<host>` variable.
    - `CI_SERVER_PROTOCOL`:
      - used to construct for `TF_TOKEN_<host>` variable.
    
    ### Auto-forwarded predefined CI variables
    
    The `gitlab-tofu` script auto-forwards some "popular"
    [predefined CI/CD variables](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)
    as OpenTofu variables.
    
    The forwarded variables are:
    
    - `CI_JOB_ID`
    - `CI_COMMIT_SHA`
    - `CI_JOB_STAGE`
    - `CI_PROJECT_ID`
    - `CI_PROJECT_NAME`
    - `CI_PROJECT_NAMESPACE`
    - `CI_PROJECT_PATH`
    - `CI_PROJECT_URL`
    
    To use them in your OpenTofu configuration you can define a string variable with the same name
    but in *lower snake_case*. For example the `CI_PROJECT_NAME` CI/CD variable can be accessed in
    the OpenTofu configuration like this:
    
    ```hcl
    variable "ci_project_name" {
      type        = string
      description = "The name of the directory for the project."
    }
    ```
    
    
    ### Install additional tools
    
    The `gitlab-opentofu` container image deliberately comes with minimal tooling
    to keep the image size small and be the least common denominator for our users.
    
    However, it is sometimes necessary to install additional tools. To do that you
    can overwrite the included jobs with a `before_script` entry. The `gitlab-opentofu`
    image uses `alpine` as its base image and therefore `apk` can be used to install
    the tools. For example to install `jq`:
    
    ```yaml
    include:
    
      - component: $CI_SERVER_FQDN/components/opentofu/validate-plan@<VERSION>
    
        inputs:
          version: <VERSION>
          opentofu_version: 1.6.1
    
    plan:
      before_script:
        - apk add jq
    ```
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    
    ### Source `gitlab-tofu` script to run custom commands later
    
    The `gitlab-tofu` script can be sourced instead of executed.
    This allows you to setup the shell and run your own `tofu` or
    `gitlab-tofu` commands.
    
    You can use the following snipped either in your script,
    directly in the shell and also in `script` keywords of your
    pipeline job:
    
    ```shell
    . $(which gitlab-tofu)
    ```
    
    There is a slight chance when doing this in a more exotic environment
    or shell that `gitlab-tofu` is not able to detect that it is sourced
    and will try to execute a `tofu` command.
    In this case you can set the `GITLAB_TOFU_SOURCE` environment variable
    to `true` before sourcing `gitlab-tofu`.
    
    
    When the `gitlab-tofu` script is sourced it'll set the `GITLAB_TOFU_SOURCED`
    variable to `true`. This variable is not exported by the script itself.
    
    
    ### Best Practices
    
    This section is a collection of *some* best practices.
    Feel free to contribute more that generally apply.
    If a best practice really becomes the de-facto standard
    we may make it the default behavior if possible.
    
    #### Lockfile Handling
    
    If you commit the Lockfile (`.terraform.lock.hcl`) to your repository
    
    we recommend setting either the `GITLAB_TOFU_INIT_FLAGS` (handled by this component)
    
    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.
    
    
    ### Examples
    
    Here are some example repositories to demonstrate how this component maybe used:
    
    - [timofurrer/opentofu-test](https://gitlab.com/timofurrer/opentofu-test): uses multiple environments configured for different kinds of pipelines with a single branch.
    
    Please contribute your own examples!
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    ## Releases & Versioning
    
    
    This project currently releases tagged commits.
    
    An overview of releases can be found on the [Releases page](https://gitlab.com/components/opentofu/-/releases)
    and a Changelog can be found [here](CHANGELOG.md).
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    Each release is accessible in the [CI/CD Catalog](https://gitlab.com/explore/catalog).
    
    ### Component Versions
    
    The component release versions follow [Semantic Versioning 2.0.0](https://semver.org/).
    
    ### Image Versions
    
    This project releases multiple OCI image variants that can be used with the component.
    The intention is that the images used in a component have the same version and or not mixed.
    
    Due to the limitations described in https://gitlab.com/gitlab-org/gitlab/-/issues/438275
    
    Timo Furrer's avatar
    Timo Furrer committed
    it's currently required to provide the component version in the `component` include field
    and as the `version` input. Check out the [Usage](#Usage) section for examples.
    
    
    There are `alpine` and `debian` variants available.
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    Each component release deploys the following images:
    
    
    - `$CI_TEMPLATE_REGISTRY_HOST/components/opentofu/gitlab-opentofu:<VERSION>-opentofu<OPENTOFU_VERSION>-<OS_VARIANT>`
    - `$CI_TEMPLATE_REGISTRY_HOST/components/opentofu/gitlab-opentofu:<VERSION>-opentofu-<OS_VARIANT>`
    
    Timo Furrer's avatar
    Timo Furrer committed
      - Includes the latest stable OpenTofu version at the time of releasing the component
    
    - `$CI_TEMPLATE_REGISTRY_HOST/components/opentofu/gitlab-opentofu:<VERSION>-<OS_VARIANT>`
    
    Timo Furrer's avatar
    Timo Furrer committed
      - Includes the latest stable OpenTofu version at the time of releasing the component
    
    
    In the above examples `<VERSION>` references the component version, `<OPENTOFU_VERSION>`
    an OpenTofu release, from [here](https://github.com/opentofu/opentofu/releases) and
    `OS_VARIANT` either `alpine` or `debian`.
    
    The release notes contain a full list of images deployed to the registry.
    
    Timo Furrer's avatar
    Timo Furrer committed
    
    *Note: unfortunately, these image versions are not SemVer compatible,
    because `-` indicates a prerelease (which they are not in this case).
    However, we cannot use the alternative `+` which would indicate build metadata
    as we'd like.
    See https://github.com/distribution/distribution/issues/1201*
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    ### Image Signing
    
    Every released image is [signed](https://docs.gitlab.com/ee/ci/yaml/signing_examples.html)
    using [`sigstore/cosign`](https://github.com/sigstore/cosign).
    
    
    You can use the following command to verify the signatures:
    
    ```shell
    VERSION=X.Y.Z # put a released components/opentofu version here
    IMAGE_REF=... # put a released components/opentofu image reference here
    cosign verify "${IMAGE_REF}" --certificate-identity="https://gitlab.com/components/opentofu//.gitlab-ci.yml@refs/tags/${VERSION}" --certificate-oidc-issuer="https://gitlab.com"
    ```
    
    For example, for image ref `registry.gitlab.com/components/opentofu/gitlab-opentofu:0.34.0-opentofu1.6.0-alpine` and version `0.34.0`:
    
    ```shell
    cosign verify "registry.gitlab.com/components/opentofu/gitlab-opentofu:0.34.0-opentofu1.6.0-alpine" \
        --certificate-identity "https://gitlab.com/components/opentofu//.gitlab-ci.yml@refs/tags/0.34.0" \
        --certificate-oidc-issuer "https://gitlab.com"
    ```
    
    For self-managed mirrors the OIDC issuer must be changed, too.
    
    mptr's avatar
    mptr committed
    ### Using with Renovate
    
    To keep the component versions up to date you could use [Renovate](https://docs.renovatebot.com/).
    
    Renovate users who use the component input `opentofu_version` should include the following `extends`
    so that the OpenTofu version is raised to a maximum of the version suitable for the component:
    
    ```json
    {
      "$schema": "https://docs.renovatebot.com/renovate-schema.json",
      "extends": ["local>components/opentofu"],
      ...
    }
    ```
    
    
    The above renovate config allows to update the `version` input together with the component include
    version if the `version` input has a `# component version` comment suffix, like so:
    
    ```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>
    
    stages: [validate, test, build, deploy, cleanup]
    ```
    
    
    mptr's avatar
    mptr committed
    (You may need to adjust the path to the `components/opentofu` to match your mirror.)
    Fore more details refer to the [Renovate documentation](https://docs.renovatebot.com/config-presets/).
    
    Some more example configurations for your `renovate.json`:
    
    - Package Rule to update all CI-Components
      ```json
      {
    			"matchFileNames": [
    				".gitlab-ci.yaml",
            ".gitlab-ci.yml",
    				"templates/**/*.yaml",
            "templates/**/*.yml"
    			],
    			"groupName": "Pipeline",
    			"semanticCommitType": "ci",
    			"automerge": true
    	},
      ```
    - Package rule to pin only `major.minor` versions:
      ```json
      {
    		  "matchManagers": ["gitlabci"],
    		  "extractVersion": "^(?<version>\\d+\\.\\d+)"
    	},
      ```
    - Package rule to target a specific component:
      ```json
      {
          "matchPackageNames": ["components/opentofu"],
          "matchManagers": ["gitlabci"]
      },
      ```
    
    
    **Example Repositories**:
    
    - [timofurrer/opentofu-test](https://gitlab.com/timofurrer/opentofu-test)
    
    
    ## Usage on self-managed
    
    GitLab CI/CD components are not yet distributed and available on self-managed GitLab instances.
    (see details [here](https://gitlab.com/gitlab-org/gitlab/-/issues/415638)).
    It's also not possible to just include CI/CD components across instance, thus an include like
    `- component: gitlab.com/components/opentofu/full-pipeline@~latest` won't work from a
    self-managed instance.
    
    However, you could mirror this project from GitLab.com onto any self-managed instance using
    
    a [repository pull mirror](https://docs.gitlab.com/ee/user/project/repository/mirror/pull.html).
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    If the component is being mirrored to another path than `components/opentofu`, then you also
    
    need to change that path in the `include:component` and additionally provide the correct
    `image_registry_base` input.
    
    See also the official GitLab documentation for it
    [here](https://docs.gitlab.com/ee/ci/components/#use-a-gitlabcom-component-in-a-self-managed-instance).
    
    
    If you want to save runner resources you may disable the unit and integration tests
    by setting the `SKIP_TESTS` CI/CD variable to `true`.
    
    
    Marlon Moser's avatar
    Marlon Moser committed
    The component builds by default a multi-arch image for `linux/amd64` and `linux/arm64`.
    
    There are multiple reasons why you might want to change this behavior, like saving runner resources.
    To configure for what architectures the container image should be built, you can go to
    the CI/CD variables in the project settings and add a variable called `PLATFORMS`.
    The value is one or more `OS/ARCH[/VARIANT]`. If you have multiple platforms, they have to be comma separated.
    
    Marlon Moser's avatar
    Marlon Moser committed
    **Keep in mind that the component is tested with `linux/amd64` and `linux/arm64`,
    
    other platforms are not officially supported!**
    
    
    You can set the `OPENTOFU_COMPONENT_IMAGE_BUILD_RUNNER_TAG` CI/CD variable to a custom runner tag
    to use for the image build job. This may be useful if you require a dedicated privileged runner.
    
    
    The pipeline of this component respects the
    [GitLab Dependency Proxy](https://docs.gitlab.com/ee/user/packages/dependency_proxy/) configuration
    by detecting the `CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX` environment variable
    and configuring `buildah` to use it when building the container images.
    
    
    If you need to use this CI/CD component with a custom root CA, please set a CI/CD file variable called `CUSTOM_CA`. The
    certificate needs to be in the PEM format. Currently the certificate is applied to the following jobs:
    
    - `gitlab-opentofu-image:build`
    - `gitlab-opentofu-image:deploy`
    - `gitlab-opentofu-image:verify-signature`
    
    ## Migrating from the Terraform CI/CD templates
    
    When migrating from the GitLab Terraform CI/CD templates you can use the following migration rules:
    
    - Used `Terraform.gitlab-ci.yml` -> Migrate to `validate-plan-apply`.
    - Used `Terraform/Base.gitlab-ci.yml` -> Migrate to `job-templates`.
        - Migrate the `.terraform:` job prefix to `.opentofu:`.
    - Used the `kics-iac-sast` job -> Additionally include the `Jobs/SAST-IaC.latest.gitlab-ci.yml` template.
    - Migrate the following job names:
        - `build` -> `plan`
        - `deploy` -> `apply`
    - Migrate the `TF_ROOT` variable to the `root_dir` input.
        - Although the `TF_ROOT` variable is still used and maybe overwritten after the import on individual jobs.
    
        - Note that this component deprecated the `TF_ROOT` variable and uses `GITLAB_TOFU_ROOT_DIR` instead.
    
    - Migrate the `TF_STATE_NAME` variable to the `state_name` input.
        - Although the `TF_STATE_NAME` variable is still used and maybe overwritten after the import on individual jobs.
    
        - Note that this component deprecated the `TF_STATE_NAME` variable and uses `GITLAB_TOFU_STATE_NAME` instead.
    
    - Migrate the `TF_AUTO_DEPLOY` variable to custom `rules` inputs.
    
    Timo Furrer's avatar
    Timo Furrer committed
    - Used other variables -> Use the same variables with this component.
    
    
    The same rules apply for the `latest` templates.
    We also recommend to check out the [Usage](#Usage) section for more details about the available templates and inputs.
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    ### OpenTofu component `inputs` vs. Terraform template `variables`
    
    This OpenTofu CI/CD component makes use of [`inputs`](https://docs.gitlab.com/ee/ci/yaml/#specinputs)
    whereas the Terraform CI/CD templates used [`variables`](https://docs.gitlab.com/ee/ci/yaml/#variables).
    We recommend that you use the `inputs` with the OpenTofu component where available and required.
    However, if needed you may overwrite the jobs and set the `variables` you like.
    
    
    ## Can I use this component with Terraform?
    
    Probably. Although, we don't officially support it or maintain any compatibility layer where necessary.
    
    The OpenTofu CI/CD component job mainly interface with the [`gitlab-tofu`](src/gitlab-tofu.sh) script
    that is distributed with the `gitlab-opentofu` container image used as the base image for the jobs.
    This base image also contains the `tofu` binary.
    
    If you'd want to use Terraform instead you may provide your own container image
    that contains at least a script called `gitlab-tofu` so that it's compatible with the component jobs.
    Everything else in the job can be custom, like replacing `tofu` with `terraform`.
    
    You may provide the `image_registry_base` input to any of the component includes, pointing to the
    container registry URI hosting the container image. The container image name can be configured in
    the `image_name` input. The image has be versioned so that it is compatible
    with [the image versioning of this project](#image-versions).
    
    
    Timo Furrer's avatar
    Timo Furrer committed
    ## Contributing
    
    See the [CONTRIBUTING.md](CONTRIBUTING.md) guide.