From e6460418e6f1846f16474f6cadab2b935be556e7 Mon Sep 17 00:00:00 2001
From: Timo Furrer <tfurrer@gitlab.com>
Date: Tue, 29 Oct 2024 15:41:18 +0100
Subject: [PATCH] Support specifying image digests

Refs https://gitlab.com/components/opentofu/-/issues/34

Changelog: feature
---
 .gitlab/release-notes.md.template   |  4 ++++
 templates/apply.yml                 |  7 ++++++-
 templates/custom-command.yml        |  7 ++++++-
 templates/destroy.yml               |  7 ++++++-
 templates/fmt.yml                   |  7 ++++++-
 templates/full-pipeline.yml         | 11 +++++++++++
 templates/graph.yml                 |  7 ++++++-
 templates/job-templates.yml         | 11 +++++++++++
 templates/plan.yml                  |  7 ++++++-
 templates/test.yml                  |  7 ++++++-
 templates/validate-plan-apply.yml   |  9 +++++++++
 templates/validate-plan-destroy.yml |  9 +++++++++
 templates/validate-plan.yml         |  8 ++++++++
 templates/validate.yml              |  7 ++++++-
 14 files changed, 100 insertions(+), 8 deletions(-)

diff --git a/.gitlab/release-notes.md.template b/.gitlab/release-notes.md.template
index 6a3da5b..d9edef9 100644
--- a/.gitlab/release-notes.md.template
+++ b/.gitlab/release-notes.md.template
@@ -52,6 +52,10 @@ cosign verify \
   --certificate-oidc-issuer "https://gitlab.com"
 ```
 
+The `image_digest` input can be used to strictly pull by the image digest.
+The `image_digest` input value needs to have the format of `@<type>:<hash>`
+where `<type>:<hash>` is the `digest` value from the images list above.
+
 > **Note:**
 >
 > When using the component with the inputs `version` and `opentofu_version`,<br>
diff --git a/templates/apply.yml b/templates/apply.yml
index 8e99206..ed26796 100644
--- a/templates/apply.yml
+++ b/templates/apply.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -101,6 +106,6 @@ spec:
     TF_APPLY_NO_PLAN: $[[ inputs.no_plan ]]
     TF_PLAN_NAME: $[[ inputs.plan_name ]]
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]$[[ inputs.image_digest ]]'
   script:
     - gitlab-tofu apply
diff --git a/templates/custom-command.yml b/templates/custom-command.yml
index 8d705ab..16ce8d5 100644
--- a/templates/custom-command.yml
+++ b/templates/custom-command.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -80,7 +85,7 @@ spec:
     __CACHE_KEY_HACK: "$[[ inputs.root_dir ]]"
     TF_ROOT: $[[ inputs.root_dir ]]
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
     - gitlab-tofu $[[ inputs.command ]]
 
diff --git a/templates/destroy.yml b/templates/destroy.yml
index a224964..d91c490 100644
--- a/templates/destroy.yml
+++ b/templates/destroy.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -100,6 +105,6 @@ spec:
     TF_APPLY_NO_PLAN: $[[ inputs.no_plan ]]
     TF_PLAN_NAME: $[[ inputs.plan_name ]]
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
     - gitlab-tofu apply -destroy
diff --git a/templates/fmt.yml b/templates/fmt.yml
index 45219cc..c9ca935 100644
--- a/templates/fmt.yml
+++ b/templates/fmt.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -88,6 +93,6 @@ spec:
     __CACHE_KEY_HACK: "$[[ inputs.root_dir ]]"
     TF_ROOT: $[[ inputs.root_dir ]]
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
     - gitlab-tofu fmt
diff --git a/templates/full-pipeline.yml b/templates/full-pipeline.yml
index fece43f..4261b77 100644
--- a/templates/full-pipeline.yml
+++ b/templates/full-pipeline.yml
@@ -67,6 +67,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -98,6 +103,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
   - local: '/templates/validate.yml'
     inputs:
@@ -108,6 +114,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
   - local: '/templates/test.yml'
@@ -119,6 +126,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
     rules:
@@ -133,6 +141,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       artifacts_access: $[[ inputs.plan_artifacts_access ]]
@@ -145,6 +154,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_apply: $[[ inputs.auto_apply ]]
@@ -157,6 +167,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_destroy: $[[ inputs.auto_destroy ]]
diff --git a/templates/graph.yml b/templates/graph.yml
index fa6c21f..3ec38af 100644
--- a/templates/graph.yml
+++ b/templates/graph.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -86,7 +91,7 @@ spec:
     TF_ROOT: $[[ inputs.root_dir ]]
     TF_STATE_NAME: $[[ inputs.state_name ]]
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
     - gitlab-tofu graph > "$[[ inputs.graph_file ]]"
   artifacts:
diff --git a/templates/job-templates.yml b/templates/job-templates.yml
index 93ced34..8767a31 100644
--- a/templates/job-templates.yml
+++ b/templates/job-templates.yml
@@ -67,6 +67,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     job_name_prefix:
       default: '.opentofu:'
@@ -98,6 +103,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
   - local: '/templates/validate.yml'
     inputs:
@@ -108,6 +114,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
   - local: '/templates/graph.yml'
@@ -129,6 +136,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
   - local: '/templates/plan.yml'
@@ -140,6 +148,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
   - local: '/templates/apply.yml'
@@ -151,6 +160,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_apply: $[[ inputs.auto_apply ]]
@@ -163,6 +173,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_destroy: $[[ inputs.auto_destroy ]]
diff --git a/templates/plan.yml b/templates/plan.yml
index a62a546..3fd49ec 100644
--- a/templates/plan.yml
+++ b/templates/plan.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -110,7 +115,7 @@ spec:
     TF_STATE_NAME: $[[ inputs.state_name ]]
     TF_PLAN_NAME: $[[ inputs.plan_name ]]
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
     - "args=\"\"\nif [ \"$[[ inputs.destroy ]]\" == \"true\" ]; then \n  echo \"Planning for a destroy\"\n  args=\"-destroy\"\nfi\n"
     - gitlab-tofu plan $args
diff --git a/templates/test.yml b/templates/test.yml
index 37e659a..f11343f 100644
--- a/templates/test.yml
+++ b/templates/test.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -86,6 +91,6 @@ spec:
     TF_ROOT: $[[ inputs.root_dir ]]
     TF_STATE_NAME: $[[ inputs.state_name ]]
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
     - gitlab-tofu test
diff --git a/templates/validate-plan-apply.yml b/templates/validate-plan-apply.yml
index a48b552..75d7a43 100644
--- a/templates/validate-plan-apply.yml
+++ b/templates/validate-plan-apply.yml
@@ -61,6 +61,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -88,6 +93,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
   - local: '/templates/validate.yml'
     inputs:
@@ -98,6 +104,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
   - local: '/templates/plan.yml'
@@ -109,6 +116,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       artifacts_access: $[[ inputs.plan_artifacts_access ]]
@@ -121,6 +129,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       auto_apply: $[[ inputs.auto_apply ]]
diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml
index ac01f06..a801d93 100644
--- a/templates/validate-plan-destroy.yml
+++ b/templates/validate-plan-destroy.yml
@@ -61,6 +61,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -91,6 +96,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
   - local: '/templates/validate.yml'
     inputs:
@@ -101,6 +107,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
   - local: '/templates/plan.yml'
@@ -112,6 +119,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       plan_name: $[[ inputs.plan_name ]]
@@ -126,6 +134,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       no_plan: false
diff --git a/templates/validate-plan.yml b/templates/validate-plan.yml
index 69fe8cc..fd1eb1f 100644
--- a/templates/validate-plan.yml
+++ b/templates/validate-plan.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -81,6 +86,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
   - local: '/templates/validate.yml'
     inputs:
@@ -91,6 +97,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
   - local: '/templates/plan.yml'
@@ -102,6 +109,7 @@ include:
       opentofu_version: $[[ inputs.opentofu_version ]]
       image_registry_base: $[[ inputs.image_registry_base ]]
       image_name: $[[ inputs.image_name ]]
+      image_digest: $[[ inputs.image_digest ]]
       root_dir: $[[ inputs.root_dir ]]
       state_name: $[[ inputs.state_name ]]
       artifacts_access: $[[ inputs.artifacts_access ]]
diff --git a/templates/validate.yml b/templates/validate.yml
index fb096b2..2f0306f 100644
--- a/templates/validate.yml
+++ b/templates/validate.yml
@@ -58,6 +58,11 @@ spec:
       default: 'gitlab-opentofu'
       description: 'Image name for the job images. Hosted under `image_registry_base`.'
 
+    image_digest:
+      default: ''
+      regex: '^(@sha256:[a-z0-9]{64})?$'
+      description: 'Image digest of the image you want to use. The format must be `@<image_digest>`, e.g. `@sha256:abc..`, see regex of this input. Please consult the release page at https://gitlab.com/components/opentofu/-/releases to obtain the image digests.'
+
     # Configuration
     root_dir:
       default: ${CI_PROJECT_DIR}
@@ -86,6 +91,6 @@ spec:
     TF_STATE_NAME: $[[ inputs.state_name ]]
     TF_IGNORE_INIT_ERRORS: 'true' # Tofu can report errors which might be the reason init failed.
   image:
-    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]'
+    name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
     - gitlab-tofu validate
-- 
GitLab