Skip to content

Instantly share code, notes, and snippets.

@AdamLuchjenbroers
Created April 29, 2021 00:36
Show Gist options
  • Save AdamLuchjenbroers/3165ab18bb0ee9da95ad6bf514f415e0 to your computer and use it in GitHub Desktop.
Save AdamLuchjenbroers/3165ab18bb0ee9da95ad6bf514f415e0 to your computer and use it in GitHub Desktop.
Simple Cloudformation Macro to take a CIDR block and translate into subnet / netmask format
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Macros and other utility components for use by other CheapSeats stacks",
"Resources": {
"MacroLambdaRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"RoleName": "Cheapseats-MacroLambdaRole",
"Path": "/cheapseats/",
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
]
}
},
"MacroLambdaPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "Cheapseats-MacroLambdaRole",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MacroCFAccess",
"Effect": "Allow",
"Action": "cloudformation:ListExports",
"Resource": "*"
}
]
},
"Roles": [
{
"Ref": "MacroLambdaRole"
}
]
}
},
"NetworkInfoLambda": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": "./NetworkInfo",
"Handler": "NetworkInfo.macro_handler",
"Runtime": "python3.6",
"Role": {
"Fn::GetAtt": [
"MacroLambdaRole",
"Arn"
]
}
}
},
"MacroLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/Cheapseats/Macros"
}
},
"NetworkInfoMacro": {
"Type": "AWS::CloudFormation::Macro",
"Properties": {
"Name": "NetworkInfo",
"Description": "Utility Macro to derive network information from CIDR Range",
"FunctionName": {
"Fn::GetAtt": [
"NetworkInfoLambda",
"Arn"
]
},
"LogGroupName": {
"Ref": "MacroLogGroup"
}
}
},
"MacroIAMPolicy": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"ManagedPolicyName": "Cheapseats-UtilsMacroAccess",
"Path": "/cheapseats/",
"Description": "Grants required permissions to use Cheapseats macros",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MacroCFAccess",
"Effect": "Allow",
"Action": "cloudformation:CreateChangeSet",
"Resource": [
{
"Fn::Sub": "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:transform/${NetworkInfoMacro}"
}
]
},
{
"Sid": "MacroLambdaAccess",
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": [
{
"Fn::GetAtt": [
"NetworkInfoLambda",
"Arn"
]
}
]
},
{
"Sid": "MacroLoggingAccess",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams",
"logs:DescribeLogGroups"
],
"Resource": [
{
"Fn::GetAtt": [
"MacroLogGroup",
"Arn"
]
},
{
"Fn::Sub": [
"${LogArn}:*",
{
"LogArn": {
"Fn::GetAtt": [
"MacroLogGroup",
"Arn"
]
}
}
]
}
]
}
]
}
}
}
},
"Outputs": {
"MacroPolicy": {
"Description": "IAM Policy for using Cloudformation Macros",
"Value": {
"Ref": "MacroIAMPolicy"
},
"Export": {
"Name": "CheapSeats-Policy-MacroAccess"
}
}
}
}
import boto3
import jmespath
import re
cfn = boto3.client('cloudformation')
ip_pattern = re.compile('(?P<subnet>([0-9]+\.){3}([0-9]+))\/(?P<size>[0-9]+)$')
def parse_cidr(cidr):
ip_info = ip_pattern.search(cidr)
if ip_info:
subnet = ip_info.group('subnet')
size = int(ip_info.group('size'))
return (subnet, size)
else:
raise ValueError('Malformed or invalid Subnet CIDR: %s' % cidr)
def compute_netmask(size):
mask = (0xffffffff >> (32 - size)) << (32 - size)
return (str( (0xff000000 & mask) >> 24) + '.' +
str( (0x00ff0000 & mask) >> 16) + '.' +
str( (0x0000ff00 & mask) >> 8) + '.' +
str( (0x000000ff & mask)))
def fetch_stack_export(export_name):
list = cfn.list_exports()
data = jmespath.search('Exports[*].[Name, Value]', list)
for row in data:
if row[0] == export_name:
return row[1]
def macro_handler(event, context):
try:
if 'CIDR' in event['params']:
cidr = event['params']['CIDR']
elif 'CIDR-export' in event['params']:
cidr = fetch_stack_export(event['params']['CIDR-export'])
(subnet, size) = parse_cidr(cidr)
return {
"requestId" : event["requestId"]
, "status" : "success"
, "fragment": {
"CIDR" : cidr,
"subnet" : subnet,
"netmask" : compute_netmask(size)
}
}
except Exception as e:
return {
"requestId" : event["requestId"]
, "status" : "failure"
, "fragment": {}
, "errorMessage" : str(e)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment