diff --git a/README.md b/README.md
index a61c903ceca2a2fa6a5d1f5bd4b183af8b533473..8b0fd8f74250a651aebaa836a4116d07cdc089ed 100644
--- a/README.md
+++ b/README.md
@@ -322,6 +322,7 @@ The following environment variables are respected by the `gitlab-tofu` script:
 - `GITLAB_TOFU_AUTO_ENCRYPTION`: if set to true, enables auto state and plan encryption. Defaults to `false`.
 - `GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE`: the passphrase to use for state and plan encryption. Required if `GITLAB_TOFU_AUTO_ENCRYPTION` is true.
 - `GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED_ENABLED`: if set to true, enables a fallback for state and plan encryption to migrate unencrypted plans and states to encrypted ones. Defaults to `false`.
+- `GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.
 
 #### Respected OpenTofu Environment Variables
 
diff --git a/src/gitlab-tofu.sh b/src/gitlab-tofu.sh
index 4b98d618f4a3d9deba7cf0eaa8d0535797a34e18..408e43c2a4e4897d562ea90ee0771f849fdcba87 100644
--- a/src/gitlab-tofu.sh
+++ b/src/gitlab-tofu.sh
@@ -30,6 +30,7 @@
 # - `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.
 #
 # #### Respected OpenTofu Environment Variables
 #
@@ -199,6 +200,7 @@ plan_jq_filter='
     "delete":(map(select(.=="delete")) | length)
   }
 '
+allow_developer_role=${GITLAB_TOFU_ALLOW_DEVELOPER_ROLE:-false}
 
 # auto encryption related variables
 auto_encryption_enabled=${GITLAB_TOFU_AUTO_ENCRYPTION:-false}
@@ -389,6 +391,9 @@ if [ $sourced -eq 0 ]; then
       if $plan_with_detailed_exitcode; then
         plan_args='-detailed-exitcode'
       fi
+      if $allow_developer_role; then
+        plan_args="$plan_args -lock=false"
+      fi
 
       $should_do_implicit_init && tofu_init
       # shellcheck disable=SC2086
diff --git a/templates/full-pipeline.yml b/templates/full-pipeline.yml
index 4830ac373be14a71196d2a2249db5f30590c6171..0f4b27f44af2038de1987992579c7706ad0eb22d 100644
--- a/templates/full-pipeline.yml
+++ b/templates/full-pipeline.yml
@@ -201,6 +201,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.'
+    allow_developer_role_to_plan:
+      default: false
+      type: boolean
+      description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.'
 
 ---
 
@@ -284,6 +288,7 @@ include:
       auto_encryption: $[[ inputs.auto_encryption ]]
       auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
       auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+      allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]]
   - local: '/templates/apply.yml'
     rules:
       - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"'
@@ -409,6 +414,7 @@ stages:
           auto_encryption: $[[ inputs.auto_encryption ]]
           auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
           auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+          allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]]
           trigger_in_child_pipeline: false
     forward:
       yaml_variables: true
diff --git a/templates/job-templates.yml b/templates/job-templates.yml
index 5657e959f72e3d4d62f40ef1f0edf96a0d6648ae..b64ecd4a599e633ee1801e45767052f413491617 100644
--- a/templates/job-templates.yml
+++ b/templates/job-templates.yml
@@ -106,6 +106,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.'
+    allow_developer_role_to_plan:
+      default: false
+      type: boolean
+      description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.'
 
 ---
 
@@ -185,6 +189,7 @@ include:
       auto_encryption: $[[ inputs.auto_encryption ]]
       auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
       auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+      allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]]
   - local: '/templates/apply.yml'
     inputs:
       as: '$[[ inputs.job_name_prefix ]]apply'
diff --git a/templates/plan.yml b/templates/plan.yml
index 02461eea9ca6b22c1699375f97775a84361ebd52..69dae9552cf5686951750f45e55ebf6131e0fe9b 100644
--- a/templates/plan.yml
+++ b/templates/plan.yml
@@ -112,6 +112,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.'
+    allow_developer_role:
+      default: false
+      type: boolean
+      description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.'
 
 ---
 
@@ -177,6 +181,7 @@ spec:
     GITLAB_TOFU_AUTO_ENCRYPTION: '$[[ inputs.auto_encryption ]]'
     GITLAB_TOFU_AUTO_ENCRYPTION_PASSPHRASE: '$[[ inputs.auto_encryption_passphrase ]]'
     GITLAB_TOFU_AUTO_ENCRYPTION_ENABLE_MIGRATION_FROM_UNENCRYPTED: '$[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]'
+    GITLAB_TOFU_ALLOW_DEVELOPER_ROLE: '$[[ inputs.allow_developer_role ]]'
   image:
     name: '$[[ inputs.image_registry_base ]]/$[[ inputs.image_name ]]:$[[ inputs.version ]]-opentofu$[[ inputs.opentofu_version ]]-$[[ inputs.base_os ]]$[[ inputs.image_digest ]]'
   script:
diff --git a/templates/validate-plan-apply.yml b/templates/validate-plan-apply.yml
index a643819de7464a402d1f815ad753b8841895fcff..b89116f3e3e823da6b880ce70c3cb361ba1ee86f 100644
--- a/templates/validate-plan-apply.yml
+++ b/templates/validate-plan-apply.yml
@@ -165,6 +165,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.'
+    allow_developer_role_to_plan:
+      default: false
+      type: boolean
+      description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.'
 
 ---
 
@@ -227,6 +231,7 @@ include:
       auto_encryption: $[[ inputs.auto_encryption ]]
       auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
       auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+      allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]]
   - local: '/templates/apply.yml'
     rules:
       - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"'
@@ -304,6 +309,7 @@ stages:
           auto_encryption: $[[ inputs.auto_encryption ]]
           auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
           auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+          allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]]
           trigger_in_child_pipeline: false
     forward:
       yaml_variables: true
diff --git a/templates/validate-plan-destroy.yml b/templates/validate-plan-destroy.yml
index 76f942e33717c351582814f4f1a2cf66c40ad7f7..5fc8a720faf228badc58953182f431c322cbceca 100644
--- a/templates/validate-plan-destroy.yml
+++ b/templates/validate-plan-destroy.yml
@@ -171,6 +171,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.'
+    allow_developer_role_to_plan:
+      default: false
+      type: boolean
+      description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.'
 
 ---
 
@@ -234,6 +238,7 @@ include:
       auto_encryption: $[[ inputs.auto_encryption ]]
       auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
       auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+      allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]]
   - local: '/templates/destroy.yml'
     rules:
       - if: '"$[[ inputs.trigger_in_child_pipeline ]]" == "false"'
@@ -337,6 +342,7 @@ stages:
           auto_encryption: $[[ inputs.auto_encryption ]]
           auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
           auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+          allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]]
           trigger_in_child_pipeline: false
     forward:
       yaml_variables: true
diff --git a/templates/validate-plan.yml b/templates/validate-plan.yml
index 99fd4652bf6673802617529a7a663831e15bf274..8262da9c6d010d2d29211b2c14ec788834f65b61 100644
--- a/templates/validate-plan.yml
+++ b/templates/validate-plan.yml
@@ -149,6 +149,10 @@ spec:
       default: false
       type: boolean
       description: 'Whether to setup automatic state and plan encryption for currently unencrypted state. This is only temporarily useful when migrating from an unencrypted state.'
+    allow_developer_role_to_plan:
+      default: false
+      type: boolean
+      description: 'Users with the Developer role are not able to lock the state. Thus a regular `tofu plan` fails. When set to `true` a `-lock=false` is passed to plan.'
 
 ---
 
@@ -211,6 +215,7 @@ include:
       auto_encryption: $[[ inputs.auto_encryption ]]
       auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
       auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+      allow_developer_role: $[[ inputs.allow_developer_role_to_plan ]]
 
 
 # NOTE: the following configuration is only used if `trigger_in_child_pipeline` is enabled.
@@ -264,6 +269,7 @@ stages:
           auto_encryption: $[[ inputs.auto_encryption ]]
           auto_encryption_passphrase: $[[ inputs.auto_encryption_passphrase ]]
           auto_encryption_enable_migration_from_unencrypted: $[[ inputs.auto_encryption_enable_migration_from_unencrypted ]]
+          allow_developer_role_to_plan: $[[ inputs.allow_developer_role_to_plan ]]
           trigger_in_child_pipeline: false
     forward:
       yaml_variables: true