From 3ba6d5eb122ab5f9eb5a303bf473feac9ffe2848 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Mon, 16 Mar 2026 23:51:31 -0300 Subject: [PATCH 1/3] Add support for scope configurations in scope-definition module --- modules/nullplatform/scope-definition/main.tf | 58 ++++++++++++++----- .../nullplatform/scope-definition/outputs.tf | 10 ++++ .../scope-definition/variables.tf | 16 ++++- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/modules/nullplatform/scope-definition/main.tf b/modules/nullplatform/scope-definition/main.tf index 1d9dac8..bddf673 100644 --- a/modules/nullplatform/scope-definition/main.tf +++ b/modules/nullplatform/scope-definition/main.tf @@ -7,6 +7,12 @@ data "http" "service_spec_template" { url = "${var.github_repo_url}/raw/${var.github_ref}/${var.github_scope_path}/specs/service-spec.json.tpl" } +# Fetch scope configuration template (optional - may not exist for all scopes) +data "http" "scope_configuration_template" { + count = var.fetch_scope_configuration ? 1 : 0 + url = "${var.github_repo_url}/raw/${var.github_ref}/${var.github_scope_path}/specs/scope-configuration.json.tpl" +} + # Fetch action specification templates data "http" "action_templates" { for_each = toset(var.action_spec_names) @@ -18,22 +24,22 @@ data "http" "action_templates" { ################################################################################ locals { - # Process the template by replacing the template variables - # replace is done because some old templates contain gomplate placeholders - service_spec_rendered = replace( - data.http.service_spec_template.response_body, - "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", - "\"${var.nrn}\"" - ) - service_spec_parsed = jsondecode(local.service_spec_rendered) + # Process the template by replacing the template variables + # replace is done because some old templates contain gomplate placeholders + service_spec_rendered = replace( + data.http.service_spec_template.response_body, + "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", + "\"${var.nrn}\"" + ) + service_spec_parsed = jsondecode(local.service_spec_rendered) } # Create service specification resource "nullplatform_service_specification" "from_template" { name = local.service_spec_parsed.name visible_to = [var.nrn] - type = local.service_spec_parsed.type - attributes = jsonencode(local.service_spec_parsed.attributes) + type = local.service_spec_parsed.type + attributes = jsonencode(local.service_spec_parsed.attributes) use_default_actions = local.service_spec_parsed.use_default_actions selectors { @@ -56,6 +62,32 @@ locals { } } +################################################################################ +# Step 2.5: Process Scope Configuration (optional) +################################################################################ + +locals { + # Replace the NRN gomplate placeholder with the organization_nrn variable + scope_configuration_rendered = var.fetch_scope_configuration ? replace( + data.http.scope_configuration_template[0].response_body, + "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", + "\"${var.organization_nrn}\"" + ) : "{}" + scope_configuration = var.fetch_scope_configuration ? jsondecode(local.scope_configuration_rendered) : null +} + +resource "nullplatform_provider_specification" "from_scope_configuration" { + count = var.fetch_scope_configuration ? 1 : 0 + + name = local.scope_configuration.name + description = local.scope_configuration.description + category = local.scope_configuration.category + allow_dimensions = local.scope_configuration.allow_dimensions + nrn = var.organization_nrn + type = local.scope_configuration.type + spec_schema = jsonencode(local.scope_configuration.schema) +} + ################################################################################ # Step 3: Process and Create Scope Type ################################################################################ @@ -82,9 +114,9 @@ locals { action_specs_parsed = { for name in var.action_spec_names : name => jsondecode(replace( - data.http.action_templates[name].response_body, - "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", - "\"\"" + data.http.action_templates[name].response_body, + "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", + "\"\"" )) } } diff --git a/modules/nullplatform/scope-definition/outputs.tf b/modules/nullplatform/scope-definition/outputs.tf index d6bc51c..08e8ab1 100644 --- a/modules/nullplatform/scope-definition/outputs.tf +++ b/modules/nullplatform/scope-definition/outputs.tf @@ -49,4 +49,14 @@ output "scope_name" { output "scope_description" { value = var.scope_description description = "The name of the scope definition" +} + +output "scope_configuration" { + value = local.scope_configuration + description = "Parsed scope configuration from scope-configuration.json.tpl, or null if not fetched" +} + +output "provider_config_id" { + value = var.fetch_scope_configuration ? nullplatform_provider_config.from_scope_configuration[0].id : null + description = "The ID of the created provider config, or null if scope configuration was not fetched" } \ No newline at end of file diff --git a/modules/nullplatform/scope-definition/variables.tf b/modules/nullplatform/scope-definition/variables.tf index 062dcaa..bb72d93 100644 --- a/modules/nullplatform/scope-definition/variables.tf +++ b/modules/nullplatform/scope-definition/variables.tf @@ -22,7 +22,7 @@ variable "github_ref" { variable "github_scope_path" { type = string default = "k8s" - description = "Path within the repository for the specific scope (e.g., k8s, ecs)" + description = "Path within the repository for the specific scope (e.g., k8s, ecs)" } variable "scope_name" { @@ -32,7 +32,7 @@ variable "scope_name" { variable "scope_description" { type = string description = "Description of the scope type to be created" -} +} variable "action_spec_names" { type = list(string) @@ -54,6 +54,18 @@ variable "action_spec_names" { description = "List of action specification template names to fetch and create" } +variable "organization_nrn" { + type = string + description = "Organization NRN used to replace the NRN placeholder in scope-configuration.json.tpl" + default = "" +} + +variable "fetch_scope_configuration" { + type = bool + default = false + description = "Whether to fetch and apply scope-configuration.json.tpl from the template repo. Set to true only if the file exists for this scope." +} + # NRN Patch Configuration variable "np_api_key" { type = string From ccf5bdcda52ba734e74fb97d5e5e53eaddb967a6 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Wed, 18 Mar 2026 09:45:14 -0300 Subject: [PATCH 2/3] Add support to define scope configurations --- .gitignore | 6 +- examples/scope-definition/main.tf | 56 +++++++++++++++++++ .../terraform.tfvars.json.example | 11 ++++ examples/scope-definition/variables.tf | 43 ++++++++++++++ modules/nullplatform/scope-definition/main.tf | 23 ++++---- .../nullplatform/scope-definition/outputs.tf | 6 +- .../scope-definition/variables.tf | 2 +- 7 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 examples/scope-definition/main.tf create mode 100644 examples/scope-definition/terraform.tfvars.json.example create mode 100644 examples/scope-definition/variables.tf diff --git a/.gitignore b/.gitignore index 4f8d1a8..c95322c 100644 --- a/.gitignore +++ b/.gitignore @@ -138,4 +138,8 @@ dist .terraform/ terraform.tfstate terraform.tfstate.backup -*.tfvars \ No newline at end of file +*.tfvars + +# Example secrets and lock files +examples/scope-definition/terraform.tfvars.json +examples/scope-definition/.terraform.lock.hcl \ No newline at end of file diff --git a/examples/scope-definition/main.tf b/examples/scope-definition/main.tf new file mode 100644 index 0000000..a966ca1 --- /dev/null +++ b/examples/scope-definition/main.tf @@ -0,0 +1,56 @@ +terraform { + required_providers { + nullplatform = { + source = "nullplatform/nullplatform" + } + http = { + source = "hashicorp/http" + } + } +} + +provider "nullplatform" { + api_key = var.np_api_key +} + +################################################################################ +# Example: scope-definition module +# +# This example shows how to use the scope-definition module to create a scope +# type, service specification, and action specifications from templates stored +# in a GitHub repository. +# +# Set create_scope_configuration = true when the scope repository contains a +# /specs/scope-configuration.json.tpl file. This will also create a +# nullplatform_provider_specification resource from the template. +################################################################################ + +module "scope_definition" { + source = "../../modules/nullplatform/scope-definition" + + nrn = var.nrn + organization_nrn = var.organization_nrn + + github_repo_url = var.github_repo_url + github_ref = var.github_ref + github_scope_path = var.github_scope_path + + scope_name = var.scope_name + scope_description = var.scope_description + + create_scope_configuration = var.create_scope_configuration + + np_api_key = var.np_api_key +} + +output "service_specification_id" { + value = module.scope_definition.service_specification_id +} + +output "scope_type_id" { + value = module.scope_definition.scope_type_id +} + +output "provider_specification_id" { + value = module.scope_definition.provider_specification_id +} diff --git a/examples/scope-definition/terraform.tfvars.json.example b/examples/scope-definition/terraform.tfvars.json.example new file mode 100644 index 0000000..98fb5d5 --- /dev/null +++ b/examples/scope-definition/terraform.tfvars.json.example @@ -0,0 +1,11 @@ +{ + "np_api_key": "your-nullplatform-api-key", + "nrn": "organization=1234567890:account=987654321", + "organization_nrn": "organization=1234567890", + "github_repo_url": "https://github.com/nullplatform/scopes-static-files", + "github_ref": "main", + "github_scope_path": "static-files", + "scope_name": "my-scope", + "scope_description": "My custom scope type", + "create_scope_configuration": false +} diff --git a/examples/scope-definition/variables.tf b/examples/scope-definition/variables.tf new file mode 100644 index 0000000..34fb915 --- /dev/null +++ b/examples/scope-definition/variables.tf @@ -0,0 +1,43 @@ +variable "np_api_key" { + type = string + sensitive = true +} + +variable "nrn" { + type = string + description = "Nullplatform Resource Name (organization:account format)" +} + +variable "organization_nrn" { + type = string + description = "Organization NRN used when create_scope_configuration is true" + default = "" +} + +variable "github_repo_url" { + type = string + default = "https://github.com/nullplatform/scopes-static-files" +} + +variable "github_ref" { + type = string + default = "main" +} + +variable "github_scope_path" { + type = string + default = "static-files" +} + +variable "scope_name" { + type = string +} + +variable "scope_description" { + type = string +} + +variable "create_scope_configuration" { + type = bool + default = false +} diff --git a/modules/nullplatform/scope-definition/main.tf b/modules/nullplatform/scope-definition/main.tf index bddf673..5237854 100644 --- a/modules/nullplatform/scope-definition/main.tf +++ b/modules/nullplatform/scope-definition/main.tf @@ -9,7 +9,7 @@ data "http" "service_spec_template" { # Fetch scope configuration template (optional - may not exist for all scopes) data "http" "scope_configuration_template" { - count = var.fetch_scope_configuration ? 1 : 0 + count = var.create_scope_configuration ? 1 : 0 url = "${var.github_repo_url}/raw/${var.github_ref}/${var.github_scope_path}/specs/scope-configuration.json.tpl" } @@ -68,24 +68,23 @@ locals { locals { # Replace the NRN gomplate placeholder with the organization_nrn variable - scope_configuration_rendered = var.fetch_scope_configuration ? replace( + scope_configuration_rendered = var.create_scope_configuration ? replace( data.http.scope_configuration_template[0].response_body, "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", "\"${var.organization_nrn}\"" ) : "{}" - scope_configuration = var.fetch_scope_configuration ? jsondecode(local.scope_configuration_rendered) : null + scope_configuration = var.create_scope_configuration ? jsondecode(local.scope_configuration_rendered) : null } resource "nullplatform_provider_specification" "from_scope_configuration" { - count = var.fetch_scope_configuration ? 1 : 0 + count = var.create_scope_configuration ? 1 : 0 - name = local.scope_configuration.name - description = local.scope_configuration.description - category = local.scope_configuration.category + name = local.scope_configuration.name + description = local.scope_configuration.description + category = local.scope_configuration.category allow_dimensions = local.scope_configuration.allow_dimensions - nrn = var.organization_nrn - type = local.scope_configuration.type - spec_schema = jsonencode(local.scope_configuration.schema) + visible_to = [var.organization_nrn] + schema = jsonencode(local.scope_configuration.schema) } ################################################################################ @@ -111,6 +110,7 @@ resource "nullplatform_scope_type" "from_template" { # Process action templates - direct JSON parsing (they don't contain template variables) # replace is done because some old templates contain gomplate placeholders locals { + # Only parse templates that returned a 200 — missing files return an HTML 404 page action_specs_parsed = { for name in var.action_spec_names : name => jsondecode(replace( @@ -118,12 +118,13 @@ locals { "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", "\"\"" )) + if data.http.action_templates[name].status_code == 200 } } # Create action specifications resource "nullplatform_action_specification" "from_templates" { - for_each = toset(var.action_spec_names) + for_each = toset(keys(local.action_specs_parsed)) depends_on = [nullplatform_service_specification.from_template] service_specification_id = local.service_specification_id diff --git a/modules/nullplatform/scope-definition/outputs.tf b/modules/nullplatform/scope-definition/outputs.tf index 08e8ab1..c702dd7 100644 --- a/modules/nullplatform/scope-definition/outputs.tf +++ b/modules/nullplatform/scope-definition/outputs.tf @@ -56,7 +56,7 @@ output "scope_configuration" { description = "Parsed scope configuration from scope-configuration.json.tpl, or null if not fetched" } -output "provider_config_id" { - value = var.fetch_scope_configuration ? nullplatform_provider_config.from_scope_configuration[0].id : null - description = "The ID of the created provider config, or null if scope configuration was not fetched" +output "provider_specification_id" { + value = var.create_scope_configuration ? nullplatform_provider_specification.from_scope_configuration[0].id : null + description = "The ID of the created provider specification, or null if scope configuration was not fetched" } \ No newline at end of file diff --git a/modules/nullplatform/scope-definition/variables.tf b/modules/nullplatform/scope-definition/variables.tf index bb72d93..9d90000 100644 --- a/modules/nullplatform/scope-definition/variables.tf +++ b/modules/nullplatform/scope-definition/variables.tf @@ -60,7 +60,7 @@ variable "organization_nrn" { default = "" } -variable "fetch_scope_configuration" { +variable "create_scope_configuration" { type = bool default = false description = "Whether to fetch and apply scope-configuration.json.tpl from the template repo. Set to true only if the file exists for this scope." From 1f66f5a94ddf3548456f0de566703524da2163b5 Mon Sep 17 00:00:00 2001 From: Federico Maleh Date: Wed, 18 Mar 2026 09:51:38 -0300 Subject: [PATCH 3/3] Fix terraform format --- modules/nullplatform/scope-definition/backend.tf | 8 ++++---- modules/nullplatform/scope-definition/main.tf | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/nullplatform/scope-definition/backend.tf b/modules/nullplatform/scope-definition/backend.tf index 8fda109..ebd7fc0 100644 --- a/modules/nullplatform/scope-definition/backend.tf +++ b/modules/nullplatform/scope-definition/backend.tf @@ -1,16 +1,16 @@ terraform { required_providers { nullplatform = { - source = "nullplatform/nullplatform" + source = "nullplatform/nullplatform" } http = { - source = "hashicorp/http" + source = "hashicorp/http" } external = { - source = "hashicorp/external" + source = "hashicorp/external" } null = { - source = "hashicorp/null" + source = "hashicorp/null" } } } diff --git a/modules/nullplatform/scope-definition/main.tf b/modules/nullplatform/scope-definition/main.tf index 5237854..ead8c77 100644 --- a/modules/nullplatform/scope-definition/main.tf +++ b/modules/nullplatform/scope-definition/main.tf @@ -84,7 +84,7 @@ resource "nullplatform_provider_specification" "from_scope_configuration" { category = local.scope_configuration.category allow_dimensions = local.scope_configuration.allow_dimensions visible_to = [var.organization_nrn] - schema = jsonencode(local.scope_configuration.schema) + schema = jsonencode(local.scope_configuration.schema) } ################################################################################