Configure Apps with Parameter Store

Use Parameter Store for configuration in this hands-on AWS Cloud & DevOps with Python lesson — learn the core concept, walk through a practical exercise, and see how it connects to your next step.

Focus: use parameter store for configuration

Sponsored

You've built a Python app, containerized it, and pushed it to AWS — now you need to store the database URL, API keys, and feature flags without hardcoding them. Copy-pasting secrets into code or environment files is a fast track to leaked credentials and brittle deployments. AWS Systems Manager Parameter Store gives you a secure, versioned, serverless home for configuration, and it's dead simple to use from Python with boto3.

The problem this lesson solves

Configuration is the quiet killer of deployments. Hardcoded values in source code get committed to Git, leaked in screenshots, and mixed up across environments. Environment variables are better, but they're still scattered across EC2 user data, Lambda settings, and CI/CD panels — and every engineer has to ask "where is that set?"

Parameter Store centralizes configuration in one AWS service. Your Python code reads parameters at runtime, pulling the right value for the right environment (dev, staging, prod) with zero code changes. No more if ENV == 'prod': blocks checking environment variables. No more hunting through 40-line docker run commands for the Postgres password.

Parameter Store also gives you versioning, encryption, and IAM-based access control — features you'd have to build yourself if you rolled your own config file. And unlike a database, it costs nothing for standard parameters and doesn't require you to manage any infrastructure.

Core concept / mental model

Think of Parameter Store as a key-value vault in the cloud. Each parameter is a named entry that holds a string (or a structured value like JSON or a list). Your Python application becomes a client that asks the vault for values by name, just like a dictionary lookup.

A simple mental model:

  1. Store — a parameter like myapp/database_url is created in Parameter Store.
  2. Fetch — your Python code calls get_parameter() for that name.
  3. Use — the returned value configures your app's database connection, third-party API, etc.

The name itself can create a hierarchy: /myapp/prod/db_url, /myapp/staging/db_url. This lets you fetch a whole tree of parameters with one call and keep environments isolated.

Pro tip: Treat parameter names like a filesystem path. Use a consistent prefix (e.g., /project/env/) so you can query by path and apply IAM policies to subsets.

Definitions

  • Parameter: a named key-value pair, e.g., name: /myapp/db_url, value: postgresql://user:pass@host:5432/db
  • Standard parameter: free, unencrypted (or encrypted with an AWS-managed key), max 4KB value
  • Advanced parameter: paid, up to 8KB, supports policies (e.g., expiration)
  • Version: every update increments the version number; you can retrieve a specific version if needed

How it works step by step

Let's trace the journey of a configuration value from your AWS account to a Python variable:

  1. Create the parameter — You (or Terraform/CloudFormation) call put_parameter() for each config value. You pick a name, a value, a type (String, StringList, SecureString), and an optional KMS key for encryption.
  2. Attach IAM permissions — Your EC2 instance or Lambda role needs ssm:GetParameter (or ssm:GetParametersByPath) on the parameter's ARN. Without it, boto3 throws AccessDeniedException.
  3. Fetch from Python — Your app calls boto3's get_parameter(). If the value is a SecureString, boto3 automatically decrypts it (provided the role has kms:Decrypt on that key).
  4. Cache and use — Since API calls cost time and can hit throttling limits, best practice is to fetch once at startup and cache the value.

The cause-and-effect is simple: the IAM policy allows the call, the SDK reads the parameter, and the returned string configures your app.

IAM policy requirement

A minimal policy for reading one parameter:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["ssm:GetParameter"],
      "Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/myapp/*"
    },
    {
      "Effect": "Allow",
      "Action": ["kms:Decrypt"],
      "Resource": "*"
    }
  ]
}

Pro tip: Scope the resource to a path prefix (like myapp/*) so your app can't read all parameters — least privilege is a habit worth building.

Hands-on walkthrough

Let's build a practical example: a Python app that reads its database URL from Parameter Store.

Step 1: Create the parameter

Use the AWS CLI to create a SecureString for the database URL:

aws ssm put-parameter \
  --name "/myapp/prod/db_url" \
  --value "postgresql://admin:supersecret@prod-db.example.com:5432/mydb" \
  --type SecureString

To update it later, add --overwrite:

aws ssm put-parameter --name "/myapp/db_url" --value "new-url" --type String --overwrite

Step 2: Read it from Python

Now the fun part — a complete Python script that fetches and uses the parameter:

import boto3
import json

ssm = boto3.client('ssm', region_name='us-east-1')

def get_parameter(name, with_decryption=True):
    """Fetch a parameter value, optionally decrypting SecureString."""
    response = ssm.get_parameter(
        Name=name,
        WithDecryption=with_decryption
    )
    return response['Parameter']['Value']

db_url = get_parameter('/myapp/prod/db_url')
print(f"Connecting to: {db_url}")

Expected output (if you print the URL without masking — don't do this in production!):

Connecting to: postgresql://admin:supersecret@prod-db.example.com:5432/mydb

Step 3: Fetch multiple parameters by path

For apps with several config values, query a whole path:

import boto3

ssm = boto3.client('ssm', region_name='us-east-1')

def get_parameters_by_path(path):
    """Return a dict of all parameters under the path."""
    parameters = {}
    paginator = ssm.get_paginator('get_parameters_by_path')
    for page in paginator.paginate(Path=path, Recursive=True, WithDecryption=True):
        for p in page['Parameters']:
            # Strip the path prefix to use as a simple key
            key = p['Name'].split('/')[-1]
            parameters[key] = p['Value']
    return parameters

config = get_parameters_by_path('/myapp/prod')
print(config['db_url'])
print(config['api_key'])

Step 4: Cache the values

To avoid hitting API limits and to speed up startup, cache the config in a global dict:

import boto3
from functools import lru_cache

ssm = boto3.client('ssm', region_name='us-east-1')

@lru_cache(maxsize=1)
def load_config():
    """Load all parameters once and cache."""
    params = {}
    paginator = ssm.get_paginator('get_parameters_by_path')
    for page in paginator.paginate(Path='/myapp/prod', WithDecryption=True):
        for p in page['Parameters']:
            params[p['Name']] = p['Value']
    return params

# Use it
config = load_config()
print(config['/myapp/prod/db_url'])
# Subsequent calls hit the cache, not the API

Pro tip: For Lambda, cache outside the handler function to reuse across warm starts. For EC2, cache at process start.

Compare options / when to choose what

Parameter Store isn't the only way to manage config on AWS. Here's how it stacks up:

Feature Parameter Store Secrets Manager Environment Variables
Cost Free (standard) $0.40/secret/month Free
Max size 4KB (standard), 8KB (advanced) 64KB ~4KB per env var
Rotation Manual or via Lambda Built-in scheduled rotation N/A
Encryption Optional (SecureString) Automatic Depends on platform
Versioning Yes Yes No
IAM policies Fine-grained per path Full ARN access Limited
Best for Configs, simple secrets Rotating credentials (DB passwords, API keys) Short-lived or ephemeral values

Choose Parameter Store when you need a cheap, central config hub with path-based hierarchy. Choose Secrets Manager when you need automatic rotation or larger secrets. Use environment variables only for non-sensitive, per-process settings like DEBUG=true.

Troubleshooting & edge cases

Let's tackle the most common problems you'll hit.

1. AccessDeniedException

Error: An error occurred (AccessDeniedException) when calling the GetParameter operation

Cause: The IAM role attached to your EC2/Lambda doesn't have ssm:GetParameter, or the resource ARN doesn't match.

Fix: Attach the policy we created earlier. Verify the parameter ARN format: arn:aws:ssm:region:account-id:parameter/path/name. Note the parameter part is singular.

2. ParameterNotFound

Error: ParameterNotFound when calling get_parameter

Cause: The name doesn't exist, or you're in the wrong region or account.

Fix: Double-check the name including the leading slash (e.g., /myapp/db_url). Use the AWS Console to confirm the parameter exists in the same region as your Python code.

3. ThrottlingException

Error: ThrottlingException: Rate exceeded

Cause: You're calling get_parameter inside a hot loop or without caching.

Fix: Implement caching as shown above. If you need frequent reads, use GetParameters to batch multiple names in one call, or use a local cache library like cachetools.

4. Decryption failure for SecureString

Error: KMSException or IncorrectKeyException when calling get_parameter with WithDecryption=True

Cause: Your IAM role doesn't have kms:Decrypt on the KMS key that encrypted the parameter.

Fix: Attach a KMS policy that allows kms:Decrypt on the specific key ARN. If you used the default alias/aws/ssm, the AWS-managed key is used — ensure your role has decrypt on that key.

5. Wrong value returned in production

Symptom: Your app uses a staging URL in production.

Cause: The parameter path is hardcoded to /myapp/staging/.

Fix: Use an environment variable or a profile string to select the environment:

import os
env = os.getenv('APP_ENV', 'dev')
params = get_parameters_by_path(f'/myapp/{env}')

Now you switch environments by changing APP_ENV, not by editing code.

What you learned & what's next

You now understand how to use Parameter Store for configuration: create parameters, read them securely from Python, and manage them via IAM and path hierarchies. You can handle secrets without hardcoding, version your config, and switch environments cleanly. This is a foundational skill for any AWS deployment.

Next in your AWS Cloud & DevOps with Python journey, you'll learn how to manage infrastructure as code — using Terraform or CloudFormation to define all your resources, including these Parameter Store entries, so your entire stack is reproducible and reviewable.

Keep building — you're one step closer to deploying robust, professional Python apps on AWS.

Practice recap

Now try it yourself: create a parameter for a fake API key (SecureString), write a Python script that reads it and prints a masked version (e.g., sk-****1234), then update the parameter value and verify your script picks up the change. This cements the read-update cycle you'll use daily in production.

Common mistakes

  • Hardcoding parameter names in the app instead of using environment variables to select the environment — this locks you to one env and forces code changes.
  • Calling get_parameter on every request without caching, causing throttling and slow startup.
  • Using plain String type for secrets instead of SecureString, leaving sensitive data unencrypted in AWS.
  • Forgetting to attach kms:Decrypt permission for SecureString parameters, causing mysterious decryption errors.
  • Putting the full parameter ARN in the IAM policy without the parameter keyword — the ARN must be arn:aws:ssm:region:acct:parameter/...

Variations

  1. Use get_parameters to fetch a batch of specified names in one call, reducing API round-trips.
  2. Use advanced parameters when you need values larger than 4KB or parameter policies like expiration.
  3. Use AWS Secrets Manager instead of Parameter Store for credentials that require automatic rotation.

Real-world use cases

  • A Django or Flask app on EC2 reads its database URL and secret key from Parameter Store at startup, enabling environment-specific configs without code changes.
  • A Lambda function fetches feature-flag JSON from Parameter Store to toggle behavior in production without redeploying.
  • A CI/CD pipeline stores build configuration values (e.g., artifact bucket, deploy region) in Parameter Store and reads them during deployment scripts.

Key takeaways

  • Parameter Store centralizes configuration as versioned key-value pairs, removing hardcoded values from code.
  • Use SecureString for secrets and String for non-sensitive config; WithDecryption=True in boto3 handles decryption automatically.
  • IAM permissions are mandatory — grant ssm:GetParameter and kms:Decrypt to your compute role.
  • Leverage path hierarchies (/app/env/param) to organize and fetch related parameters in one call.
  • Cache parameter values to avoid throttling and improve app performance.
  • Parameter Store is free for standard parameters, making it a cost-effective choice for most config needs.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.