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.
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:GetParameterandssm:DescribeParameters - a current AWS CLI v2 (older versions already know the
--data-typeoption 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
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
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
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"
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
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']);
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',
});
}
}
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');
npx tsc
npx cdk synth
npx cdk deploy FahrradverleihConfig
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',
};
});
}
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;
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.
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');
});
npx tsc
node --test dist/src/*.test.js
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;
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
- AWS What’s New: Parameter Store allows organization and validation with new data types (Nov 5, 2026)
- AWS Systems Manager User Guide: Parameter data types
- API Reference: PutParameter
- API Reference: ParameterStringFilter
- CloudFormation: AWS::SSM::Parameter
- AWS Systems Manager Pricing
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)