Skip to content

Commit

Permalink
feat(ecs): secret JSON key for environment variables
Browse files Browse the repository at this point in the history
Amazon Elastic Container Service now supports reading AWS Secrets Manager secrets from a key within a JSON object.

See https://aws.amazon.com/about-aws/whats-new/2020/02/amazon-ecs-now-supports-aws-secrets-manager-version-and-json-keys/

Closes aws#5665
  • Loading branch information
jogold committed Feb 25, 2020
1 parent 77b998f commit 810e93d
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 2 deletions.
10 changes: 8 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/container-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@ export abstract class Secret {
/**
* Creates a environment variable value from a secret stored in AWS Secrets
* Manager.
*
* @param secret the secret stored in AWS Secrets Manager
* @param key the name of the key in a key-value pair with the value that you
* want to set as the environment variable value. Only values in JSON format
* are supported. If you do not specify a JSON key, then the full contents of
* the secret is used.
*/
public static fromSecretsManager(secret: secretsmanager.ISecret): Secret {
public static fromSecretsManager(secret: secretsmanager.ISecret, key?: string): Secret {
return {
arn: secret.secretArn,
arn: key ? `${secret.secretArn}:${key}::` : secret.secretArn,
grantRead: grantee => secret.grantRead(grantee),
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{
"Resources": {
"SecretA720EF05": {
"Type": "AWS::SecretsManager::Secret",
"Properties": {
"GenerateSecretString": {
"GenerateStringKey": "password",
"SecretStringTemplate": "{\"username\":\"user \"}"
}
}
},
"TaskDefTaskRole1EDB4A67": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"TaskDef54694570": {
"Type": "AWS::ECS::TaskDefinition",
"Properties": {
"ContainerDefinitions": [
{
"Essential": true,
"Image": "amazon/amazon-ecs-sample",
"Name": "web",
"Secrets": [
{
"Name": "PASSWORD",
"ValueFrom": {
"Fn::Join": [
"",
[
{
"Ref": "SecretA720EF05"
},
":password::"
]
]
}
}
]
}
],
"Cpu": "512",
"ExecutionRoleArn": {
"Fn::GetAtt": [
"TaskDefExecutionRoleB4775C97",
"Arn"
]
},
"Family": "awsecsintegsecretjsonkeyTaskDefC01C0E99",
"Memory": "1024",
"NetworkMode": "awsvpc",
"RequiresCompatibilities": [
"FARGATE"
],
"TaskRoleArn": {
"Fn::GetAtt": [
"TaskDefTaskRole1EDB4A67",
"Arn"
]
}
}
},
"TaskDefExecutionRoleB4775C97": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"TaskDefExecutionRoleDefaultPolicy0DBB737A": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "secretsmanager:GetSecretValue",
"Effect": "Allow",
"Resource": {
"Ref": "SecretA720EF05"
}
}
],
"Version": "2012-10-17"
},
"PolicyName": "TaskDefExecutionRoleDefaultPolicy0DBB737A",
"Roles": [
{
"Ref": "TaskDefExecutionRoleB4775C97"
}
]
}
}
}
}
27 changes: 27 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/fargate/integ.secret-json-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
import * as cdk from '@aws-cdk/core';
import * as ecs from '../../lib';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'aws-ecs-integ-secret-json-key');

const secret = new secretsmanager.Secret(stack, 'Secret', {
generateSecretString: {
generateStringKey: 'password',
secretStringTemplate: JSON.stringify({ username: 'user '})
}
});

const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef', {
memoryLimitMiB: 1024,
cpu: 512
});

taskDefinition.addContainer('web', {
image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
secrets: {
PASSWORD: ecs.Secret.fromSecretsManager(secret, 'password')
}
});

app.synth();
44 changes: 44 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/test.container-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,50 @@ export = {

},

'use a specific secret JSON key as environment variable'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');

const secret = new secretsmanager.Secret(stack, 'Secret');

// WHEN
taskDefinition.addContainer('cont', {
image: ecs.ContainerImage.fromRegistry('test'),
memoryLimitMiB: 1024,
secrets: {
SECRET_KEY: ecs.Secret.fromSecretsManager(secret, 'specificKey'),
}
});

// THEN
expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', {
ContainerDefinitions: [
{
Secrets: [
{
Name: 'SECRET_KEY',
ValueFrom: {
'Fn::Join': [
'',
[
{
Ref: 'SecretA720EF05'
},
':specificKey::'
]
]
}
},
]
}
]
}));

test.done();

},

'can add AWS logging to container definition'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down

0 comments on commit 810e93d

Please sign in to comment.