Skip to content

refactor: cross object reference for input variable validation #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 5, 2025
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ module "es_kubernetes_secret" {

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.9.0 |
| <a name="requirement_helm"></a> [helm](#requirement\_helm) | >= 2.11.0, < 3.0.0 |
| <a name="requirement_kubernetes"></a> [kubernetes](#requirement\_kubernetes) | >= 2.16.1, < 3.0.0 |

Expand Down
22 changes: 0 additions & 22 deletions examples/all-combined/importedcertificate.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,6 @@
# imported certificate for secrets manager
##################################################################

locals {

# validation for secrets manager region to be set for existing secrets manager instance
validate_imported_sm_region_cnd = var.imported_certificate_sm_id != null && var.imported_certificate_sm_region == null
validate_imported_sm_region_msg = "imported_certificate_sm_region must also be set when value given for imported_certificate_sm_id"
# tflint-ignore: terraform_unused_declarations
validate_imported_sm_region_chk = regex(
"^${local.validate_imported_sm_region_msg}$",
(!local.validate_imported_sm_region_cnd
? local.validate_imported_sm_region_msg
: ""))

validate_imported_sm_cnd = (var.imported_certificate_public_secret_id != null && var.imported_certificate_private_secret_id != null) && var.imported_certificate_sm_id == null
validate_imported_sm_msg = "If imported_certificate_public_secret_id and imported_certificate_private_secret_id to create an imported certificate also imported_certificate_sm_id must be set"
# tflint-ignore: terraform_unused_declarations
validate_imported_sm_chk = regex(
"^${local.validate_imported_sm_msg}$",
(!local.validate_imported_sm_cnd
? local.validate_imported_sm_msg
: ""))
}

# loading from Secrets Manager the three components (private key, intermediate and public cert) composing the imported certificate
data "ibm_sm_arbitrary_secret" "imported_certificate_intermediate" {
count = var.imported_certificate_intermediate_secret_id != null ? 1 : 0
Expand Down
21 changes: 0 additions & 21 deletions examples/all-combined/secretsmanager.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,6 @@
##############################################################################

locals {

# validation for secrets manager region to be set for existing secrets manager instance
validate_sm_region_cnd = var.existing_sm_instance_guid != null && var.existing_sm_instance_region == null
validate_sm_region_msg = "existing_sm_instance_region must also be set when value given for existing_sm_instance_guid."
# tflint-ignore: terraform_unused_declarations
validate_sm_region_chk = regex(
"^${local.validate_sm_region_msg}$",
(!local.validate_sm_region_cnd
? local.validate_sm_region_msg
: ""))

# validation for secrets manager crn to be set for existing secrets manager instance if using private service endpoints
validate_sm_crn_cnd = var.existing_sm_instance_guid != null && var.existing_sm_instance_crn == null && var.service_endpoints == "private"
validate_sm_crn_msg = "existing_sm_instance_crn must also be set when value given for existing_sm_instance_guid if service_endpoints is private."
# tflint-ignore: terraform_unused_declarations
validate_sm_crn_chk = regex(
"^${local.validate_sm_crn_msg}$",
(!local.validate_sm_crn_cnd
? local.validate_sm_crn_msg
: ""))

# setting the secrets manager resource id to use
sm_guid = var.existing_sm_instance_guid == null ? ibm_resource_instance.secrets_manager[0].guid : var.existing_sm_instance_guid

Expand Down
16 changes: 16 additions & 0 deletions examples/all-combined/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ variable "existing_sm_instance_guid" {
type = string
description = "Existing Secrets Manager GUID. If not provided a new instance will be provisioned"
default = null
validation {
condition = var.existing_sm_instance_guid != null ? var.existing_sm_instance_region != null : true
error_message = "existing_sm_instance_region must also be set when value given for existing_sm_instance_guid."
}
validation {
condition = var.existing_sm_instance_guid != null && var.service_endpoints == "private" ? var.existing_sm_instance_crn != null : true
error_message = "existing_sm_instance_crn must also be set when value given for existing_sm_instance_guid if service_endpoints is private."
}
}

variable "existing_sm_instance_crn" {
Expand Down Expand Up @@ -287,6 +295,14 @@ variable "imported_certificate_sm_id" {
type = string
default = null
description = "Secrets Manager instance id where the components for the intermediate certificate are stored"
validation {
condition = var.imported_certificate_sm_id != null ? var.imported_certificate_sm_region != null : true
error_message = "imported_certificate_sm_region must also be set when value given for imported_certificate_sm_id"
}
validation {
condition = (var.imported_certificate_public_secret_id != null && var.imported_certificate_private_secret_id != null) && var.imported_certificate_sm_id == null ? false : true
error_message = "If imported_certificate_public_secret_id and imported_certificate_private_secret_id to create an imported certificate also imported_certificate_sm_id must be set"
}
}

variable "imported_certificate_sm_region" {
Expand Down
2 changes: 1 addition & 1 deletion examples/all-combined/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.1.0"
required_version = ">= 1.9.0"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
Expand Down
10 changes: 0 additions & 10 deletions examples/basic/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,6 @@

locals {

# general
validate_sm_region_cnd = var.existing_sm_instance_guid != null && var.existing_sm_instance_region == null
validate_sm_region_msg = "existing_sm_instance_region must also be set when value given for existing_sm_instance_guid."
# tflint-ignore: terraform_unused_declarations
validate_sm_region_chk = regex(
"^${local.validate_sm_region_msg}$",
(!local.validate_sm_region_cnd
? local.validate_sm_region_msg
: ""))

sm_guid = var.existing_sm_instance_guid == null ? ibm_resource_instance.secrets_manager[0].guid : var.existing_sm_instance_guid


Expand Down
4 changes: 4 additions & 0 deletions examples/basic/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ variable "existing_sm_instance_guid" {
type = string
description = "Existing Secrets Manager GUID. If not provided a new instance will be provisioned"
default = null
validation {
condition = var.existing_sm_instance_guid != null ? var.existing_sm_instance_region != null : true
error_message = "existing_sm_instance_region must also be set when value given for existing_sm_instance_guid."
}
}

variable "existing_sm_instance_region" {
Expand Down
2 changes: 1 addition & 1 deletion examples/basic/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.0.0"
required_version = ">= 1.9.0"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
Expand Down
19 changes: 0 additions & 19 deletions examples/trusted-profiles-authentication/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,6 @@ locals {
secret_group_name = "${var.prefix}-sm-secret-group" #checkov:skip=CKV_SECRET_6
es_kubernetes_namespaces = ["${var.prefix}-tp-test-1", "${var.prefix}-tp-test-2"] # namespace to create the externalsecrets resources for secrets sync

validate_sm_region_cnd = var.existing_sm_instance_guid != null && var.existing_sm_instance_region == null
validate_sm_region_msg = "existing_sm_instance_region must also be set when value given for existing_sm_instance_guid."
# tflint-ignore: terraform_unused_declarations
validate_sm_region_chk = regex(
"^${local.validate_sm_region_msg}$",
(!local.validate_sm_region_cnd
? local.validate_sm_region_msg
: ""))

# validation for secrets manager crn to be set for existing secrets manager instance if using private service endpoints
validate_sm_crn_cnd = var.existing_sm_instance_guid != null && var.existing_sm_instance_crn == null && var.service_endpoints == "private"
validate_sm_crn_msg = "existing_sm_instance_crn must also be set when value given for existing_sm_instance_guid if service_endpoints is private."
# tflint-ignore: terraform_unused_declarations
validate_sm_crn_chk = regex(
"^${local.validate_sm_crn_msg}$",
(!local.validate_sm_crn_cnd
? local.validate_sm_crn_msg
: ""))

sm_guid = var.existing_sm_instance_guid == null ? ibm_resource_instance.secrets_manager[0].guid : var.existing_sm_instance_guid
# if service_endpoints is not private the crn for SM is not needed because of VPE creation is not needed
sm_crn = var.existing_sm_instance_crn == null ? (var.service_endpoints == "private" ? ibm_resource_instance.secrets_manager[0].crn : "") : var.existing_sm_instance_crn
Expand Down
8 changes: 8 additions & 0 deletions examples/trusted-profiles-authentication/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ variable "existing_sm_instance_guid" {
type = string
description = "Existing Secrets Manager GUID. If not provided a new instance will be provisioned"
default = null
validation {
condition = var.existing_sm_instance_guid != null ? var.existing_sm_instance_region != null : true
error_message = "existing_sm_instance_region must also be set when value given for existing_sm_instance_guid."
}
validation {
condition = var.existing_sm_instance_guid != null && var.service_endpoints == "private" ? var.existing_sm_instance_crn != null : true
error_message = "existing_sm_instance_crn must also be set when value given for existing_sm_instance_guid if service_endpoints is private."
}
}

variable "existing_sm_instance_crn" {
Expand Down
2 changes: 1 addition & 1 deletion examples/trusted-profiles-authentication/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.0.0"
required_version = ">= 1.9.0"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
Expand Down
2 changes: 1 addition & 1 deletion modules/eso-clusterstore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ For more information about Trusted Profiles refer to the IBM Cloud documentation

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.9.0 |
| <a name="requirement_helm"></a> [helm](#requirement\_helm) | >= 2.8.0 |
| <a name="requirement_kubernetes"></a> [kubernetes](#requirement\_kubernetes) | >= 2.16.1, <3.0.0 |

Expand Down
20 changes: 0 additions & 20 deletions modules/eso-clusterstore/main.tf
Original file line number Diff line number Diff line change
@@ -1,23 +1,3 @@
locals {
# preliminary authentication validation - one of clusterstore_secret_apikey and clusterstore_trusted_profile_name must be valid
auth_validate_condition = var.clusterstore_secret_apikey == null && var.clusterstore_trusted_profile_name == null
auth_clusterstore_msg = "One of the variables clusterstore_secret_apikey and clusterstore_trusted_profile_name must be provided, cannot be both set to null"
# tflint-ignore: terraform_unused_declarations
auth_validate_check = regex("^${local.auth_clusterstore_msg}$", (!local.auth_validate_condition ? local.auth_clusterstore_msg : ""))

# auth is apikey so the variable clusterstore_secret_apikey cannot be null
api_key_clusterstore_validate_condition = var.eso_authentication == "api_key" && var.clusterstore_secret_apikey == null
api_key_clusterstore_msg = "API Key authentication is enabled and scope for store is cluster, therefore clusterstore_secret_apikey must be provided."
# tflint-ignore: terraform_unused_declarations
api_key_clusterstore_validate_check = regex("^${local.api_key_clusterstore_msg}$", (!local.api_key_clusterstore_validate_condition ? local.api_key_clusterstore_msg : ""))

# auth is trustedprofile so the variable clusterstore_trusted_profile_name cannot be null
tp_clusterstore_validate_condition = var.eso_authentication == "trusted_profile" && var.clusterstore_trusted_profile_name == null
tp_clusterstore_msg = "Trusted profile authentication is enabled, therefore clusterstore_trusted_profile_name must be provided."
# tflint-ignore: terraform_unused_declarations
tp_clusterstore_validate_check = regex("^${local.tp_clusterstore_msg}$", (!local.tp_clusterstore_validate_condition ? local.tp_clusterstore_msg : ""))
}

locals {
helm_raw_chart_name = "raw"
helm_raw_chart_version = "0.2.5"
Expand Down
12 changes: 12 additions & 0 deletions modules/eso-clusterstore/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ variable "eso_authentication" {
condition = contains(["api_key", "trusted_profile"], var.eso_authentication)
error_message = "Authentication mode allowed are api_key or/and trusted_profile."
}
validation {
condition = var.eso_authentication == "trusted_profile" ? var.clusterstore_trusted_profile_name != null : true
error_message = "Trusted profile authentication is enabled, therefore clusterstore_trusted_profile_name must be provided."
}
}

variable "clusterstore_secret_name" {
Expand All @@ -56,6 +60,14 @@ variable "clusterstore_secret_apikey" {
description = "APIkey to be configured in the clusterstore_secret_name secret in the ESO clusterstore. One between clusterstore_secret_apikey and clusterstore_trusted_profile_name must be filled"
sensitive = true
default = null
validation {
condition = var.eso_authentication == "api_key" ? var.clusterstore_secret_apikey != null : true
error_message = "API Key authentication is enabled and scope for store is cluster, therefore clusterstore_secret_apikey must be provided."
}
validation {
condition = var.clusterstore_secret_apikey != null || var.clusterstore_trusted_profile_name != null
error_message = "One of the variables clusterstore_secret_apikey and clusterstore_trusted_profile_name must be provided, cannot be both set to null"
}
}

####### trusted profile
Expand Down
2 changes: 1 addition & 1 deletion modules/eso-clusterstore/version.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.0.0"
required_version = ">= 1.9.0"
required_providers {
# Use "greater than or equal to" range in modules
kubernetes = {
Expand Down
2 changes: 1 addition & 1 deletion modules/eso-external-secret/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ For more information about ExternalSecrets on ESO please refer to the ESO docume

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.9.0 |
| <a name="requirement_helm"></a> [helm](#requirement\_helm) | >= 2.8.0 |

### Modules
Expand Down
30 changes: 0 additions & 30 deletions modules/eso-external-secret/main.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
locals {
# Validation approach based on https://stackoverflow.com/a/66682419
validate_condition_secret = var.es_kubernetes_secret_data_key == null && (var.es_kubernetes_secret_type == "opaque" && (var.sm_secret_type == "arbitrary" || var.sm_secret_type == "iam_credentials")) # checkov:skip=CKV_SECRET_6: does not require high entropy string as is static value
validate_msg_secret = "A value for 'es_kubernetes_secret_data_key' must be passed when 'es_kubernetes_secret_type = opaque' and 'sm_secret_type' is either 'arbitrary' or 'iam_credentials'"
# tflint-ignore: terraform_unused_declarations
validate_check_secret = regex("^${local.validate_msg_secret}$", (!local.validate_condition_secret ? local.validate_msg_secret : ""))

# reloader annotation
reloader_annotation = var.reloader_watching ? "'reloader.stakater.com/auto': 'true'" : "{}"
}
Expand All @@ -17,18 +11,6 @@ locals {
# dockerjsonconfig secrets chain flag
is_dockerjsonconfig_chain = length(var.es_container_registry_secrets_chain) > 0 ? true : false

# validation for dockerjsonconfig secrets chain -> if it is a chain the kube secret type must be dockerconfigjson and sm secret types iam_credentials or trusted_profile
validate_condition_chain = local.is_dockerjsonconfig_chain == true && (var.es_kubernetes_secret_type != "dockerconfigjson" || (var.sm_secret_type != "iam_credentials" && var.sm_secret_type != "trusted_profile")) # checkov:skip=CKV_SECRET_6: does not require high entropy string as is static value
validate_msg_chain = "If the externalsecret is expected to generate a dockerjsonconfig secrets chain the only supported value for es_kubernetes_secret_type is dockerconfigjson and for sm_secret_type is iam_credentials or trusted_profile"
# tflint-ignore: terraform_unused_declarations
validate_check_chain = regex("^${local.validate_msg_chain}$", (!local.validate_condition_chain ? local.validate_msg_chain : ""))

# validation of sm_secret_id => it can be null only in the case of a dockerjsonconfig chain (secret_ids will be stored )
validate_condition_sm_secret_id = var.sm_secret_id == null && local.is_dockerjsonconfig_chain == false
validate_msg_sm_secret_id = "The input variable sm_secret_id can be null only a dockerjsonconfig secrets chain is going to be created"
# tflint-ignore: terraform_unused_declarations
validate_check_sm_secret_id = regex("^${local.validate_msg_sm_secret_id}$", (!local.validate_condition_sm_secret_id ? local.validate_msg_sm_secret_id : ""))

# for certificate secrets public_cert and private_cert the id is the last part of the sm_secret_sm
cert_remoteref_key = local.is_certificate ? "${var.sm_secret_type}/${var.sm_secret_id}" : ""
# defining the template data structure according to the type of certificate
Expand Down Expand Up @@ -112,18 +94,6 @@ locals {
# key-value secret management
is_kv = can(regex("^kv$", var.sm_secret_type))

# validation for key-value secret type
validate_condition_kv_secret = local.is_kv && var.sm_kv_keyid != null && var.sm_kv_keypath != null
validate_msg_kv_secret = "For key-value secrets only one of input variables 'sm_kv_keyid' or 'sm_kv_keypath' can be set."
# tflint-ignore: terraform_unused_declarations
validate_check_kv_secret = regex("^${local.validate_msg_kv_secret}$", (!local.validate_condition_kv_secret ? local.validate_msg_kv_secret : ""))

# second validation for key-value secret type - allowing only opaque for es_kubernetes_secret_type if sm_secret_type is kv
validate_condition_kv_kube_type = local.is_kv && var.es_kubernetes_secret_type != "opaque"
validate_msg_kv_kube_type = "For key-value secrets-manager secrets types es_kubernetes_secret_type cannot be different than opaque - found ${var.es_kubernetes_secret_type}"
# tflint-ignore: terraform_unused_declarations
validate_check_kv_kube_type = regex("^${local.validate_msg_kv_kube_type}$", (!local.validate_condition_kv_kube_type ? local.validate_msg_kv_kube_type : ""))

# setting up the remoteref property for kv
kv_remoteref_property = var.sm_kv_keyid != null ? var.sm_kv_keyid : (var.sm_kv_keypath != null ? var.sm_kv_keypath : "")

Expand Down
Loading