Skip to content

Grant dev Terraform CI access to Key Vault#83

Merged
JustAGhosT merged 1 commit into
mainfrom
fix/dev-ci-keyvault-access
Jul 17, 2026
Merged

Grant dev Terraform CI access to Key Vault#83
JustAGhosT merged 1 commit into
mainfrom
fix/dev-ci-keyvault-access

Conversation

@JustAGhosT

Copy link
Copy Markdown
Collaborator

Summary

  • add a managed dev Key Vault access policy for the GitHub Actions OIDC service principal
  • keep existing secret resources dependent on both deployer and CI access policies
  • record the CI object ID in dev tfvars so local applies preserve CI data-plane access

Fixes

Validation

  • terraform fmt -recursive infra/terraform/env/dev
  • git diff --check -- infra/terraform/env/dev/main.tf infra/terraform/env/dev/variables.tf infra/terraform/env/dev/terraform.tfvars

Follow-up

  • One-time bootstrap is still needed on nl-dev-convolens-kv if CI reruns before this policy has been applied by an actor that already has Key Vault data-plane access.

@github-actions

Copy link
Copy Markdown

Terraform Plan (dev)

Click to expand
Acquiring state lock. This may take a few moments...
data.azurerm_client_config.current: Reading...
data.azurerm_client_config.current: Read complete after 0s [id=Y2xpZW50Q29uZmlncy9jbGllbnRJZD04ZjU3YTM0OS1lYjg4LTQwZmQtODFlYi0xMDY1Zjg0ZTY2OGI7b2JqZWN0SWQ9ZDQ4NzYyOWQtMDc1OC00MTkyLWJmMDAtZGZkNGYyMTRhNzM4O3N1YnNjcmlwdGlvbklkPWJiNGUzODgyLTIwNzktNGJhYi04OTc0LTYxMWJjMGI4YmI1ODt0ZW5hbnRJZD05NTMwY2QzMi05ZTMzLTQ3ZjAtOTI0Ny1lZDk2NDczMGI1ODA=]
azurerm_resource_group.rg: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg]
azurerm_key_vault.kv: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.KeyVault/vaults/nl-dev-convolens-kv]
azurerm_log_analytics_workspace.law: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.OperationalInsights/workspaces/nl-dev-convolens-law]
azurerm_static_web_app.swa[0]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.Web/staticSites/nl-dev-convolens-swa]
azurerm_cosmosdb_account.cosmos[0]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos]
azurerm_storage_account.st: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.Storage/storageAccounts/nldevconvolensst]
azurerm_application_insights.ai: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.Insights/components/nl-dev-convolens-appi]
azurerm_container_app_environment.cae[0]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.App/managedEnvironments/nl-dev-convolens-cae]
azurerm_key_vault_access_policy.deployer: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.KeyVault/vaults/nl-dev-convolens-kv/objectId/99b63adb-8f1a-4d7a-a98c-5bfe9c7fcd96]
azurerm_key_vault_secret.cosmos_key[0]: Refreshing state... [id=https://nl-dev-convolens-kv.vault.azure.net/secrets/cosmos-db-key/9f5c0fca48b048ff97f93e9099863eb1]
azurerm_cosmosdb_sql_database.db[0]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos/sqlDatabases/convolens]
azurerm_key_vault_secret.cosmos_endpoint[0]: Refreshing state... [id=https://nl-dev-convolens-kv.vault.azure.net/secrets/cosmos-db-endpoint/83825780119644b2bc46d5a75da2b552]
azurerm_key_vault_secret.cosmos_connection_string[0]: Refreshing state... [id=https://nl-dev-convolens-kv.vault.azure.net/secrets/cosmos-db-connection-string/257323432daf46b0bea651cb955280b4]
azurerm_cosmosdb_sql_container.containers["chats"]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos/sqlDatabases/convolens/containers/chats]
azurerm_cosmosdb_sql_container.containers["users"]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos/sqlDatabases/convolens/containers/users]
azurerm_cosmosdb_sql_container.containers["summaries"]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos/sqlDatabases/convolens/containers/summaries]
azurerm_storage_container.containers["user-uploads"]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.Storage/storageAccounts/nldevconvolensst/blobServices/default/containers/user-uploads]
azurerm_key_vault_secret.storage_connection_string: Refreshing state... [id=https://nl-dev-convolens-kv.vault.azure.net/secrets/storage-connection-string/f437ca08a54d46ebbf12b9a39387efa2]
azurerm_storage_container.containers["chat-exports"]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.Storage/storageAccounts/nldevconvolensst/blobServices/default/containers/chat-exports]
azurerm_storage_container.containers["summaries"]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.Storage/storageAccounts/nldevconvolensst/blobServices/default/containers/summaries]
azurerm_key_vault_secret.appinsights_connection_string: Refreshing state... [id=https://nl-dev-convolens-kv.vault.azure.net/secrets/appinsights-connection-string/b49878c8df8c451986ff9d02d8905090]
azurerm_container_app.api[0]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.App/containerApps/nl-dev-convolens-api]
azurerm_key_vault_access_policy.container_app[0]: Refreshing state... [id=/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.KeyVault/vaults/nl-dev-convolens-kv/objectId/801ba185-32e1-4a45-a385-629c79cb6a4c]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place
-/+ destroy and then create replacement

Terraform planned the following actions, but then encountered a problem:

  # azurerm_container_app.api[0] will be updated in-place
  ~ resource "azurerm_container_app" "api" {
        id                            = "/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.App/containerApps/nl-dev-convolens-api"
        name                          = "nl-dev-convolens-api"
        tags                          = {
            "costCenter"  = "development"
            "environment" = "dev"
            "managedBy"   = "terraform"
            "org"         = "nl"
            "project"     = "convolens"
        }
        # (10 unchanged attributes hidden)

      ~ template {
            # (6 unchanged attributes hidden)

          ~ container {
              ~ memory            = "1Gi" -> "1.0Gi"
                name              = "api"
                # (5 unchanged attributes hidden)

                # (8 unchanged blocks hidden)
            }

            # (1 unchanged block hidden)
        }

        # (3 unchanged blocks hidden)
    }

  # azurerm_cosmosdb_sql_container.containers["chats"] will be updated in-place
  ~ resource "azurerm_cosmosdb_sql_container" "containers" {
        id                  = "/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos/sqlDatabases/convolens/containers/chats"
        name                = "chats"
        # (5 unchanged attributes hidden)

      ~ indexing_policy {
            # (1 unchanged attribute hidden)

          + excluded_path {
              + path = "/\"_etag\"/?"
            }

            # (1 unchanged block hidden)
        }

        # (1 unchanged block hidden)
    }

  # azurerm_cosmosdb_sql_container.containers["summaries"] will be updated in-place
  ~ resource "azurerm_cosmosdb_sql_container" "containers" {
        id                  = "/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos/sqlDatabases/convolens/containers/summaries"
        name                = "summaries"
        # (5 unchanged attributes hidden)

      ~ indexing_policy {
            # (1 unchanged attribute hidden)

          + excluded_path {
              + path = "/\"_etag\"/?"
            }

            # (1 unchanged block hidden)
        }

        # (1 unchanged block hidden)
    }

  # azurerm_cosmosdb_sql_container.containers["users"] will be updated in-place
  ~ resource "azurerm_cosmosdb_sql_container" "containers" {
        id                  = "/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.DocumentDB/databaseAccounts/nl-dev-convolens-cosmos/sqlDatabases/convolens/containers/users"
        name                = "users"
        # (5 unchanged attributes hidden)

      ~ indexing_policy {
            # (1 unchanged attribute hidden)

          + excluded_path {
              + path = "/\"_etag\"/?"
            }

            # (1 unchanged block hidden)
        }

        # (1 unchanged block hidden)
    }

  # azurerm_key_vault_access_policy.ci_deployers["d487629d-0758-4192-bf00-dfd4f214a738"] will be created
  + resource "azurerm_key_vault_access_policy" "ci_deployers" {
      + id                 = (known after apply)
      + key_vault_id       = "/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.KeyVault/vaults/nl-dev-convolens-kv"
      + object_id          = "d487629d-0758-4192-bf00-dfd4f214a738"
      + secret_permissions = [
          + "Get",
          + "List",
          + "Set",
          + "Delete",
          + "Purge",
          + "Recover",
        ]
      + tenant_id          = "9530cd32-9e33-47f0-9247-ed964730b580"
    }

  # azurerm_key_vault_access_policy.deployer must be replaced
-/+ resource "azurerm_key_vault_access_policy" "deployer" {
      - certificate_permissions = [] -> null
      ~ id                      = "/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.KeyVault/vaults/nl-dev-convolens-kv/objectId/99b63adb-8f1a-4d7a-a98c-5bfe9c7fcd96" -> (known after apply)
      - key_permissions         = [] -> null
      ~ object_id               = "99b63adb-8f1a-4d7a-a98c-5bfe9c7fcd96" -> "d487629d-0758-4192-bf00-dfd4f214a738" # forces replacement
      - storage_permissions     = [] -> null
        # (4 unchanged attributes hidden)
    }

  # azurerm_storage_account.st will be updated in-place
  ~ resource "azurerm_storage_account" "st" {
        id                                 = "/subscriptions/bb4e3882-2079-4bab-8974-611bc0b8bb58/resourceGroups/nl-dev-convolens-rg/providers/Microsoft.Storage/storageAccounts/nldevconvolensst"
        name                               = "nldevconvolensst"
        tags                               = {
            "costCenter"  = "development"
            "environment" = "dev"
            "managedBy"   = "terraform"
            "org"         = "nl"
            "project"     = "convolens"
        }
        # (97 unchanged attributes hidden)

      + network_rules {
          + bypass                     = [
              + "AzureServices",
            ]
          + default_action             = "Allow"
          + ip_rules                   = (known after apply)
          + virtual_network_subnet_ids = (known after apply)
        }

        # (3 unchanged blocks hidden)
    }

Plan: 2 to add, 5 to change, 1 to destroy.
Releasing state lock. This may take a few moments...
::error::Terraform exited with code 1.

Run ID: 29597850001

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69a8c91c14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +197 to +202
resource "azurerm_key_vault_access_policy" "ci_deployers" {
for_each = var.ci_deployer_object_ids

key_vault_id = azurerm_key_vault.kv.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = each.value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bootstrap CI access before refreshing managed secrets

For the existing dev vault state that this change targets, the CI identity still has no data-plane permission when the workflow invokes its normal terraform apply -auto-approve (.github/workflows/infrastructure.yml:222-227). Terraform refreshes the tracked azurerm_key_vault_secret resources before it can create this policy, so those reads receive 403 and the apply exits before this resource or its depends_on edges can take effect. Consequently, merging this commit alone leaves the failed CI run unable to apply; the policy must be granted through a bootstrap path that does not require reading the existing secrets first.

Useful? React with 👍 / 👎.

@JustAGhosT
JustAGhosT merged commit 0bded45 into main Jul 17, 2026
4 checks passed
@JustAGhosT
JustAGhosT deleted the fix/dev-ci-keyvault-access branch July 17, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant