AWS Serverless Application Model: Here we go!

AWS Serverless Application Model (SAM) was released a couple months ago. The punch line of this new release in my mind is the ability to version your lambda function code and your cloudformation template next to each other. The idea being to have completely packaged serverless application that deploy from a single repository.

I spent an afternoon playing around with AWS SAM, and I’m already a pretty big fan. It makes deploying lambda functions a lot easier, especially when you have different accounts you want to use them in.

The example below is to create a lambda function that tags EBS volumes as they become available

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
EbsVolumeAvailableTagger:
Type: AWS::Serverless::Function
Properties:
Handler: ebs_available_date_tagger.lambda_handler
Role: !GetAtt EbsCleanerIAMRole.Arn
Runtime: python2.7
IAMEbsVolumeListTagPolicy:
Type: "AWS::IAM::Policy"
DependsOn: EbsCleanerIAMRole
Properties:
PolicyName: !Sub "Role=EBSCleaner,Env=${AccountParameter},Service=Lambda,Rights=RW"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "ec2:CreateTags"
- "ec2:DeleteTags"
- "ec2:DescribeTags"
- "ec2:DescribeVolumeAttribute"
- "ec2:DescribeVolumeStatus"
- "ec2:DescribeVolumes"
Resource:
- "*"
Roles:
- !Ref EbsCleanerIAMRole
EbsCleanerIAMRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: !Sub "Role=EbsCleaner,Env=${AccountParameter},Service=Lambda"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
Parameters:
AccountParameter:
Type: String
Default: NoPHI
AllowedValues:
- Prod
- Staging
- Corporate
Description: Enter the account where this lambda function is being created. Will
be used to properly name the created IAM role

And then the python that it runs

import boto3
import re
import logging
import time

def lambda_handler(event, context):

# Number of days to wait before deleting a volume

volumeDaysOld = 30

# Get a cloudwatch logger
logger = logging.getLogger('EbsVolumeCleanup')
logger.setLevel(logging.DEBUG)

# Obtain boto3 resources
logger.info('Getting boto 3 resources')
opsworksClient = boto3.client('opsworks')
ec2Client = boto3.client('ec2')

availableVolumes = ec2Client.describe_volumes(Filters=[{'Name':'status','Values':['available']}])

availableVolumesToTag = []

for volume in availableVolumes['Volumes']:
logger.info(volume)
if 'Tags' in volume:
tags = volume['Tags']
availableDate = (tag for tag in tags if tag['Key'] == 'volumeAvailableDate').next()
if availableDate:
logger.info('Volume was available' + availableDate['Value'])
else:
logger.info('Volume not yet tagged')
availableVolumesToTag.append(volume['VolumeId'])
else:
availableVolumesToTag.append(volume['VolumeId'])

logger.info('Volumes to be tagged available: ' + "-" + str(len(availableVolumesToTag)) + " " + "|".join(availableVolumesToTag))
if availableVolumesToTag:
ec2Client.create_tags(Resources=availableVolumesToTag,Tags=[{'Key':'volumeAvailableDate','Value':time.strftime("%d/%m/%Y")}])

return 0

If you put the two of these into a directory together, you can use the aws cloudformation package and deploy CLI commands to push them to your account.

aws cloudformation package --template-file ec2-management-cft.yml --output-template-file instance-management-cft-staging.yml --s3-bucket cft-deployment-bucket --s3-prefix "lambda/ec2-management

aws cloudformation deploy --template-file instance-management-cft-sandbox.yml --stack-name Sandbox-InstanceManagement --capabilities CAPABILITY_NAMED_IAM --parameter-overrides AccountParameter=Staging

Those commands will package your lambda function, inserting the correct CodeUri property.

Getting an AWS IAM Certificate ARN

I was recently working on a cloudformation template that needed an ELB with an HTTPS listener. My company already has a wildcard cert uploaded to IAM for use in staging environments, so I wanted to use that cert rather than create a new one.

The classic load balancer and the newer Application Load Balancer look a little different for creating HTTPS listeners, but both require you to include the certificate ARN in your template.

I spent some time poking around in the console, looking for how to find the ARN of a certificate you’ve uploaded with no success. As far as I can tell, there’s no where besides editing an ELB listener to see the certificates that you’ve uploaded.

Finally I turned to the AWS CLI and found “get-server-certificate” which returns the ARN of a certificate uploaded to IAM.

If you already have the AWS CLI setup with your secret keys, it’s pretty straightforward

aws iam get-server-certificate --server-certificate-name wildcard-****

And it will kick back the relevant data

As it turns out, the ARN of a certificate is just the combination of your account number and the name you gave it.

And lastly, because I insist on believing that lots of people use Powershell for AWS management when maybe none of you do, here’s the same command in good ol’ PS.

(Get-IAMServerCertificate -servercertificatename wildcard-***********).ServerCertificateMetadata

Interestingly, the powershell tools have a few different objects built in so you won’t get the metadata by default.