{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Metadata": {
        "AWS::CloudFormation::Designer": {
            "0a162613-8f2e-4864-be99-75d946934a4a": {
                "size": {
                    "width": 350,
                    "height": 440
                },
                "position": {
                    "x": 290,
                    "y": 70
                },
                "z": 1,
                "embeds": [
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2"
                ]
            },
            "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2": {
                "size": {
                    "width": 290,
                    "height": 360
                },
                "position": {
                    "x": 310,
                    "y": 110
                },
                "z": 2,
                "parent": "0a162613-8f2e-4864-be99-75d946934a4a",
                "embeds": [
                    "9d4cbbc2-f521-436d-bb4a-85b82cf22a2a",
                    "464ea4ae-199c-4917-9404-aed674a8615a",
                    "ec256f27-66c3-423c-9d98-b9f0f634e7b8",
                    "4731d93c-f3fc-420a-b535-f0b99840f356",
                    "40c2d4e7-f01a-45b2-8878-a06680aa2216"
                ],
                "dependson": [
                    "0a162613-8f2e-4864-be99-75d946934a4a",
                    "464ea4ae-199c-4917-9404-aed674a8615a"
                ]
            },
            "4731d93c-f3fc-420a-b535-f0b99840f356": {
                "size": {
                    "width": 230,
                    "height": 130
                },
                "position": {
                    "x": 350,
                    "y": 320
                },
                "z": 3,
                "parent": "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                "embeds": [
                    "5262ea47-2337-4be8-a4d1-1f0af38a1731"
                ],
                "iscontainedinside": [
                    "0a162613-8f2e-4864-be99-75d946934a4a"
                ],
                "dependson": [
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2"
                ]
            },
            "5262ea47-2337-4be8-a4d1-1f0af38a1731": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 440,
                    "y": 350
                },
                "z": 4,
                "parent": "4731d93c-f3fc-420a-b535-f0b99840f356",
                "embeds": [],
                "isassociatedwith": [
                    "db7c3441-9f9a-4677-a14d-bccfc06714d1"
                ],
                "dependson": [
                    "4731d93c-f3fc-420a-b535-f0b99840f356",
                    "9d3d19ab-d561-4f59-89de-73498eeeebda",
                    "464ea4ae-199c-4917-9404-aed674a8615a"
                ]
            },
            "464ea4ae-199c-4917-9404-aed674a8615a": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 510,
                    "y": 220
                },
                "z": 3,
                "parent": "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                "embeds": [],
                "dependson": [
                    "0a162613-8f2e-4864-be99-75d946934a4a"
                ]
            },
            "40c2d4e7-f01a-45b2-8878-a06680aa2216": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 430,
                    "y": 140
                },
                "z": 3,
                "parent": "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                "embeds": [],
                "iscontainedinside": [
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                    "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2"
                ],
                "dependson": [
                    "4731d93c-f3fc-420a-b535-f0b99840f356",
                    "9d4cbbc2-f521-436d-bb4a-85b82cf22a2a",
                    "99fce86e-18b8-4b1b-a572-7bef3c5cece7",
                    "58a1ab6f-49ac-4ffa-93c7-3f708bf65871",
                    "ec256f27-66c3-423c-9d98-b9f0f634e7b8"
                ]
            },
            "9d4cbbc2-f521-436d-bb4a-85b82cf22a2a": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 350,
                    "y": 140
                },
                "z": 3,
                "parent": "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                "embeds": []
            },
            "ec256f27-66c3-423c-9d98-b9f0f634e7b8": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 430,
                    "y": 220
                },
                "z": 3,
                "parent": "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2",
                "embeds": [],
                "iscontainedinside": [
                    "0a162613-8f2e-4864-be99-75d946934a4a"
                ]
            },
            "5bb16646-dc1e-4661-9164-6ecc6848dc83": {
                "source": {
                    "id": "4731d93c-f3fc-420a-b535-f0b99840f356"
                },
                "target": {
                    "id": "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2"
                },
                "z": 3
            },
            "99fce86e-18b8-4b1b-a572-7bef3c5cece7": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 150,
                    "y": 250
                },
                "z": 1,
                "embeds": []
            },
            "58a1ab6f-49ac-4ffa-93c7-3f708bf65871": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 150,
                    "y": 170
                },
                "z": 1,
                "embeds": []
            },
            "d3fab7a7-d694-435e-930d-ff7693dffbbc": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 110,
                    "y": 90
                },
                "z": 1,
                "embeds": []
            },
            "2c5cc5a9-5a17-4d54-80ea-56e204c9c1a1": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 70,
                    "y": 170
                },
                "z": 1,
                "embeds": []
            },
            "e81dfbbc-e8ee-4f4b-adb0-b314056ab0b3": {
                "size": {
                    "width": 60,
                    "height": 60
                },
                "position": {
                    "x": 70,
                    "y": 250
                },
                "z": 1,
                "embeds": []
            },
            "9d3d19ab-d561-4f59-89de-73498eeeebda": {
                "source": {
                    "id": "0a162613-8f2e-4864-be99-75d946934a4a"
                },
                "target": {
                    "id": "464ea4ae-199c-4917-9404-aed674a8615a"
                },
                "z": 3
            },
            "361e0035-6c5a-48df-8339-3e31f19bf032": {
                "source": {
                    "id": "9d4cbbc2-f521-436d-bb4a-85b82cf22a2a"
                },
                "target": {
                    "id": "40c2d4e7-f01a-45b2-8878-a06680aa2216"
                },
                "z": 3
            }
        }
    },
    "Resources": {
        "VpnVpc": {
            "Type": "AWS::EC2::VPC",
            "Properties": {
                "CidrBlock": "10.0.0.0/24"
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "0a162613-8f2e-4864-be99-75d946934a4a"
                }
            }
        },
        "VpnSubnet": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VpnVpc"
                },
                "CidrBlock": "10.0.0.0/24",
                "MapPublicIpOnLaunch": true,
                "AvailabilityZone": {
                    "Fn::Sub": "${AWS::Region}a"
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "5198eb6d-da4f-43e2-8a4b-b9bff02b26a2"
                }
            },
            "DependsOn": [
                "VpnVpc",
                "VpcInternetGateway"
            ]
        },
        "VpnRouteTable": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VpnVpc"
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "4731d93c-f3fc-420a-b535-f0b99840f356"
                }
            },
            "DependsOn": [
                "VpnSubnet"
            ]
        },
        "PublicInternetRoute": {
            "Type": "AWS::EC2::Route",
            "Properties": {
                "DestinationCidrBlock": "0.0.0.0/0",
                "RouteTableId": {
                    "Ref": "VpnRouteTable"
                },
                "GatewayId": {
                    "Ref": "VpcInternetGateway"
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "5262ea47-2337-4be8-a4d1-1f0af38a1731"
                }
            },
            "DependsOn": [
                "VpnRouteTable",
                "VpcInternetGateway",
                "InternetGatewayAttachment"
            ]
        },
        "VpnInstance": {
            "Type": "AWS::EC2::Instance",
            "CreationPolicy": {
                "ResourceSignal": {
                    "Timeout": "PT15M"
                }
            },
            "Properties": {
                "UserData": {
                    "Fn::Base64": {
                        "Fn::Join": [
                            "",
                            [
                                "#!/bin/bash -xe\n",
                                "trap 'cfn-signal -e 1 ",
                                " --stack ",
                                {
                                    "Ref": "AWS::StackName"
                                },
                                " --resource VpnInstance ",
                                " --region ",
                                {
                                    "Ref": "AWS::Region"
                                },
                                "' ERR\n",
                                "sleep 60\n",
                                "export DEBIAN_FRONTEND=noninteractive\n",
                                "apt-get -yq update\n",
                                "apt-get -yq install python3-pip\n",
                                "pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz\n",
                                "export VPN_IPSEC_PSK='",
                                {
                                    "Ref": "VpnIpsecPsk"
                                },
                                "'\n",
                                "export VPN_USER='",
                                {
                                    "Ref": "VpnUser"
                                },
                                "'\n",
                                "export VPN_PASSWORD='",
                                {
                                    "Ref": "VpnPassword"
                                },
                                "'\n",
                                "wget -t 3 -T 30 -nv -O vpnsetup.sh https://git.io/vpnsetup\n",
                                "sh vpnsetup.sh\n",
                                "cfn-signal -e 0 ",
                                " --stack ",
                                {
                                    "Ref": "AWS::StackName"
                                },
                                " --resource VpnInstance ",
                                " --region ",
                                {
                                    "Ref": "AWS::Region"
                                },
                                "\n"
                            ]
                        ]
                    }
                },
                "SecurityGroupIds": [
                    {
                        "Fn::GetAtt": [
                            "VpnSecurityGroup",
                            "GroupId"
                        ]
                    }
                ],
                "SubnetId": {
                    "Ref": "VpnSubnet"
                },
                "AvailabilityZone": {
                    "Fn::Sub": "${AWS::Region}a"
                },
                "InstanceType": {
                    "Ref": "InstanceType"
                },
                "KeyName": {
                    "Fn::GetAtt": [
                        "KeyPairInfo",
                        "KeyName"
                    ]
                },
                "ImageId": {
                    "Fn::GetAtt": [
                        "AMIInfo",
                        "AMIId"
                    ]
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "40c2d4e7-f01a-45b2-8878-a06680aa2216"
                }
            },
            "DependsOn": [
                "VpnRouteTable",
                "VpnServerVolume",
                "KeyPairCreation",
                "AMIInfoFunction",
                "VpnSecurityGroup"
            ]
        },
        "VpnSecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "The VPN Security Group, allowing ingress UDP traffic at port 4500 and 500.",
                "GroupName": "VpnSecurityGroup",
                "VpcId": {
                    "Ref": "VpnVpc"
                },
                "SecurityGroupIngress": [
                    {
                        "CidrIp": "0.0.0.0/0",
                        "IpProtocol": "tcp",
                        "FromPort": 22,
                        "ToPort": 22
                    },
                    {
                        "CidrIp": "0.0.0.0/0",
                        "IpProtocol": "udp",
                        "FromPort": 500,
                        "ToPort": 500
                    },
                    {
                        "CidrIp": "0.0.0.0/0",
                        "IpProtocol": "udp",
                        "FromPort": 4500,
                        "ToPort": 4500
                    }
                ],
                "SecurityGroupEgress": [
                    {
                        "CidrIp": "0.0.0.0/0",
                        "IpProtocol": -1
                    }
                ]
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "ec256f27-66c3-423c-9d98-b9f0f634e7b8"
                }
            }
        },
        "VpnServerVolume": {
            "Type": "AWS::EC2::Volume",
            "Properties": {
                "AvailabilityZone": {
                    "Fn::Sub": "${AWS::Region}a"
                },
                "Size": 8
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "9d4cbbc2-f521-436d-bb4a-85b82cf22a2a"
                }
            }
        },
        "VpcInternetGateway": {
            "Type": "AWS::EC2::InternetGateway",
            "Properties": {},
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "464ea4ae-199c-4917-9404-aed674a8615a"
                }
            },
            "DependsOn": [
                "VpnVpc"
            ]
        },
        "EC2SRTA4VJU5": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "RouteTableId": {
                    "Ref": "VpnRouteTable"
                },
                "SubnetId": {
                    "Ref": "VpnSubnet"
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "5bb16646-dc1e-4661-9164-6ecc6848dc83"
                }
            }
        },
        "KeyPairCreation": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Handler": "index.handler",
                "Runtime": "python3.7",
                "Role": {
                    "Fn::GetAtt": [
                        "LambdaExecutionRole",
                        "Arn"
                    ]
                },
                "Timeout": 30,
                "Code": {
                    "ZipFile": {
                        "Fn::Join": [
                            "\n",
                            [
                                "import boto3",
                                "import cfnresponse",
                                "import string",
                                "import random",
                                "'''",
                                "This python program should be embedded into its designated cloudformation",
                                "template as the inline code of one of the lambda functions.",
                                "'''",
                                "def handler(event, context):",
                                "   try:",
                                "       keyName = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))",
                                "       region = event['ResourceProperties']['Region']",
                                "       ec2 = boto3.client('ec2',region)",
                                "       response = ec2.create_key_pair(",
                                "           KeyName=keyName",
                                "       )",
                                "       keyMaterial = response['KeyMaterial']",
                                "       cfnresponse.send(event, context, cfnresponse.SUCCESS, {'KeyMaterial':keyMaterial, 'KeyName':keyName}, 'KeyPairInfo')",
                                "   except Exception:",
                                "       cfnresponse.send(event, context, cfnresponse.FAILED, {})"
                            ]
                        ]
                    }
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "99fce86e-18b8-4b1b-a572-7bef3c5cece7"
                }
            },
            "DependsOn": [
                "LambdaExecutionRole"
            ]
        },
        "AMIInfo": {
            "Type": "Custom::AMIInfo",
            "Properties": {
                "Region": {
                    "Ref": "AWS::Region"
                },
                "ServiceToken": {
                    "Fn::GetAtt": [
                        "AMIInfoFunction",
                        "Arn"
                    ]
                },
                "Distribution": {
                    "Ref": "OS"
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "2c5cc5a9-5a17-4d54-80ea-56e204c9c1a1"
                }
            },
            "DependsOn": [
                "AMIInfoFunction"
            ]
        },
        "AMIInfoFunction": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Handler": "index.handler",
                "Runtime": "python3.7",
                "Role": {
                    "Fn::GetAtt": [
                        "LambdaExecutionRole",
                        "Arn"
                    ]
                },
                "Code": {
                    "ZipFile": {
                        "Fn::Join": [
                            "\n",
                            [
                                "import boto3",
                                "import cfnresponse",
                                "def creation_date(e):",
                                "   return e['CreationDate']",
                                "",
                                "def handler(event, context):",
                                "   try:",
                                "       regionName = event['ResourceProperties']['Region']",
                                "       distribution = event['ResourceProperties']['Distribution']",
                                "       ec2 = boto3.client('ec2',regionName)",
                                "       IAMName = ''",
                                "       if distribution == 'Ubuntu16.04':",
                                "           IAMName = 'ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*'",
                                "       elif distribution == 'Ubuntu18.04':",
                                "           IAMName = 'ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*'",
                                "       elif distribution == 'Ubuntu20.04':",
                                "           IAMName = 'ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*'",
                                "       elif distribution == 'Debian9':",
                                "           IAMName = 'debian-stretch-hvm-x86_64-gp2-*'",
                                "       response = ec2.describe_images(Filters=[{'Name':'name', 'Values':[IAMName]}], Owners=['099720109477', '379101102735'])",
                                "       images = response['Images']",
                                "       images.sort(key=creation_date,reverse=True)",
                                "       AMIId = images[0]['ImageId']",
                                "       cfnresponse.send(event, context, cfnresponse.SUCCESS, {'AMIId':AMIId}, 'AMIInfo')",
                                "   except Exception:",
                                "       cfnresponse.send(event, context, cfnresponse.FAILED, {})"
                            ]
                        ]
                    }
                },
                "Timeout": 30
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "58a1ab6f-49ac-4ffa-93c7-3f708bf65871"
                }
            },
            "DependsOn": [
                "LambdaExecutionRole"
            ]
        },
        "LambdaExecutionRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": "lambda.amazonaws.com"
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        },
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "ec2.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
                "Path": "/",
                "Policies": [
                    {
                        "PolicyName": "root",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": "*",
                                    "Resource": "*"
                                }
                            ]
                        }
                    }
                ]
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "d3fab7a7-d694-435e-930d-ff7693dffbbc"
                }
            }
        },
        "KeyPairInfo": {
            "Type": "Custom::KeyPairInfo",
            "Properties": {
                "Region": {
                    "Ref": "AWS::Region"
                },
                "ServiceToken": {
                    "Fn::GetAtt": [
                        "KeyPairCreation",
                        "Arn"
                    ]
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "e81dfbbc-e8ee-4f4b-adb0-b314056ab0b3"
                }
            },
            "DependsOn": [
                "KeyPairCreation"
            ]
        },
        "InternetGatewayAttachment": {
            "Type": "AWS::EC2::VPCGatewayAttachment",
            "Properties": {
                "InternetGatewayId": {
                    "Ref": "VpcInternetGateway"
                },
                "VpcId": {
                    "Ref": "VpnVpc"
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "9d3d19ab-d561-4f59-89de-73498eeeebda"
                }
            }
        },
        "EC2VA41EUF": {
            "Type": "AWS::EC2::VolumeAttachment",
            "Properties": {
                "Device": "/dev/sdh",
                "VolumeId": {
                    "Ref": "VpnServerVolume"
                },
                "InstanceId": {
                    "Ref": "VpnInstance"
                }
            },
            "Metadata": {
                "AWS::CloudFormation::Designer": {
                    "id": "361e0035-6c5a-48df-8339-3e31f19bf032"
                }
            }
        }
    },
    "Parameters": {
        "VpnUser": {
            "Type": "String",
            "Description": "Your VPN username"
        },
        "VpnIpsecPsk": {
            "Type": "String",
            "Description": "Your VPN IPsec PSK (pre-shared key)"
        },
        "VpnPassword": {
            "Type": "String",
            "Description": "Your VPN password"
        },
        "OS": {
            "Type": "String",
            "Description": "The OS of your VPN server. Default: Ubuntu 20.04",
            "Default": "Ubuntu20.04",
            "AllowedValues": [
                "Ubuntu20.04",
                "Ubuntu18.04",
                "Ubuntu16.04",
                "Debian9"
            ]
        },
        "InstanceType": {
            "Type": "String",
            "Description": "The instance type of your VPN server. Using t2.micro may qualify for the AWS Free Tier.",
            "AllowedValues": [
                "t2.micro",
                "t3.nano",
                "m5.large",
                "t3.micro",
                "t3.small",
                "t2.nano",
                "t2.small",
                "t3a.nano",
                "t3a.micro",
                "t3a.small",
                "m5a.large",
                "t1.micro"
            ],
            "Default": "t2.micro"
        }
    },
    "Outputs": {
        "VPNAddress": {
            "Description": "This is the public IP of your newly-launched VPN server.",
            "Value": {
                "Fn::GetAtt": [
                    "VpnInstance",
                    "PublicIp"
                ]
            }
        },
        "VPNUsername": {
            "Description": "Your VPN username",
            "Value": {
                "Ref": "VpnUser"
            }
        },
        "VPNPassword": {
            "Description": "Your VPN password",
            "Value": {
                "Ref": "VpnPassword"
            }
        },
        "VPNKey": {
            "Description": "Your VPN IPsec PSK (pre-shared key)",
            "Value": {
                "Ref": "VpnIpsecPsk"
            }
        },
        "EC2PrivateKeyMaterial": {
            "Description": "The content of your private key for accessing the VPN server via SSH. Save it as a file for use when connecting.",
            "Value": {
                "Fn::GetAtt": [
                    "KeyPairInfo",
                    "KeyMaterial"
                ]
            }
        },
        "NextStep": {
            "Description": "Learn how to configure VPN clients.",
            "Value": "https://github.com/hwdsl2/setup-ipsec-vpn#next-steps"
        }
    }
}
