Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tofu-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
test-nullplatform-modules:
uses: nullplatform/actions-nullplatform/.github/workflows/tofu-test.yml@main
with:
modules: '["nullplatform/account", "nullplatform/agent", "nullplatform/api_key", "nullplatform/dimension", "nullplatform/dimension_value", "nullplatform/users", "nullplatform/metrics", "nullplatform/asset/docker_server", "nullplatform/cloud/azure/cloud", "nullplatform/cloud/gcp/cloud", "nullplatform/cloud/aws/cloud", "nullplatform/code_repository", "nullplatform/base", "nullplatform/agent"]'
modules: '["nullplatform/account", "nullplatform/agent", "nullplatform/api_key", "nullplatform/dimension", "nullplatform/dimension_value", "nullplatform/users", "nullplatform/metrics", "nullplatform/asset/docker_server", "nullplatform/cloud/azure/cloud", "nullplatform/cloud/gcp/cloud", "nullplatform/cloud/aws/cloud", "nullplatform/code_repository", "nullplatform/base", "nullplatform/agent", "nullplatform/scope_configuration", "nullplatform/parameter_storage_configuration"]'

test-container-orchestration-modules:
uses: nullplatform/actions-nullplatform/.github/workflows/tofu-test.yml@main
Expand Down
34 changes: 29 additions & 5 deletions nullplatform/parameter_storage_configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ resource "example_resource" "this" {

`type` selects the provider specification this configuration targets. Each type has its own payload and its own set of type-specific variables; adding a new type means adding it to the list below along with its variables.

### aws-secrets-manager (default)
### aws-secrets-manager

| Variable | Maps to |
|----------|---------|
| `applies_to` | `sensibility.applies_to` |
| `kms_key_id` | `setup.kms_key_id` |
| Variable | Maps to | Default |
|----------|---------|---------|
| `applies_to` | `sensibility.applies_to` | `["secret"]` |
| `kms_key_id` | `setup.kms_key_id` | `""` (aws/secretsmanager managed key) |

```hcl
module "parameter_storage_configuration" {
Expand All @@ -62,6 +62,30 @@ module "parameter_storage_configuration" {
}
```

### aws-parameter-store

Stores parameters in AWS SSM Parameter Store (spec declared by
`nullplatform/parameters-provider`, `aws-parameter-store-configuration.json.tpl`).

| Variable | Maps to | Default |
|----------|---------|---------|
| `applies_to` | `sensibility.applies_to` | `["non_secret"]` |
| `kms_key_id` | `setup.kms_key_id` | `""` (alias/aws/ssm managed key) |
| `tier` | `setup.tier` | `"Standard"` (also `Advanced`, `Intelligent-Tiering`) |

```hcl
module "parameter_storage_configuration" {
source = "git::https://github.com/nullplatform/tofu-modules.git//nullplatform/parameter_storage_configuration?ref=vX.Y.Z"

nrn = "your-nrn"
type = "aws-parameter-store"

applies_to = ["non_secret"]
tier = "Standard"
dimensions = { environment = "development" }
}
```

<!-- BEGIN_TF_DOCS -->
## Requirements

Expand Down
24 changes: 22 additions & 2 deletions nullplatform/parameter_storage_configuration/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,35 @@ locals {
kms_key_id = ""
}
}
"aws-parameter-store" = {
sensibility = {
applies_to = ["non_secret"]
}
setup = {
kms_key_id = ""
tier = "Standard"
}
}
}

# Per-type override, merged on top of type_defaults. Each entry indexes
# type_defaults by its own literal key (not the dynamic local.defaults) —
# this map is evaluated in full regardless of the selected type.
type_overrides = {
"aws-secrets-manager" = {
sensibility = merge(local.type_defaults["aws-secrets-manager"].sensibility, { applies_to = var.applies_to })
setup = merge(local.type_defaults["aws-secrets-manager"].setup, { kms_key_id = var.kms_key_id })
sensibility = merge(local.type_defaults["aws-secrets-manager"].sensibility, {
applies_to = coalesce(var.applies_to, local.type_defaults["aws-secrets-manager"].sensibility.applies_to)
})
setup = merge(local.type_defaults["aws-secrets-manager"].setup, { kms_key_id = var.kms_key_id })
}
"aws-parameter-store" = {
sensibility = merge(local.type_defaults["aws-parameter-store"].sensibility, {
applies_to = coalesce(var.applies_to, local.type_defaults["aws-parameter-store"].sensibility.applies_to)
})
setup = merge(local.type_defaults["aws-parameter-store"].setup, {
kms_key_id = var.kms_key_id
tier = coalesce(var.tier, local.type_defaults["aws-parameter-store"].setup.tier)
})
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
mock_provider "nullplatform" {}

variables {
nrn = "organization=1:account=2"
}

run "secrets_manager_defaults" {
command = plan

variables {
type = "aws-secrets-manager"
}

assert {
condition = nullplatform_provider_config.parameter_store_configuration.type == "aws-secrets-manager"
error_message = "type should be aws-secrets-manager"
}

assert {
condition = jsonencode(jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).sensibility.applies_to) == jsonencode(["secret"])
error_message = "aws-secrets-manager should default applies_to to [secret]"
}

assert {
condition = !can(jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).setup.tier)
error_message = "tier must not be sent for aws-secrets-manager"
}
}

run "parameter_store_defaults" {
command = plan

variables {
type = "aws-parameter-store"
}

assert {
condition = nullplatform_provider_config.parameter_store_configuration.type == "aws-parameter-store"
error_message = "type should be aws-parameter-store"
}

assert {
condition = jsonencode(jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).sensibility.applies_to) == jsonencode(["non_secret"])
error_message = "aws-parameter-store should default applies_to to [non_secret]"
}

assert {
condition = jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).setup.tier == "Standard"
error_message = "tier should default to Standard"
}

assert {
condition = jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).setup.kms_key_id == ""
error_message = "kms_key_id should default to empty"
}
}

run "parameter_store_overrides" {
command = plan

variables {
type = "aws-parameter-store"
tier = "Advanced"
kms_key_id = "alias/my-key"
applies_to = ["secret", "non_secret"]
}

assert {
condition = jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).setup.tier == "Advanced"
error_message = "tier override should be applied"
}

assert {
condition = jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).setup.kms_key_id == "alias/my-key"
error_message = "kms_key_id override should be applied"
}

assert {
condition = length(jsondecode(nullplatform_provider_config.parameter_store_configuration.attributes).sensibility.applies_to) == 2
error_message = "applies_to override should be applied"
}
}

run "tier_rejected_for_secrets_manager" {
command = plan

variables {
type = "aws-secrets-manager"
tier = "Standard"
}

expect_failures = [var.tier]
}

run "unknown_type_rejected" {
command = plan

variables {
type = "hashicorp-vault"
}

expect_failures = [var.type]
}
40 changes: 29 additions & 11 deletions nullplatform/parameter_storage_configuration/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,48 @@ variable "type" {
type = string

validation {
condition = contains(["aws-secrets-manager"], var.type)
error_message = "type must be one of: aws-secrets-manager."
condition = contains(["aws-secrets-manager", "aws-parameter-store"], var.type)
error_message = "type must be one of: aws-secrets-manager, aws-parameter-store."
}
}

variable "kms_key_id" {
description = "aws-secrets-manager only. Customer-managed KMS key ARN or alias. If empty, the default aws/secretsmanager managed key is used."
description = "Customer-managed KMS key ARN or alias. If empty, the service's AWS-managed key is used (aws/secretsmanager for aws-secrets-manager, alias/aws/ssm for aws-parameter-store)."
type = string
default = ""
}

variable "applies_to" {
description = "Which parameters this backend stores: any of secret, non_secret. Defaults to the spec's own default for the type — [\"secret\"] for aws-secrets-manager, [\"non_secret\"] for aws-parameter-store."
type = list(string)
default = null

validation {
condition = var.applies_to == null || alltrue([
for v in coalesce(var.applies_to, []) : contains(["secret", "non_secret"], v)
])
error_message = "applies_to entries must be one of: secret, non_secret."
}

validation {
condition = var.type == "aws-secrets-manager" || var.kms_key_id == ""
error_message = "kms_key_id only applies when type is 'aws-secrets-manager'."
condition = var.applies_to == null || length(var.applies_to) > 0
error_message = "applies_to must list at least one parameter kind when set."
}
}

variable "applies_to" {
description = "aws-secrets-manager only. Resource types this parameter storage configuration applies to."
type = list(string)
default = ["secret"]
variable "tier" {
description = "aws-parameter-store only. SSM parameter tier: Standard (free up to 10,000 parameters), Advanced (larger values, billed per parameter) or Intelligent-Tiering. Defaults to Standard."
type = string
default = null

validation {
condition = var.tier == null || contains(["Standard", "Advanced", "Intelligent-Tiering"], var.tier)
error_message = "tier must be one of: Standard, Advanced, Intelligent-Tiering."
}

validation {
condition = var.type == "aws-secrets-manager" || var.applies_to == ["secret"]
error_message = "applies_to only applies when type is 'aws-secrets-manager'."
condition = var.type == "aws-parameter-store" || var.tier == null
error_message = "tier only applies when type is 'aws-parameter-store'."
}
}

Expand Down
12 changes: 11 additions & 1 deletion nullplatform/scope_configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The module creates a single nullplatform_provider_config resource that encodes p
## Features

- Creates a nullplatform_provider_config resource with type-dispatched JSON attributes for static-files or aws-lambda provider specs
- Configures AWS CloudFront distribution settings with optional WAF WebACL attachment for static file delivery
- Configures AWS CloudFront distribution settings with optional WAF WebACL attachment and Lambda@Edge function associations for static file delivery
- Configures Route53 DNS network settings including public hosted zone binding for static-files deployments
- Configures OpenTofu remote state bucket and placeholder ECR image URI for aws-lambda scope deployments
- Optionally attaches a nullplatform agent Lambda layer ARN when USE_NULL_AGENT is enabled on the scope
Expand Down Expand Up @@ -48,6 +48,16 @@ module "scope_configuration" {
}
```

Lambda@Edge functions on the CloudFront default cache behavior are declared with
`aws_lambda_associations`, one entry per CloudFront event. The key is only sent
when the list is non-empty, so configurations without associations do not drift:

```hcl
aws_lambda_associations = [
{ event_type = "viewer-response", function_arn = "arn:aws:lambda:us-east-1:123456789012:function:edge-headers:1" },
]
```

### Usage with AWS Lambda

```hcl
Expand Down
8 changes: 7 additions & 1 deletion nullplatform/scope_configuration/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ locals {
aws_region = var.aws_region
aws_state_bucket = var.aws_state_bucket
}
distribution = merge(local.static_files_defaults.distribution, { aws_distribution = var.aws_distribution })
# lambda_associations has no default in the spec: only sent when the
# caller declares some, so a config without them never drifts.
distribution = merge(
local.static_files_defaults.distribution,
{ aws_distribution = var.aws_distribution },
length(var.aws_lambda_associations) > 0 ? { lambda_associations = var.aws_lambda_associations } : {},
)
network = merge(local.static_files_defaults.network, {
aws_network = var.aws_network
aws_hosted_public_zone_id = var.aws_hosted_public_zone_id
Expand Down
Loading
Loading