Skip to content

Instantly share code, notes, and snippets.

@reidca
Last active November 19, 2020 13:27
Show Gist options
  • Save reidca/c181d2e959ea070bb902073a690a7c22 to your computer and use it in GitHub Desktop.
Save reidca/c181d2e959ea070bb902073a690a7c22 to your computer and use it in GitHub Desktop.
import json
import os
import os.path
import sys
import logging
from botocore.exceptions import ClientError
from str2bool import str2bool
from crhelper import CfnResource
helper = CfnResource(json_logging=False, log_level='DEBUG',
boto_level='CRITICAL')
logger = logging.getLogger(__name__)
try:
# Init code goes here
# The code below is used for loading the bundled version of boto rather than the one in the lambda runtime environment
envLambdaTaskRoot = os.environ["LAMBDA_TASK_ROOT"]
print("LAMBDA_TASK_ROOT env var:" + os.environ["LAMBDA_TASK_ROOT"])
print("sys.path:" + str(sys.path))
sys.path.insert(0, envLambdaTaskRoot+"/NewBotoVersion")
print("sys.path:"+str(sys.path))
import botocore
import boto3
print("boto3 version:"+boto3.__version__)
print("botocore version:"+botocore.__version__)
ec2_client = boto3.client('ec2')
pass
except Exception as e:
helper.init_failure(e)
def cast_tunnel_options(tunnel_options):
# Go through the dictionary and convert any strings that can be numbers to ints and if the item is a list then go through the items in the list doing the same
for key in tunnel_options:
if isinstance(tunnel_options[key], list):
for d in tunnel_options[key]:
if isinstance(d, dict):
if d["Value"].isnumeric():
d["Value"] = int(d["Value"])
else:
if tunnel_options[key].isnumeric():
tunnel_options[key] = int(tunnel_options[key])
return tunnel_options
@helper.create
@helper.update
def update_create(event, context):
props = event['ResourceProperties']
kwargs = {}
# Mandatory properties
if "CustomerGatewayId" in props:
kwargs["CustomerGatewayId"] = props["CustomerGatewayId"]
if "VpnGatewayId" in props:
kwargs["VpnGatewayId"] = props["VpnGatewayId"]
if "TransitGatewayId" in props:
kwargs["TransitGatewayId"] = props["TransitGatewayId"]
# Does resource already exist? If so update instead of create new
if event["RequestType"] == "Update":
vpn_connection_id = event['PhysicalResourceId']
kwargs["VpnConnectionId"] = vpn_connection_id
#Check to see if any of the details are different or whether there is nothing
response = ec2_client.describe_vpn_connections(
VpnConnectionIds=[vpn_connection_id]
)
#Assume no work to do
update_flag = False
current_vpn_connection = response["VpnConnections"][0]
if "CustomerGatewayId" in kwargs:
if not("CustomerGatewayId" in current_vpn_connection) or (current_vpn_connection["CustomerGatewayId"] != kwargs["CustomerGatewayId"]):
update_flag = True
else:
del kwargs["CustomerGatewayId"]
if "VpnGatewayId" in kwargs:
if not("VpnGatewayId" in current_vpn_connection) or (current_vpn_connection["VpnGatewayId"] != kwargs["VpnGatewayId"]):
update_flag = True
else:
del kwargs["VpnGatewayId"]
if "TransitGatewayId" in kwargs:
if not("TransitGatewayId" in current_vpn_connection) or (current_vpn_connection["TransitGatewayId"] != kwargs["TransitGatewayId"]):
update_flag = True
else:
del kwargs["TransitGatewayId"]
if update_flag:
logger.info(
f"Updating VPN connection '{vpn_connection_id}' using parameters {kwargs}")
response = ec2_client.modify_vpn_connection(**kwargs)
logger.info(f"VPN connection modified successfully'")
else:
logger.info(f"VPN connection '{vpn_connection_id}' does not require updating")
else:
# Some properties can only be specified on the create
kwargs["Type"] = props["Type"]
options = {}
if 'StaticRoutesOnly' in props:
options["StaticRoutesOnly"] = str2bool(props["StaticRoutesOnly"])
if 'EnableAcceleration' in props:
options["EnableAcceleration "] = str2bool(
props["EnableAcceleration"])
#Tunnel options can be passed a list (separate options for each tunnel, so we need to call the cast method on each list item)
if 'TunnelOptions' in props:
options["TunnelOptions"] = list(map(cast_tunnel_options,props["TunnelOptions"]))
# If any options have been specified then set the key on the main arguments
if options:
kwargs["Options"] = options
if 'Tags' in props:
kwargs["TagSpecifications"] = [
{'ResourceType': 'vpn-connection', 'Tags': props["Tags"]}]
logger.info(f"Creating VPN connection with properties {kwargs}")
response = ec2_client.create_vpn_connection(**kwargs)
vpn_connection_id = response["VpnConnection"]["VpnConnectionId"]
logger.info(
f"A Vpn connection with id '{vpn_connection_id}' has been created")
physical_resource_id = vpn_connection_id
response_data = {}
response_data["VpnConnectionId"] = vpn_connection_id
helper.Data.update(response_data)
return physical_resource_id
@helper.delete
def delete(event, context):
"""
Place your code to handle Delete events here
To return a failure to CloudFormation simply raise an exception, the exception message will be sent to CloudFormation Events.
"""
vpn_connection_id = event["PhysicalResourceId"]
logger.info(f"Deleting Vpn Connection with id '{vpn_connection_id}'")
try:
response = ec2_client.delete_vpn_connection(
VpnConnectionId=vpn_connection_id)
except botocore.exceptions.ClientError as error:
if error.response['Error']['Code'] == 'InvalidVpnConnectionID.NotFound':
logger.warning(
f"A Vpn connection with id '{vpn_connection_id}' does not exist")
else:
raise error
def handler(event, context):
"""
Main handler function, passes off it's work to crhelper's cfn_handler
"""
helper(event, context)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment