def add_alarm(self, description, metric, namespace, threshold, instance): """ Adds an alarm to this sns topic. For this to be useful, subscriptions must be added to this topic using the add_subscription function above. :param description: A description for the Alarm being created :param metric: The metric to track and alarm on. :param namespace: the namespace that the provided metric belongs to. :param threshold: The threshold to alarm on :param instance: an instance to refer to in particular """ alarm = cloudwatch.Alarm( '{0}Alarm{1}'.format(self.trop_topic.title, str(len(self.alarms))), AlarmDescription=description, AlarmActions=[Ref(self.trop_topic.title)], OKActions=[Ref(self.trop_topic.title)], MetricName=metric, Namespace=namespace, Threshold=threshold, ComparisonOperator="GreaterThanOrEqualToThreshold", EvaluationPeriods='1', Period='300', Statistic='Sum', DependsOn=self.trop_topic.title, Dimensions=[ cloudwatch.MetricDimension(Name='InstanceId', Value=Ref(instance)) ]) self.alarms.append(alarm) self.template.add_resource(alarm)
def add_autorecovery_to_instance(template_obj, instance): """Creates a cloudwatch alarm to recover a failed instance.""" # This url was really helpful in configuring this: # http://docs.aws.amazon.com/ \ # AWSEC2/latest/UserGuide/UsingAlarmActions.html template_obj.add_resource( cloudwatch.Alarm( 'InstanceAutoRecovery%s' % (instance.name, ), ActionsEnabled='true', AlarmActions=[ Join('', ['arn:aws:automate:', Ref('AWS::Region'), ':ec2:recover']) ], AlarmDescription='Recover failed instance', ComparisonOperator='GreaterThanThreshold', Dimensions=[ cloudwatch.MetricDimension(Name='InstanceId', Value=Ref(instance)) ], EvaluationPeriods=2, MetricName='StatusCheckFailed_System', Namespace='AWS/EC2', Period=60, Statistic='Average', Threshold='0', ))
def add_resource(self): """Add resources to template.""" template = self.template variables = self.get_variables() template.add_resource( cloudwatch.Alarm( 'CloudWatchAlarm', AlarmActions=[ Join(':', [ 'arn:aws:automate', Ref('AWS::Region'), 'ec2:recover' ]) ], AlarmDescription='Trigger a recovery when the instance' ' status check fails for 15 consecutive' ' minutes.', AlarmName=variables['AlarmName'].ref, ComparisonOperator='GreaterThanThreshold', Dimensions=[ cloudwatch.MetricDimension( Name='InstanceId', Value=variables['InstanceId'].ref) ], EvaluationPeriods=15, MetricName='EC2_SystemStatusCheck_Failed', Namespace='AWS/EC2', Period=60, Statistic='Minimum', Threshold='0', ))
def _build_alarm(self, template, topics): t = template elb_name = self.elb_stack.stack_name elb_output = self.elb_stack.output_elb() if elb_output in t.parameters: elb_ref = Ref(t.parameters[elb_output]) else: elb_ref = Ref(t.add_parameter(Parameter(elb_output, Type='String'))) t.add_resource( alarm.Alarm('{}HealthHosts'.format(elb_name), AlarmDescription='{} Health Hosts'.format(elb_name), MetricName='HealthyHostCount', Namespace='AWS/ELB', Statistic='Average', Period=str(60), Dimensions=[ alarm.MetricDimension(Name='LoadBalancerName', Value=elb_ref) ], EvaluationPeriods=str(3), Threshold=str(2), ComparisonOperator='LessThanThreshold', AlarmActions=topics, InsufficientDataActions=[], OKActions=topics, TreatMissingData='missing'))
def _build_alarm(self, template, topics): instance_output = self.ec2_stack.output_instance() if instance_output in template.parameters: instance_param = template.parameters[instance_output] else: instance_param = template.add_parameter( Parameter(instance_output, Type='String')) template.add_resource( alarm.Alarm( '{}InstanceFailAlarm'.format(self.ec2_stack.get_stack_name()), AlarmDescription='Alarm for instance failure', Namespace='AWS/EC2', MetricName="StatusCheckFailed_System", Dimensions=[ alarm.MetricDimension(Name="InstanceId", Value=Ref(instance_param)) ], Statistic="Average", Period=str(self.alarm_params['period']), Threshold=str(self.alarm_params['threshold']), EvaluationPeriods=str(self.alarm_params['evaluations']), ComparisonOperator="GreaterThanThreshold", AlarmActions=topics, InsufficientDataActions=topics, OKActions=topics))
def _build_alarm(self, t, topics): defaults = self.get_defaults() default_attrs = self.get_default_attrs() instance_output = self.ec2_stack.output_instance() if instance_output in t.parameters: instance_param = t.parameters[instance_output] else: instance_param = t.add_parameter( Parameter(instance_output, Type='String')) a = t.add_resource( alarm.Alarm( '{}EC2HighCpuAlarm'.format(self.name), AlarmDescription='Alarm for high CPU (>{})'.format( defaults['threshold']), Period=str(defaults['period']), Threshold=str(defaults['threshold']), EvaluationPeriods=str(defaults['evaluations']), AlarmActions=topics, InsufficientDataActions=topics, OKActions=topics, Dimensions=[ alarm.MetricDimension(Name='InstanceId', Value=Ref(instance_param)) ], )) for k, v in default_attrs.items(): setattr(a, k, v)
def create_elasticache_cloudwatch_alarms(self, elasticache_cache_cluster): for index in [1, 2]: self.add_resource( cloudwatch.Alarm( 'alarmCacheCluster{0:0>3}CPUUtilization'.format(index), AlarmDescription='Cache cluster CPU utilization', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=300, Threshold='75', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='CPUUtilization', Namespace='AWS/ElastiCache', Dimensions=[ cloudwatch.MetricDimension( 'metricCacheClusterName', Name='CacheClusterId', Value=Join('-', [ Ref(elasticache_cache_cluster), '{0:0>3}'.format(index) ])) ], )) self.add_resource( cloudwatch.Alarm( 'alarmCacheCluster{0:0>3}FreeableMemory'.format(index), AlarmDescription='Cache cluster freeable memory', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=60, Threshold=str(int(5e+06)), # 5MB in bytes EvaluationPeriods=1, ComparisonOperator='LessThanThreshold', MetricName='FreeableMemory', Namespace='AWS/ElastiCache', Dimensions=[ cloudwatch.MetricDimension( 'metricCacheClusterName', Name='CacheClusterId', Value=Join('-', [ Ref(elasticache_cache_cluster), '{0:0>3}'.format(index) ])) ], ))
def __init__(self, key): for n, v in getattr(cfg, key).items(): if not ('Enabled' in v and v['Enabled'] is True): continue resname = f'{key}{n}' # parameters p_EvaluationPeriods = Parameter(f'{resname}EvaluationPeriods') p_EvaluationPeriods.Description = ( 'Number of periods for alarm evaluation - 0 to disable - ' 'empty for default based on env/role') p_EvaluationPeriods.AllowedValues = [ '', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ] p_Period = Parameter(f'{resname}Period') p_Period.Description = ( 'Period lenght in seconds (multiple of 60) - ' 'empty for default based on env/role') p_Period.AllowedValues = ['', '60', '120', '180', '240', '300'] p_Threshold = Parameter(f'{resname}Threshold') p_Threshold.Description = ('Threshold for alarm triggering - ' 'empty for default based on env/role') p_Threshold.AllowedValues = [ '', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55', '60', '65', '70', '75', '80', '85', '90', '95', '100' ] add_obj([ p_EvaluationPeriods, # p_Period, p_Threshold, ]) # conditions c_EvaluationPeriods = get_condition(resname, 'not_equals', '0', f'{resname}EvaluationPeriods') add_obj(c_EvaluationPeriods) # resources r_Alarm = clw.Alarm(resname) auto_get_props(r_Alarm, v, recurse=True) if hasattr(r_Alarm, 'Metrics'): r_Alarm.Period = Ref('AWS::NoValue') if not hasattr(r_Alarm, 'Condition'): r_Alarm.Condition = resname add_obj(r_Alarm) # outputs o_Alarm = Output(resname) o_Alarm.Value = get_subvalue( 'Period=${1M},EvaluationPeriods=${2M},Threshold=${3M}', [ f'{resname}Period', f'{resname}EvaluationPeriods', f'{resname}Threshold' ]) add_obj(o_Alarm)
def create_cloud_watch_resources(self, app_server_lb): self.add_resource(cw.Alarm( 'alarmAppServerBackend4XX', AlarmDescription='Application server backend 4XXs', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Sum', Period=300, Threshold='20', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='HTTPCode_Backend_4XX', Namespace='AWS/ELB', Dimensions=[ cw.MetricDimension( 'metricLoadBalancerName', Name='LoadBalancerName', Value=Ref(app_server_lb) ) ], )) self.add_resource(cw.Alarm( 'alarmAppServerBackend5XX', AlarmDescription='Application server backend 5XXs', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Sum', Period=60, Threshold='0', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='HTTPCode_Backend_5XX', Namespace='AWS/ELB', Dimensions=[ cw.MetricDimension( 'metricLoadBalancerName', Name='LoadBalancerName', Value=Ref(app_server_lb) ) ], ))
def add_alarm(self, name, dimensions, alarm, description, namespace, threshold, comparison_operator, statistic, metric_name): return self.add_resource( cloudwatch.Alarm(name, MetricName=metric_name, ComparisonOperator=comparison_operator, Period=300, EvaluationPeriods=1, Statistic=statistic, Namespace=namespace, AlarmDescription=description, Dimensions=dimensions, Threshold=threshold, AlarmActions=[alarm]))
def add_cpu_usage_alarm_ondemand_high(self): self.CPUUsageAlarmHighOndemand = self.template.add_resource( cloudwatch.Alarm( "CPUUsageAlarmHighOndemand", AlarmDescription="Alarm if CPUUtilization go above 60%", Namespace="AWS/EC2", MetricName="CPUUtilization", Dimensions=[ cloudwatch.MetricDimension( Name="AutoScalingGroupName", Value=Ref(self.AutoscalingGroupOnDemand)), ], Statistic="Average", Period="300", EvaluationPeriods="2", Threshold="60", ComparisonOperator="GreaterThanOrEqualToThreshold", AlarmActions=[Ref(self.scalingPolicyOndemandUp)]))
def add_cpu_usage_alarm_ondemand_low(self): self.CPUUsageAlarmLowOndemand = self.template.add_resource( cloudwatch.Alarm( "CPUUsageAlarmLowOndemand", AlarmDescription="Alarm if CPUUtilization go below 5%", Namespace="AWS/EC2", MetricName="CPUUtilization", Dimensions=[ cloudwatch.MetricDimension( Name="AutoScalingGroupName", Value=Ref(self.AutoscalingGroupOnDemand)), ], Statistic="Average", Period="300", EvaluationPeriods="1", Threshold="5", ComparisonOperator="LessThanThreshold", AlarmActions=[Ref(self.scalingPolicyOndemandDown)]))
def add_cpu_credit_balance_ondemand_low(self): self.CPUCreditAlarmLowOndemand = self.template.add_resource( cloudwatch.Alarm( "CPUCreditAlarmLowOndemand", AlarmDescription="Alarm if CPU Credits go below 10", Namespace="AWS/EC2", MetricName="CPUCreditBalance", Dimensions=[ cloudwatch.MetricDimension( Name="AutoScalingGroupName", Value=Ref(self.AutoscalingGroupOnDemand)), ], Statistic="Average", Period="60", EvaluationPeriods="10", Threshold="10", ComparisonOperator="LessThanThreshold", AlarmActions=[Ref(self.scalingPolicyOndemandUp)]))
def create_cloud_watch_resources(self, worker_auto_scaling_group): self.add_resource( cw.Alarm('alarmWorkerCPU', AlarmDescription='Worker scaling group high CPU', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=300, Threshold='50', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='CPUUtilization', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension( 'metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(worker_auto_scaling_group)) ]))
def _inject_alarms(template, packages): for package in packages: cannot_package_alarm = cloudwatch.Alarm( 'CannotPackage%s' % package, AlarmDescription=( 'Alarm that triggers if Chalice fails to package %s.' % package ), ComparisonOperator='LessThanThreshold', EvaluationPeriods=1, Period=3600, MetricName='package', Namespace='ChalicePackageCanary', Threshold='1', Statistic='Minimum', Dimensions=[cloudwatch.MetricDimension( Name='Name', Value=package )] ) template.add_resource(cannot_package_alarm)
def add_resources(self): """Add resources to template.""" template = self.template variables = self.get_variables() template.add_resource( cloudwatch.Alarm( 'Alarm', AlarmDescription=If( 'AlarmDescriptionSpecified', variables['AlarmDescription'].ref, Ref('AWS::NoValue') ), AlarmName=If( 'AlarmNameSpecified', variables['AlarmName'].ref, Ref('AWS::NoValue') ), Namespace=variables['Namespace'].ref, Statistic=variables['Statistic'].ref, Period=variables['StatisticPeriod'].ref, EvaluationPeriods=variables['EvaluationPeriods'].ref, Threshold=variables['AlarmThreshold'].ref, AlarmActions=variables['AlertTopicArn'].ref, OKActions=variables['AlertTopicArn'].ref, ComparisonOperator=variables['ComparisonOperator'].ref, Dimensions=If( 'MetricDimensionSpecified', [cloudwatch.MetricDimension( Name=variables['DimensionName'].ref, Value=variables['DimensionValue'].ref)], Ref('AWS::NoValue')), MetricName=variables['MetricName'].ref, TreatMissingData=If( 'TreatMissingDataSpecified', variables['TreatMissingData'].ref, Ref('AWS::NoValue') ) ) )
t.add_description( "A set of serverless Lambda functions that shut down services that exceed" " billing limits. **WARNING** This template creates Lambda functions and " "Amazon CloudWatch alarms. You will be billed for the AWS resources used " "if you create a stack from this template.") if ALL_SERVICES_BILLING_ALARM_LIMIT: exceed_billing_limit_alarm = t.add_resource( cloudwatch.Alarm( 'AllServicesBillingAlarm', AlarmDescription='Alarm if all services exceed billing limit', Namespace='AWS/Billing', MetricName='EstimatedCharges', Dimensions=[ cloudwatch.MetricDimension(Name='Currency', Value='USD'), ], Statistic='Maximum', Period='300', EvaluationPeriods='1', Threshold=str(ALL_SERVICES_BILLING_ALARM_LIMIT), ComparisonOperator='GreaterThanThreshold', #AlarmActions=Ref('BillingAlarmNotification'), #InsufficientDataActions=Ref('BillingAlarmNotification'), )) print(t.to_yaml()) #with open('aws_kill_switch_cloudfront.yaml', 'w') as f: # f.write(t.to_yaml())
def __init__(self, parameters, vpc, loadbalancer): """ :type parameters Parameters :type vpc VPC :type loadbalancer LoadBalancer """ super(EC2, self).__init__() # Ec2 instance self.instance_role = iam.Role( "InstanceRole", AssumeRolePolicyDocument=aws.Policy(Statement=[ aws.Statement(Effect=aws.Allow, Action=[sts.AssumeRole], Principal=aws.Principal("Service", ["ec2.amazonaws.com"])) ]), Path="/", ) self.instance_role_policy = iam.PolicyType( "InstanceRolePolicy", PolicyName=Join("-", [Ref("AWS::StackName"), "instance-policy"]), PolicyDocument=aws.Policy(Statement=[ aws.Statement(Effect=aws.Allow, Action=[ aws.Action("logs", "CreateLogGroup"), aws.Action("logs", "CreateLogStream"), aws.Action("logs", "PutLogEvents"), aws.Action("logs", "DescribeLogStreams"), ], Resource=["arn:aws:logs:*:*:*"]) ]), Roles=[Ref(self.instance_role)]) self.instance_profile = iam.InstanceProfile( "InstanceProfile", Path="/", Roles=[Ref(self.instance_role)]) self.launch_configuration = autoscaling.LaunchConfiguration( "LaunchConfiguration", ImageId=FindInMap("AMIMap", Ref(AWS_REGION), "AMI"), InstanceType=Ref(parameters.ec2_instance_type), KeyName=Ref(parameters.key_pair), InstanceMonitoring=True, SecurityGroups=[ GetAtt(loadbalancer.instance_security_group, "GroupId"), ], IamInstanceProfile=Ref(self.instance_profile), ) self.auto_scaling_group = autoscaling.AutoScalingGroup( "AutoScalingGroup", LaunchConfigurationName=Ref(self.launch_configuration), MinSize=1, DesiredCapacity=1, MaxSize=10, HealthCheckType='ELB', HealthCheckGracePeriod=300, VPCZoneIdentifier=[ Ref(vpc.public_subnet_1), Ref(vpc.public_subnet_2) ], LoadBalancerNames=[Ref(loadbalancer.load_balancer)], Tags=[autoscaling.Tag("Name", Ref("AWS::StackName"), True)], UpdatePolicy=policies.UpdatePolicy( AutoScalingRollingUpdate=policies.AutoScalingRollingUpdate( PauseTime="PT30S", MinInstancesInService=1, MaxBatchSize=10, WaitOnResourceSignals=False)), TerminationPolicies=[ 'OldestLaunchConfiguration', 'ClosestToNextInstanceHour', 'Default' ], MetricsCollection=[ autoscaling.MetricsCollection(Granularity="1Minute") ]) self.scale_up_policy = autoscaling.ScalingPolicy( "ScaleUPPolicy", AdjustmentType='ChangeInCapacity', AutoScalingGroupName=Ref(self.auto_scaling_group), PolicyType='StepScaling', MetricAggregationType='Average', StepAdjustments=[ autoscaling.StepAdjustments(MetricIntervalLowerBound=0, ScalingAdjustment=1) ], ) self.scale_down_policy = autoscaling.ScalingPolicy( "ScaleDOWNPolicy", AdjustmentType='ChangeInCapacity', AutoScalingGroupName=Ref(self.auto_scaling_group), PolicyType='StepScaling', MetricAggregationType='Average', StepAdjustments=[ autoscaling.StepAdjustments(MetricIntervalUpperBound=0, ScalingAdjustment=-1) ], ) self.ec2_high_cpu_usage_alarm = cloudwatch.Alarm( "EC2HighCPUUsageAlarm", ActionsEnabled=True, AlarmActions=[Ref(self.scale_up_policy)], ComparisonOperator='GreaterThanThreshold', Dimensions=[ cloudwatch.MetricDimension(Name='AutoScalingGroupName', Value=Ref(self.auto_scaling_group)) ], EvaluationPeriods=3, MetricName='CPUUtilization', Namespace='AWS/EC2', Period=300, Statistic='Average', Threshold='70', ) self.ec2_low_cpu_usage_alarm = cloudwatch.Alarm( "EC2LowCPUUsageAlarm", ActionsEnabled=True, AlarmActions=[Ref(self.scale_down_policy)], ComparisonOperator='LessThanThreshold', Dimensions=[ cloudwatch.MetricDimension(Name='AutoScalingGroupName', Value=Ref(self.auto_scaling_group)) ], EvaluationPeriods=3, MetricName='CPUUtilization', Namespace='AWS/EC2', Period=300, Statistic='Average', Threshold='20', )
route53.HealthCheck('MainDomainCheck', HealthCheckConfig=route53.HealthCheckConfiguration( EnableSNI=True, FullyQualifiedDomainName=Ref(main_domain), Port='443', Type='HTTPS'))) main_domain_alarm = template.add_resource( cloudwatch.Alarm( 'MainDomainAlarm', AlarmActions=[Ref(notifications)], AlarmDescription=Join( '', ['Health check for ', Ref(main_domain)]), ComparisonOperator='LessThanThreshold', Dimensions=[ cloudwatch.MetricDimension(Name='HealthCheckId', Value=Ref(main_domain_check)) ], EvaluationPeriods=1, MetricName='HealthCheckStatus', Namespace='AWS/Route53', OKActions=[Ref(notifications)], Period=60, # seconds Statistic='Minimum', Threshold='1.0', )) # endregion if __name__ == '__main__': print(template.to_json())
def create_rds_cloudwatch_alarms(self, rds_database): self.add_resource( cloudwatch.Alarm( 'alarmDatabaseServerCPUUtilization', AlarmDescription='Database server CPU utilization', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=300, Threshold='75', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='CPUUtilization', Namespace='AWS/RDS', Dimensions=[ cloudwatch.MetricDimension('metricDatabaseServerName', Name='DBInstanceIdentifier', Value=Ref(rds_database)) ], )) self.add_resource( cloudwatch.Alarm( 'alarmDatabaseServerDiskQueueDepth', AlarmDescription='Database server disk queue depth', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=60, Threshold='10', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='DiskQueueDepth', Namespace='AWS/RDS', Dimensions=[ cloudwatch.MetricDimension('metricDatabaseServerName', Name='DBInstanceIdentifier', Value=Ref(rds_database)) ], )) self.add_resource( cloudwatch.Alarm( 'alarmDatabaseServerFreeStorageSpace', AlarmDescription='Database server free storage space', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=60, Threshold=str(int(5.0e+09)), # 5GB in bytes EvaluationPeriods=1, ComparisonOperator='LessThanThreshold', MetricName='FreeStorageSpace', Namespace='AWS/RDS', Dimensions=[ cloudwatch.MetricDimension('metricDatabaseServerName', Name='DBInstanceIdentifier', Value=Ref(rds_database)) ], )) self.add_resource( cloudwatch.Alarm( 'alarmDatabaseServerFreeableMemory', AlarmDescription='Database server freeable memory', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=60, Threshold=str(int(1.28e+08)), # 128MB in bytes EvaluationPeriods=1, ComparisonOperator='LessThanThreshold', MetricName='FreeableMemory', Namespace='AWS/RDS', Dimensions=[ cloudwatch.MetricDimension('metricDatabaseServerName', Name='DBInstanceIdentifier', Value=Ref(rds_database)) ], ))
def set_up_stack(self): """Sets up the stack""" if not self.INPUTS or not self.STACK_NAME_PREFIX or not self.HEALTH_ENDPOINT: raise MKInputError( 'Must define INPUTS, STACK_NAME_PREFIX, and HEALTH_ENDPOINT') super(AppServerStack, self).set_up_stack() tags = self.get_input('Tags').copy() self.add_description('{} App Server Stack for Cac'.format( self.STACK_NAME_PREFIX)) assert isinstance(tags, dict), 'tags must be a dictionary' self.availability_zones = get_availability_zones() tags.update({'StackType': 'AppServer'}) self.default_tags = tags self.app_server_instance_type_parameter = self.add_parameter( Parameter( 'AppServerInstanceType', Type='String', Default='t2.medium', Description='NAT EC2 instance type', AllowedValues=EC2_INSTANCE_TYPES, ConstraintDescription='must be a valid EC2 instance type.'), source='AppServerInstanceType') self.param_app_server_iam_profile = self.add_parameter( Parameter('AppServerIAMProfile', Type='String', Description='IAM Profile for instances'), source='AppServerIAMProfile') self.app_server_ami = self.add_parameter(Parameter( 'AppServerAMI', Type='String', Description='{} Server EC2 AMI'.format(self.STACK_NAME_PREFIX)), source='AppServerAMI') self.keyname_parameter = self.add_parameter(Parameter( 'KeyName', Type='String', Default='cac', Description='Name of an existing EC2 key pair'), source='KeyName') self.param_color = self.add_parameter(Parameter( 'StackColor', Type='String', Description='Stack color', AllowedValues=['Blue', 'Green', 'Orange']), source='StackColor') self.param_stacktype = self.add_parameter(Parameter( 'StackType', Type='String', Description='Stack type', AllowedValues=['Development', 'Staging', 'Production']), source='StackType') self.param_public_hosted_zone_name = self.add_parameter( Parameter('PublicHostedZoneName', Type='String', Description='Public hosted zone name'), source='PublicHostedZoneName') self.param_vpc = self.add_parameter(Parameter( 'VpcId', Type='String', Description='Name of an existing VPC'), source='VpcId') self.param_notification_arn = self.add_parameter( Parameter( 'GlobalNotificationsARN', Type='String', Description='Physical resource ID on an AWS::SNS::Topic for ' 'notifications'), source='GlobalNotificationsARN') self.param_ssl_certificate_arn = self.add_parameter( Parameter('SSLCertificateARN', Type='String', Description= 'Physical resource ID on an AWS::IAM::ServerCertificate ' 'for the application server load balancer'), source='SSLCertificateARN') self.param_public_subnets = self.add_parameter( Parameter('PublicSubnets', Type='CommaDelimitedList', Description='A list of public subnets'), source='AppServerPublicSubnets') self.param_private_subnets = self.add_parameter( Parameter('PrivateSubnets', Type='CommaDelimitedList', Description='A list of private subnets'), source='AppServerPrivateSubnets') self.param_bastion_security_group = self.add_parameter( Parameter('BastionSecurityGroup', Type='String', Description='The ID of the bastion security group'), source='BastionSecurityGroup') self.param_database_security_group = self.add_parameter( Parameter('DatabaseSecurityGroup', Type='String', Description='The ID of the database security group'), source='DatabaseSecurityGroup') self.param_nat_security_group = self.add_parameter( Parameter('NATSecurityGroup', Type='String', Description='The ID of the NAT security group'), source='NATSecurityGroup') self.param_min_size = self.add_parameter(Parameter( 'ASGMinSize', Type='Number', Default='1', Description='Min size of ASG'), source='ASGMinSize') self.param_max_size = self.add_parameter(Parameter( 'ASGMaxSize', Type='Number', Default='1', Description='Max size of ASG'), source='ASGMaxSize') self.param_desired_capacity = self.add_parameter( Parameter('ASGDesiredCapacity', Type='Number', Default='1', Description='Desired capacity of ASG'), source='ASGDesiredCapacity') # # Security Group # app_server_load_balancer_security_group = self.add_resource( ec2.SecurityGroup( 'sgAppServerLoadBalancer', GroupDescription= 'Enables access to app servers via a load balancer', VpcId=Ref(self.param_vpc), SecurityGroupIngress=[ ec2.SecurityGroupRule(IpProtocol='tcp', CidrIp=ALLOW_ALL_CIDR, FromPort=p, ToPort=p) for p in [80, 443] ], Tags=Tags(Name='sgAppServerLoadBalancer', Color=Ref(self.param_color)))) app_server_security_group = self.add_resource( ec2.SecurityGroup( 'sgAppServer', GroupDescription='Enables access to App Servers', VpcId=Ref(self.param_vpc), SecurityGroupIngress=[ ec2.SecurityGroupRule(IpProtocol='tcp', CidrIp=VPC_CIDR, FromPort=p, ToPort=p) for p in [22, 80, 443] ] + [ ec2.SecurityGroupRule(IpProtocol='tcp', SourceSecurityGroupId=Ref(sg), FromPort=80, ToPort=80) for sg in [app_server_load_balancer_security_group] ] + [ ec2.SecurityGroupRule(IpProtocol='tcp', SourceSecurityGroupId=Ref(sg), FromPort=443, ToPort=443) for sg in [app_server_load_balancer_security_group] ], SecurityGroupEgress=[ ec2.SecurityGroupRule(IpProtocol='tcp', CidrIp=ALLOW_ALL_CIDR, FromPort=p, ToPort=p) for p in [80, 443, PAPERTRAIL_PORT] ], Tags=Tags(Name='sgAppServer', Color=Ref(self.param_color)))) # ELB to App Server self.add_resource( ec2.SecurityGroupEgress( 'sgEgressELBtoAppHTTP', GroupId=Ref(app_server_load_balancer_security_group), DestinationSecurityGroupId=Ref(app_server_security_group), IpProtocol='tcp', FromPort=80, ToPort=80)) self.add_resource( ec2.SecurityGroupEgress( 'sgEgressELBtoAppHTTPS', GroupId=Ref(app_server_load_balancer_security_group), DestinationSecurityGroupId=Ref(app_server_security_group), IpProtocol='tcp', FromPort=443, ToPort=443)) # Bastion to App Server, app server to db, app server to inet rules = [(self.param_bastion_security_group, app_server_security_group, [80, 443, 22]), (app_server_security_group, self.param_database_security_group, [POSTGRES]), (app_server_security_group, self.param_nat_security_group, [80, 443, 22, 587, PAPERTRAIL_PORT])] for num, (srcsg, destsg, ports) in enumerate(rules): for port in ports: self.add_resource( ec2.SecurityGroupEgress( 'sgEgress{}p{}'.format(num, port), GroupId=Ref(srcsg), DestinationSecurityGroupId=Ref(destsg), IpProtocol='tcp', FromPort=port, ToPort=port)) self.add_resource( ec2.SecurityGroupIngress('sgIngress{}p{}'.format( num, port), GroupId=Ref(destsg), SourceSecurityGroupId=Ref(srcsg), IpProtocol='tcp', FromPort=port, ToPort=port)) # # ELB # app_server_load_balancer = self.add_resource( elb.LoadBalancer( 'elbAppServer', ConnectionDrainingPolicy=elb.ConnectionDrainingPolicy( Enabled=True, Timeout=300), CrossZone=True, SecurityGroups=[Ref(app_server_load_balancer_security_group)], Listeners=[ elb.Listener(LoadBalancerPort='80', Protocol='HTTP', InstancePort='80', InstanceProtocol='HTTP'), elb.Listener(LoadBalancerPort='443', Protocol='HTTPS', InstancePort='443', InstanceProtocol='HTTP', SSLCertificateId=Ref( self.param_ssl_certificate_arn)) ], HealthCheck=elb.HealthCheck( Target=self.HEALTH_ENDPOINT, HealthyThreshold='3', UnhealthyThreshold='2', Interval='30', Timeout='5', ), Subnets=Ref(self.param_public_subnets), Tags=Tags(Name='elbAppServer', Color=Ref(self.param_color)))) self.add_resource( cw.Alarm('alarmAppServerBackend4xx', AlarmActions=[Ref(self.param_notification_arn)], Statistic='Sum', Period=300, Threshold='5', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='HTTPCode_Backend_4XX', Namespace='AWS/ELB', Dimensions=[ cw.MetricDimension( 'metricLoadBalancerName', Name='LoadBalancerName', Value=Ref(app_server_load_balancer)) ])) self.add_resource( cw.Alarm('alarmAppServerBackend5xx', AlarmActions=[Ref(self.param_notification_arn)], Statistic='Sum', Period=60, Threshold='0', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='HTTPCode_Backend_5XX', Namespace='AWS/ELB', Dimensions=[ cw.MetricDimension( 'metricLoadBalancerName', Name='LoadBalancerName', Value=Ref(app_server_load_balancer)) ])) # # ASG # app_server_launch_config = self.add_resource( asg.LaunchConfiguration( 'lcAppServer', ImageId=Ref(self.app_server_ami), IamInstanceProfile=Ref(self.param_app_server_iam_profile), InstanceType=Ref(self.app_server_instance_type_parameter), KeyName=Ref(self.keyname_parameter), SecurityGroups=[Ref(app_server_security_group)])) autoscaling_group = self.add_resource( asg.AutoScalingGroup( 'asgAppServer', AvailabilityZones=self.get_input( 'AppServerAvailabilityZones').split(','), Cooldown=300, DesiredCapacity=Ref(self.param_desired_capacity), HealthCheckGracePeriod=600, HealthCheckType='ELB', LaunchConfigurationName=Ref(app_server_launch_config), LoadBalancerNames=[Ref(app_server_load_balancer)], MaxSize=Ref(self.param_max_size), MinSize=Ref(self.param_min_size), NotificationConfiguration=asg.NotificationConfiguration( TopicARN=Ref(self.param_notification_arn), NotificationTypes=[ asg.EC2_INSTANCE_LAUNCH, asg.EC2_INSTANCE_LAUNCH_ERROR, asg.EC2_INSTANCE_TERMINATE, asg.EC2_INSTANCE_TERMINATE_ERROR ]), VPCZoneIdentifier=Ref(self.param_private_subnets), Tags=[ asg.Tag('Name', '{}Server'.format(self.STACK_NAME_PREFIX), True), asg.Tag('Color', Ref(self.param_color), True) ])) # autoscaling policies autoscaling_policy_add = self.add_resource( asg.ScalingPolicy('scalingPolicyAddAppServer', AdjustmentType='ChangeInCapacity', AutoScalingGroupName=Ref(autoscaling_group), Cooldown=600, ScalingAdjustment='1')) autoscaling_policy_remove = self.add_resource( asg.ScalingPolicy('scalingPolicyRemoveAppServer', AdjustmentType='ChangeInCapacity', AutoScalingGroupName=Ref(autoscaling_group), Cooldown=600, ScalingAdjustment='-1')) if self.STACK_NAME_PREFIX == 'Otp': # trigger scale down if CPU avg usage < 10% for 3 consecutive 5 min periods self.add_resource( cw.Alarm('alarmAppServerLowCPU', AlarmActions=[Ref(autoscaling_policy_remove)], Statistic='Average', Period=300, Threshold='10', EvaluationPeriods=3, ComparisonOperator='LessThanThreshold', MetricName='CPUUtilization', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) # trigger scale up if CPU avg usage >= 30% for a 5 min period self.add_resource( cw.Alarm('alarmAppServerHighCPU', AlarmActions=[ Ref(self.param_notification_arn), Ref(autoscaling_policy_add) ], Statistic='Average', Period=300, Threshold='30', EvaluationPeriods=1, ComparisonOperator='GreaterThanOrEqualToThreshold', MetricName='CPUUtilization', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) else: # scale web servers based on network usage self.add_resource( cw.Alarm('alarmAppServerLowNetworkUsage', AlarmActions=[Ref(autoscaling_policy_remove)], Statistic='Average', Period=300, Threshold='500000', EvaluationPeriods=3, ComparisonOperator='LessThanThreshold', MetricName='NetworkOut', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) self.add_resource( cw.Alarm('alarmAppServerHighNetworkUsage', AlarmActions=[ Ref(self.param_notification_arn), Ref(autoscaling_policy_add) ], Statistic='Average', Period=300, Threshold='10000000', EvaluationPeriods=1, ComparisonOperator='GreaterThanOrEqualToThreshold', MetricName='NetworkOut', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) # # DNS name # self.create_resource( route53.RecordSetType( 'dnsName', Name=Join('.', [ Ref(self.param_color), Ref(self.param_stacktype), self.STACK_NAME_PREFIX, Ref(self.param_public_hosted_zone_name) ]), Type='A', AliasTarget=route53.AliasTarget( GetAtt(app_server_load_balancer, 'CanonicalHostedZoneNameID'), GetAtt(app_server_load_balancer, 'DNSName')), HostedZoneName=Ref(self.param_public_hosted_zone_name))) self.add_output([ Output('{}ServerLoadBalancerEndpoint'.format( self.STACK_NAME_PREFIX), Description='Application server endpoint', Value=GetAtt(app_server_load_balancer, 'DNSName')), Output('{}ServerLoadBalancerHostedZoneNameID'.format( self.STACK_NAME_PREFIX), Description='ID of canonical hosted zone name for ELB', Value=GetAtt(app_server_load_balancer, 'CanonicalHostedZoneNameID')) ])
def add_resources(self): metadata = { "AWS::CloudFormation::Init": { "configSets": { "wordpress_install": [ "install_wordpress"] }, "install_wordpress": { "packages": { "apt": { "apache2": [], "php": [], "php-mysql": [], "php7.0": [], "php7.0-mysql": [], "libapache2-mod-php7.0": [], "php7.0-cli": [], "php7.0-cgi": [], "php7.0-gd": [], "mysql-client": [], "sendmail": [] } }, "sources": { "/var/www/html": "http://wordpress.org/latest.tar.gz" }, "files": { "/tmp/create-wp-config": { "content": { "Fn::Join": ["", [ "#!/bin/bash\n", "cp /var/www/html/wordpress/wp-config-sample.php /var/www/html/wordpress/wp-config.php\n", "sed -i \"s/'database_name_here'/'", Ref( self.DBName), "'/g\" wp-config.php\n", "sed -i \"s/'username_here'/'", Ref( self.DBUser), "'/g\" wp-config.php\n", "sed -i \"s/'password_here'/'", Ref( self.DBPass), "'/g\" wp-config.php\n", "sed -i \"s/'localhost'/'", Ref( self.RDSEndpoint), "'/g\" wp-config.php\n" ]] }, "mode": "000500", "owner": "root", "group": "root" } }, "commands": { "01_configure_wordpress": { "command": "/tmp/create-wp-config", "cwd": "/var/www/html/wordpress" } } } } } self.WaitHandle = self.template.add_resource(cloudformation.WaitConditionHandle( "WaitHandle", )) self.WaitCondition = self.template.add_resource(cloudformation.WaitCondition( "WaitCondition", Handle=Ref(self.WaitHandle), Timeout="600", DependsOn="WebServerAutoScalingGroup", )) self.WebServerLaunchConfiguration = self.template.add_resource(autoscaling.LaunchConfiguration( "WebServerLaunchConfiguration", Metadata=metadata, UserData=Base64(Join("", [ "#!/bin/bash -x\n", "apt-get update\n", "apt-get install python-pip nfs-common -y \n", "mkdir -p /var/www/html/\n", "EC2_AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)\n", "echo \"$EC2_AZ.", Ref(self.FileSystemID), ".efs.", Ref( "AWS::Region"), ".amazonaws.com:/ /var/www/html/ nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 0 0\" >> /etc/fstab\n" "mount -a\n", "pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n", # "exec > /tmp/userdata.log 2>&1\n", "/usr/local/bin/cfn-init -v --stack ", Ref("AWS::StackName"), " --resource WebServerLaunchConfiguration ", " --configsets wordpress_install ", " --region ", Ref("AWS::Region"), "\n", "/bin/mv /var/www/html/wordpress/* /var/www/html/\n", "/bin/rm -f /var/www/html/index.html\n", "/bin/rm -rf /var/www/html/wordpress/\n", "chown www-data:www-data /var/www/html/* -R\n", "/usr/sbin/service apache2 restart\n", "/usr/bin/curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar\n", "/bin/chmod +x wp-cli.phar\n", "/bin/mv wp-cli.phar /usr/local/bin/wp\n", "cd /var/www/html/\n", "if ! $(sudo -u www-data /usr/local/bin/wp core is-installed); then\n", "sudo -u www-data /usr/local/bin/wp core install ", "--url='", Ref(self.Hostname), ".", Ref(self.Domain), "' ", "--title='Cloudreach Meetup - ", Ref( self.Environment), "' ", "--admin_user='******' ", "--admin_password='******' ", "--admin_email='*****@*****.**'\n", "wget https://s3-eu-west-1.amazonaws.com/sceptre-meetup-munich/header.jpg -O /var/www/html/wp-content/themes/twentyseventeen/assets/images/header.jpg\n", "chown www-data:www-data /var/www/html/wp-content/themes/twentyseventeen/assets/images/header.jpg\n", "fi\n", "/usr/local/bin/cfn-signal -e $? --stack ", Ref( "AWS::StackName"), " -r \"Webserver setup complete\" '", Ref(self.WaitHandle), "'\n" ] )), ImageId=FindInMap("AWSRegion2AMI", Ref("AWS::Region"), "AMI"), KeyName=Ref(self.KeyName), SecurityGroups=[Ref(self.WebSecurityGroup)], InstanceType=Ref(self.InstanceType), AssociatePublicIpAddress=True, )) self.WebServerAutoScalingGroup = self.template.add_resource(autoscaling.AutoScalingGroup( "WebServerAutoScalingGroup", MinSize=Ref(self.WebServerCapacity), DesiredCapacity=Ref(self.WebServerCapacity), MaxSize=Ref(self.WebServerCapacity), VPCZoneIdentifier=[Ref(self.Subnet1), Ref(self.Subnet2)], AvailabilityZones=[Ref(self.AvailabilityZone1), Ref(self.AvailabilityZone2)], Tags=autoscaling.Tags( Name=Join("-", [Ref(self.Project), "web", "asg"]), Environment=Ref(self.Environment), Project=Ref(self.Project), ), LoadBalancerNames=[Ref(self.ElasticLoadBalancer)], LaunchConfigurationName=Ref(self.WebServerLaunchConfiguration), )) self.WebServerScaleUpPolicy = self.template.add_resource(autoscaling.ScalingPolicy( "WebServerScaleUpPolicy", ScalingAdjustment="1", Cooldown="60", AutoScalingGroupName=Ref(self.WebServerAutoScalingGroup), AdjustmentType="ChangeInCapacity", )) self.WebServerScaleDownPolicy = self.template.add_resource(autoscaling.ScalingPolicy( "WebServerScaleDownPolicy", ScalingAdjustment="-1", Cooldown="60", AutoScalingGroupName=Ref(self.WebServerAutoScalingGroup), AdjustmentType="ChangeInCapacity", )) self.CPUAlarmLow = self.template.add_resource(cloudwatch.Alarm( "CPUAlarmLow", EvaluationPeriods="2", Dimensions=[ cloudwatch.MetricDimension( Name="AutoScalingGroupName", Value=Ref(self.WebServerAutoScalingGroup) ), ], AlarmActions=[Ref(self.WebServerScaleDownPolicy)], AlarmDescription="Scale-down if CPU < 70% for 1 minute", Namespace="AWS/EC2", Period="60", ComparisonOperator="LessThanThreshold", Statistic="Average", Threshold="70", MetricName="CPUUtilization", )) self.CPUAlarmHigh = self.template.add_resource(cloudwatch.Alarm( "CPUAlarmHigh", EvaluationPeriods="2", Dimensions=[ cloudwatch.MetricDimension( Name="AutoScalingGroupName", Value=Ref("WebServerAutoScalingGroup") ), ], AlarmActions=[Ref(self.WebServerScaleUpPolicy)], AlarmDescription="Scale-up if CPU > 50% for 1 minute", Namespace="AWS/EC2", Period="60", ComparisonOperator="GreaterThanThreshold", Statistic="Average", Threshold="50", MetricName="CPUUtilization", ))
def main(): t = Template() t.add_version('2010-09-09') t.set_description("AWS CloudFormation ECS example") # Add the Parameters AMI = t.add_parameter(Parameter( "AMI", Type="String", )) ClusterSize = t.add_parameter(Parameter( "ClusterSize", Type="String", )) ClusterType = t.add_parameter(Parameter( "ClusterType", Type="String", )) InstanceType = t.add_parameter(Parameter( "InstanceType", Type="String", )) IamInstanceProfile = t.add_parameter( Parameter( "IamInstanceProfile", Type="String", )) KeyName = t.add_parameter( Parameter( "KeyName", Type="AWS::EC2::KeyPair::KeyName", )) MaxClusterSize = t.add_parameter( Parameter( "MaxClusterSize", Type="String", )) RollingUpdate = t.add_parameter(Parameter( "RollingUpdate", Type="String", )) Stage = t.add_parameter(Parameter( "Stage", Type="String", )) Subnets = t.add_parameter( Parameter( "Subnets", Type="List<AWS::EC2::Subnet::Id>", )) VpcCidr = t.add_parameter(Parameter( "VpcCidr", Type="String", )) VpcId = t.add_parameter(Parameter( "VpcId", Type="AWS::EC2::VPC::Id", )) ContainerInstances = t.add_resource( LaunchConfiguration( 'ContainerInstances', UserData=Base64( Join('', [ '#!/bin/bash -xe\n', 'echo ECS_CLUSTER=', Ref('AWS::StackName'), '>> /etc/ecs/ecs.config\n', 'systemctl enable [email protected]\n', 'systemctl start [email protected]\n', '/usr/bin/cfn-signal -e $? ', ' --stack ', Ref('AWS::StackName'), ' --resource ECSAutoScalingGroup ', ' --region ', Ref('AWS::Region'), '\n' ])), ImageId=Ref(AMI), KeyName=Ref(KeyName), SecurityGroups=[Ref('EcsSecurityGroup')], IamInstanceProfile=Ref(IamInstanceProfile), InstanceType=Ref(InstanceType))) ECSCluster = t.add_resource( Cluster('EcsCluster', ClusterName=Ref('AWS::StackName'))) ECSAutoScalingGroup = t.add_resource( AutoScalingGroup( 'ECSAutoScalingGroup', DesiredCapacity=Ref(ClusterSize), MinSize=Ref(ClusterSize), MaxSize=Ref(MaxClusterSize), VPCZoneIdentifier=Ref(Subnets), LaunchConfigurationName=Ref('ContainerInstances'), HealthCheckType="EC2", UpdatePolicy=UpdatePolicy( AutoScalingReplacingUpdate=AutoScalingReplacingUpdate( WillReplace=True, ), AutoScalingRollingUpdate=AutoScalingRollingUpdate( PauseTime='PT5M', MinInstancesInService=Ref(ClusterSize), MaxBatchSize='1', WaitOnResourceSignals=True)), Tags=[ Tag("Project", "demo", True), Tag("Stage", Ref(Stage), True), Tag("Name", "home-ecs", True), ])) t.add_resource( ScalingPolicy("EcsAsgScaleDown", AdjustmentType="PercentChangeInCapacity", AutoScalingGroupName=Ref("ECSAutoScalingGroup"), MetricAggregationType="Average", MinAdjustmentMagnitude="1", PolicyType="StepScaling", StepAdjustments=[ StepAdjustments(MetricIntervalLowerBound="-10", MetricIntervalUpperBound="0", ScalingAdjustment="-10"), StepAdjustments(MetricIntervalUpperBound="-10", ScalingAdjustment="-20") ])) t.add_resource( ScalingPolicy('EcsScaleUp', AdjustmentType="PercentChangeInCapacity", AutoScalingGroupName=Ref("ECSAutoScalingGroup"), EstimatedInstanceWarmup="300", MetricAggregationType="Average", MinAdjustmentMagnitude="1", PolicyType="StepScaling", StepAdjustments=[ StepAdjustments(MetricIntervalLowerBound="0", MetricIntervalUpperBound="10", ScalingAdjustment="10"), StepAdjustments(MetricIntervalLowerBound="10", ScalingAdjustment="20") ])) t.add_resource( cloudwatch.Alarm("EcsScaleDownAlarm", ActionsEnabled="True", MetricName="CPUUtilization", AlarmActions=[Ref("EcsAsgScaleDown")], AlarmDescription="Scale down ECS Instances", Namespace="AWS/EC2", Statistic="Average", Period="60", EvaluationPeriods="6", Threshold="25", ComparisonOperator="LessThanThreshold", Dimensions=[ cloudwatch.MetricDimension( Name="AutoScalingGroupName", Value=Ref("ECSAutoScalingGroup")) ])) t.add_resource( cloudwatch.Alarm("EcsAsgScaleUpAlarm", ActionsEnabled="True", MetricName="CPUUtilization", AlarmActions=[Ref("EcsScaleUp")], AlarmDescription="Scale up ECS Instances", Namespace="AWS/EC2", Statistic="Average", Period="60", EvaluationPeriods="3", Threshold="65", ComparisonOperator="GreaterThanThreshold", Dimensions=[ cloudwatch.MetricDimension( Name="AutoScalingGroupName", Value=Ref("ECSAutoScalingGroup")) ])) EC2SecurityGroup = t.add_resource( ec2.SecurityGroup('EcsSecurityGroup', GroupDescription='ECS InstanceSecurityGroup', SecurityGroupIngress=[ ec2.SecurityGroupRule(IpProtocol='tcp', FromPort='22', ToPort='22', CidrIp='0.0.0.0/0'), ec2.SecurityGroupRule(IpProtocol='tcp', FromPort='31000', ToPort='61000', CidrIp='0.0.0.0/0') ], VpcId=Ref(VpcId))) with open("ecs-ec2-cluster-cf.yaml", "w") as yamlout: yamlout.write(t.to_yaml())
Tags=[asg.Tag('Name', 'Tiler', True)] )) # # CloudWatch Resources # t.add_resource(cw.Alarm( 'alarmTilerBackend4XX', AlarmDescription='Tiler API server backend 4XXs', AlarmActions=[Ref(notification_arn_param)], Statistic='Sum', Period=300, Threshold='20', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='HTTPCode_Backend_4XX', Namespace='AWS/ELB', Dimensions=[ cw.MetricDimension( 'metricLoadBalancerName', Name='LoadBalancerName', Value=Ref(tiler_load_balancer) ) ], )) t.add_resource(cw.Alarm( 'alarmTilerBackend5XX', AlarmDescription='Tiler API server backend 5XXs', AlarmActions=[Ref(notification_arn_param)], Statistic='Sum',
def add_resources(self): """Add resources to template.""" template = self.template variables = self.get_variables() additional_tags = {} for key in variables['OtherTags'].iterkeys(): if isinstance(variables['OtherTags'][key], dict): tag_name = variables['OtherTags'][key]['Name'] else: tag_name = key additional_tags[tag_name] = variables[key].ref rdsclientsecuritygroup = template.add_resource( ec2.SecurityGroup( 'RdsClientSecurityGroup', VpcId=variables['VpcId'].ref, SecurityGroupEgress=[ ec2.SecurityGroupRule( IpProtocol='-1', FromPort='0', ToPort='65535', CidrIp='0.0.0.0/0') ], GroupDescription=Join('-', [variables['ApplicationName'].ref, 'RdsClientSecurityGroup', variables['EnvironmentName'].ref]), Tags=Tags( Name=Join('-', ['rds-clients', variables['ApplicationName'].ref, variables['EnvironmentName'].ref]), Environment=variables['EnvironmentName'].ref, Application=variables['ApplicationName'].ref, **additional_tags ) ) ) template.add_output( Output( 'RdsClientSecurityGroup', Description='The ID of the RDS client security group ' 'associated with the environment', Value=Ref(rdsclientsecuritygroup) ) ) rdsserversecuritygroup = template.add_resource( ec2.SecurityGroup( 'RdsServerSecurityGroup', SecurityGroupIngress=[ ec2.SecurityGroupRule( IpProtocol='tcp', FromPort=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsPort'), ToPort=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsPort'), SourceSecurityGroupId=Ref(rdsclientsecuritygroup)) ], VpcId=variables['VpcId'].ref, SecurityGroupEgress=[ ec2.SecurityGroupRule( IpProtocol='-1', FromPort='0', ToPort='65535', CidrIp='0.0.0.0/0') ], GroupDescription=Join('-', [variables['ApplicationName'].ref, 'RdsServerSecurityGroup', variables['EnvironmentName'].ref]), Tags=Tags( Name=Join('-', ['rds-server', variables['ApplicationName'].ref, variables['EnvironmentName'].ref]), Environment=variables['EnvironmentName'].ref, Application=variables['ApplicationName'].ref, **additional_tags ) ) ) template.add_output( Output( rdsserversecuritygroup.title, Description='The ID of the RDS server security group ' 'associated with the rds', Value=Ref(rdsserversecuritygroup) ) ) template.add_resource( ec2.SecurityGroupIngress( 'VpnSgIngress', Condition='VpnAccessEnabled', GroupId=Ref(rdsserversecuritygroup), IpProtocol='tcp', FromPort=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsPort'), ToPort=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsPort'), SourceSecurityGroupId=variables['VPNSecurityGroup'].ref ) ) rdsdatabaseinstance = template.add_resource( rds.DBInstance( 'RdsDatabaseInstance', DBParameterGroupName=If( 'CustomParameterGroup', variables['ParameterGroupName'].ref, FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsParameterGroupName')), AllowMajorVersionUpgrade=variables['AllowMajorVersionUpgrade'].ref, # noqa MasterUsername=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsMasterUsername'), LicenseModel=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsLicenseModel'), VPCSecurityGroups=variables['RdsServerSecurityGroup'].ref, Engine=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsEngine'), MultiAZ=variables['MultiAZ'].ref, Tags=Tags( Name=variables['RdsInstanceIdentifier'].ref, Environment=variables['EnvironmentName'].ref, Application=variables['ApplicationName'].ref, **additional_tags ), AutoMinorVersionUpgrade=variables['AutoMinorVersionUpgrade'].ref, # noqa PreferredBackupWindow='03:00-04:00', AllocatedStorage=variables['RdsAllocatedStorage'].ref, DBSubnetGroupName=variables['DBSubnetGroupName'].ref, PreferredMaintenanceWindow='sat:06:00-sat:07:00', EngineVersion=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsEngineVersion'), BackupRetentionPeriod=variables['BackupRetentionDays'].ref, StorageType='gp2', MasterUserPassword=variables['DBPassword'].ref, KmsKeyId=If('KmsKeyEnabled', variables['KmsKey'].ref, Ref('AWS::NoValue')), StorageEncrypted=variables['Encrypted'].ref, DBInstanceClass=variables['RdsInstanceClass'].ref, Port=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsPort'), DBInstanceIdentifier=If( 'IdentifierSpecified', variables['RdsInstanceIdentifier'].ref, Join( '-', [variables['ApplicationName'].ref, variables['EnvironmentName'].ref])), DBSnapshotIdentifier=If('SnapshotSpecified', variables['RdsSnapshotIdentifier'].ref, Ref('AWS::NoValue')) ) ) rdsdatabasereadreplica = template.add_resource( rds.DBInstance( 'RdsDatabaseReadReplica', DBParameterGroupName=If( 'CustomParameterGroup', variables['ParameterGroupName'].ref, FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsParameterGroupName')), VPCSecurityGroups=variables['RdsServerSecurityGroup'].ref, SourceDBInstanceIdentifier=Ref(rdsdatabaseinstance), DBInstanceClass=variables['RdsInstanceClass'].ref, StorageType='gp2', DBInstanceIdentifier=If( 'IdentifierSpecified', Join( '-', [variables['RdsInstanceIdentifier'].ref, 'readreplica']), Ref('AWS::NoValue')), SourceRegion=If( 'SourceRegionSpecified', variables['SourceRegion'].ref, Ref('AWS::NoValue')), Engine=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsEngine'), EngineVersion=FindInMap('RdsMap', variables['RdsEngineType'].ref, 'RdsEngineVersion'), Condition='IsReadReplica', ) ) template.add_resource( cloudwatch.Alarm( 'HighCPUAlarm', Condition='SnsTopicSpecified', ActionsEnabled='true', AlarmActions=variables['SNSTopic'].ref, AlarmDescription='CPU Utilization Alarm for RDS', AlarmName=Join('-', [variables['EnvironmentName'].ref, 'CPUUtilization', 'RDS', 'alarm']), ComparisonOperator=variables['CpuComparisonOperator'].ref, OKActions=variables['SNSTopic'].ref, EvaluationPeriods=variables['CpuEvaluationPeriods'].ref, MetricName='CPUUtilization', Namespace='AWS/RDS', Period=variables['CpuPeriod'].ref, Statistic=variables['CpuStatistic'].ref, Threshold=variables['CpuThreshold'].ref ) ) template.add_resource( cloudwatch.Alarm( 'FreeStorageSpace', Condition='SnsTopicSpecified', ActionsEnabled='true', AlarmActions=variables['SNSTopic'].ref, AlarmDescription='Disk Space Alarm for RDS', AlarmName=Join('-', [variables['EnvironmentName'].ref, 'FreeDiskSpace', 'RDS', 'alarm']), ComparisonOperator=variables['DiskComparisonOperator'].ref, OKActions=variables['SNSTopic'].ref, EvaluationPeriods=variables['DiskEvaluationPeriods'].ref, MetricName='FreeStorageSpace', Namespace='AWS/RDS', Period=variables['DiskPeriod'].ref, Statistic=variables['DiskStatistic'].ref, Threshold=variables['DiskThreshold'].ref ) ) template.add_resource( cloudwatch.Alarm( 'FreeableMemory', Condition='SnsTopicSpecified', ActionsEnabled='true', AlarmActions=variables['SNSTopic'].ref, AlarmDescription='Free memory Alarm for RDS', AlarmName=Join('-', [variables['EnvironmentName'].ref, 'FreeableMemory', 'RDS', 'alarm']), ComparisonOperator=variables['MemoryComparisonOperator'].ref, OKActions=variables['SNSTopic'].ref, EvaluationPeriods=variables['MemoryEvaluationPeriods'].ref, MetricName='FreeableMemory', Namespace='AWS/RDS', Period=variables['MemoryPeriod'].ref, Statistic=variables['MemoryStatistic'].ref, Threshold=variables['MemoryThreshold'].ref ) ) template.add_output( Output( 'RdsDatabaseInstance', Description='The name of the RDS instance for the environment', Value=GetAtt(rdsdatabaseinstance, 'Endpoint.Address') ) ) template.add_output( Output( 'RdsDatabaseReadReplica', Description='The name of the RDS read replica for the ' 'environment', Value=GetAtt(rdsdatabasereadreplica, 'Endpoint.Address'), Condition='IsReadReplica' ) )
def add_ec2(self, ami_name, instance_type, asg_size, cidr, hosted_zone): """ Helper method creates ingress given a source cidr range and a set of ports @param ami_name [string] Name of the AMI for launching the app @param instance_type [string] Instance for the application @param asg_size [int] Sets the size of the asg @param cidr [string] Range of addresses for this vpc @param hosted_zone [string] Name of the hosted zone the elb will be mapped to """ print "Creating EC2" self.internal_security_group = self.add_sg_with_cidr_port_list( "ASGSG", "Security Group for EC2", 'vpcId', cidr, [{"443": "443"}, {"80": "80"}] ) self.public_lb_security_group = self.add_sg_with_cidr_port_list( "ELBSG", "Security Group for accessing EC2 publicly", 'vpcId', '0.0.0.0/0', [{"443": "443"}] ) name = self.env_name.replace('-', '') public_subnet_count = len(self._subnets.get('public').get('public')) public_subnets = [{'Ref': x} for x in ["publicAZ%d" % n for n in range(0, public_subnet_count)]] public_alb = self.add_resource(alb.LoadBalancer( "PublicALB", Scheme='internet-facing', Subnets=public_subnets, SecurityGroups=[Ref(sg) for sg in [self.public_lb_security_group]] )) target_group = self.add_resource(alb.TargetGroup( "AppTargetGroup80", Port=80, Protocol="HTTP", VpcId=self.vpc_id )) certificate = 'arn:aws:acm:us-east-1:422548007577:certificate/d9b8fbd2-13bb-4d6e-aba4-53061b1580f9' alb_ssl_listener = self.add_resource(alb.Listener( "ALBListner", Port=443, Certificates=[alb.Certificate(CertificateArn=certificate)], Protocol="HTTPS", DefaultActions=[alb.Action( Type="forward", TargetGroupArn=Ref(target_group))], LoadBalancerArn=Ref(public_alb) )) self.add_elb_dns_alias(public_alb, '', hosted_zone) policies = ['cloudwatchlogs'] policies_for_profile = [self.get_policy(policy, 'EC2') for policy in policies] asg = self.add_asg( "EC2", min_size=asg_size, max_size=6, ami_name=ami_name, # load_balancer=public_elb, instance_profile=self.add_instance_profile(name, policies_for_profile, name), instance_type=instance_type, security_groups=['commonSecurityGroup', Ref(self.internal_security_group)], subnet_layer='private', update_policy=UpdatePolicy( AutoScalingRollingUpdate=AutoScalingRollingUpdate( PauseTime='PT5M', MinInstancesInService=1, # The maximum number of instances that are terminated at a given time, left at 1 to ease into updates. # Can be increased at a later time MaxBatchSize='1' ) ), user_data=Base64(Join('', [ '#!/bin/bash\n', 'echo Good to go' ]))) asg.resource['Properties']['TargetGroupARNs'] = [Ref(target_group)] # Cluster Memory Scaling policies asg_scale_up_policy = self.add_resource( autoscaling.ScalingPolicy( name + 'ScaleUpPolicy', AdjustmentType='ChangeInCapacity', AutoScalingGroupName=Ref(asg), Cooldown=300, ScalingAdjustment=1 ) ) # ELB latency above a threshold self.add_resource( cloudwatch.Alarm( name + 'LatencyHigh', MetricName='Latency', ComparisonOperator='GreaterThanThreshold', Period=300, EvaluationPeriods=1, Statistic='Average', Namespace='AWS/ELB', AlarmDescription=name + 'LatencyHigh', Dimensions=[cloudwatch.MetricDimension(Name='LoadBalancerName', Value=Ref(public_alb))], Threshold='6', AlarmActions=[ Ref(asg_scale_up_policy), 'arn:aws:sns:us-east-1:422548007577:notify-pat' ] ) )
def create_cloudfront_distributions(self): blue_tile_distribution = self.add_resource( cf.Distribution( 'tileDistributionBlue', DistributionConfig=cf.DistributionConfig( Origins=[ cf.Origin(Id='tileOriginId', DomainName=Join('.', [ 'blue-tiles', Ref(self.public_hosted_zone_name) ]), CustomOriginConfig=cf.CustomOrigin( OriginProtocolPolicy='http-only')) ], DefaultCacheBehavior=cf.DefaultCacheBehavior( ForwardedValues=cf.ForwardedValues(QueryString=True), TargetOriginId='tileOriginId', ViewerProtocolPolicy='allow-all'), Enabled=True))) self.add_resource( cw.Alarm('alarmTileDistributionBlueOrigin4XX', AlarmDescription='Tile distribution origin 4XXs', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=300, Threshold='20', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='4xxErrorRate', Namespace='AWS/CloudFront', Dimensions=[ cw.MetricDimension('metricDistributionId', Name='DistributionId', Value=Ref(blue_tile_distribution)), cw.MetricDimension('metricRegion', Name='Region', Value='Global') ])) self.add_resource( cw.Alarm('alarmTileDistributionBlueOrigin5XX', AlarmDescription='Tile distribution origin 5XXs', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=60, Threshold='0', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='5xxErrorRate', Namespace='AWS/CloudFront', Dimensions=[ cw.MetricDimension('metricDistributionId', Name='DistributionId', Value=Ref(blue_tile_distribution)), cw.MetricDimension('metricRegion', Name='Region', Value='Global') ])) green_tile_distribution = self.add_resource( cf.Distribution( 'tileDistributionGreen', DistributionConfig=cf.DistributionConfig( Origins=[ cf.Origin(Id='tileOriginId', DomainName=Join('.', [ 'green-tiles', Ref(self.public_hosted_zone_name) ]), CustomOriginConfig=cf.CustomOrigin( OriginProtocolPolicy='http-only')) ], DefaultCacheBehavior=cf.DefaultCacheBehavior( ForwardedValues=cf.ForwardedValues(QueryString=True), TargetOriginId='tileOriginId', ViewerProtocolPolicy='allow-all'), Enabled=True))) self.add_resource( cw.Alarm('alarmTileDistributionGreenOrigin4XX', AlarmDescription='Tile distribution origin 4XXs', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=300, Threshold='20', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='4xxErrorRate', Namespace='AWS/CloudFront', Dimensions=[ cw.MetricDimension( 'metricDistributionId', Name='DistributionId', Value=Ref(green_tile_distribution)), cw.MetricDimension('metricRegion', Name='Region', Value='Global') ])) self.add_resource( cw.Alarm('alarmTileDistributionGreenOrigin5XX', AlarmDescription='Tile distribution origin 5XXs', AlarmActions=[Ref(self.notification_topic_arn)], Statistic='Average', Period=60, Threshold='0', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='5xxErrorRate', Namespace='AWS/CloudFront', Dimensions=[ cw.MetricDimension( 'metricDistributionId', Name='DistributionId', Value=Ref(green_tile_distribution)), cw.MetricDimension('metricRegion', Name='Region', Value='Global') ])) return blue_tile_distribution, green_tile_distribution
"WebServerScaleDownPolicy", ScalingAdjustment="-1", Cooldown="60", AutoScalingGroupName=Ref(WebServerAutoScalingGroup), AdjustmentType="ChangeInCapacity", )) CPUAlarmLow = t.add_resource( cloudwatch.Alarm( "CPUAlarmLow", EvaluationPeriods="2", Dimensions=[ cloudwatch.MetricDimension(Name="AutoScalingGroupName", Value=Ref("WebServerAutoScalingGroup")), ], AlarmActions=[Ref("WebServerScaleDownPolicy")], AlarmDescription="Scale-down if CPU < 70% for 1 minute", Namespace="AWS/EC2", Period="60", ComparisonOperator="LessThanThreshold", Statistic="Average", Threshold="70", MetricName="CPUUtilization", )) CPUAlarmHigh = t.add_resource( cloudwatch.Alarm( "CPUAlarmHigh", EvaluationPeriods="2", Dimensions=[ cloudwatch.MetricDimension(Name="AutoScalingGroupName", Value=Ref("WebServerAutoScalingGroup")),
])) alarm = t.add_resource( cloudwatch.Alarm( 'Alarm', Condition='AlarmEnabledCondition', ActionsEnabled=Ref(param_notification_enabled), MetricName=Ref(param_metric_name), Namespace=Ref(param_metric_namespace), ComparisonOperator=Ref(param_alarm_comparison_operator), Statistic=Ref(param_alarm_statistic), Threshold=Ref(param_alarm_threshold), EvaluationPeriods=Ref(param_alarm_evaluation_periods), Period=Ref(param_alarm_period), Unit=Ref(param_alarm_unit), AlarmActions=[ If('AlarmTopicCondition', Ref(param_alarm_topic), Ref(AWS_NO_VALUE)) ], OKActions=[ If('OkTopicCondition', Ref(param_ok_topic), Ref(AWS_NO_VALUE)) ], InsufficientDataActions=[ If('InsufficientDataTopicCondition', Ref(param_insufficient_data_topic), Ref(AWS_NO_VALUE)) ], TreatMissingData=Ref(param_treat_missing_data), )) # # Output