Skip to content

Instantly share code, notes, and snippets.

@monkey-codes
Last active June 17, 2020 01:03
Show Gist options
  • Select an option

  • Save monkey-codes/7791936dbdfe3c25e256697740a10af8 to your computer and use it in GitHub Desktop.

Select an option

Save monkey-codes/7791936dbdfe3c25e256697740a10af8 to your computer and use it in GitHub Desktop.
CFN Create Database Callout
Resources:
...
CreateDatabaseRole:
Type: 'AWS::IAM::Role'
Properties:
RoleName: !Sub "${AWS::StackName}-CreateDatabase-Lambda"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'sts:AssumeRole'
Principal:
Service: lambda.amazonaws.com
Policies:
- PolicyName: WriteCloudWatchLogs
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: 'arn:aws:logs:*:*:*'
- PolicyName: paramspolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: 'Allow'
Action:
- 'ssm:GetParameters'
- 'ssm:DescribeParameters'
- 'ssm:PutParameter'
- 'ssm:GetParameter'
- 'ssm:DeleteParameter'
- 'ssm:DeleteParameters'
Resource: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*'
- PolicyName: InvokeLambdaFunction
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'lambda:InvokeFunction'
Resource: 'arn:aws:lambda:*:*:function:*'
CreateDatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub '${AWS::StackName}-createdb-sg'
GroupDescription: 'Used to provide lambda access to rds'
VpcId: {'Fn::ImportValue': !Sub '${NetworkStackName}:VpcId'}
SecurityGroupEgress:
- IpProtocol: '-1'
FromPort: '-1'
ToPort: '-1'
CidrIp: 0.0.0.0/0
CreateDatabaseLambda:
Type: 'AWS::Lambda::Function'
Properties:
FunctionName: !Sub "${AWS::StackName}-CreateDatabase"
Runtime: python3.6
Code:
S3Bucket: !Ref CodeBucket
S3Key: !Ref CodeBucketKey
Handler: create_database.handler
Timeout: '180'
VpcConfig:
SecurityGroupIds: [ !Ref CreateDatabaseSecurityGroup ]
SubnetIds:
- {'Fn::ImportValue': !Sub '${NetworkStackName}:PrivateSubnet1'}
- {'Fn::ImportValue': !Sub '${NetworkStackName}:PrivateSubnet2'}
Role: !GetAtt CreateDatabaseRole.Arn
...
@handler.create
def create_database(event, context):
log.info("Creating RDS Database")
handle(event, context)
return {"PhysicalResourceId": "arn:aws:fake:rds-database"}
def handle(event, context):
arguments = event['ResourceProperties']['Arguments']
result = generate_credentials.handle(event, context)
username = result["Data"]["Username"]
password = result["Data"]["Password"]
db_name = arguments["DatabaseName"]
db_host = arguments["DatabaseHost"]
db_port = arguments["DatabasePort"]
db_admin_username = get_parameter_safely(arguments["SSMDBAdminUsernameKey"], False)
db_admin_password = get_parameter_safely(arguments["SSMDBAdminPasswordKey"], True)
create_db(db_host, db_port, db_admin_username, db_admin_password, db_name, username, password)
def create_db(host, port, username, password, db_name, db_user, db_password):
log.info('here')
log.info('username: %s, host: %s, port: %s' % (username, host, port))
con = psycopg2.connect(database="postgres", user=username, password=password, host=host, port=port)
con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
log.info('Connected to db')
if not db_exists(con, db_name):
log.info("Creating database %s" % db_name)
execute_update(con,"CREATE DATABASE %s" % db_name, {})
if not user_exists(con, db_user):
log.info("Creating user %s" % db_user)
params = { "username": db_user, "db_name": db_name, "password": db_password }
execute_update(con, "CREATE USER %(username)s WITH ENCRYPTED PASSWORD '%(password)s'" % params, {})
execute_update(con, "GRANT ALL PRIVILEGES on DATABASE %(db_name)s to %(username)s" % params, {})
con.close()
def db_exists(con, db_name):
cur = con.cursor()
cur.execute("SELECT 1 FROM pg_database WHERE datname = %(db_name)s", {"db_name": db_name})
rows = cur.fetchall()
return len(rows) > 0
def user_exists(con, username):
cur = con.cursor()
cur.execute("SELECT 1 FROM pg_roles WHERE rolname = %(username)s", {"username": username})
rows = cur.fetchall()
return len(rows) > 0
def execute_update(con, query, params):
cur = con.cursor()
cur.execute(query, params)
con.commit()
cur.close()
Resources:
...
CreateServiceDatabase:
Type: 'Custom::CreateDatabaseLambda'
Properties:
ServiceToken: {'Fn::ImportValue': !Sub '${CfnLambdasStackName}:CreateDatabaseLambda'}
StackName: !Sub '${AWS::StackName}'
Arguments:
Hierarchy: !Sub '/${Environment}/${ServiceName}/db'
Username: !Sub '${Environment}_${ServiceName}_user'
SSMUsernameKey: 'username'
SSMPasswordKey: 'password'
KMSKeyId: !Ref KMSKey
DatabaseName: !Sub '${Environment}_${ServiceName}'
DatabaseHost: {'Fn::ImportValue': !Sub '${RDSStackName}:DBClusterEndpointAddress'}
DatabasePort: {'Fn::ImportValue': !Sub '${RDSStackName}:DBClusterPort'}
SSMDBAdminUsernameKey: !Sub '/${Environment}/rds/cluster/username'
SSMDBAdminPasswordKey: !Sub '/${Environment}/rds/cluster/password'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment