Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ssm): Add L2 resource for SSM Parameters #1515

Merged
merged 5 commits into from
Feb 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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+$``
*
eladb marked this conversation as resolved.
Show resolved Hide resolved
* @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 {
eladb marked this conversation as resolved.
Show resolved Hide resolved
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',
RomainMuller marked this conversation as resolved.
Show resolved Hide resolved
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!));
RomainMuller marked this conversation as resolved.
Show resolved Hide resolved
}

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