Right, let’s talk about SecureString parameters. This is the part where I have to give you some good news and some bad news. The good news is that they are a way to store secrets directly in Parameter Store, encrypted at rest by a KMS key. The bad news? AWS themselves will tell you they are basically a legacy feature at this point, and you should probably be using Secrets Manager instead. But since you’re here, and because you’ll inevitably run into them in the wild (or in a legacy system you’ve inherited), we need to dig in.

The core idea is simple: instead of storing a plaintext string like MyDatabasePassword123 (please tell me you don’t do this), you store it as a SecureString. Parameter Store immediately encrypts it using the KMS key you specify. When you retrieve it via the API, CLI, or SDK, it decrypts it on the fly and gives you the plaintext value. The encryption/decryption magic happens transparently, which is both its greatest strength and its most significant weakness.

The Default Key Trap

Here’s the first “questionable choice” you need to be aware of. When you create a SecureString without specifying a KMS key ID, it uses the default SSM service key, affectionately known as alias/aws/ssm. This key is created automatically in your account. It’s convenient, sure, but it’s a shared resource. The permissions for this key are managed by AWS, and it’s used by everyone in your region. The real problem? You cannot control its key policy. You can’t audit its usage as granularly, and it often leads to overly broad permissions like kms:Decrypt on * because developers got tired of fighting it.

The professional move is to always use your own Customer Managed Key (CMK). This gives you full control over the key policy, allows for rotation, and lets you grant and revoke access on a per-key basis. It’s a night-and-day difference for security.

# The 'move fast and break security' way (please don't)
aws ssm put-parameter \
    --name "/myapp/dev/db/password" \
    --value "supersecret123" \
    --type SecureString

# The 'I know what I'm doing' way
aws ssm put-parameter \
    --name "/myapp/dev/db/password" \
    --value "supersecret123" \
    --type SecureString \
    --key-id "alias/my-app-key"

The Decryption Permissions Gotcha

This is the pitfall that catches everyone. Storing the parameter is the easy part. Retrieving it is where the pain lives. To get the plaintext value back, the principal (a user, role, or service) calling GetParameter or GetParameters needs permission to decrypt using the KMS key that was used to encrypt it.

If you used the default key, your IAM policy needs to include kms:Decrypt for the resource arn:aws:kms:us-east-1:123456789012:key/alias/aws/ssm. If you used your own CMK, it needs permission for that specific key. The SSM GetParameter permission alone is not enough. I’ve lost count of the hours wasted debugging “Access Denied” errors because a Lambda function’s role had SSM read permissions but forgot the crucial KMS decrypt permission.

Here’s a minimal IAM policy snippet that grants the necessary permissions for a parameter encrypted with your own key:

{
    "Effect": "Allow",
    "Action": [
        "ssm:GetParameter"
    ],
    "Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/myapp/dev/db/password"
},
{
    "Effect": "Allow",
    "Action": [
        "kms:Decrypt"
    ],
    "Resource": "arn:aws:kms:us-east-1:123456789012:key/your-key-id-here"
}

Why Secrets Manager is Usually the Better Call

I told you I’d be honest. For actual secrets (database credentials, API keys), you should lean heavily towards Secrets Manager. Why? Three killer features SecureString lacks:

  1. Automatic Rotation: Secrets Manager can automatically rotate credentials based on a schedule or on demand using a Lambda function. With Parameter Store, you’re rolling your own rotation process (and probably getting it wrong).
  2. Cross-Account Access: Sharing a secret across accounts is a built-in, permission-based feature in Secrets Manager. With Parameter Store, you’re back to crafting complex cross-account KMS key policies, and it’s a fiddly nightmare.
  3. Auditing and Versioning: While Parameter Store has versioning, Secrets Manager’s integration with auditing tools is more robust for secret management.

So, use SecureString for configuration parameters that are sensitive but not the crown jewels—think license keys, internal API endpoints with credentials in the URL, or encrypted tokens. For the crown jewels—your database passwords—just use Secrets Manager. Seriously. The few extra pennies are worth the sanity. Parameter Store’s SecureString is the duct tape of AWS secrets; useful in a pinch, but not something you want holding up the most important parts of your architecture.