def subscribe_emails_to_topic(self, sns_topic: Topic, email_address: str) -> None: aws_sns.Subscription(self, "eternal-guess-error-subscription", topic=sns_topic, protocol=aws_sns.SubscriptionProtocol.EMAIL, endpoint=email_address)
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) db_table_name = "findAflatDB" # create dynamoDB table table = aws_dynamodb.Table(self, "findAflat_db", table_name=db_table_name, partition_key=aws_dynamodb.Attribute( name="dataID", type=aws_dynamodb.AttributeType.STRING)) # create lambda function function = aws_lambda.Function( self, "findAflat_fnc", runtime=aws_lambda.Runtime.PYTHON_3_7, code=aws_lambda.Code.asset("findAFlat"), handler="lambda_function.lambda_handler", timeout=core.Duration.minutes(1)) function.add_environment("LAMBDA", "True") function.add_environment("DB_TABLE", db_table_name) table.grant_read_write_data(function) # create and configure sns topic sns_topic = aws_sns.Topic(self, "findAflat_sns_tpc", display_name="flatsWeeklyUpdate", topic_name="flatsWeeklyUpdate") sns_topic.grant_publish(function) sns_subscr = aws_sns.Subscription( self, "findAflat_sns_sbscr", topic=sns_topic, protocol=aws_sns.SubscriptionProtocol.EMAIL, endpoint="*****@*****.**") function.add_environment("SNS_TOPIC", sns_topic.topic_arn) # add EventBridge event = aws_events.Rule(self, "findAflat_event", enabled=True, rule_name="daily_at_17", schedule=aws_events.Schedule.cron(hour="17", minute="00")) event.add_target(aws_events_targets.LambdaFunction(handler=function))
def __init__( self, scope: core.Construct, id: str, map_params: dict, **kwargs ): # pylint: disable=W0622 super().__init__(scope, id, **kwargs) LOGGER.debug('Notification configuration required for %s', map_params['name']) stack = core.Stack.of(self) # pylint: disable=no-value-for-parameter _slack_func = _lambda.Function.from_function_arn( self, 'slack_lambda_function', f'arn:{stack.partition}:lambda:{ADF_DEPLOYMENT_REGION}:' f'{ADF_DEPLOYMENT_ACCOUNT_ID}:function:SendSlackNotification' ) kms_alias = _kms.Alias.from_alias_name(self, "KMSAlias", f"alias/codepipeline-{ADF_DEPLOYMENT_ACCOUNT_ID}") _topic = _sns.Topic(self, "PipelineTopic", master_key=kms_alias) _statement = _iam.PolicyStatement( actions=["sns:Publish"], effect=_iam.Effect.ALLOW, principals=[ _iam.ServicePrincipal("sns.amazonaws.com"), _iam.ServicePrincipal("codecommit.amazonaws.com"), _iam.ServicePrincipal("events.amazonaws.com"), ], resources=["*"], ) _topic.add_to_resource_policy(_statement) _endpoint = map_params.get("params", {}).get("notification_endpoint", "") _sub = _sns.Subscription( self, "sns_subscription", topic=_topic, endpoint=_endpoint if "@" in _endpoint else _slack_func.function_arn, protocol=_sns.SubscriptionProtocol.EMAIL if "@" in _endpoint else _sns.SubscriptionProtocol.LAMBDA, ) if "@" not in _endpoint: _lambda.CfnPermission( self, "slack_notification_sns_permissions", principal="sns.amazonaws.com", action="lambda:InvokeFunction", source_arn=_topic.topic_arn, function_name="SendSlackNotification", ) _slack_func.add_event_source(source=_event_sources.SnsEventSource(_topic)) self.topic_arn = _topic.topic_arn
def __init__(self, scope: core.Construct, id: str, map_params: dict, **kwargs): #pylint: disable=W0622 super().__init__(scope, id, **kwargs) LOGGER.debug('Notification configuration required for %s', map_params['name']) # pylint: disable=no-value-for-parameter _slack_func = _lambda.Function.from_function_arn( self, 'slack_lambda_function', 'arn:aws:lambda:{0}:{1}:function:SendSlackNotification'.format( ADF_DEPLOYMENT_REGION, ADF_DEPLOYMENT_ACCOUNT_ID)) _topic = _sns.Topic(self, 'PipelineTopic') _statement = _iam.PolicyStatement( actions=["sns:Publish"], effect=_iam.Effect.ALLOW, principals=[ _iam.ServicePrincipal('sns.amazonaws.com'), _iam.ServicePrincipal('codecommit.amazonaws.com'), _iam.ServicePrincipal('events.amazonaws.com') ], resources=["*"]) _topic.add_to_resource_policy(_statement) _lambda.CfnPermission(self, 'slack_notification_sns_permissions', principal='sns.amazonaws.com', action='lambda:InvokeFunction', source_arn=_topic.topic_arn, function_name='SendSlackNotification') _endpoint = map_params.get('params', {}).get('notification_endpoint', '') _sub = _sns.Subscription( self, 'sns_subscription', topic=_topic, endpoint=_endpoint if '@' in _endpoint else _slack_func.function_arn, protocol=_sns.SubscriptionProtocol.EMAIL if '@' in _endpoint else _sns.SubscriptionProtocol.LAMBDA) if '@' not in _endpoint: _slack_func.add_event_source( source=_event_sources.SnsEventSource(_topic)) self.topic_arn = _topic.topic_arn
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) test_queue = sqs.Queue(self, 'test-queue', queue_name='test1') test_topic = sns.Topic(self, 'test-topic') sns.Subscription(self, 'test-subscription', topic=test_topic, endpoint=test_queue.queue_arn, protocol=sns.SubscriptionProtocol.SQS) kinesis.Stream(self, 'test-stream', stream_name='donut-sales', shard_count=2) create_order = step.Pass(self, 'create-order', result=step.Result.from_object({ "Order": { "Customer": "Alice", "Product": "Coffee", "Billing": { "Price": 10.0, "Quantity": 4.0 } } })) calculate_amount = step.Pass(self, 'calculate-amount', result=step.Result.from_number(40.0), result_path='$.Order.Billing.Amount', output_path='$.Order.Billing') order_definition = create_order.next(calculate_amount) step.StateMachine(self, 'test-state-machine', state_machine_name='order-machine', definition=order_definition) make_tea = step.Choice( self, 'make-tea', comment='Input should look like {"tea":"green"}') green = step.Pass(self, 'green', result=step.Result.from_string('Green tea')) make_tea.when(step.Condition.string_equals('$.tea', 'green'), green) black = step.Pass(self, 'black', result=step.Result.from_string('Black tea')) make_tea.when(step.Condition.string_equals('$.tea', 'black'), black) orange = step.Pass(self, 'orange', result=step.Result.from_string('Black tea')) make_tea.when(step.Condition.string_equals('$.tea', 'orange'), orange) error = step.Pass(self, 'error', result=step.Result.from_string('Bad input')) make_tea.otherwise(error) step.StateMachine(self, 'test-state-machine-2', state_machine_name='tea-machine', definition=make_tea)
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) bucket_name = 'devassoc-monitored' bucket = s3.Bucket(self, 'bucket-monitored', bucket_name=bucket_name, removal_policy=core.RemovalPolicy.DESTROY, auto_delete_objects=True) core.CfnOutput(self, 'monitored-bucket', value=bucket.bucket_name) size_metric = cw.Metric(namespace='AWS/S3', metric_name='BucketSizeBytes', dimensions={ 'BucketName': bucket.bucket_name, 'StorageType': 'StandardStorage' }, period=core.Duration.days(1)) size_alarm = size_metric.create_alarm( self, 'bucket-alarm', alarm_name='S3 Storage Alarm', comparison_operator=cw.ComparisonOperator. GREATER_THAN_OR_EQUAL_TO_THRESHOLD, evaluation_periods=1, period=core.Duration.days(1), threshold=1000, actions_enabled=True) size_topic = sns.Topic(self, 'size-topic', display_name='My S3 Alarm List') email_param = ssm.StringParameter.from_string_parameter_name( self, 'email-param', 'notification-email') size_topic_sub = sns.Subscription( self, 'size-topic-sub', topic=size_topic, protocol=sns.SubscriptionProtocol.EMAIL, endpoint=email_param.string_value) size_action = cwa.SnsAction(size_topic) size_alarm.add_alarm_action(size_action) bucket_name = 'devassoc-s3-logs' log_bucket = s3.Bucket(self, 'bucket-s3-logs', bucket_name=bucket_name, removal_policy=core.RemovalPolicy.DESTROY, auto_delete_objects=True) s3_trail = ct.Trail(self, 'bucket-trail', bucket=log_bucket, trail_name='s3_logs') s3_trail.add_s3_event_selector([ct.S3EventSelector(bucket=bucket)]) s3_trail.log_all_s3_data_events() single_value_widget = cw.SingleValueWidget(metrics=[size_metric]) graph_widget = cw.GraphWidget(left=[size_metric]) cw.Dashboard(self, 'cloudwatch-dashboard', dashboard_name='S3Dashboard', widgets=[[single_value_widget, graph_widget]])
def __init__(self, scope: Cdk.Construct, id: str, **kwargs) -> None: super().__init__( scope, id, description= "2.2.1 CloudFormation Custom Resource Lambda for invoking cross-region Custom Resources", **kwargs) allowedRoleArnsParameter = Cdk.CfnParameter( self, "AllowedRoleArns", description= "Role ARNs to allow to publish to the SNS topic of the Custom Resource Proxy to enable cross-account use", type="CommaDelimitedList") organizationIdParameter = Cdk.CfnParameter( self, "OrganizationId", description= "Organization ID to use to allow access to the SNS topic of the Custom Resource Proxy", type="String") path = os.path.join(os.path.dirname(__file__), "src/app.py") with open(path) as f: code = Lambda.Code.from_inline(f.read()) function = Lambda.Function( self, "Function", code=code, function_name="LittleOrangeCustomResourceProxy", handler="index.handler", runtime=Lambda.Runtime.PYTHON_3_7, timeout=Cdk.Duration.seconds(30)) topic = Sns.Topic(self, "Topic", topic_name="LittleOrangeCustomResourceProxy") topic.add_to_resource_policy(statement=Iam.PolicyStatement( sid="OrganizationsCloudFormationAccess", actions=["sns:Publish"], conditions={ "StringLike": { "aws:PrincipalArn": allowedRoleArnsParameter.value_as_list }, "StringEquals": { "aws:CalledViaLast": "cloudformation.amazonaws.com", "aws:PrincipalOrgID": organizationIdParameter.value_as_string } }, effect=Iam.Effect.ALLOW, principals=[Iam.AnyPrincipal()], resources=[topic.topic_arn])) subscription = Sns.Subscription( self, "TopicSubscription", topic=topic, endpoint=function.function_arn, protocol=Sns.SubscriptionProtocol.LAMBDA) function.add_permission( "CloudFormationPermission", principal=Iam.ServicePrincipal("cloudformation.amazonaws.com"), action="lambda:InvokeFunction") function.add_permission( "SNSPermission", principal=Iam.ServicePrincipal("sns.amazonaws.com"), action="lambda:InvokeFunction", source_arn=topic.topic_arn) function.add_to_role_policy( Iam.PolicyStatement(actions=["lambda:InvokeFunction"], effect=Iam.Effect.ALLOW, resources=["*"])) parameter = Ssm.StringParameter( self, "ServiceTokenParameter", parameter_name= "/LittleOrange/CloudFormation/CustomResourceProxyServiceToken", description= "Lambda ARN for the Custom Resource Proxy CloudFormation Custom Resource in this region", type=Ssm.ParameterType.STRING, string_value=function.function_arn) parameterSns = Ssm.StringParameter( self, "SNSServiceTokenParameter", parameter_name= "/LittleOrange/CloudFormation/CustomResourceProxySNSServiceToken", description= "SNS Topic ARN for the Custom Resource Proxy CloudFormation Custom Resource in this region", type=Ssm.ParameterType.STRING, string_value=topic.topic_arn) output = Cdk.CfnOutput(self, "ServiceToken", value=function.function_arn) snsOutput = Cdk.CfnOutput(self, "SNSServiceToken", value=topic.topic_arn)
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) high_cpu_topic = sns.Topic(self, 'high-cpu-topic', display_name='myHighCpuAlarm') # phone number format must be 12225558888 for US phone_param = ssm.StringParameter.from_string_parameter_name(self, 'phone-param', 'notification-phone') high_cpu_topic_sub = sns.Subscription(self, 'high-cpu-topic-sub', topic=high_cpu_topic, protocol=sns.SubscriptionProtocol.SMS, endpoint=phone_param.string_value) default_vpc = ec2.Vpc.from_lookup(self, 'default-vpc', is_default=True) monitored_instance = ec2.Instance(self, 'monitored-instance', instance_name='devassoc-monitored', instance_type=type.R3_XLARGE, machine_image=ec2.MachineImage.generic_linux( ami_map=ami_map ), vpc=default_vpc) high_cpu_metric = cw.Metric(namespace='AWS/EC2', metric_name='CPUUtilization', dimensions={ 'InstanceId': monitored_instance.instance_id }, statistic='Average', unit=cw.Unit.PERCENT, period=core.Duration.seconds(300)) high_cpu_alarm = high_cpu_metric.create_alarm(self, 'high-cpu-alarm', alarm_name='cpu-mon', alarm_description='Alarm when CPU exceeds 70%', comparison_operator=cw.ComparisonOperator.GREATER_THAN_THRESHOLD, evaluation_periods=2, period=core.Duration.seconds(300), threshold=70, actions_enabled=True) high_cpu_action = cwa.SnsAction(high_cpu_topic) high_cpu_alarm.add_alarm_action(high_cpu_action) ec2.CfnEIP(self, 'devassoc-elastic-ip') # not really a service role, but there are problems with that, per # https://github.com/aws/aws-cdk/issues/3492 config_service_role = iam.Role(self, 'devassoc-config-service-role', assumed_by=iam.ServicePrincipal('config.amazonaws.com'), managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name('service-role/AWSConfigRole') ]) config_recorder = config.CfnConfigurationRecorder(self, 'devassoc-recorder', name='ConfigRecorder', role_arn=config_service_role.role_arn, recording_group=config.CfnConfigurationRecorder.RecordingGroupProperty( all_supported=True) ) config_bucket = s3.Bucket(self, 'config-bucket', bucket_name='devassoc-config', removal_policy=core.RemovalPolicy.DESTROY, auto_delete_objects=True) config_bucket.add_to_resource_policy(iam.PolicyStatement(effect=iam.Effect.ALLOW, principals=[iam.ServicePrincipal('config.amazonaws.com')], resources=[config_bucket.bucket_arn], actions=['s3:GetBucketAcl'])) config_bucket.add_to_resource_policy(iam.PolicyStatement(effect=iam.Effect.ALLOW, principals=[iam.ServicePrincipal('config.amazonaws.com')], resources=[config_bucket.arn_for_objects( f"AWSLogs/{core.Stack.of(self).account}/Config/*")], actions=['s3:PutObject'], conditions={'StringEquals': { 's3:x-amz-acl': 'bucket-owner-full-control'}})) eip_rule = config.ManagedRule(self, 'devassoc-managed-rule', identifier=config.ManagedRuleIdentifiers.EIP_ATTACHED, config_rule_name='devassoc-eip-rule') eip_rule.node.add_dependency(config_recorder) eip_compliance_topic = sns.Topic(self, 'eip-compliance-topic', display_name='EIP Compliance Topic') eip_compliance_topic_sub = sns.Subscription(self, 'eip-compliance-topic-sub', topic=eip_compliance_topic, protocol=sns.SubscriptionProtocol.SMS, endpoint=phone_param.string_value) eip_rule.on_compliance_change('eip-compliance-change', target=targets.SnsTopic(eip_compliance_topic)) config.CfnDeliveryChannel(self, 'devassoc-config-delivery', s3_bucket_name=config_bucket.bucket_name, sns_topic_arn=eip_compliance_topic.topic_arn)
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) security_distribution_list_email = '*****@*****.**' # securityhub_instance = securityhub.CfnHub(self, 'SecurityHub') # Ensure AWS Config is enabled / Ensure CloudTrail is enabled in all Regions 2.1 - 2.8 cloudtrail_bucket_accesslogs = s3.Bucket( self, "CloudTrailS3Accesslogs", block_public_access=s3.BlockPublicAccess.BLOCK_ALL, encryption=s3.BucketEncryption.S3_MANAGED, removal_policy=core.RemovalPolicy.RETAIN) cloudtrail_bucket = s3.Bucket( self, "CloudTrailS3", block_public_access=s3.BlockPublicAccess.BLOCK_ALL, encryption=s3.BucketEncryption.S3_MANAGED, removal_policy=core.RemovalPolicy.RETAIN, server_access_logs_bucket=cloudtrail_bucket_accesslogs, ) cloudtrail_kms = kms.Key(self, "CloudTrailKey", enable_key_rotation=True) # CloudTrail - single account, not Organization trail = cloudtrail.Trail( self, "CloudTrail", enable_file_validation=True, is_multi_region_trail=True, include_global_service_events=True, send_to_cloud_watch_logs=True, cloud_watch_logs_retention=logs.RetentionDays.FOUR_MONTHS, bucket=cloudtrail_bucket, kms_key=cloudtrail_kms) cloudtrail_kms.grant(iam.ServicePrincipal('cloudtrail.amazonaws.com'), 'kms:DescribeKey') cloudtrail_kms.grant( iam.ServicePrincipal( 'cloudtrail.amazonaws.com', conditions={ 'StringLike': { 'kms:EncryptionContext:aws:cloudtrail:arn': 'arn:aws:cloudtrail:*:' + core.Stack.of(self).account + ':trail/*' } }), 'kms:GenerateDataKey*') cloudtrail_kms.add_to_resource_policy( iam.PolicyStatement( actions=["kms:Decrypt", "kms:ReEncryptFrom"], conditions={ 'StringEquals': { 'kms:CallerAccount': core.Stack.of(self).account }, 'StringLike': { 'kms:EncryptionContext:aws:cloudtrail:arn': 'arn:aws:cloudtrail:*:' + core.Stack.of(self).account + ':trail/*' } }, effect=iam.Effect.ALLOW, principals=[iam.AnyPrincipal()], resources=['*'])) cloudtrail_kms.add_to_resource_policy( iam.PolicyStatement(actions=["kms:CreateAlias"], conditions={ 'StringEquals': { 'kms:CallerAccount': core.Stack.of(self).account, 'kms:ViaService': 'ec2.' + core.Stack.of(self).region + '.amazonaws.com' } }, effect=iam.Effect.ALLOW, principals=[iam.AnyPrincipal()], resources=['*'])) cloudtrail_kms.add_to_resource_policy( iam.PolicyStatement( actions=["kms:Decrypt", "kms:ReEncryptFrom"], conditions={ 'StringEquals': { 'kms:CallerAccount': core.Stack.of(self).account }, 'StringLike': { 'kms:EncryptionContext:aws:cloudtrail:arn': 'arn:aws:cloudtrail:*:' + core.Stack.of(self).account + ':trail/*' } }, effect=iam.Effect.ALLOW, principals=[iam.AnyPrincipal()], resources=['*'])) config_role = iam.CfnServiceLinkedRole( self, id='ServiceLinkedRoleConfig', aws_service_name='config.amazonaws.com') global_config = config.CfnConfigurationRecorder(self, 'ConfigRecorder', name='default', # role_arn=config_role.role_arn, role_arn="arn:aws:iam::" + \ core.Stack.of( self).account+":role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig", # role_arn=config_role.get_att( # attribute_name='resource.arn').to_string(), recording_group=config.CfnConfigurationRecorder.RecordingGroupProperty( all_supported=True, include_global_resource_types=True ) ) config_bucket = s3.Bucket( self, "ConfigS3", block_public_access=s3.BlockPublicAccess.BLOCK_ALL, encryption=s3.BucketEncryption.S3_MANAGED, removal_policy=core.RemovalPolicy.RETAIN, ) config_bucket.add_to_resource_policy( iam.PolicyStatement( actions=['s3:GetBucketAcl'], effect=iam.Effect.ALLOW, principals=[iam.ServicePrincipal('config.amazonaws.com')], resources=[config_bucket.bucket_arn])) config_bucket.add_to_resource_policy( iam.PolicyStatement( actions=['s3:PutObject'], effect=iam.Effect.ALLOW, principals=[iam.ServicePrincipal('config.amazonaws.com')], resources=[ config_bucket.arn_for_objects('AWSLogs/' + core.Stack.of(self).account + '/Config/*') ], conditions={ "StringEquals": { 's3:x-amz-acl': 'bucket-owner-full-control', } })) config_delivery_stream = config.CfnDeliveryChannel( self, "ConfigDeliveryChannel", s3_bucket_name=config_bucket.bucket_name) # Config Aggregator in Organizations account # config_aggregator = config.CfnConfigurationAggregator(self, 'ConfigAggregator', # configuration_aggregator_name='ConfigAggregator', # organization_aggregation_source=config.CfnConfigurationAggregator.OrganizationAggregationSourceProperty( # role_arn=iam.Role(self, "AWSConfigRoleForOrganizations", # assumed_by=iam.ServicePrincipal( # 'config.amazonaws.com'), # managed_policies=[iam.ManagedPolicy.from_aws_managed_policy_name( # 'service-role/AWSConfigRoleForOrganizations')] # ).role_arn, # all_aws_regions=True # ) # ) # 2.9 – Ensure VPC flow logging is enabled in all VPCs # vpc = ec2.Vpc.from_lookup(self, "VPC", # is_default=True, # ) # S3 for VPC flow logs # vpc_flow_logs_bucket = s3.Bucket(self, "VPCFlowLogsBucket", # block_public_access=s3.BlockPublicAccess.BLOCK_ALL, # encryption=s3.BucketEncryption.S3_MANAGED, # removal_policy=core.RemovalPolicy.RETAIN # ) # Ensure a log metric filter and alarm exist for 3.1 – 3.14 security_notifications_topic = sns.Topic(self, 'CIS_Topic', display_name='CIS_Topic', topic_name='CIS_Topic') sns.Subscription(self, 'CIS_Subscription', topic=security_notifications_topic, protocol=sns.SubscriptionProtocol.EMAIL, endpoint=security_distribution_list_email) cloudwatch_actions_cis = cloudwatch_actions.SnsAction( security_notifications_topic) cis_metricfilter_alarms = { 'CIS-3.1-UnauthorizedAPICalls': '($.errorCode="*UnauthorizedOperation") || ($.errorCode="AccessDenied*")', 'CIS-3.2-ConsoleSigninWithoutMFA': '($.eventName="ConsoleLogin") && ($.additionalEventData.MFAUsed !="Yes")', 'RootAccountUsageAlarm': '$.userIdentity.type="Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !="AwsServiceEvent"', 'CIS-3.4-IAMPolicyChanges': '($.eventName=DeleteGroupPolicy) || ($.eventName=DeleteRolePolicy) || ($.eventName=DeleteUserPolicy) || ($.eventName=PutGroupPolicy) || ($.eventName=PutRolePolicy) || ($.eventName=PutUserPolicy) || ($.eventName=CreatePolicy) || ($.eventName=DeletePolicy) || ($.eventName=CreatePolicyVersion) || ($.eventName=DeletePolicyVersion) || ($.eventName=AttachRolePolicy) || ($.eventName=DetachRolePolicy) || ($.eventName=AttachUserPolicy) || ($.eventName=DetachUserPolicy) || ($.eventName=AttachGroupPolicy) || ($.eventName=DetachGroupPolicy)', 'CIS-3.5-CloudTrailChanges': '($.eventName=CreateTrail) || ($.eventName=UpdateTrail) || ($.eventName=DeleteTrail) || ($.eventName=StartLogging) || ($.eventName=StopLogging)', 'CIS-3.6-ConsoleAuthenticationFailure': '($.eventName=ConsoleLogin) && ($.errorMessage="Failed authentication")', 'CIS-3.7-DisableOrDeleteCMK': '($.eventSource=kms.amazonaws.com) && (($.eventName=DisableKey) || ($.eventName=ScheduleKeyDeletion))', 'CIS-3.8-S3BucketPolicyChanges': '($.eventSource=s3.amazonaws.com) && (($.eventName=PutBucketAcl) || ($.eventName=PutBucketPolicy) || ($.eventName=PutBucketCors) || ($.eventName=PutBucketLifecycle) || ($.eventName=PutBucketReplication) || ($.eventName=DeleteBucketPolicy) || ($.eventName=DeleteBucketCors) || ($.eventName=DeleteBucketLifecycle) || ($.eventName=DeleteBucketReplication))', 'CIS-3.9-AWSConfigChanges': '($.eventSource=config.amazonaws.com) && (($.eventName=StopConfigurationRecorder) || ($.eventName=DeleteDeliveryChannel) || ($.eventName=PutDeliveryChannel) || ($.eventName=PutConfigurationRecorder))', 'CIS-3.10-SecurityGroupChanges': '($.eventName=AuthorizeSecurityGroupIngress) || ($.eventName=AuthorizeSecurityGroupEgress) || ($.eventName=RevokeSecurityGroupIngress) || ($.eventName=RevokeSecurityGroupEgress) || ($.eventName=CreateSecurityGroup) || ($.eventName=DeleteSecurityGroup)', 'CIS-3.11-NetworkACLChanges': '($.eventName=CreateNetworkAcl) || ($.eventName=CreateNetworkAclEntry) || ($.eventName=DeleteNetworkAcl) || ($.eventName=DeleteNetworkAclEntry) || ($.eventName=ReplaceNetworkAclEntry) || ($.eventName=ReplaceNetworkAclAssociation)', 'CIS-3.12-NetworkGatewayChanges': '($.eventName=CreateCustomerGateway) || ($.eventName=DeleteCustomerGateway) || ($.eventName=AttachInternetGateway) || ($.eventName=CreateInternetGateway) || ($.eventName=DeleteInternetGateway) || ($.eventName=DetachInternetGateway)', 'CIS-3.13-RouteTableChanges': '($.eventName=CreateRoute) || ($.eventName=CreateRouteTable) || ($.eventName=ReplaceRoute) || ($.eventName=ReplaceRouteTableAssociation) || ($.eventName=DeleteRouteTable) || ($.eventName=DeleteRoute) || ($.eventName=DisassociateRouteTable)', 'CIS-3.14-VPCChanges': '($.eventName=CreateVpc) || ($.eventName=DeleteVpc) || ($.eventName=ModifyVpcAttribute) || ($.eventName=AcceptVpcPeeringConnection) || ($.eventName=CreateVpcPeeringConnection) || ($.eventName=DeleteVpcPeeringConnection) || ($.eventName=RejectVpcPeeringConnection) || ($.eventName=AttachClassicLinkVpc) || ($.eventName=DetachClassicLinkVpc) || ($.eventName=DisableVpcClassicLink) || ($.eventName=EnableVpcClassicLink)', } for x, y in cis_metricfilter_alarms.items(): str_x = str(x) str_y = str(y) logs.MetricFilter( self, "MetricFilter_" + str_x, log_group=trail.log_group, filter_pattern=logs.JsonPattern(json_pattern_string=str_y), metric_name=str_x, metric_namespace="LogMetrics", metric_value='1') cloudwatch.Alarm( self, "Alarm_" + str_x, alarm_name=str_x, alarm_description=str_x, statistic='Sum', period=core.Duration.minutes(5), comparison_operator=cloudwatch.ComparisonOperator. GREATER_THAN_OR_EQUAL_TO_THRESHOLD, evaluation_periods=1, threshold=1, metric=cloudwatch.Metric(metric_name=str_x, namespace="LogMetrics"), ).add_alarm_action(cloudwatch_actions_cis) # IAM Password Policy custom resource CIS 1.5 - 1.11 cfn_template = cfn_inc.CfnInclude( self, "includeTemplate", template_file="account-password-policy.yaml", parameters={ "MaxPasswordAge": 90, "MinimumPasswordLength": 14, "PasswordReusePrevention": 24, "RequireLowercaseCharacters": True, "RequireNumbers": True, "RequireSymbols": True, "RequireUppercaseCharacters": True, }) # CIS 1.20 support_role = iam.Role( self, "SupportRole", assumed_by=iam.AccountPrincipal( account_id=core.Stack.of(self).account), managed_policies=[ iam.ManagedPolicy.from_aws_managed_policy_name( 'AWSSupportAccess') ], role_name='AWSSupportAccess') guardduty_detector = guardduty.CfnDetector(self, 'GuardDutyDetector', enable=True) guardduty_event = events.Rule( self, 'GuardDutyEvent', rule_name='guardduty-notification', description='GuardDuty Notification', event_pattern=events.EventPattern( source=['aws.guardduty'], detail_type=['GuardDuty Finding']), targets=[events_targets.SnsTopic(security_notifications_topic)])
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Parameters notification_email_address = CfnParameter( self, "notification_email_address", type="String", min_length=7, description= "The E-mail address subscribed to notifications when an S3 bucket is detected as open to the public." ) profiling = CfnParameter( self, "profiling", type="String", allowed_values=["TRUE", "FALSE"], default="FALSE", description= "Enable Profiling on Lambda functions: TRUE or FALSE. Default: FALSE" ) tracing = CfnParameter( self, "tracing", type="String", allowed_values=["TRUE", "FALSE"], default="FALSE", description= "Enable tracing on Lambda functions: TRUE or FALSE. Default: FALSE" ) trusted_advisor_refresh_minutes = CfnParameter( self, "trusted_advisor_refresh_minutes", type="Number", default=6, min_value=5, max_value=1440, description= "Number of minutes to schedule a trusted advisor refresh. Default: 6" ) enable_profiling = profiling.value_as_string == 'TRUE' enable_tracing = aws_lambda.Tracing.ACTIVE if tracing.value_as_string != 'TRUE': enable_tracing = aws_lambda.Tracing.DISABLED # Layers dependencies_layer = aws_lambda.LayerVersion( self, "dependenciesLayer", code=aws_lambda.Code.from_asset( "lambda_functions/dependencies_layer/"), compatible_runtimes=[aws_lambda.Runtime.PYTHON_3_8], ) # create SNS target email_notification_topic = sns.Topic( self, 'taEmailNotificationTopic', display_name='taEmailNotificationTopic', topic_name='taEmailNotificationTopic') # add subscription sns.Subscription(self, 'emailSubscription', protocol=sns.SubscriptionProtocol.EMAIL, endpoint=notification_email_address.value_as_string, topic=email_notification_topic) default_event_bus = events.EventBus.from_event_bus_name( self, 'default', 'default') ta_event_pattern = events.EventPattern( source=['aws.trustedadvisor'], detail_type=['Trusted Advisor Check Item Refresh Notification'], detail={ 'check-name': ['Amazon S3 Bucket Permissions'], 'status': ['WARN', 'ERROR'] }) # Lambda function to trigger when TA check flagged ta_check_s3_open_lambda_function_code = aws_lambda.AssetCode( 'lambda_functions/s3openbucket') ta_check_s3_open_lambda_function = aws_lambda.Function( self, 'ta_s3_open_bucket', code=ta_check_s3_open_lambda_function_code, runtime=aws_lambda.Runtime.PYTHON_3_8, handler='s3openbucket.lambda_handler', description='Function Triggered from Trusted Advisor ' 'to Block public access to an S3 Bucket', function_name='ta-check-s3-open-lambda-function', memory_size=128, profiling=enable_profiling, tracing=enable_tracing, log_retention=aws_logs.RetentionDays.ONE_WEEK, timeout=Duration.seconds(10), environment={'topic_arn': email_notification_topic.topic_arn}, initial_policy=[ aws_iam.PolicyStatement(actions=[ 's3:GetBucketPolicy', 's3:DeleteBucketPolicy', 's3:PutBucketPolicy', 's3:GetAccountPublicAccessBlock', 's3:GetBucketPublicAccessBlock', 's3:PutAccountPublicAccessBlock', 's3:PutBucketPublicAccessBlock', 's3:GetBucketAcl', 's3:GetObjectAcl', 's3:PutBucketAcl', 's3:PutObjectAcl' ], effect=aws_iam.Effect.ALLOW, resources=['*']), aws_iam.PolicyStatement( actions=['SNS:Publish'], effect=aws_iam.Effect.ALLOW, resources=[email_notification_topic.topic_arn]) ]) events.Rule( self, 's3PublicBucketRule', description= 'Blocks Public access on an S3 bucket once detected by Trusted Advisor', event_pattern=ta_event_pattern, event_bus=default_event_bus, targets=[targets.LambdaFunction(ta_check_s3_open_lambda_function)]) # Refresh TA check every X minutes # Lambda function to trigger when TA check flagged ta_refresh_lambda_function_code = aws_lambda.AssetCode( 'lambda_functions/refreshTrustedAdvisorCheck') ta_refresh_lambda_function = aws_lambda.Function( self, 'refresh_ta_check', code=ta_refresh_lambda_function_code, runtime=aws_lambda.Runtime.PYTHON_3_8, handler='refreshTrustedAdvisorCheck.lambda_handler', description='Refreshes Trusted Advisor checks', function_name='ta-refresh-ta-check-lambda-function', memory_size=128, profiling=enable_profiling, tracing=enable_tracing, log_retention=aws_logs.RetentionDays.ONE_WEEK, timeout=Duration.seconds(5), initial_policy=[ aws_iam.PolicyStatement(actions=[ 'support:DescribeTrustedAdvisorChecks', 'support:RefreshTrustedAdvisorCheck', 'support:DescribeTrustedAdvisorCheckResult' ], effect=aws_iam.Effect.ALLOW, resources=['*']) ]) ta_refresh_lambda_function.add_layers(dependencies_layer) events.Rule( self, 'refreshTAS3BucketPermissionsRule', schedule=events.Schedule.rate( Duration.minutes( trusted_advisor_refresh_minutes.value_as_number)), rule_name='refreshTAS3BucketPermissionsRule', targets=[targets.LambdaFunction(ta_refresh_lambda_function)])
def __init__(self, scope: cdk.Stack, id: str, sla_stream_monitor_dynamo_table, git_hash, notifier_zip='change-notifier.zip', **kwargs): super().__init__(scope, id, **kwargs) self.sla_stream_monitor_dynamo_table = sla_stream_monitor_dynamo_table self.notifier_zip = git_hash + "-" + notifier_zip self.sns_topic = aws_sns.Topic(self, "SNSTopic", display_name="SLA Notification Topic", topic_name=self.stack_name) self.subscribe_to_topic = aws_sns.Subscription( self, "TopicSubscribe", endpoint=os.getenv("EMAIL_NOTIFICATION"), protocol=aws_sns.SubscriptionProtocol.Email, topic=self.sns_topic) self.sla_notifier_lambda_function = aws_lambda.Function( self, "SLANotifierLambdaFunction", function_name=self.stack_name, code=aws_lambda.AssetCode(self.notifier_zip), handler="sns.lambda_handler", runtime=aws_lambda.Runtime.PYTHON37, #layers=[self.dynamodb_lambda_layer], description="Notifies SNS Topic if SLAs change", environment={ "STACK_NAME": self.stack_name, "SNS_TOPIC_ARN": self.sns_topic.topic_arn, "DYNAMO_TABLE_NAME": self.sla_stream_monitor_dynamo_table.table_name }, memory_size=128, timeout=90, ) # TODO: Make into reusable function self.notifier_cw_event_rule = aws_events.Rule( self, "LambdaCWEventRuleSLANotifier", description="Scheduled event to trigger AWS SLA monitor", enabled=True, #schedule_expression='cron(10 22 */3 * ? *)', schedule_expression='cron(*/6 * * * ? *)', targets=[ aws_events_targets.LambdaFunction( handler=self.sla_notifier_lambda_function) ], ) # Permissions to access stream_monitor dynamo table self.sla_stream_monitor_dynamo_table.grant_read_write_data( self.sla_notifier_lambda_function.role) # Grant publish access from Lambda function to SNS topic self.sns_topic.grant_publish(self.sla_notifier_lambda_function)