├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── cform └── vpn-monitor.template └── code └── vpn-monitor.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Amazon Software License 2 | This Amazon Software License ("License") governs your use, reproduction, and distribution of the accompanying software as specified below. 3 | 1. Definitions 4 | "Licensor" means any person or entity that distributes its Work. 5 | 6 | "Software" means the original work of authorship made available under this License. 7 | 8 | "Work" means the Software and any additions to or derivative works of the Software that are made available under this License. 9 | 10 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the meaning as provided under U.S. copyright law; provided, however, that for the purposes of this License, derivative works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work. 11 | 12 | Works, including the Software, are "made available" under this License by including in or with the Work either (a) a copyright notice referencing the applicability of this License to the Work, or (b) a copy of this License. 13 | 2. License Grants 14 | 2.1 Copyright Grant. Subject to the terms and conditions of this License, each Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free, copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense and distribute its Work and any resulting derivative works in any form. 15 | 2.2 Patent Grant. Subject to the terms and conditions of this License, each Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free patent license to make, have made, use, sell, offer for sale, import, and otherwise transfer its Work, in whole or in part. The foregoing license applies only to the patent claims licensable by Licensor that would be infringed by Licensor"s Work (or portion thereof) individually and excluding any combinations with any other materials or technology. 16 | 3. Limitations 17 | 3.1 Redistribution. You may reproduce or distribute the Work only if (a) you do so under this License, (b) you include a complete copy of this License with your distribution, and (c) you retain without modification any copyright, patent, trademark, or attribution notices that are present in the Work. 18 | 3.2 Derivative Works. You may specify that additional or different terms apply to the use, reproduction, and distribution of your derivative works of the Work ("Your Terms") only if (a) Your Terms provide that the use limitation in Section 3.3 applies to your derivative works, and (b) you identify the specific derivative works that are subject to Your Terms. Notwithstanding Your Terms, this License (including the redistribution requirements in Section 3.1) will continue to apply to the Work itself. 19 | 3.3 Use Limitation. The Work and any derivative works thereof only may be used or intended for use with the web services, computing platforms or applications provided by Amazon.com, Inc. or its affiliates, including Amazon Web Services, Inc. 20 | 3.4 Patent Claims. If you bring or threaten to bring a patent claim against any Licensor (including any claim, cross-claim or counterclaim in a lawsuit) to enforce any patents that you allege are infringed by any Work, then your rights under this License from such Licensor (including the grants in Sections 2.1 and 2.2) will terminate immediately. 21 | 3.5 Trademarks. This License does not grant any rights to use any Licensor"s or its affiliates" names, logos, or trademarks, except as necessary to reproduce the notices described in this License. 22 | 3.6 Termination. If you violate any term of this License, then your rights under this License (including the grants in Sections 2.1 and 2.2) will terminate immediately. 23 | 4. Disclaimer of Warranty. 24 | THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF M ERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER THIS LICENSE. SOME STATES" CONSUMER LAWS DO NOT ALLOW EXCLUSION OF AN IMPLIED WARRANTY, SO THIS DISCLAIMER MAY NOT APPLY TO YOU. 25 | 5. Limitation of Liability. 26 | EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER COMM ERCIAL DAMAGES OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 27 | Effective Date - April 18, 2008 (Copyright) 2008 Amazon.com, Inc. or its affiliates. All rights reserved. -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | AWS VPN Monitor 2 | Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-vpn-monitor 2 | 3 | The [AWS VPN Monitor](https://aws.amazon.com/answers/vpn-monitor) is an AWS solution that gives customers an easy way to monitor the status of all VPN connections and tunnels in any AWS Region in their account, and also to record, store, and view two-weeks of historical data. With this capability, customers can identify and troubleshoot issues to ensure that their VPN connections operate correctly. 4 | 5 | Source code for the AWS solution "AWS VPN Monitor". 6 | 7 | ## Cloudformation templates 8 | 9 | - cform/vpn-monitor.template 10 | 11 | ## Lambda source code 12 | 13 | - code/vpn-monitor.py 14 | 15 | *** 16 | 17 | Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 18 | 19 | Licensed under the Amazon Software License (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 20 | 21 | http://aws.amazon.com/asl/ 22 | 23 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and limitations under the License. 24 | -------------------------------------------------------------------------------- /cform/vpn-monitor.template: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "(SO0004) - VPN Monitor: This template implements a VPN connection monitoring solution that creates CloudWatch custom metrics for all VPN connections in this account.", 4 | "Parameters": { 5 | "CWEventSchedule": { 6 | "Description": "Choose between 1-minute (default) or 5-minute interval", 7 | "Type": "String", 8 | "Default": "cron(* * * * ? *)", 9 | "AllowedValues": [ 10 | "cron(* * * * ? *)", 11 | "cron(0/5 * * * ? *)" 12 | ] 13 | }, 14 | "SendAnonymousData": { 15 | "Description": "Send anonymous data to AWS", 16 | "Type": "String", 17 | "Default": "Yes", 18 | "AllowedValues": [ 19 | "Yes", 20 | "No" 21 | ] 22 | } 23 | }, 24 | "Metadata": { 25 | "AWS::CloudFormation::Interface": { 26 | "ParameterGroups": [ 27 | { 28 | "Label": { 29 | "default": "CloudWatch Event Schedule Configuration" 30 | }, 31 | "Parameters": [ 32 | "CWEventSchedule" 33 | ] 34 | }, 35 | { 36 | "Label": { 37 | "default": "Anonymous Metrics Request" 38 | }, 39 | "Parameters": [ 40 | "SendAnonymousData" 41 | ] 42 | } 43 | ] 44 | } 45 | }, 46 | "Resources": { 47 | "vpnMonitorRole": { 48 | "Type": "AWS::IAM::Role", 49 | "Properties": { 50 | "AssumeRolePolicyDocument": { 51 | "Version": "2012-10-17", 52 | "Statement": [ 53 | { 54 | "Effect": "Allow", 55 | "Principal": { 56 | "Service": "lambda.amazonaws.com" 57 | }, 58 | "Action": "sts:AssumeRole" 59 | } 60 | ] 61 | }, 62 | "Path": "/", 63 | "Policies": [ 64 | { 65 | "PolicyName": "vpnMonitorPermissions", 66 | "PolicyDocument": { 67 | "Version": "2012-10-17", 68 | "Statement": [ 69 | { 70 | "Effect": "Allow", 71 | "Action": [ 72 | "logs:CreateLogGroup", 73 | "logs:CreateLogStream", 74 | "logs:PutLogEvents" 75 | ], 76 | "Resource": "arn:aws:logs:*:*:*" 77 | }, 78 | { 79 | "Effect": "Allow", 80 | "Action": [ 81 | "ec2:DescribeVpnConnections", 82 | "ec2:DescribeRegions", 83 | "cloudwatch:PutMetricData", 84 | "cloudformation:DescribeStacks" 85 | ], 86 | "Resource": "*" 87 | } 88 | ] 89 | } 90 | } 91 | ] 92 | } 93 | }, 94 | "vpnTunnelStatus": { 95 | "Type": "AWS::Lambda::Function", 96 | "Properties": { 97 | "Handler": "vpn-monitor.lambda_handler", 98 | "Role": { 99 | "Fn::GetAtt": [ 100 | "vpnMonitorRole", 101 | "Arn" 102 | ] 103 | }, 104 | "Code": { 105 | "S3Bucket": { 106 | "Fn::Join": [ 107 | "", 108 | [ 109 | "solutions-builder-", 110 | { 111 | "Ref": "AWS::Region" 112 | } 113 | ] 114 | ] 115 | }, 116 | "S3Key": "vpn-monitor/v1/vpn-monitor.zip" 117 | }, 118 | "Runtime": "python2.7", 119 | "Timeout": "300" 120 | } 121 | }, 122 | "ScheduledRule": { 123 | "Type": "AWS::Events::Rule", 124 | "Properties": { 125 | "Description": "Rule to trigger VPN Monitor function on a schedule", 126 | "ScheduleExpression": { 127 | "Ref": "CWEventSchedule" 128 | }, 129 | "State": "ENABLED", 130 | "Targets": [ 131 | { 132 | "Arn": { 133 | "Fn::GetAtt": [ 134 | "vpnTunnelStatus", 135 | "Arn" 136 | ] 137 | }, 138 | "Id": "TargetFunctionV1" 139 | } 140 | ] 141 | } 142 | }, 143 | "PermissionForEventsToInvokeLambda": { 144 | "Type": "AWS::Lambda::Permission", 145 | "Properties": { 146 | "FunctionName": { 147 | "Ref": "vpnTunnelStatus" 148 | }, 149 | "Action": "lambda:InvokeFunction", 150 | "Principal": "events.amazonaws.com", 151 | "SourceArn": { 152 | "Fn::GetAtt": [ 153 | "ScheduledRule", 154 | "Arn" 155 | ] 156 | } 157 | } 158 | }, 159 | "SolutionHelperRole": { 160 | "Type": "AWS::IAM::Role", 161 | "Properties": { 162 | "AssumeRolePolicyDocument": { 163 | "Version": "2012-10-17", 164 | "Statement": [ 165 | { 166 | "Effect": "Allow", 167 | "Principal": { 168 | "Service": "lambda.amazonaws.com" 169 | }, 170 | "Action": "sts:AssumeRole" 171 | } 172 | ] 173 | }, 174 | "Path": "/", 175 | "Policies": [ 176 | { 177 | "PolicyName": "Solution_Helper_Permissions", 178 | "PolicyDocument": { 179 | "Version": "2012-10-17", 180 | "Statement": [ 181 | { 182 | "Effect": "Allow", 183 | "Action": [ 184 | "logs:CreateLogGroup", 185 | "logs:CreateLogStream", 186 | "logs:PutLogEvents" 187 | ], 188 | "Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*" 189 | }, 190 | { 191 | "Effect": "Allow", 192 | "Action": [ 193 | "lambda:AddPermission", 194 | "lambda:CreateFunction", 195 | "lambda:DeleteFunction", 196 | "lambda:GetFunction", 197 | "lambda:UpdateFunctionCode", 198 | "lambda:UpdateFunctionConfiguration", 199 | "iam:PassRole" 200 | ], 201 | "Resource": "*" 202 | } 203 | ] 204 | } 205 | } 206 | ] 207 | } 208 | }, 209 | "SolutionHelper": { 210 | "Type": "AWS::Lambda::Function", 211 | "Properties": { 212 | "Handler": "solution-helper.lambda_handler", 213 | "Role": { 214 | "Fn::GetAtt": [ 215 | "SolutionHelperRole", 216 | "Arn" 217 | ] 218 | }, 219 | "Description": "This function creates a CloudFormation custom lambda resource that creates custom lambda functions by finding and replacing specific values from existing lambda function code.", 220 | "Code": { 221 | "S3Bucket": { 222 | "Fn::Join": [ 223 | "", 224 | [ 225 | "solutions-", 226 | { 227 | "Ref": "AWS::Region" 228 | } 229 | ] 230 | ] 231 | }, 232 | "S3Key": "library/solution-helper/v1/solution-helper.zip" 233 | }, 234 | "Runtime": "python2.7", 235 | "Timeout": "60" 236 | } 237 | }, 238 | "CreateUniqueID": { 239 | "Type": "Custom::CreateUUID", 240 | "Properties": { 241 | "ServiceToken": { 242 | "Fn::GetAtt": [ 243 | "SolutionHelper", 244 | "Arn" 245 | ] 246 | }, 247 | "Region": { 248 | "Ref": "AWS::Region" 249 | }, 250 | "CreateUniqueID": "true" 251 | } 252 | } 253 | }, 254 | "Outputs": { 255 | "UUID": { 256 | "Description": "Newly created random UUID.", 257 | "Value": { 258 | "Fn::GetAtt": [ 259 | "CreateUniqueID", 260 | "UUID" 261 | ] 262 | } 263 | }, 264 | "AnonymousData": { 265 | "Description": "Send Anonymous Data", 266 | "Value": { 267 | "Ref": "SendAnonymousData" 268 | } 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /code/vpn-monitor.py: -------------------------------------------------------------------------------- 1 | ###################################################################################################################### 2 | # Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 3 | # # 4 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in compliance # 5 | # with the License. A copy of the License is located at # 6 | # # 7 | # http://aws.amazon.com/asl/ # 8 | # # 9 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES # 10 | # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions # 11 | # and limitations under the License. # 12 | ###################################################################################################################### 13 | 14 | import json 15 | import boto3 16 | import logging 17 | import datetime 18 | from urllib2 import Request 19 | from urllib2 import urlopen 20 | 21 | log = logging.getLogger() 22 | log.setLevel(logging.INFO) 23 | 24 | log.debug('Loading function') 25 | cw = boto3.client('cloudwatch') 26 | 27 | 28 | # Save the connection status in the CloudWatch Custom Metric 29 | def putCloudWatchMetric(metricName, value, vgw, cgw, region): 30 | cw.put_metric_data( 31 | Namespace='VPNStatus', 32 | MetricData=[{ 33 | 'MetricName': metricName, 34 | 'Value': value, 35 | 'Unit': 'Count', 36 | 'Dimensions': [{ 37 | 'Name': 'VGW', 38 | 'Value': vgw 39 | }, 40 | { 41 | 'Name': 'CGW', 42 | 'Value': cgw 43 | }, 44 | { 45 | 'Name': 'Region', 46 | 'Value': region 47 | }] 48 | }] 49 | ) 50 | 51 | 52 | def lambda_handler(event, context): 53 | # Create connections 54 | ec2 = boto3.client('ec2') 55 | AWS_Regions = ec2.describe_regions()['Regions'] 56 | cf = boto3.client('cloudformation') 57 | 58 | # Declare variables 59 | numConnections = 0 60 | countDict = {} 61 | regionDict = {} 62 | regionsLabelDict = {} 63 | postDict = {} 64 | outputs = {} 65 | TimeNow = datetime.datetime.utcnow().isoformat() 66 | TimeStamp = str(TimeNow) 67 | 68 | # Read output variables from CloudFormation Template 69 | stack_name = context.invoked_function_arn.split(':')[6].rsplit('-', 2)[0] 70 | response = cf.describe_stacks(StackName=stack_name) 71 | for e in response['Stacks'][0]['Outputs']: 72 | outputs[e['OutputKey']] = e['OutputValue'] 73 | uuid = outputs['UUID'] 74 | sendData = outputs['AnonymousData'] 75 | 76 | # Check VPN connections status in all the regions 77 | for region in AWS_Regions: 78 | try: 79 | ec2 = boto3.client('ec2', region_name=region['RegionName']) 80 | awsregion = region['RegionName'] 81 | vpns = ec2.describe_vpn_connections()['VpnConnections'] 82 | connections = 0 83 | for vpn in vpns: 84 | if vpn['State'] == "available": 85 | numConnections += 1 86 | connections += 1 87 | active_tunnels = 0 88 | if vpn['VgwTelemetry'][0]['Status'] == "UP": 89 | active_tunnels += 1 90 | if vpn['VgwTelemetry'][1]['Status'] == "UP": 91 | active_tunnels += 1 92 | log.info('{} VPN ID: {}, State: {}, Tunnel0: {}, Tunnel1: {} -- {} active tunnels'.format(region['RegionName'], vpn['VpnConnectionId'],vpn['State'],vpn['VgwTelemetry'][0]['Status'],vpn['VgwTelemetry'][1]['Status'], active_tunnels)) 93 | putCloudWatchMetric(vpn['VpnConnectionId'], active_tunnels, vpn['VpnGatewayId'], vpn['CustomerGatewayId'], region['RegionName']) 94 | # Build anonymous data 95 | if sendData == "Yes": 96 | countDict['vpn_connections'] = connections 97 | regionDict[awsregion] = dict(countDict) 98 | except Exception as e: 99 | log.error("Exception: "+str(e)) 100 | continue 101 | 102 | # Send anonymous data 103 | if sendData == "Yes": 104 | regionsLabelDict['regions'] = regionDict 105 | postDict['Data'] = regionsLabelDict 106 | postDict['TimeStamp'] = TimeStamp 107 | postDict['Solution'] = 'SO0004' 108 | postDict['UUID'] = uuid 109 | 110 | # API Gateway URL to make HTTP POST call 111 | url = 'https://metrics.awssolutionsbuilder.com/generic' 112 | data = json.dumps(postDict) 113 | headers = {'content-type': 'application/json'} 114 | req = Request(url, data, headers) 115 | rsp = urlopen(req) 116 | content = rsp.read() 117 | rspcode = rsp.getcode() 118 | log.debug('Response Code: {}'.format(rspcode)) 119 | log.debug('Response Content: {}'.format(content)) 120 | return numConnections 121 | --------------------------------------------------------------------------------