Minimal KMS permissions to copy a database snapshot

I found another root cause for this, and another solution:

Just create, then delete, a RDS in the target region!

AWS RDS simply refused to copy a snapshot, no matter what I did to key policies, UNTIL I created a small, automatic RDS. Now any key works "out-of-box", even new ones without any policy change!


Now, I figured it out by trial and error. Since I don't like to do the same task more than once, I automated it (see script below).

This are the required permissions for copying a RDS snapshot:

["kms:CreateGrant","kms:DescribeKey"]

This is the script I used. Maybe it is useful for other people which have a similar problem. It is hacked together, so don't expect it to work out of the box.

#!/bin/bash

set -euo pipefail

unknown=(
    kms:CancelKeyDeletion
    kms:CreateAlias
    kms:CreateAlias
    kms:CreateGrant
    kms:CreateKey
    kms:Decrypt
    kms:DeleteAlias
    kms:DeleteAlias
    kms:DescribeKey
    kms:DisableKey
    kms:DisableKeyRotation
    kms:EnableKey
    kms:EnableKeyRotation
    kms:Encrypt
    kms:GenerateRandom
    kms:GenerateDataKey
    kms:GenerateDataKeyWithoutPlaintext
    kms:GetKeyPolicy
    kms:GetKeyRotationStatus
    kms:ListAliases
    kms:ListGrants
    kms:ListKeyPolicies
    kms:ListKeys
    kms:ListRetirableGrants
    kms:PutKeyPolicy
    kms:ReEncryptFrom
    kms:ReEncryptTo
    kms:RetireGrant
    kms:RevokeGrant
    kms:ScheduleKeyDeletion
    kms:UpdateAlias
    kms:UpdateAlias
    kms:UpdateKeyDescription
)
required=()

KEY_ID=86a6300d-38f9-4892-b7a1-d8f821e8438c
export AWS_DEFAULT_OUTPUT=json

function check_copy {
    permissions=$( echo -n "${required[*]} ${unknown[*]}" | jq -R -s 'split(" ")' )

    policy=$( aws kms \
        get-key-policy \
        --key-id ${KEY_ID} \
        --policy-name default \
      | jq ".Policy" -r \
      | jq ".Statement[1].Action |= ${permissions}"
    )

    aws kms \
        put-key-policy \
        --key-id ${KEY_ID} \
        --policy-name default \
        --policy "${policy}"

    aws rds \
        delete-db-snapshot \
        --db-snapshot-identifier rds-backup-share-mysql-reencrypted \
        || true

    (
        set -x
        AWS_ACCESS_KEY_ID=XXX \
        AWS_SECRET_ACCESS_KEY=XXX \
        aws rds \
            copy-db-snapshot \
            --source-db-snapshot-identifier rds-backup-share-mysql \
            --target-db-snapshot-identifier rds-backup-share-mysql-reencrypted \
            --kms-key-id alias/rds-snapshot-share \
            || return 1

    aws rds \
        wait db-snapshot-completed \
        --db-snapshot-identifier rds-backup-share-mysql-reencrypted
    ) || return 1

    return 0
}

check_copy
while [ ${#unknown[@]} -gt 0 ]
do
    removed=${unknown[0]}
    unknown=(${unknown[@]:1})

    if ! check_copy
    then
        required+=($removed)
    fi

    echo "Required permissions so far: ${required[*]}"
    echo "Unknown permissions so far: ${unknown[*]}"
done

echo -n "Minimal permissions: "
echo -n "${required[*]}" | jq -R -s -c 'split(" ")'

I'm also doing a copy of an RDS database snapshot, but using Powershell and my database is MS SQL. I'm getting the same error:

The target snapshot KMS key [<kms-arn>] does not exist, 
is not enabled or you do not have permissions to access it.

I ended up here with my searches, checking my profile, credentials, roles, etc... but all seems good.

My mistake was not specifying the region in the Copy-RDSDBSnapshot (in Powershell; equivalent to copy-db-snapshot in CLI). Since I'm copying across regions for a Disaster Recovery scenario, I must specify the KmsKeyId of the target Region (as it will defer from the source Region). The source region us-east-1 is my default region, so executing this didn't work, it failed with the error above, since i'm not specify anything, it runs in my default region us-east-1 and therefore it can't find the us-west-2 key, just as the error say, it "does not exist" in that region, it's nothing to do with permissions:

Copy-RDSDBSnapshot -SourceDBSnapshotIdentifier $dBSnapshotArn `
                   -SourceRegion 'us-east-1' `
                   -TargetDBSnapshotIdentifier $targetSnapshotName `
                   -KmsKeyId 'arn:aws:kms:us-west-2:xxxxx:key/xxxxx' `
                   -CopyTag $true -OptionGroupName 'myOptionGroup'

Adding the so called "Common Parameter" Region did the trick:

Copy-RDSDBSnapshot -SourceDBSnapshotIdentifier $dBSnapshotArn `
                   -SourceRegion 'us-east-1' `
                   -TargetDBSnapshotIdentifier $targetSnapshotName `
                   -KmsKeyId 'arn:aws:kms:us-west-2:xxxxx:key/xxxxx' `
                   -CopyTag $true -OptionGroupName 'myOptionGroup' `
                   -Region 'us-west-2'  # <-- new line