SECRET MANAGER + CLOUD KMS → STACKIT · TUTORIAL

Google Secret Manager and Cloud KMS to STACKIT

Secret Manager secrets become keys in one STACKIT Secrets Manager instance, and Cloud KMS keys become keys in a STACKIT KMS key ring. The run writes the instance, the key ring and the keys, and lists what it cannot carry: secret values, rotation schedules, notifications, access grants and hardware protection. This tutorial follows a demo estate with one secret, a symmetric data key and a hardware-protected signing key from input to a plan for each object.

One secret in, one instance out

A demo estate written for this page and assessed by StackToIT on 4 November 2026 with the google provider pinned to 8.4.0: a secret with a user-managed replica, a rotation period and a notification topic, a service account that reads it, a key ring, an encryption key and a signing key. Left: the secret from the input. Right: the instance the engine wrote; the review comments above it in the file are left out here.

Example inputmain.tf
resource "google_secret_manager_secret" "db_password" {
  secret_id = "orders-db-password"

  replication {
    user_managed {
      replicas {
        location = "europe-west3"
      }
    }
  }

  rotation {
    next_rotation_time = "2027-01-01T00:00:00Z"
    rotation_period    = "7776000s"
  }

  topics {
    name = "projects/checkout-prod/topics/secret-rotation"
  }
}
STACKIT Terraform by StackToITterraform/security.tf
resource "stackit_secretsmanager_instance" "migrated_secrets" {
  project_id = var.project_id
  name       = "migrated-secrets"
}
  • The instance is all the draft writes for secrets

    The run creates one shared Secrets Manager instance for all migrated secrets, named migrated-secrets, and nothing inside it. The comment says each secret is recreated as a key in the instance by hand.

  • Values never enter the draft

    The secret value comes from a sensitive variable in the source, and the run lists it as not carried, together with the secret id, the replication, the rotation and the notification topic. The same variable leaves the source status unresolved until a variable file supplies it, and the run never asks for the value.

  • One region, no replication choice

    Secrets Manager is available only when the provider region is eu01, according to the provider documentation. The user-managed replica location of the source has no counterpart to set, and the run lists it among the settings for a person.

Two keys, one key ring

From the same run. Left: the encryption key with scheduled rotation, and the signing key with hardware protection. Right: the key ring and the two keys the engine wrote; comments above each block are left out here. The key ring has one name for all migrated keys.

Example inputmain.tf
resource "google_kms_crypto_key" "data" {
  name            = "data-encryption"
  key_ring        = google_kms_key_ring.checkout.id
  purpose         = "ENCRYPT_DECRYPT"
  rotation_period = "7776000s"

  version_template {
    algorithm        = "GOOGLE_SYMMETRIC_ENCRYPTION"
    protection_level = "SOFTWARE"
  }

  lifecycle {
    prevent_destroy = true
  }
}

resource "google_kms_crypto_key" "signing" {
  name     = "token-signing"
  key_ring = google_kms_key_ring.checkout.id
  purpose  = "ASYMMETRIC_SIGN"

  version_template {
    algorithm        = "EC_SIGN_P256_SHA256"
    protection_level = "HSM"
  }
}
STACKIT Terraform by StackToITterraform/security.tf
resource "stackit_kms_keyring" "migrated" {
  project_id   = var.project_id
  display_name = "migrated-keys"
}

resource "stackit_kms_key" "data" {
  project_id   = var.project_id
  keyring_id   = stackit_kms_keyring.migrated.keyring_id
  display_name = "data-encryption"
  algorithm    = "aes_256_gcm"
  purpose      = "symmetric_encrypt_decrypt"
  protection   = var.kms_protection
  lifecycle {
    prevent_destroy = true
  }
}

resource "stackit_kms_key" "signing" {
  project_id   = var.project_id
  keyring_id   = stackit_kms_keyring.migrated.keyring_id
  display_name = "token-signing"
  algorithm    = "ecdsa_p256_sha256"
  purpose      = "asymmetric_sign_verify"
  protection   = var.kms_protection
}
  • Algorithm and purpose are chosen for you

    The symmetric key becomes aes_256_gcm with the purpose symmetric_encrypt_decrypt, and the elliptic-curve signing key becomes ecdsa_p256_sha256 with the purpose asymmetric_sign_verify. The provider documentation lists both values.

  • Protection is a variable without a default

    Both keys take protection from the variable kms_protection, which has no default. The run could not keep the hardware protection of the signing key and says so in a finding, shown below.

  • prevent_destroy travels, rotation does not

    The lifecycle block of the encryption key is carried, because it is a Terraform meta-argument. The 90-day rotation period is not, and the signing key sets none in the input.

The findings that matter

Three findings as assessment.json holds them (for the signing key, the data key and the secret; evidence lists cut), then the lines of the draft that name what is not carried, then the console lines of the run.

Findings (excerpt)assessment.json
{"check_id":"kms-protection-level","severity":"warning","message":"Key material is protected by an HSM or held outside Google (EXTERNAL / EXTERNAL_VPC). STACKIT KMS keys are software-protected today: the API names the protection values \"software\" or \"hsm\", but the documentation says \"For now only Software is available.\", and there is no external-key option in the API. The protection level cannot be kept — decide whether software protection is acceptable, or keep the key outside STACKIT."
{"check_id":"kms-rotation","severity":"warning","message":"Automatic key rotation is scheduled (rotation_period). STACKIT KMS rotates a key on demand (Rotate key creates a new key version); automatic, scheduled rotation is not documented and stackit_kms_key has no rotation attribute — rotation becomes an operational process."
{"check_id":"secret-rotation","severity":"info","message":"Rotation notifications are configured. STACKIT Secrets Manager has no rotation schedule or notification, so rotation has to be driven by a job on STACKIT."
Comment lines in the draftterraform/security.tf
# NOT CARRIED (google_kms_crypto_key.data): rotation_period = 7776000s — scheduled rotation: stackit_kms_key (pinned provider) has no rotation attribute.
# NOT CARRIED (google_secret_manager_secret.db_password): replication, rotation, secret_id, topics — the secret is recreated as a key in the shared Secrets Manager instance by hand: its name, value (never copied into the draft) and access (grant it with Secrets Manager users or role bindings) are not generated.
# NOT CARRIED (google_secret_manager_secret_version.db_password): secret_data — the secret is recreated as a key in the shared Secrets Manager instance by hand: its name, value (never copied into the draft) and access (grant it with Secrets Manager users or role bindings) are not generated.
Console summary (selected lines)terminal output
Source        8 resources · 4 units · visibility: Variable unresolved
Migration     4 drafted (of 4 units the draft considered)
  • Hardware protection cannot be kept

    The run states that STACKIT KMS keys are software-protected today and that there is no external-key option in the API. It leaves you two choices: accept software protection or keep the key outside STACKIT. This is a security decision, not a mapping.

  • Rotation becomes a job

    STACKIT KMS rotates a key on demand, which creates a new key version, and scheduled rotation is not documented. Secrets Manager has no rotation schedule or notification either. Both rotations are driven by something you run.

  • Old data stays under the old key

    The Google documentation states that rotation does not re-encrypt existing data. The run says keys are re-created and not exported, so data encrypted with a Cloud KMS key is decrypted and re-encrypted with the new key during the data move.

Where each object goes

Google Cloud objectSTACKIT targetWhat the run says
SecretA key in the shared Secrets Manager instanceValue set by a person, never in the draft
Secret versionA secret version, counted for billingSet a version limit; lowering it deletes the oldest versions
Replication and rotation settingsNoneNot carried; rotation is driven by a job
Pub/Sub notification topicNoneNot carried
Secret accessor grantSecrets Manager userCredentials are generated and cannot be changed
Key ring and symmetric keyKMS key ring and key, aes_256_gcmGenerated; rotation not carried
Asymmetric signing key, HSMKMS key, ecdsa_p256_sha256, softwareProtection level decided by a person
Key encrypt and decrypt grantProject role for a service accountNot generated; a person translates it

Classes are RECONFIGURE at medium confidence for the secret, both keys and the service account. The grants are part of the units of the secret and the key they belong to.

Moving a value and its access

Not engine output. The Vault commands follow the STACKIT Secrets Manager pages; the user resource follows the provider documentation. The mount path is the instance ID.

Vault CLIshell
# Written by hand from the STACKIT documentation, not engine output.
# 1. A secret is a key in the instance (mount path = instance ID)
vault kv put -mount=$INSTANCE_ID orders-db-password password=@db-password.txt

# 2. Version limit: billing counts versions; 0 means unlimited
vault write $INSTANCE_ID/config max_versions=10
vault kv metadata patch -mount=$INSTANCE_ID -max-versions=5 orders-db-password

# 3. Read back
vault kv get -mount=$INSTANCE_ID -field=password orders-db-password
Read-only usersecrets.tf
# Written by hand from the provider documentation, not engine output.
resource "stackit_secretsmanager_user" "api_reads" {
  project_id    = var.project_id
  instance_id   = stackit_secretsmanager_instance.migrated_secrets.instance_id
  description   = "checkout-api, read only"
  write_enabled = false
}
  • Compatible with KV2, and only KV2

    STACKIT states compatibility with HashiCorp Vault KV2 with secret versioning and supports no other secrets engine. A client that uses another Vault engine does not move as it is.

  • Versions cost money

    A secret has an unlimited number of versions by default and you pay per version. A limit set on the instance or on a secret caps the cost; lowering a limit below the current count deletes the oldest versions.

  • Backups do not restore a single secret

    STACKIT states that individual secrets cannot be restored from backup, which serves disaster recovery only. If you overwrite a secret, older versions remain while the limit is above one. Keep an external copy of values you rely on.

  • The password cannot be changed

    A user's password is generated and cannot be changed; a new one means a new user. The provider can recreate a user on a trigger through rotate_when_changed.

The migration in order

  1. Inventory secrets, keys and consumers

    List each secret, its versions, its accessors, each key, its purpose and every service that calls Secret Manager or Cloud KMS. The run lists what the Terraform declares; objects made outside it are not seen.

  2. Decide key protection first

    Decide whether software protection is acceptable for each hardware-backed key, or whether it stays outside STACKIT. Set kms_protection once that is decided.

  3. Create the instance, key ring and keys

    Apply the generated blocks in a test project. Check the algorithm and purpose of every key against how your code uses it.

  4. Move secrets

    Write each value with a Vault client, set version limits and create read-only users for consumers. Replace the Secret Manager client in applications with a Vault client or with injection by the platform, for example External Secrets Operator on SKE.

  5. Plan the re-encryption

    Data under a Cloud KMS key stays readable only through Cloud KMS. Schedule decrypt-and-encrypt passes with the new key, and keep the Google key until the last pass has verified.

  6. Build the rotation jobs

    Rotate keys on demand from a scheduled job and rotate secret values from a job that writes a new version. Document who owns each job.

Questions security teams ask

Can we export a Cloud KMS key and import it into STACKIT?

Google’s Cloud KMS API reference states that the raw key material of a key version can never be viewed or exported, and the run plans accordingly: keys are re-created and data is re-encrypted. STACKIT KMS accepts imported key material, but the source has nothing to export.

Is there scheduled rotation on STACKIT?

Not documented for KMS keys and not offered by Secrets Manager. KMS rotates a key on demand and creates a new version. Plan a job for each rotation the source did on a schedule.

What replaces the IAM roles on secrets and keys?

Secrets Manager users for secrets and project role assignments for service accounts on KMS. The run does not generate these grants, and it lists them as settings for a person.

Which regions are available?

The provider documentation states that Secrets Manager is available when the provider region is eu01. The KMS page states availability in EU02 from April 2026; check both against your target region.

Sources

Your next move: STACKIT.

Tell us about your estate. We show you what StackToIT delivers for it: target architecture, Terraform artifacts and a structured migration plan.