From 5b1abfceec01e7e393376b00466d83498a75ce69 Mon Sep 17 00:00:00 2001 From: Gonzalo Rojas Date: Fri, 5 Jun 2026 14:03:26 -0300 Subject: [PATCH 1/5] fix(azure/cloud): require authentication credentials as variables Previously the module hardcoded `authentication = {}` and relied on users to fill credentials via the UI after apply. This caused a chicken-and-egg: the UI does not display providers with an empty authentication block, so users had no way to complete the setup. Add required variables for client_id, client_secret, subscription_id, and tenant_id. Remove the `lifecycle { ignore_changes = [attributes] }` block since Terraform now owns the full attribute set. Co-Authored-By: Claude Sonnet 4.6 --- nullplatform/cloud/azure/cloud/main.tf | 10 ++++++---- nullplatform/cloud/azure/cloud/variables.tf | 22 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/nullplatform/cloud/azure/cloud/main.tf b/nullplatform/cloud/azure/cloud/main.tf index bfb0682c..0203ad15 100644 --- a/nullplatform/cloud/azure/cloud/main.tf +++ b/nullplatform/cloud/azure/cloud/main.tf @@ -6,7 +6,12 @@ resource "nullplatform_provider_config" "azure" { dimensions = var.dimensions attributes = jsonencode({ - authentication = {}, + authentication = { + client_id = var.client_id + client_secret = var.client_secret + subscription_id = var.subscription_id + tenant_id = var.tenant_id + }, networking = { application_domain = var.application_domain, domain_name = var.domain_name, @@ -16,7 +21,4 @@ resource "nullplatform_provider_config" "azure" { private_dns_zone_resource_group_name = var.private_dns_resource_group_name } }) - lifecycle { - ignore_changes = [attributes] - } } diff --git a/nullplatform/cloud/azure/cloud/variables.tf b/nullplatform/cloud/azure/cloud/variables.tf index a719859c..a2857f3b 100644 --- a/nullplatform/cloud/azure/cloud/variables.tf +++ b/nullplatform/cloud/azure/cloud/variables.tf @@ -3,6 +3,28 @@ variable "nrn" { description = "The NRN of your nullplatform account" } +variable "client_id" { + type = string + description = "Azure Service Principal client ID" + sensitive = true +} + +variable "client_secret" { + type = string + description = "Azure Service Principal client secret" + sensitive = true +} + +variable "subscription_id" { + type = string + description = "Azure subscription ID" +} + +variable "tenant_id" { + type = string + description = "Azure Active Directory tenant ID" +} + variable "domain_name" { description = "The domain name to be used" type = string From d77c9528b0d2c65b571c62a15b96393c0b955ea9 Mon Sep 17 00:00:00 2001 From: Gonzalo Rojas Date: Fri, 5 Jun 2026 14:39:21 -0300 Subject: [PATCH 2/5] test(azure/cloud): add required authentication variables to test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new client_id, client_secret, subscription_id and tenant_id variables are required — add dummy values to the test fixtures so the plan succeeds. Also add assertions to verify authentication fields are present in attributes. Co-Authored-By: Claude Sonnet 4.6 --- .../azure/cloud/tests/azure_cloud.tftest.hcl | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl b/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl index 1496e78c..3ae008ed 100644 --- a/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl +++ b/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl @@ -4,6 +4,10 @@ variables { nrn = "organization=myorg:account=myaccount" azure_resource_group_name = "myorg-rg" private_dns_resource_group_name = "myorg-dns-rg" + client_id = "11111111-0000-0000-0000-000000000001" + client_secret = "dummy-secret-for-testing-purposes-only!" + subscription_id = "22222222-0000-0000-0000-000000000002" + tenant_id = "33333333-0000-0000-0000-000000000003" } run "azure_provider_type" { @@ -34,6 +38,25 @@ run "attributes_contain_resource_groups" { } } +run "attributes_contain_authentication" { + command = plan + + assert { + condition = strcontains(nullplatform_provider_config.azure.attributes, "11111111-0000-0000-0000-000000000001") + error_message = "Attributes should contain the client_id" + } + + assert { + condition = strcontains(nullplatform_provider_config.azure.attributes, "22222222-0000-0000-0000-000000000002") + error_message = "Attributes should contain the subscription_id" + } + + assert { + condition = strcontains(nullplatform_provider_config.azure.attributes, "33333333-0000-0000-0000-000000000003") + error_message = "Attributes should contain the tenant_id" + } +} + run "with_domain_name" { command = plan From def24ffc3ce451f55ab0050f71ec4021564d169e Mon Sep 17 00:00:00 2001 From: Gonzalo Rojas Date: Fri, 5 Jun 2026 14:41:20 -0300 Subject: [PATCH 3/5] fix(azure/cloud): make authentication variables optional with parent inheritance When authentication variables are omitted (default = null), the attributes block sends authentication = {} which triggers inheritance from a parent cloud provider at org or account scope. When provided, Terraform manages the credentials explicitly. This replaces the previous lifecycle { ignore_changes = [attributes] } workaround: Terraform now correctly owns what it sets and inherits the rest from the NullPlatform provider hierarchy. Co-Authored-By: Claude Sonnet 4.6 --- nullplatform/cloud/azure/cloud/main.tf | 10 ++++--- .../azure/cloud/tests/azure_cloud.tftest.hcl | 28 +++++++++++++------ nullplatform/cloud/azure/cloud/variables.tf | 12 +++++--- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/nullplatform/cloud/azure/cloud/main.tf b/nullplatform/cloud/azure/cloud/main.tf index 0203ad15..48a81b40 100644 --- a/nullplatform/cloud/azure/cloud/main.tf +++ b/nullplatform/cloud/azure/cloud/main.tf @@ -7,10 +7,12 @@ resource "nullplatform_provider_config" "azure" { attributes = jsonencode({ authentication = { - client_id = var.client_id - client_secret = var.client_secret - subscription_id = var.subscription_id - tenant_id = var.tenant_id + for k, v in { + client_id = var.client_id + client_secret = var.client_secret + subscription_id = var.subscription_id + tenant_id = var.tenant_id + } : k => v if v != null }, networking = { application_domain = var.application_domain, diff --git a/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl b/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl index 3ae008ed..cd17d22a 100644 --- a/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl +++ b/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl @@ -4,10 +4,6 @@ variables { nrn = "organization=myorg:account=myaccount" azure_resource_group_name = "myorg-rg" private_dns_resource_group_name = "myorg-dns-rg" - client_id = "11111111-0000-0000-0000-000000000001" - client_secret = "dummy-secret-for-testing-purposes-only!" - subscription_id = "22222222-0000-0000-0000-000000000002" - tenant_id = "33333333-0000-0000-0000-000000000003" } run "azure_provider_type" { @@ -38,22 +34,38 @@ run "attributes_contain_resource_groups" { } } -run "attributes_contain_authentication" { +run "with_authentication_credentials" { command = plan + variables { + client_id = "11111111-0000-0000-0000-000000000001" + client_secret = "dummy-secret-for-testing-purposes-only!" + subscription_id = "22222222-0000-0000-0000-000000000002" + tenant_id = "33333333-0000-0000-0000-000000000003" + } + assert { condition = strcontains(nullplatform_provider_config.azure.attributes, "11111111-0000-0000-0000-000000000001") - error_message = "Attributes should contain the client_id" + error_message = "Attributes should contain the client_id when provided" } assert { condition = strcontains(nullplatform_provider_config.azure.attributes, "22222222-0000-0000-0000-000000000002") - error_message = "Attributes should contain the subscription_id" + error_message = "Attributes should contain the subscription_id when provided" } assert { condition = strcontains(nullplatform_provider_config.azure.attributes, "33333333-0000-0000-0000-000000000003") - error_message = "Attributes should contain the tenant_id" + error_message = "Attributes should contain the tenant_id when provided" + } +} + +run "without_authentication_credentials" { + command = plan + + assert { + condition = strcontains(nullplatform_provider_config.azure.attributes, "\"authentication\":{}") || strcontains(nullplatform_provider_config.azure.attributes, "\"authentication\": {}") + error_message = "Authentication block should be empty when no credentials provided (inherits from parent)" } } diff --git a/nullplatform/cloud/azure/cloud/variables.tf b/nullplatform/cloud/azure/cloud/variables.tf index a2857f3b..e47c0393 100644 --- a/nullplatform/cloud/azure/cloud/variables.tf +++ b/nullplatform/cloud/azure/cloud/variables.tf @@ -5,24 +5,28 @@ variable "nrn" { variable "client_id" { type = string - description = "Azure Service Principal client ID" + description = "Azure Service Principal client ID. If omitted, inherits from a parent cloud provider." sensitive = true + default = null } variable "client_secret" { type = string - description = "Azure Service Principal client secret" + description = "Azure Service Principal client secret. If omitted, inherits from a parent cloud provider." sensitive = true + default = null } variable "subscription_id" { type = string - description = "Azure subscription ID" + description = "Azure subscription ID. If omitted, inherits from a parent cloud provider." + default = null } variable "tenant_id" { type = string - description = "Azure Active Directory tenant ID" + description = "Azure Active Directory tenant ID. If omitted, inherits from a parent cloud provider." + default = null } variable "domain_name" { From abbf668e758047ab95cfd8f90cb1335764a99541 Mon Sep 17 00:00:00 2001 From: Gonzalo Rojas Date: Fri, 5 Jun 2026 14:42:56 -0300 Subject: [PATCH 4/5] fix(azure/cloud): restore lifecycle ignore_changes as safety net Prevents Terraform from overwriting authentication credentials set via UI or API after initial apply. Co-Authored-By: Claude Sonnet 4.6 --- nullplatform/cloud/azure/cloud/main.tf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nullplatform/cloud/azure/cloud/main.tf b/nullplatform/cloud/azure/cloud/main.tf index 48a81b40..5532744f 100644 --- a/nullplatform/cloud/azure/cloud/main.tf +++ b/nullplatform/cloud/azure/cloud/main.tf @@ -23,4 +23,7 @@ resource "nullplatform_provider_config" "azure" { private_dns_zone_resource_group_name = var.private_dns_resource_group_name } }) + lifecycle { + ignore_changes = [attributes] + } } From 4add9501bab88dccc13918cc1fe49da550ab3c58 Mon Sep 17 00:00:00 2001 From: Gonzalo Rojas Date: Fri, 5 Jun 2026 14:57:49 -0300 Subject: [PATCH 5/5] feat(azure/cloud): support passing authentication credentials as variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove sensitive = true from client_id (not a secret — appears in Azure logs and OAuth URLs; keeping it sensitive hides networking changes in plan output) - Add precondition enforcing all-or-none: caller must provide all four credentials (client_id, client_secret, subscription_id, tenant_id) or none — partial sets produce an inconsistent authentication block - Add test case verifying that partial credentials trigger the precondition Co-Authored-By: Claude Sonnet 4.6 --- nullplatform/cloud/azure/cloud/main.tf | 8 ++++++++ .../cloud/azure/cloud/tests/azure_cloud.tftest.hcl | 13 +++++++++++++ nullplatform/cloud/azure/cloud/variables.tf | 1 - 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/nullplatform/cloud/azure/cloud/main.tf b/nullplatform/cloud/azure/cloud/main.tf index 5532744f..d0d52b0a 100644 --- a/nullplatform/cloud/azure/cloud/main.tf +++ b/nullplatform/cloud/azure/cloud/main.tf @@ -24,6 +24,14 @@ resource "nullplatform_provider_config" "azure" { } }) lifecycle { + precondition { + condition = ( + (var.client_id == null) == (var.client_secret == null) && + (var.client_id == null) == (var.subscription_id == null) && + (var.client_id == null) == (var.tenant_id == null) + ) + error_message = "Authentication credentials must all be set or all be null (client_id, client_secret, subscription_id, tenant_id)." + } ignore_changes = [attributes] } } diff --git a/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl b/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl index cd17d22a..c482961e 100644 --- a/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl +++ b/nullplatform/cloud/azure/cloud/tests/azure_cloud.tftest.hcl @@ -69,6 +69,19 @@ run "without_authentication_credentials" { } } +run "partial_authentication_fails" { + command = plan + + variables { + client_id = "11111111-0000-0000-0000-000000000001" + # client_secret, subscription_id, tenant_id omitted intentionally + } + + expect_failures = [ + nullplatform_provider_config.azure, + ] +} + run "with_domain_name" { command = plan diff --git a/nullplatform/cloud/azure/cloud/variables.tf b/nullplatform/cloud/azure/cloud/variables.tf index e47c0393..c1fcc476 100644 --- a/nullplatform/cloud/azure/cloud/variables.tf +++ b/nullplatform/cloud/azure/cloud/variables.tf @@ -6,7 +6,6 @@ variable "nrn" { variable "client_id" { type = string description = "Azure Service Principal client ID. If omitted, inherits from a parent cloud provider." - sensitive = true default = null }