diff --git a/.gitignore b/.gitignore
index 5bb94ce6..33454bbe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -171,4 +171,6 @@ terraform.tfstate.backup
.claude/
# Local examples (for testing only, not part of the module distribution)
-nullplatform/scope_definition/examples/
\ No newline at end of file
+nullplatform/scope_definition/examples/
+# Local API skill token (credential — never commit)
+np-api-skill.token
diff --git a/nullplatform/identity-access-control/.terraform.lock.hcl b/nullplatform/identity-access-control/.terraform.lock.hcl
new file mode 100644
index 00000000..8358a393
--- /dev/null
+++ b/nullplatform/identity-access-control/.terraform.lock.hcl
@@ -0,0 +1,25 @@
+# This file is maintained automatically by "tofu init".
+# Manual edits may be lost in future updates.
+
+provider "registry.opentofu.org/nullplatform/nullplatform" {
+ version = "0.0.92"
+ constraints = ">= 0.0.86"
+ hashes = [
+ "h1:anULseuczNv7bKlwfIP4cFkd2o9FGwJGUm+g+BnkuaE=",
+ "zh:09ceed1197d54ea5ac0338a2c542646382a68df72b0ad98ff13389e1c50e00a2",
+ "zh:132329b0a0304663e7a5826a3e667bb4337e89fda0c5e66bd43dcadf608b3b27",
+ "zh:3a055e9eb7e8d1ed7a06e95a227ab59c387eec3b043fafeadcbe8c4bfd8db41a",
+ "zh:3b3182b4052e50a3357cad70c8a8eceb6cb95ee34101eef3fc931b9d7b8c771d",
+ "zh:3db68b8c66913e30eb2d566ba3d1a433bed923d2f7da629b73591921cd4547c3",
+ "zh:56a40f23e80ed1226d1d72ad2e6514d3e7ec031b64c70da8ee1a24fd2e30c068",
+ "zh:6278739e6710e91a0b1c2720bc4dc2e50b51042bd4f848ffa9781e3f4f6cc51c",
+ "zh:73d452f3b9a34a1f360313c55c660f8b367355588d710dea457095b600029e55",
+ "zh:95b649f230267988c1d85f6d497b63a4ed79317f6f79af385da180702f8e196d",
+ "zh:a54b3ac2ebc339e2464c1f65bd3feb471adfd30bc6ac4cbabe40107a29e98a3d",
+ "zh:c00790341e04dd0fdeffb58eb19bec0536d56e5fc878eb8f804cf1414a685f4d",
+ "zh:cd756a988f2ed19da02d55179225d449296fbc8df781f2ba2f5ea5b143df4578",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ "zh:fc2bc683df56dd1087114d43ad6864dd3f3dd8a34ca2c81626b1f08600a88b94",
+ "zh:fffb4fb18254dc734ff5610c22caa9e9f45627bfb060a595a296cde180b28be6",
+ ]
+}
diff --git a/nullplatform/identity-access-control/README.md b/nullplatform/identity-access-control/README.md
new file mode 100644
index 00000000..cc39adac
--- /dev/null
+++ b/nullplatform/identity-access-control/README.md
@@ -0,0 +1,104 @@
+# Module: identity-access-control
+
+## Description
+
+Configures an identity & access control provider in nullplatform via a `nullplatform_provider_config` resource. The provider type defaults to the AWS IAM provider (`aws-iam-configuration`) but is exposed as a variable, so new clouds can be supported by passing their own `type` and `attributes`.
+
+## Architecture
+
+The module creates a single `nullplatform_provider_config` resource. The `type` input selects which provider specification to configure (default `aws-iam-configuration`), and the `attributes` input carries the provider-specific configuration, JSON-encoded to match that specification's schema. `dimensions` metadata is supported for environment- or region-specific configuration. The module is intentionally generic: it does not validate cloud-specific attribute shapes, leaving that to the caller, so adding a new cloud requires no changes here. Unlike provider configs that hold externally-rotated secrets, this module does not set `ignore_changes` on `attributes`, so Terraform remains the source of truth and changes are propagated on apply.
+
+For AWS, this module is the platform-side counterpart to `infrastructure/aws/iam/agent`: that module grants the agent `sts:AssumeRole` permission over the role ARNs, while this module publishes those ARNs to nullplatform under friendly selectors.
+
+## Features
+
+- Creates a nullplatform identity & access control provider configuration
+- Cloud-agnostic: `type` and `attributes` are inputs, defaulting to AWS IAM
+- For AWS IAM, maps friendly selectors to assumable IAM role ARNs for use in scope/service code
+- Supports dimensions for environment- or region-specific configuration
+- Keeps Terraform as the source of truth for the configuration (no attribute drift suppression)
+
+## Basic Usage (AWS IAM — default)
+
+```hcl
+module "identity_access_control" {
+ source = "git::https://github.com/nullplatform/tofu-modules.git//nullplatform/identity-access-control?ref=v4.0.1"
+
+ nrn = "your-nrn"
+
+ attributes = {
+ iam_role_arns = {
+ arns = [
+ {
+ selector = "billing"
+ arn = "arn:aws:iam::123456789012:role/billing-reader"
+ },
+ {
+ selector = "analytics"
+ arn = "arn:aws:iam::123456789012:role/analytics-reader"
+ },
+ ]
+ }
+ }
+}
+```
+
+## Usage for a new cloud
+
+```hcl
+module "identity_access_control" {
+ source = "git::https://github.com/nullplatform/tofu-modules.git//nullplatform/identity-access-control?ref=v4.0.1"
+
+ nrn = "your-nrn"
+ type = "azure-iam-configuration" # slug of the provider specification
+
+ attributes = {
+ # ... shape matching the azure-iam-configuration schema
+ }
+}
+```
+
+## Using Outputs
+
+```hcl
+# Reference outputs in other resources
+resource "example_resource" "this" {
+ example_attribute = module.identity_access_control.id
+}
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [nullplatform](#requirement\_nullplatform) | >= 0.0.86 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [nullplatform](#provider\_nullplatform) | >= 0.0.86 |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [nullplatform_provider_config.identity_access_control](https://registry.terraform.io/providers/nullplatform/nullplatform/latest/docs/resources/provider_config) | resource |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [nrn](#input\_nrn) | nullplatform Resource Name where the provider configuration applies | `string` | n/a | yes |
+| [attributes](#input\_attributes) | Provider-specific configuration, matching the schema of the selected provider type | `any` | n/a | yes |
+| [type](#input\_type) | Slug of the nullplatform provider specification to configure | `string` | `"aws-iam-configuration"` | no |
+| [dimensions](#input\_dimensions) | Dimensions used to scope this provider configuration | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [id](#output\_id) | ID of the provider configuration |
+| [nrn](#output\_nrn) | NRN the provider configuration is attached to |
+
diff --git a/nullplatform/identity-access-control/main.tf b/nullplatform/identity-access-control/main.tf
new file mode 100644
index 00000000..c879d968
--- /dev/null
+++ b/nullplatform/identity-access-control/main.tf
@@ -0,0 +1,6 @@
+resource "nullplatform_provider_config" "identity_access_control" {
+ nrn = var.nrn
+ type = var.type
+ dimensions = var.dimensions
+ attributes = jsonencode(var.attributes)
+}
diff --git a/nullplatform/identity-access-control/outputs.tf b/nullplatform/identity-access-control/outputs.tf
new file mode 100644
index 00000000..eadc8254
--- /dev/null
+++ b/nullplatform/identity-access-control/outputs.tf
@@ -0,0 +1,9 @@
+output "id" {
+ description = "ID of the provider configuration"
+ value = nullplatform_provider_config.identity_access_control.id
+}
+
+output "nrn" {
+ description = "NRN the provider configuration is attached to"
+ value = nullplatform_provider_config.identity_access_control.nrn
+}
diff --git a/nullplatform/identity-access-control/providers.tf b/nullplatform/identity-access-control/providers.tf
new file mode 100644
index 00000000..506a5a50
--- /dev/null
+++ b/nullplatform/identity-access-control/providers.tf
@@ -0,0 +1,8 @@
+terraform {
+ required_providers {
+ nullplatform = {
+ source = "nullplatform/nullplatform"
+ version = ">= 0.0.86"
+ }
+ }
+}
diff --git a/nullplatform/identity-access-control/tests/identity_access_control.tftest.hcl b/nullplatform/identity-access-control/tests/identity_access_control.tftest.hcl
new file mode 100644
index 00000000..111a9e9b
--- /dev/null
+++ b/nullplatform/identity-access-control/tests/identity_access_control.tftest.hcl
@@ -0,0 +1,59 @@
+mock_provider "nullplatform" {}
+
+variables {
+ nrn = "organization=myorg:account=myaccount"
+ attributes = {
+ iam_role_arns = {
+ arns = [
+ { selector = "billing", arn = "arn:aws:iam::123456789012:role/billing-reader" },
+ { selector = "analytics", arn = "arn:aws:iam::123456789012:role/analytics-reader" }
+ ]
+ }
+ }
+}
+
+run "default_type_is_aws_iam_configuration" {
+ command = plan
+
+ assert {
+ condition = nullplatform_provider_config.identity_access_control.type == "aws-iam-configuration"
+ error_message = "Provider config type should default to 'aws-iam-configuration'"
+ }
+
+ assert {
+ condition = nullplatform_provider_config.identity_access_control.nrn == "organization=myorg:account=myaccount"
+ error_message = "NRN should match input"
+ }
+}
+
+run "custom_type_for_new_cloud" {
+ command = plan
+
+ variables {
+ type = "azure-iam-configuration"
+ }
+
+ assert {
+ condition = nullplatform_provider_config.identity_access_control.type == "azure-iam-configuration"
+ error_message = "Provider config type should honor the type variable"
+ }
+}
+
+run "attributes_are_json_encoded" {
+ command = plan
+
+ assert {
+ condition = can(jsondecode(nullplatform_provider_config.identity_access_control.attributes))
+ error_message = "attributes should be valid JSON"
+ }
+
+ assert {
+ condition = length(jsondecode(nullplatform_provider_config.identity_access_control.attributes).iam_role_arns.arns) == 2
+ error_message = "attributes should encode the provided structure verbatim"
+ }
+
+ assert {
+ condition = strcontains(nullplatform_provider_config.identity_access_control.attributes, "arn:aws:iam::123456789012:role/billing-reader")
+ error_message = "attributes should contain the configured role ARN"
+ }
+}
diff --git a/nullplatform/identity-access-control/variables.tf b/nullplatform/identity-access-control/variables.tf
new file mode 100644
index 00000000..3d1159f3
--- /dev/null
+++ b/nullplatform/identity-access-control/variables.tf
@@ -0,0 +1,21 @@
+variable "nrn" {
+ description = "nullplatform Resource Name where the identity & access control provider configuration applies"
+ type = string
+}
+
+variable "type" {
+ description = "Slug of the nullplatform provider specification to configure (e.g. aws-iam-configuration). Set this when adding support for a new cloud."
+ type = string
+ default = "aws-iam-configuration"
+}
+
+variable "attributes" {
+ description = "Provider-specific configuration, matching the schema of the selected provider type. Encoded to JSON for the provider config. For aws-iam-configuration: { iam_role_arns = { arns = [{ selector, arn }] } }."
+ type = any
+}
+
+variable "dimensions" {
+ description = "Dimensions used to scope this provider configuration (e.g., environment, region)"
+ type = map(string)
+ default = {}
+}