DEV Community

Cover image for Parameter Store data types: validate values on write
Sascha Mirow-Stanitzok
Sascha Mirow-Stanitzok

Posted on Originally published at sascha-ms.de

Parameter Store data types: validate values on write

Parameter Store data types are no longer just for AMI IDs. Since November 5, 2026, they also cover everyday configuration values: numbers, switches, email addresses, URLs and JSON. Sounds minor. For me it is one of the more useful AWS updates of the week, because it closes a gap I keep tripping over in serverless projects.

The gap looks like this: someone changes a value in Parameter Store, types “48 hours” instead of “48”, and nobody notices. Things only break when the next Lambda function cold-starts and reads the value. In the best case your validation catches it. In the worst case the code keeps calculating with NaN. With data types, Parameter Store rejects such values on write. In this article I show you how that works, where it still falls short today (spoiler: CloudFormation) and how you can still use it cleanly with AWS CDK and TypeScript.

In short

Parameter Store now validates values on write against 13 new data types such as integer, boolean, email or json. Configuration typos show up in the pipeline instead of in production. The biggest catch: CloudFormation, and therefore CDK, does not know the new types yet, and url, json and jsonc require paid advanced parameters.

What you will learn in this article

  • Which 13 new data types exist and what exactly they check
  • How to create parameters with a data type via CLI and filter by it
  • How to set the types with CDK despite missing CloudFormation support
  • How a TypeScript script safely converts existing parameters
  • Where the cost traps with json and url are

What are Parameter Store data types?

Parameter Store is part of AWS Systems Manager and stores configuration as key-value pairs under a path, for example /fahrradverleih/prod/max-ausleihdauer-stunden (our fictional bike rental, “Fahrradverleih” in German). If this is new to you, start with my introduction Parameter Store in Lambda: loading configuration the right way. It covers caching, the Lambda extension and when to read values at deploy time versus at runtime.

Every parameter has a type (String, StringList or SecureString) and, in addition, a data type. The type describes how the value is stored, for example encrypted or as a comma-separated list. The data type, on the other hand, describes what the content may look like. Until now there was practically only text (no validation) and aws:ec2:image for AMI IDs. Now 13 more have arrived, and Parameter Store validates the value on every PutParameter. If it does not match, it is never stored.

The new data types at a glance

Data type Checks Valid Rejected Tier
integer whole number, signed 32-bit 443, -42 007, +7, 7.0 Standard
float floating-point number (64-bit) 3.14, 1e5 NaN, Infinity Standard
boolean truth value true, FALSE, 1 tRue, yes Standard
binary only zeros and ones 0011 Base64 Standard
aws:region existing Region eu-central-1 eu-middle-1 Standard
aws:account:id 12-digit account ID (format) 111122223333 1111-2222-3333 Standard
hostname host name, no IP api.example.com _dmarc.example.com Standard
email bare address help@example.com Help <help@example.com> Standard
ipv4-address one IPv4 address 192.0.2.1 10.0.0.0/16 Standard
ipv6-address one IPv6 address 2001:db8::1 CIDR, ports Standard
url URL with scheme and host https://example.com mailto:…, file:/// Advanced only
json JSON object or array {"port": 443} 42, duplicate keys Advanced only
jsonc like json, with comments {"port": 443 /* https */} trailing commas Advanced only

A few details from the documentation matter in practice. For a StringList, every item is checked on its own, and a single bad entry makes the whole call fail. SecureString supports almost all types, and validation works the same as for String. You can also combine a data type with the familiar AllowedPattern; the value then has to pass both. By the way, aws:account:id only checks the format, not whether the account exists.

Very important: nothing changes on read. GetParameter still returns a string, so 443 comes back as "443". Converting it is still your code’s job.

Why validate values on write?

Configuration is code without a compiler. A typo in TypeScript shows up at the latest when you run tsc. A typo in Parameter Store only shows up when someone reads the value. With Lambda, that is usually the next cold start: at some random time, often at night and often in production.

That is why I validate configuration in every Lambda function on start, for example with Zod (see Validate environment variables: Lambda configuration with Zod). This protects against wrong behavior, but it does not prevent the outage. The function simply refuses to start. Data types move the check to the place where the error is made: the change itself. Anyone writing an invalid value in the console or the pipeline gets an error right away, and the old, working value stays active.

Diagram: without a data type Lambda fails in prod; with data type integer PutParameter rejects the value on write

Without a data type, the error shows up in the Lambda function. With one, it is rejected on write.

Still, data types should not replace your runtime checks. They validate the format, not business limits. A maximum rental time of -5 is a valid integer but makes no sense. For that you still need AllowedPattern or a check in code.

Using Parameter Store data types with the CLI

The fastest way to try the feature is the AWS CLI. Our example is a fictional bike rental that keeps a few settings in Parameter Store.

Prerequisites

  • an AWS account with permissions for ssm:PutParameter, ssm:GetParameter and ssm:DescribeParameters
  • a current AWS CLI v2 (older versions already know the --data-type option and simply pass the value through)
  • Node.js 22 or later for the TypeScript examples below

Step 1: Create a parameter with a data type

aws ssm put-parameter \
  --name "/fahrradverleih/prod/max-ausleihdauer-stunden" \
  --value "48" \
  --type String \
  --data-type integer
Enter fullscreen mode Exit fullscreen mode

As usual, the response contains Version and Tier. From now on, the data type is checked on every change.

Step 2: Write a wrong value

aws ssm put-parameter \
  --name "/fahrradverleih/prod/max-ausleihdauer-stunden" \
  --value "48 hours" \
  --type String \
  --data-type integer \
  --overwrite
Enter fullscreen mode Exit fullscreen mode

The call fails with an error, and get-parameter still returns 48. That is exactly what we want. The API documentation does not name the exact error yet. So do not check for a specific error name in your code; treat any error from PutParameter as “value not applied”.

Step 3: JSON configuration as an advanced parameter

For url, json and jsonc you have to specify the advanced tier. Without --tier Advanced, Parameter Store rejects the call.

aws ssm put-parameter \
  --name "/fahrradverleih/prod/tarife" \
  --value '{"stadtrad": 2.5, "ebike": 6}' \
  --type String \
  --data-type json \
  --tier Advanced
Enter fullscreen mode Exit fullscreen mode

Step 4: Filter parameters by data type

With describe-parameters you find all parameters of a data type. That is handy for spotting where plain text without validation is still in use:

aws ssm describe-parameters \
  --parameter-filters "Key=Path,Option=Recursive,Values=/fahrradverleih/prod" \
                      "Key=DataType,Values=text" \
  --query "Parameters[].Name"
Enter fullscreen mode Exit fullscreen mode

The DataType filter only works with DescribeParameters. According to the API reference, GetParametersByPath does not accept it.

Setting Parameter Store data types with AWS CDK

Now for the catch. On November 6, 2026, I queried the resource schema of AWS::SSM::Parameter in the CloudFormation registry: the DataType property still only allows text and aws:ec2:image. The description in the CloudFormation reference already mentions json, but the allowed values do not. So you cannot reliably set the new types with ssm.StringParameter in CDK or directly in a serverless.yml under resources. AWS will surely catch up, but until then you need a workaround.

The workaround is called AwsCustomResource. This CDK construct creates a small Lambda function that runs any AWS SDK call during deployment. We let it call PutParameter with a data type. If Parameter Store rejects the value, the deployment fails and CloudFormation rolls back. That is exactly the behavior we want.

Step 1: Create the project

mkdir fahrradverleih-config && cd fahrradverleih-config
npm init -y
npm install aws-cdk-lib@2.275.0 constructs@10.8.1 @aws-sdk/client-ssm@3.1163.0
npm install -D aws-cdk@2.1148.0 typescript@7.0.2 @types/node@22
Enter fullscreen mode Exit fullscreen mode

In package.json, set "type": "module". The tsconfig.json uses "module": "NodeNext" and writes to dist, and cdk.json points to the compiled program with "app": "node dist/bin/app.js". All other files are shown in full in this article.

Step 2: Keep the allowed data types in one place

I keep the list of parameters and their data types in a separate file. Stack and script share it, so there is only one source of truth.

// config/parameters.ts
// Single source of truth: which parameter should have which data type.
export type DataType =
  | 'text' | 'aws:region' | 'aws:account:id' | 'boolean' | 'integer' | 'float'
  | 'binary' | 'hostname' | 'email' | 'ipv4-address' | 'ipv6-address'
  | 'url' | 'json' | 'jsonc';

export interface ParameterSpec {
  name: string;
  dataType: DataType;
}

export const PREFIX = '/fahrradverleih/prod';

export const parameterSpecs: ParameterSpec[] = [
  { name: `${PREFIX}/max-ausleihdauer-stunden`, dataType: 'integer' },
  { name: `${PREFIX}/preis-pro-stunde`, dataType: 'float' },
  { name: `${PREFIX}/wartungsmodus`, dataType: 'boolean' },
  { name: `${PREFIX}/support-mail`, dataType: 'email' },
  { name: `${PREFIX}/zahlungsdienst-url`, dataType: 'url' },
];

// These types only work with advanced parameters (cost: see article).
export const ADVANCED_ONLY: ReadonlySet<DataType> = new Set(['url', 'json', 'jsonc']);
Enter fullscreen mode Exit fullscreen mode

Step 3: A construct for typed parameters

// lib/config-stack.ts
import { Stack, type StackProps, Arn } from 'aws-cdk-lib';
import {
  AwsCustomResource,
  AwsCustomResourcePolicy,
  PhysicalResourceId,
} from 'aws-cdk-lib/custom-resources';
import { Construct } from 'constructs';
import { ADVANCED_ONLY, type DataType } from '../config/parameters.js';

interface TypedParameterProps {
  name: string;
  value: string;
  dataType: DataType;
}

// CloudFormation (AWS::SSM::Parameter) currently only accepts text and aws:ec2:image.
// That is why we call PutParameter directly via a custom resource.
export class TypedParameter extends Construct {
  constructor(scope: Construct, id: string, props: TypedParameterProps) {
    super(scope, id);
    const put = {
      service: 'SSM',
      action: 'putParameter',
      parameters: {
        Name: props.name,
        Value: props.value,
        Type: 'String',
        DataType: props.dataType,
        Tier: ADVANCED_ONLY.has(props.dataType) ? 'Advanced' : 'Standard',
        Overwrite: true,
      },
      physicalResourceId: PhysicalResourceId.of(props.name),
    };
    const parameterArn = Arn.format(
      { service: 'ssm', resource: 'parameter', resourceName: props.name.replace(/^\//, '') },
      Stack.of(this),
    );
    new AwsCustomResource(this, 'Resource', {
      onCreate: put,
      onUpdate: put,
      onDelete: { service: 'SSM', action: 'deleteParameter', parameters: { Name: props.name } },
      policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: [parameterArn] }),
      installLatestAwsSdk: false,
    });
  }
}

export class FahrradverleihConfigStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    new TypedParameter(this, 'MaxDauer', {
      name: '/fahrradverleih/prod/max-ausleihdauer-stunden', value: '48', dataType: 'integer',
    });
    new TypedParameter(this, 'Wartungsmodus', {
      name: '/fahrradverleih/prod/wartungsmodus', value: 'false', dataType: 'boolean',
    });
    new TypedParameter(this, 'SupportMail', {
      name: '/fahrradverleih/prod/support-mail', value: 'hilfe@fahrradverleih.example', dataType: 'email',
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Some decisions in there are deliberate. Overwrite: true makes sure an update replaces the value instead of failing with “already exists”. The IAM policy allows the SDK calls for exactly this one parameter. And installLatestAwsSdk: false skips downloading the SDK during deployment, because DataType is a plain string in the SDK that the SDK bundled with the Lambda runtime passes through as well.

Step 4: Create the app, synthesize and deploy

// bin/app.ts
import { App } from 'aws-cdk-lib';
import { FahrradverleihConfigStack } from '../lib/config-stack.js';

const app = new App();
new FahrradverleihConfigStack(app, 'FahrradverleihConfig');
Enter fullscreen mode Exit fullscreen mode
npx tsc
npx cdk synth
npx cdk deploy FahrradverleihConfig
Enter fullscreen mode Exit fullscreen mode

In the synthesized template you will see one resource of type Custom::AWS per parameter, with "DataType":"integer" in its call. After deployment, the console shows the data type for the parameter. As a test, change the value of MaxDauer to '48h' and deploy again: CloudFormation reports an error for the custom resource and rolls the stack back. The old value stays in place.

🎬 Flow: a CDK deployment writes a typed parameter – this animated diagram only runs on my site: watch it on sascha-ms.de

Converting existing parameters to data types

New parameters are easy. The more interesting question is how to convert the hundred existing parameters that are all text today. There is no plain “change data type” call: you have to write the parameter again with PutParameter and Overwrite, using the current value and the new data type. This creates a new version. If the current value does not match the type, Parameter Store rejects it, and you have found a bug along the way.

I split this into two parts: a pure function that calculates a plan from the target and current state, and a script that shows the plan and executes it on request. The pure function can be tested without AWS access.

Step 1: Calculate the plan

// src/plan.ts
import { ADVANCED_ONLY, type ParameterSpec } from '../config/parameters.js';

export interface CurrentParameter {
  name: string;
  type: 'String' | 'StringList' | 'SecureString';
  dataType: string;
  tier: 'Standard' | 'Advanced' | 'Intelligent-Tiering';
}

export type Step =
  | { action: 'ok'; name: string }
  | { action: 'change'; name: string; from: string; to: string; needsAdvanced: boolean }
  | { action: 'skip'; name: string; reason: string };

export function planTypeChanges(specs: ParameterSpec[], current: CurrentParameter[]): Step[] {
  const byName = new Map(current.map((p) => [p.name, p]));
  return specs.map((spec): Step => {
    const p = byName.get(spec.name);
    if (!p) return { action: 'skip', name: spec.name, reason: 'parameter does not exist' };
    if (p.dataType === spec.dataType) return { action: 'ok', name: spec.name };
    if (p.dataType === 'aws:ec2:image' || p.dataType === 'aws:ssm:integration') {
      return { action: 'skip', name: spec.name, reason: `data type ${p.dataType} cannot be changed` };
    }
    if (p.type === 'SecureString') {
      return { action: 'skip', name: spec.name, reason: 'SecureString: change manually with the right KMS key' };
    }
    return {
      action: 'change',
      name: spec.name,
      from: p.dataType,
      to: spec.dataType,
      needsAdvanced: ADVANCED_ONLY.has(spec.dataType) && p.tier === 'Standard',
    };
  });
}
Enter fullscreen mode Exit fullscreen mode

I deliberately leave out SecureStrings. When overwriting, you would have to pass the right KMS key again, otherwise the value might end up under the default key aws/ssm. That is too risky for a bulk script. According to the docs, aws:ec2:image and aws:ssm:integration cannot be converted to other types at all.

Step 2: The script with a dry run

// src/apply-types.ts
// Usage: node dist/src/apply-types.js          (plan only)
//        node dist/src/apply-types.js --apply  (write changes)
import {
  SSMClient,
  DescribeParametersCommand,
  GetParameterCommand,
  PutParameterCommand,
  type ParameterMetadata,
} from '@aws-sdk/client-ssm';
import { PREFIX, parameterSpecs } from '../config/parameters.js';
import { planTypeChanges, type CurrentParameter } from './plan.js';

const ssm = new SSMClient({});
const apply = process.argv.includes('--apply');

async function loadCurrent(): Promise<CurrentParameter[]> {
  const result: CurrentParameter[] = [];
  let NextToken: string | undefined;
  do {
    const page = await ssm.send(new DescribeParametersCommand({
      ParameterFilters: [{ Key: 'Path', Option: 'Recursive', Values: [PREFIX] }],
      NextToken,
    }));
    for (const p of page.Parameters ?? []) result.push(toCurrent(p));
    NextToken = page.NextToken;
  } while (NextToken);
  return result;
}

function toCurrent(p: ParameterMetadata): CurrentParameter {
  return {
    name: p.Name!,
    type: p.Type as CurrentParameter['type'],
    dataType: p.DataType ?? 'text',
    tier: (p.Tier ?? 'Standard') as CurrentParameter['tier'],
  };
}

const current = await loadCurrent();
const steps = planTypeChanges(parameterSpecs, current);
let failed = 0;

for (const step of steps) {
  if (step.action === 'ok') { console.log(`ok      ${step.name}`); continue; }
  if (step.action === 'skip') { console.log(`skip    ${step.name} (${step.reason})`); continue; }

  const hint = step.needsAdvanced ? ' + tier Advanced' : '';
  console.log(`change  ${step.name}: ${step.from} -> ${step.to}${hint}`);
  if (!apply) continue;

  const meta = current.find((p) => p.name === step.name)!;
  const { Parameter } = await ssm.send(new GetParameterCommand({ Name: step.name }));
  try {
    await ssm.send(new PutParameterCommand({
      Name: step.name,
      Value: Parameter!.Value!,
      Type: meta.type,
      DataType: step.to,
      Overwrite: true,
      ...(step.needsAdvanced ? { Tier: 'Advanced' as const } : {}),
    }));
    console.log('        written (new version)');
  } catch (err) {
    failed++;
    const e = err as Error;
    console.error(`        rejected: ${e.name}: ${e.message}`);
  }
}

if (!apply) console.log('\nPlan only. Start with --apply to write the changes.');
process.exitCode = failed > 0 ? 1 : 0;
Enter fullscreen mode Exit fullscreen mode

Without an argument, the script only shows what it would do. Typical output looks like this:

change  /fahrradverleih/prod/max-ausleihdauer-stunden: text -> integer
ok      /fahrradverleih/prod/preis-pro-stunde
change  /fahrradverleih/prod/wartungsmodus: text -> boolean
skip    /fahrradverleih/prod/support-mail (SecureString: change manually with the right KMS key)
change  /fahrradverleih/prod/zahlungsdienst-url: text -> url + tier Advanced

Plan only. Start with --apply to write the changes.
Enter fullscreen mode Exit fullscreen mode

Read the lines with + tier Advanced carefully: these parameters will cost money after the conversion (more on that in a moment). Only when the plan looks right do you run the script with --apply. If it reports a rejected value, you have found a real configuration error before it hits you in production.

Step 3: Test the planning logic

// src/plan.test.ts
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { planTypeChanges } from './plan.js';

test('changes text to integer', () => {
  const steps = planTypeChanges(
    [{ name: '/a', dataType: 'integer' }],
    [{ name: '/a', type: 'String', dataType: 'text', tier: 'Standard' }],
  );
  assert.deepEqual(steps, [{ action: 'change', name: '/a', from: 'text', to: 'integer', needsAdvanced: false }]);
});

test('flags advanced tier for json on a standard parameter', () => {
  const [step] = planTypeChanges(
    [{ name: '/b', dataType: 'json' }],
    [{ name: '/b', type: 'String', dataType: 'text', tier: 'Standard' }],
  );
  assert.equal(step.action, 'change');
  assert.equal(step.action === 'change' && step.needsAdvanced, true);
});

test('skips SecureString and missing parameters', () => {
  const steps = planTypeChanges(
    [{ name: '/c', dataType: 'email' }, { name: '/d', dataType: 'boolean' }],
    [{ name: '/c', type: 'SecureString', dataType: 'text', tier: 'Standard' }],
  );
  assert.deepEqual(steps.map((s) => s.action), ['skip', 'skip']);
});

test('leaves matching data types alone', () => {
  const [step] = planTypeChanges(
    [{ name: '/e', dataType: 'boolean' }],
    [{ name: '/e', type: 'String', dataType: 'boolean', tier: 'Standard' }],
  );
  assert.equal(step.action, 'ok');
});
Enter fullscreen mode Exit fullscreen mode
npx tsc
node --test dist/src/*.test.js
Enter fullscreen mode Exit fullscreen mode

You should see four green tests. I compiled and tested the code with TypeScript 7.0.2, Node.js 22, aws-cdk-lib 2.275.0 and @aws-sdk/client-ssm 3.1163.0, and checked it with cdk synth. A deployment to a real account belongs in your first trial run, ideally in a test account.

What about the Serverless Framework?

If, like me, you enjoy working with the Serverless Framework, nothing changes on the reading side. ${ssm:/fahrradverleih/prod/max-ausleihdauer-stunden} in serverless.yml still returns a string. I would not create the parameters themselves under resources, though. Instead, I would maintain them with the script above as a separate pipeline step before serverless deploy. That way a wrong value shows up before the functions are even rolled out.

Still converting values in Lambda

Because GetParameter always returns strings, the conversion stays in your handler. The difference: you can now rely on the format being correct. The conversion becomes short and boring, which is exactly how I like configuration code. In this example, the values arrive as environment variables at deploy time via ${ssm:…}.

// src/config.ts
const raw = {
  maxStunden: process.env.MAX_AUSLEIHDAUER_STUNDEN ?? '',
  wartung: process.env.WARTUNGSMODUS ?? '',
};

// The data type guarantees the format; business limits are our job.
const maxStunden = Number.parseInt(raw.maxStunden, 10);
if (!Number.isInteger(maxStunden) || maxStunden < 1 || maxStunden > 168) {
  throw new Error('MAX_AUSLEIHDAUER_STUNDEN must be between 1 and 168');
}

// boolean allows true/false in any of the documented spellings plus 1 and 0.
const wartungsmodus = ['true', '1'].includes(raw.wartung.toLowerCase());

export const config = { maxStunden, wartungsmodus } as const;
Enter fullscreen mode Exit fullscreen mode

Watch out with the boolean data type: Parameter Store accepts TRUE, False, 1 and 0. A simple raw === 'true' would therefore be wrong and would read TRUE as “off”.

What do Parameter Store data types cost?

The data types themselves cost nothing extra. What they require can get expensive, though. According to the pricing page, an advanced parameter costs USD 0.05 per month. More important for serverless: with advanced parameters you also pay USD 0.05 per 10,000 API interactions at standard throughput, while standard parameters are free to read. Interactions are counted per returned parameter.

A rough calculation: a Lambda function that reads a JSON parameter without caching on every invocation and runs one million times a month generates 100 × 0.05 = USD 5 just for reading. With a five-minute cache, almost nothing is left of that. So before you switch a big JSON configuration to json, check that your functions cache properly. The Parameter Store introduction explains how. For complex configuration with validation and gradual rollout, AppConfig is often the better choice anyway.

Common mistakes with the new data types

json on a standard parameter

The most common trap: --data-type json without --tier Advanced. The call fails. If your account uses Intelligent-Tiering, Parameter Store upgrades the parameter automatically. Then nothing fails, but your bill quietly grows.

Leading zeros and plus signs with integer

Values like 007 or +7 are not valid integer values. If you type postal codes or article numbers as numbers, you will hit this. Such values are identifiers and stay text, ideally with an AllowedPattern.

Email with a display name

email only accepts the bare address. Bike Rental <help@example.com> is rejected. Keep the display name in a separate parameter.

Forgetting labels after the conversion

Every conversion via Overwrite creates a new version. Labels, however, are attached to a specific version. If your application reads through a label such as :active, the label still points to the old, untyped version after the conversion. Check this before the run and move the label afterwards.

Trusting the data type in a CloudFormation template

As long as the resource schema only knows text and aws:ec2:image, a DataType: integer in a template may fail validation or not arrive as expected. Check the current schema before your first use instead of relying on a blog post, including this one.

My take

I like this update because it is small and still closes a real source of errors. Configuration errors are among the most annoying outages there are: the code is tested, the review was thorough, and then a single space in the wrong place is enough. Every check that moves closer to the change is a win. Especially in teams where someone without deep context occasionally adjusts a value in the console, this is a safety net that costs nothing.

I see two things critically. First, the release is incomplete as long as CloudFormation does not know the types. If you take infrastructure as code seriously, you now need a workaround, and workarounds tend to stay longer than planned. That is why I would encapsulate the AwsCustomResource as shown above, so you can later swap it for ssm.StringParameter in one place. Second, tying json and url to advanced parameters puts a price tag where nobody expects one. For single, rarely read values that does not matter. For high-throughput functions without a cache, it does.

My suggestion to get started: type all numbers, switches and addresses that run on standard parameters. That is free and done quickly. Only convert JSON configurations if you cache anyway or they are already advanced.

FAQ

Which data types does Parameter Store support?

Besides text, aws:ec2:image and aws:ssm:integration, there are 13 more since November 2026: integer, float, boolean, binary, aws:region, aws:account:id, hostname, email, ipv4-address, ipv6-address, url, json and jsonc.

Can I change the data type of an existing parameter?

Yes, by writing the parameter again with PutParameter, Overwrite and the new data type. This creates a new version. Only aws:ec2:image and aws:ssm:integration cannot be converted.

Do the data types work with SecureString?

Almost all of them, yes. Validation is the same as for String. StringList supports only some types, for example email, aws:account:id and the IP address types, and every item is checked on its own.

Do CloudFormation and CDK support the new data types?

As of November 6, 2026, not officially: the resource schema only allows text and aws:ec2:image. A custom resource that calls PutParameter directly works as a workaround.

Your next step

Run aws ssm describe-parameters with the DataType=text filter across your paths once and mark the three parameters whose misconfiguration would hurt you most. Type those first. I am curious: which configuration value has caused you an outage that a data type would have prevented?

Sources

This post was created with AI assistance.


👉 Next step: Working with AWS CDK? Grab my printable AWS CDK cheat sheet with commands, concepts and a stack skeleton.

📬 Stay updated: New articles on serverless, AI and leading tech teams via RSS, browser push or the weekly newsletter (in German).

Originally published on sascha-ms.de. More articles on serverless, AI in everyday development and leading tech teams: sascha-ms.de/en

Top comments (0)