Skip to content

Commit

Permalink
feat(ssm): Add L2 resource for SSM Parameters (#1515)
Browse files Browse the repository at this point in the history
Specialized classes for StringParameter and StringListParameter, and
their interface counterparts.
  • Loading branch information
RomainMuller committed Feb 5, 2019
1 parent 299fb6a commit 9858a64
Show file tree
Hide file tree
Showing 7 changed files with 522 additions and 1 deletion.
23 changes: 23 additions & 0 deletions packages/@aws-cdk/aws-ssm/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,25 @@
## The CDK Construct Library for AWS Systems Manager
This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project.

### Installation
Install the module:

```console
$ npm i @aws-cdk/aws-ssm
```

Import it into your code:

```ts
import ssm = require('@aws-cdk/aws-ssm');
```

### Creating SSM Parameters
You can use either the `ssm.StringParameter` or `ssm.StringListParameter` (AWS CloudFormation does not support creating
*Secret-String* SSM parameters, as those would require the secret value to be inlined in the template document) classes
to register new SSM Parameters into your application:

[creating SSM parameters](test/integ.parameter.lit.ts)

When specifying an `allowedPattern`, the values provided as string literals are validated against the pattern and an
exception is raised if a value provided does not comply.
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-ssm/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './parameter';
export * from './parameter-store-string';

// AWS::SSM CloudFormation Resources:
Expand Down
220 changes: 220 additions & 0 deletions packages/@aws-cdk/aws-ssm/lib/parameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import iam = require('@aws-cdk/aws-iam');
import cdk = require('@aws-cdk/cdk');
import ssm = require('./ssm.generated');

/**
* An SSM Parameter reference.
*/
export interface IParameter extends cdk.IConstruct {
/**
* The ARN of the SSM Parameter resource.
*/
readonly parameterArn: string;

/**
* The name of the SSM Parameter resource.
*/
readonly parameterName: string;

/**
* The type of the SSM Parameter resource.
*/
readonly parameterType: string;

/**
* Grants read (DescribeParameter, GetParameter, GetParameterHistory) permissions on the SSM Parameter.
*
* @param grantee the role to be granted read-only access to the parameter.
*/
grantRead(grantee: iam.IPrincipal): void;

/**
* Grants write (PutParameter) permissions on the SSM Parameter.
*
* @param grantee the role to be granted write access to the parameter.
*/
grantWrite(grantee: iam.IPrincipal): void;
}

/**
* A String SSM Parameter.
*/
export interface IStringParameter extends IParameter {
/**
* The parameter value. Value must not nest another parameter. Do not use {{}} in the value.
*/
readonly parameterValue: string;
}

/**
* A StringList SSM Parameter.
*/
export interface IStringListParameter extends IParameter {
/**
* The parameter value. Value must not nest another parameter. Do not use {{}} in the value. Values in the array
* cannot contain commas (``,``).
*/
readonly parameterValue: string[];
}

/**
* Properties needed to create a new SSM Parameter.
*/
export interface ParameterProps {
/**
* A regular expression used to validate the parameter value. For example, for String types with values restricted to
* numbers, you can specify the following: ``^\d+$``
*
* @default no validation is performed
*/
allowedPattern?: string;

/**
* Information about the parameter that you want to add to the system.
*
* @default none
*/
description?: string;

/**
* The name of the parameter.
*
* @default a name will be generated by CloudFormation
*/
name?: string;
}

/**
* Properties needed to create a String SSM parameter.
*/
export interface StringParameterProps extends ParameterProps {
/**
* The value of the parameter. It may not reference another parameter and ``{{}}`` cannot be used in the value.
*/
value: string;
}

/**
* Properties needed to create a StringList SSM Parameter
*/
export interface StringListParameterProps extends ParameterProps {
/**
* The values of the parameter. It may not reference another parameter and ``{{}}`` cannot be used in the value.
*/
value: string[];
}

/**
* Basic features shared across all types of SSM Parameters.
*/
export abstract class ParameterBase extends cdk.Construct implements IParameter {
public abstract readonly parameterName: string;
public abstract readonly parameterType: string;

constructor(scope: cdk.Construct, id: string, _props: ParameterProps) {
super(scope, id);
}

public get parameterArn(): string {
return cdk.Stack.find(this).formatArn({
service: 'ssm',
resource: 'parameter',
resourceName: this.parameterName,
});
}

public grantRead(grantee: iam.IPrincipal): void {
grantee.addToPolicy(new iam.PolicyStatement()
.allow()
.addActions('ssm:DescribeParameters', 'ssm:GetParameter', 'ssm:GetParameterHistory')
.addResource(this.parameterArn));
}

public grantWrite(grantee: iam.IPrincipal): void {
grantee.addToPolicy(new iam.PolicyStatement()
.allow()
.addAction('ssm:PutParameter')
.addResource(this.parameterArn));
}
}

/**
* Creates a new String SSM Parameter.
*/
export class StringParameter extends ParameterBase implements IStringParameter {
public readonly parameterName: string;
public readonly parameterType: string;
public readonly parameterValue: string;

constructor(scope: cdk.Construct, id: string, props: StringParameterProps) {
super(scope, id, props);

if (props.allowedPattern) {
_assertValidValue(props.value, props.allowedPattern);
}

const resource = new ssm.CfnParameter(this, 'Resource', {
allowedPattern: props.allowedPattern,
description: props.description,
name: props.name,
type: 'String',
value: props.value,
});

this.parameterName = resource.parameterName;
this.parameterType = resource.parameterType;
this.parameterValue = resource.parameterValue;
}
}

/**
* Creates a new StringList SSM Parameter.
*/
export class StringListParameter extends ParameterBase implements IStringListParameter {
public readonly parameterName: string;
public readonly parameterType: string;
public readonly parameterValue: string[];

constructor(scope: cdk.Construct, id: string, props: StringListParameterProps) {
super(scope, id, props);

if (props.value.find(str => !cdk.unresolved(str) && str.indexOf(',') !== -1)) {
throw new Error('Values of a StringList SSM Parameter cannot contain the \',\' character. Use a string parameter instead.');
}

if (props.allowedPattern && !cdk.unresolved(props.value)) {
props.value.forEach(str => _assertValidValue(str, props.allowedPattern!));
}

const resource = new ssm.CfnParameter(this, 'Resource', {
allowedPattern: props.allowedPattern,
description: props.description,
name: props.name,
type: 'StringList',
value: props.value.join(','),
});

this.parameterName = resource.parameterName;
this.parameterType = resource.parameterType;
this.parameterValue = cdk.Fn.split(',', resource.parameterValue);
}
}

/**
* Validates whether a supplied value conforms to the allowedPattern, granted neither is an unresolved token.
*
* @param value the value to be validated.
* @param allowedPattern the regular expression to use for validation.
*
* @throws if the ``value`` does not conform to the ``allowedPattern`` and neither is an unresolved token (per
* ``cdk.unresolved``).
*/
function _assertValidValue(value: string, allowedPattern: string): void {
if (cdk.unresolved(value) || cdk.unresolved(allowedPattern)) {
// Unable to perform validations against unresolved tokens
return;
}
if (!new RegExp(allowedPattern).test(value)) {
throw new Error(`The supplied value (${value}) does not match the specified allowedPattern (${allowedPattern})`);
}
}
19 changes: 18 additions & 1 deletion packages/@aws-cdk/aws-ssm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@
},
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/aws-iam": "^0.23.0",
"@aws-cdk/assert": "^0.23.0",
"cdk-build-tools": "^0.23.0",
"cdk-integ-tools": "^0.23.0",
"cfn2ts": "^0.23.0",
"pkglint": "^0.23.0"
},
Expand All @@ -64,9 +66,24 @@
},
"homepage": "https://github.com/awslabs/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-iam": "^0.23.0",
"@aws-cdk/cdk": "^0.23.0"
},
"engines": {
"node": ">= 8.10.0"
},
"awslint": {
"exclude": [
"export:@aws-cdk/aws-ssm.IParameter",
"import-props-interface:@aws-cdk/aws-ssm.ParameterImportProps",
"resource-attribute:@aws-cdk/aws-ssm.IParameter.parameterValue",

"*:@aws-cdk/aws-ssm.Association",
"*:@aws-cdk/aws-ssm.Document",
"*:@aws-cdk/aws-ssm.MaintenanceWindow",
"*:@aws-cdk/aws-ssm.MaintenanceWindowTask",
"*:@aws-cdk/aws-ssm.PatchBaseline",
"*:@aws-cdk/aws-ssm.ResourceDataSync"
]
}
}
}
Loading

0 comments on commit 9858a64

Please sign in to comment.