Skip to content

Instantly share code, notes, and snippets.

@talawahtech
Last active February 5, 2019 16:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save talawahtech/4827c773df7d0032f64b1d6be79197d3 to your computer and use it in GitHub Desktop.
Save talawahtech/4827c773df7d0032f64b1d6be79197d3 to your computer and use it in GitHub Desktop.
Generates a random password using SecretsManager.getRandomPassword() and stores it in the Parameter Store
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Generates a random password and stores it in the SSM Parameter Store'
Resources:
PasswordGeneratorRole:
Type: 'AWS::IAM::Role'
Properties:
Path: '/'
ManagedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']
Policies:
- PolicyName: 'GeneratePassword'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'secretsmanager:GetRandomPassword'
Resource: '*'
- PolicyName: 'CreateSsmParams'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: ['ssm:PutParameter', 'ssm:DeleteParameter', 'kms:Encrypt']
Resource: "*"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: 'lambda.amazonaws.com'
Action: 'sts:AssumeRole'
PasswordGeneratorLambda:
Type: 'AWS::Lambda::Function'
Properties:
Description: 'Generates random password'
Handler: 'index.handler'
Role: !GetAtt PasswordGeneratorRole.Arn
Code:
ZipFile: |
const response = require('cfn-response');
const AWS = require('aws-sdk');
const ssm = new AWS.SSM();
const secretsManager = new AWS.SecretsManager();
exports.handler = (event, context) => {
if (event.RequestType == 'Delete') {
// Remove param when CloudFormation deletes the resource. The param name is the PhysicalResourceId
ssm.deleteParameter({ Name: event.PhysicalResourceId }).promise()
.then((data) => {
return response.send(event, context, response.SUCCESS, data);
}).catch((err)=> {
return response.send(event, context, response.FAILED, err);
});
}
else{ // Create or Update. Update (only happens when param name changes) will return a new physical id which will cause CF to delete the old one
let responseData;
secretsManager.getRandomPassword({ PasswordLength: 45, ExcludePunctuation: true }).promise()
.then((data) => {
const password = data.RandomPassword.substring(0, 32); // We only really wanted 32 chars for the password
const randomString = data.RandomPassword.substring(32); // Last 13 used to add randomness to the SSM param name to avoid deletion on replacement
const paramName = event.ResourceProperties.ParameterNamePrefix + '-' + randomString;
responseData = {
ParameterName: paramName,
EncodedParameterName: encodeURIComponent(encodeURIComponent(paramName)), // Double encoded to work with AWS console
Password: password
}
const params = {
Name: paramName,
Type: 'SecureString',
Value: password,
Overwrite: true
};
return ssm.putParameter(params).promise();
}).then(() => {
return response.send(event, context, response.SUCCESS, responseData, responseData.ParameterName); // Use param name as PhysicalResourceId
}).catch((err)=> {
return response.send(event, context, response.FAILED, err);
});
}
};
Runtime: 'nodejs6.10'
Timeout: '30'
GeneratedPassword:
Type: 'AWS::CloudFormation::CustomResource'
Properties:
ServiceToken: !GetAtt PasswordGeneratorLambda.Arn
ParameterNamePrefix: '/mystack/database/master-password'
Outputs:
ParameterName:
Value: !GetAtt GeneratedPassword.ParameterName
Password:
Value: !GetAtt GeneratedPassword.Password
ConsoleLink:
Value: !Sub 'https://console.aws.amazon.com/systems-manager/parameters/${GeneratedPassword.EncodedParameterName}/description?region=${AWS::Region}'
@talawahtech
Copy link
Author

Updated code to no longer use the backported getRandomPassword(). Lambda has been updated to an SDK version that supports it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment