From 03b9302bf0a8c70776c47b98e6636dfd962b5db4 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:44:54 -0700 Subject: [PATCH 01/62] Bump cfn-lint version --- cfg/pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/pre-commit-config.yaml b/cfg/pre-commit-config.yaml index 0a437743c..db4819455 100644 --- a/cfg/pre-commit-config.yaml +++ b/cfg/pre-commit-config.yaml @@ -231,7 +231,7 @@ repos: files: '.*\.cfn\.yaml' id: 'cfn-lint' repo: 'https://github.com/aws-cloudformation/cfn-lint' - rev: 'v1.11.0' + rev: 'v1.14.2' - hooks: - From f7f7e77b30468640311ff48baee54aaf8cf493ef Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:48:06 -0700 Subject: [PATCH 02/62] Convert conditions to rules --- src/cloud-formation/indexer.cfn.yaml | 232 +++++++++++++++++---------- 1 file changed, 151 insertions(+), 81 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index d14825a49..c1ad52d2f 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -16,90 +16,54 @@ Conditions: EnableWafRulesWebSocket: !Equals - !Ref 'EnableWafRulesWebSocket' - 'true' - MaybeDeployAlb: !And - - !Condition 'MaybeDeployVpc' - - !Equals - - !Ref 'MaybeDeployAlb' - - 'true' - MaybeDeployAlbDnsRecord: !And - - !Condition 'MaybeDeployAlb' - - !Equals - - !Ref 'MaybeDeployAlbDnsRecord' - - 'true' - MaybeDeployBastionHost: !And - - !Condition 'MaybeDeployDb' - - !Equals - - !Ref 'MaybeDeployBastionHost' - - 'true' - MaybeDeployBroker: !And - - !Condition 'MaybeDeployContainers' - - !Condition 'MaybeDeployProcessor' - - !Equals - - !Ref 'MaybeDeployBroker' - - 'true' - MaybeDeployContainers: !And - - !Condition 'MaybeDeployDb' - - !Condition 'MaybeDeployRouteTables' - - !Equals - - !Ref 'MaybeDeployContainers' - - 'true' - MaybeDeployDb: !And - - !Condition 'MaybeDeployVpc' - - !Equals - - !Ref 'MaybeDeployDb' - - 'true' - MaybeDeployNlb: !And - - !Condition 'MaybeDeployVpc' - - !Equals - - !Ref 'MaybeDeployNlb' - - 'true' - MaybeDeployNlbVpcLink: !And - - !Condition 'MaybeDeployNlb' - - !Equals - - !Ref 'MaybeDeployNlbVpcLink' - - 'true' - MaybeDeployPostgrest: !And - - !Condition 'MaybeDeployContainers' - - !Condition 'MaybeDeployProcessor' - - !Equals - - !Ref 'MaybeDeployPostgrest' - - 'true' - MaybeDeployProcessor: !And - - !Condition 'MaybeDeployContainers' - - !Equals - - !Ref 'MaybeDeployProcessor' - - 'true' - MaybeDeployRestApi: !And - - !Condition 'MaybeDeployNlbVpcLink' - - !Condition 'MaybeDeployPostgrest' - - !Equals - - !Ref 'MaybeDeployRestApi' - - 'true' - MaybeDeployRestApiDnsRecord: !And - - !Condition 'MaybeDeployRestApi' - - !Equals - - !Ref 'MaybeDeployRestApiDnsRecord' - - 'true' - MaybeDeployRouteTables: !And - - !Condition 'MaybeDeployVpc' - - !Equals - - !Ref 'MaybeDeployRouteTables' - - 'true' + MaybeDeployAlb: !Equals + - !Ref 'MaybeDeployAlb' + - 'true' + MaybeDeployAlbDnsRecord: !Equals + - !Ref 'MaybeDeployAlbDnsRecord' + - 'true' + MaybeDeployBastionHost: !Equals + - !Ref 'MaybeDeployBastionHost' + - 'true' + MaybeDeployBroker: !Equals + - !Ref 'MaybeDeployBroker' + - 'true' + MaybeDeployContainers: !Equals + - !Ref 'MaybeDeployContainers' + - 'true' + MaybeDeployDb: !Equals + - !Ref 'MaybeDeployDb' + - 'true' + MaybeDeployNlb: !Equals + - !Ref 'MaybeDeployNlb' + - 'true' + MaybeDeployNlbVpcLink: !Equals + - !Ref 'MaybeDeployNlbVpcLink' + - 'true' + MaybeDeployPostgrest: !Equals + - !Ref 'MaybeDeployPostgrest' + - 'true' + MaybeDeployProcessor: !Equals + - !Ref 'MaybeDeployProcessor' + - 'true' + MaybeDeployRestApi: !Equals + - !Ref 'MaybeDeployRestApi' + - 'true' + MaybeDeployRestApiDnsRecord: !Equals + - !Ref 'MaybeDeployRestApiDnsRecord' + - 'true' + MaybeDeployRouteTables: !Equals + - !Ref 'MaybeDeployRouteTables' + - 'true' MaybeDeployStack: !Equals - !Ref 'MaybeDeployStack' - 'true' - MaybeDeployVpc: !And - - !Condition 'MaybeDeployStack' - - !Equals - - !Ref 'MaybeDeployVpc' - - 'true' - MaybeDeployWaf: !And - - !Condition 'MaybeDeployAlb' - - !Condition 'MaybeDeployStack' - - !Condition 'MaybeDeployRestApi' - - !Equals - - !Ref 'MaybeDeployWaf' - - 'true' + MaybeDeployVpc: !Equals + - !Ref 'MaybeDeployVpc' + - 'true' + MaybeDeployWaf: !Equals + - !Ref 'MaybeDeployWaf' + - 'true' Mappings: Constants: # These compromised credentials are not a security risk because access to @@ -147,6 +111,11 @@ Mappings: C: AvailabilityZone: 2 CidrBlock: '10.0.6.0/24' +Metadata: + cfn-lint: + config: + ignore_checks: + - 'W8001' Outputs: BastionHostId: Condition: 'MaybeDeployBastionHost' @@ -2000,5 +1969,106 @@ Resources: StageName: !Ref 'RestApiStage' WebACLArn: !GetAtt 'Waf.Arn' Type: 'AWS::WAFv2::WebACLAssociation' +Rules: + DeployAlb: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployVpc' + - Fn::Not: + - !Condition 'MaybeDeployAlb' + DeployAlbDnsRecord: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployAlb' + - Fn::Not: + - !Condition 'MaybeDeployAlbDnsRecord' + DeployBastionHost: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployDb' + - Fn::Not: + - !Condition 'MaybeDeployBastionHost' + DeployBroker: + Assertions: + - Assert: !Or + - Fn::And: + - !Condition 'MaybeDeployContainers' + - !Condition 'MaybeDeployProcessor' + - Fn::Not: + - !Condition 'MaybeDeployBroker' + DeployContainers: + Assertions: + - Assert: !Or + - Fn::And: + - !Condition 'MaybeDeployDb' + - !Condition 'MaybeDeployRouteTables' + - Fn::Not: + - !Condition 'MaybeDeployContainers' + DeployDb: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployVpc' + - Fn::Not: + - !Condition 'MaybeDeployDb' + DeployNlb: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployVpc' + - Fn::Not: + - !Condition 'MaybeDeployNlb' + DeployNlbVpcLink: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployNlb' + - Fn::Not: + - !Condition 'MaybeDeployNlbVpcLink' + DeployPostgrest: + Assertions: + - Assert: !Or + - Fn::And: + - !Condition 'MaybeDeployContainers' + - !Condition 'MaybeDeployProcessor' + - Fn::Not: + - !Condition 'MaybeDeployPostgrest' + DeployProcessor: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployContainers' + - Fn::Not: + - !Condition 'MaybeDeployProcessor' + DeployRestApi: + Assertions: + - Assert: !Or + - Fn::And: + - !Condition 'MaybeDeployNlbVpcLink' + - !Condition 'MaybeDeployPostgrest' + - Fn::Not: + - !Condition 'MaybeDeployRestApi' + DeployRestApiDnsRecord: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployRestApi' + - Fn::Not: + - !Condition 'MaybeDeployRestApiDnsRecord' + DeployRouteTables: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployVpc' + - Fn::Not: + - !Condition 'MaybeDeployRouteTables' + DeployVpc: + Assertions: + - Assert: !Or + - !Condition 'MaybeDeployStack' + - Fn::Not: + - !Condition 'MaybeDeployVpc' + DeployWaf: + Assertions: + - Assert: !Or + - Fn::And: + - !Condition 'MaybeDeployAlb' + - !Condition 'MaybeDeployRestApi' + - Fn::Not: + - !Condition 'MaybeDeployWaf' Transform: 'AWS::LanguageExtensions' ... From a425160b1d309b49d61c9fdd5faa8b3740df10e4 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:49:07 -0700 Subject: [PATCH 03/62] Change MaybeDeploy to Deploy --- src/cloud-formation/indexer.cfn.yaml | 386 +++++++++++++-------------- 1 file changed, 193 insertions(+), 193 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index c1ad52d2f..5e50bd8b5 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -7,62 +7,62 @@ # cspell:word restapis --- Conditions: - EnableWafRulesGeneral: !Equals - - !Ref 'EnableWafRulesGeneral' + DeployAlb: !Equals + - !Ref 'DeployAlb' - 'true' - EnableWafRulesRestApi: !Equals - - !Ref 'EnableWafRulesRestApi' + DeployAlbDnsRecord: !Equals + - !Ref 'DeployAlbDnsRecord' - 'true' - EnableWafRulesWebSocket: !Equals - - !Ref 'EnableWafRulesWebSocket' + DeployBastionHost: !Equals + - !Ref 'DeployBastionHost' - 'true' - MaybeDeployAlb: !Equals - - !Ref 'MaybeDeployAlb' + DeployBroker: !Equals + - !Ref 'DeployBroker' - 'true' - MaybeDeployAlbDnsRecord: !Equals - - !Ref 'MaybeDeployAlbDnsRecord' + DeployContainers: !Equals + - !Ref 'DeployContainers' - 'true' - MaybeDeployBastionHost: !Equals - - !Ref 'MaybeDeployBastionHost' + DeployDb: !Equals + - !Ref 'DeployDb' - 'true' - MaybeDeployBroker: !Equals - - !Ref 'MaybeDeployBroker' + DeployNlb: !Equals + - !Ref 'DeployNlb' - 'true' - MaybeDeployContainers: !Equals - - !Ref 'MaybeDeployContainers' + DeployNlbVpcLink: !Equals + - !Ref 'DeployNlbVpcLink' - 'true' - MaybeDeployDb: !Equals - - !Ref 'MaybeDeployDb' + DeployPostgrest: !Equals + - !Ref 'DeployPostgrest' - 'true' - MaybeDeployNlb: !Equals - - !Ref 'MaybeDeployNlb' + DeployProcessor: !Equals + - !Ref 'DeployProcessor' - 'true' - MaybeDeployNlbVpcLink: !Equals - - !Ref 'MaybeDeployNlbVpcLink' + DeployRestApi: !Equals + - !Ref 'DeployRestApi' - 'true' - MaybeDeployPostgrest: !Equals - - !Ref 'MaybeDeployPostgrest' + DeployRestApiDnsRecord: !Equals + - !Ref 'DeployRestApiDnsRecord' - 'true' - MaybeDeployProcessor: !Equals - - !Ref 'MaybeDeployProcessor' + DeployRouteTables: !Equals + - !Ref 'DeployRouteTables' - 'true' - MaybeDeployRestApi: !Equals - - !Ref 'MaybeDeployRestApi' + DeployStack: !Equals + - !Ref 'DeployStack' - 'true' - MaybeDeployRestApiDnsRecord: !Equals - - !Ref 'MaybeDeployRestApiDnsRecord' + DeployVpc: !Equals + - !Ref 'DeployVpc' - 'true' - MaybeDeployRouteTables: !Equals - - !Ref 'MaybeDeployRouteTables' + DeployWaf: !Equals + - !Ref 'DeployWaf' - 'true' - MaybeDeployStack: !Equals - - !Ref 'MaybeDeployStack' + EnableWafRulesGeneral: !Equals + - !Ref 'EnableWafRulesGeneral' - 'true' - MaybeDeployVpc: !Equals - - !Ref 'MaybeDeployVpc' + EnableWafRulesRestApi: !Equals + - !Ref 'EnableWafRulesRestApi' - 'true' - MaybeDeployWaf: !Equals - - !Ref 'MaybeDeployWaf' + EnableWafRulesWebSocket: !Equals + - !Ref 'EnableWafRulesWebSocket' - 'true' Mappings: Constants: @@ -118,11 +118,11 @@ Metadata: - 'W8001' Outputs: BastionHostId: - Condition: 'MaybeDeployBastionHost' + Condition: 'DeployBastionHost' Description: 'The instance ID of the bastion host.' Value: !Ref 'BastionHost' RestEndpoint: - Condition: 'MaybeDeployRestApiDnsRecord' + Condition: 'DeployRestApiDnsRecord' Value: !Sub - 'https://${Environment}.${RootDomain}' - RootDomain: !FindInMap @@ -130,7 +130,7 @@ Outputs: - 'Networking' - 'DnsNameRootDomain' WsEndpoint: - Condition: 'MaybeDeployAlbDnsRecord' + Condition: 'DeployAlbDnsRecord' Value: !Sub - 'wss://ws.${Environment}.${RootDomain}' - RootDomain: !FindInMap @@ -149,103 +149,103 @@ Parameters: DbMinCapacity: Default: 0.5 Type: 'Number' - EnableWafRulesGeneral: + DeployAlb: AllowedValues: - 'false' - 'true' Type: 'String' - EnableWafRulesRestApi: + DeployAlbDnsRecord: AllowedValues: - 'false' - 'true' Type: 'String' - EnableWafRulesWebSocket: + DeployBastionHost: AllowedValues: - 'false' - 'true' Type: 'String' - Environment: - Type: 'String' - MaybeDeployAlb: + DeployBroker: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployAlbDnsRecord: + DeployContainers: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployBastionHost: + DeployDb: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployBroker: + DeployNlb: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployContainers: + DeployNlbVpcLink: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployDb: + DeployPostgrest: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployNlb: + DeployProcessor: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployNlbVpcLink: + DeployRestApi: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployPostgrest: + DeployRestApiDnsRecord: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployProcessor: + DeployRouteTables: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployRestApi: + DeployStack: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployRestApiDnsRecord: + DeployVpc: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployRouteTables: + DeployWaf: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployStack: + EnableWafRulesGeneral: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployVpc: + EnableWafRulesRestApi: AllowedValues: - 'false' - 'true' Type: 'String' - MaybeDeployWaf: + EnableWafRulesWebSocket: AllowedValues: - 'false' - 'true' Type: 'String' + Environment: + Type: 'String' Network: AllowedValues: - 'mainnet' @@ -262,7 +262,7 @@ Parameters: Resources: # Public application load balancer. Alb: - Condition: 'MaybeDeployAlb' + Condition: 'DeployAlb' Properties: LoadBalancerAttributes: - Key: 'load_balancing.cross_zone.enabled' @@ -271,7 +271,7 @@ Resources: SecurityGroups: - !Ref 'AlbSecurityGroup' - !If - - 'MaybeDeployBroker' + - 'DeployBroker' - !Ref 'BrokerWsClientSecurityGroup' - !Ref 'AWS::NoValue' Subnets: @@ -282,7 +282,7 @@ Resources: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' # DNS certificate for the broker's application load balancer. AlbCertificate: - Condition: 'MaybeDeployAlb' + Condition: 'DeployAlb' Properties: DomainName: !Sub - 'ws.${Environment}.${RootDomain}' @@ -305,7 +305,7 @@ Resources: Type: 'AWS::CertificateManager::Certificate' # DNS record for the broker's application load balancer. AlbDnsRecord: - Condition: 'MaybeDeployAlbDnsRecord' + Condition: 'DeployAlbDnsRecord' Properties: AliasTarget: DNSName: !GetAtt 'Alb.DNSName' @@ -324,7 +324,7 @@ Resources: Type: 'AWS::Route53::RecordSet' # Application load balancer listener. AlbListener: - Condition: 'MaybeDeployAlb' + Condition: 'DeployAlb' Properties: Certificates: - CertificateArn: !Ref 'AlbCertificate' @@ -337,7 +337,7 @@ Resources: Type: 'AWS::ElasticLoadBalancingV2::Listener' # Security group for the application load balancer. AlbSecurityGroup: - Condition: 'MaybeDeployAlb' + Condition: 'DeployAlb' Properties: GroupDescription: !Ref 'AWS::StackName' SecurityGroupIngress: @@ -349,7 +349,7 @@ Resources: Type: 'AWS::EC2::SecurityGroup' # Minimal VM for monitoring internal processes. BastionHost: - Condition: 'MaybeDeployBastionHost' + Condition: 'DeployBastionHost' Properties: ImageId: !Ref 'BastionHostAmiId' InstanceType: 't4g.nano' @@ -359,19 +359,19 @@ Resources: - !Ref 'BastionHostSecurityGroup' - !Ref 'DbUserSecurityGroup' - !If - - 'MaybeDeployBroker' + - 'DeployBroker' - !Ref 'BrokerWsClientSecurityGroup' - !Ref 'AWS::NoValue' - !If - - 'MaybeDeployNlb' + - 'DeployNlb' - !Ref 'NlbClientSecurityGroup' - !Ref 'AWS::NoValue' - !If - - 'MaybeDeployPostgrest' + - 'DeployPostgrest' - !Ref 'PostgrestClientSecurityGroup' - !Ref 'AWS::NoValue' - !If - - 'MaybeDeployProcessor' + - 'DeployProcessor' - !Ref 'ProcessorWsClientSecurityGroup' - !Ref 'AWS::NoValue' SubnetId: !Ref 'PrivateSubnetA' @@ -393,7 +393,7 @@ Resources: echo '${PostgrestUrlExport}' >> /etc/profile echo '${ProcessorWsUrlExport}' >> /etc/profile - BrokerWsUrlExport: !If - - 'MaybeDeployBroker' + - 'DeployBroker' - !Join - '' - - 'export BROKER_WS_URL="ws://' @@ -427,7 +427,7 @@ Resources: - 'DatabaseConfig' - 'DatabaseName' NlbDnsNameExport: !If - - 'MaybeDeployNlb' + - 'DeployNlb' - !Join - '' - - 'export NLB_DNS_NAME="' @@ -435,7 +435,7 @@ Resources: - '"' - '# NLB_DNS_NAME not set (NLB not provisioned)' PostgrestUrlExport: !If - - 'MaybeDeployPostgrest' + - 'DeployPostgrest' - !Join - '' - - 'export POSTGREST_URL="http://' @@ -450,7 +450,7 @@ Resources: - '"' - '# POSTGREST_URL not set (PostgREST not provisioned)' ProcessorWsUrlExport: !If - - 'MaybeDeployProcessor' + - 'DeployProcessor' - !Join - '' - - 'export PROCESSOR_WS_URL="ws://' @@ -479,7 +479,7 @@ Resources: Type: 'AWS::EC2::Instance' # Connection endpoint allowing access to the bastion host. BastionHostConnectionEndpoint: - Condition: 'MaybeDeployBastionHost' + Condition: 'DeployBastionHost' Properties: PreserveClientIp: true SecurityGroupIds: @@ -488,7 +488,7 @@ Resources: Type: 'AWS::EC2::InstanceConnectEndpoint' # Security group for bastion host connection endpoint. BastionHostConnectionEndpointSecurityGroup: - Condition: 'MaybeDeployBastionHost' + Condition: 'DeployBastionHost' Properties: GroupDescription: !Ref 'AWS::StackName' SecurityGroupEgress: @@ -498,7 +498,7 @@ Resources: Type: 'AWS::EC2::SecurityGroup' # Security group for the bastion host. BastionHostSecurityGroup: - Condition: 'MaybeDeployBastionHost' + Condition: 'DeployBastionHost' Properties: GroupDescription: !Ref 'AWS::StackName' VpcId: !Ref 'Vpc' @@ -506,7 +506,7 @@ Resources: # Ingress policy for the bastion host security group, separated to eliminate # circular dependencies with the security group for the connection endpoint. BastionHostSecurityGroupIngress: - Condition: 'MaybeDeployBastionHost' + Condition: 'DeployBastionHost' Properties: GroupId: !Ref 'BastionHostSecurityGroup' IpProtocol: -1 @@ -515,7 +515,7 @@ Resources: Type: 'AWS::EC2::SecurityGroupIngress' # Target group for application load balancer traffic to the broker. BrokerAlbTargetGroup: - Condition: 'MaybeDeployAlb' + Condition: 'DeployAlb' DependsOn: 'Alb' Properties: HealthCheckIntervalSeconds: 5 @@ -547,7 +547,7 @@ Resources: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' # Scaling policy for broker service. BrokerAutoScalingPolicy: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' Properties: PolicyName: !Sub '${AWS::StackName}-broker' PolicyType: 'TargetTrackingScaling' @@ -561,7 +561,7 @@ Resources: Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' # Scalable target for broker service. BrokerAutoScalingTarget: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' Properties: MaxCapacity: 5 MinCapacity: 1 @@ -576,14 +576,14 @@ Resources: Type: 'AWS::ApplicationAutoScaling::ScalableTarget' # Security group for the broker's WebSocket server. BrokerPublisherSecurityGroup: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' Properties: GroupDescription: !Ref 'AWS::StackName' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' # Ingress policy for the broker's WebSocket server security group. BrokerPublisherSecurityGroupIngress: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' Properties: GroupId: !Ref 'BrokerPublisherSecurityGroup' IpProtocol: -1 @@ -591,7 +591,7 @@ Resources: Type: 'AWS::EC2::SecurityGroupIngress' # Service for running the broker. BrokerRunner: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' DependsOn: # Wait until processor server is online. - 'ProcessorRunner' @@ -606,7 +606,7 @@ Resources: # load balancer. Metadata: ConditionalDependencyProxy: !If - - 'MaybeDeployAlb' + - 'DeployAlb' - !Ref 'AlbListener' - '' Properties: @@ -617,7 +617,7 @@ Resources: Rollback: true LaunchType: 'FARGATE' LoadBalancers: !If - - 'MaybeDeployAlb' + - 'DeployAlb' - - ContainerName: !Sub '${AWS::StackName}-broker' ContainerPort: !FindInMap - 'Constants' @@ -641,7 +641,7 @@ Resources: Type: 'AWS::ECS::Service' # Service discovery for the broker. BrokerServiceDiscovery: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' Properties: DnsConfig: DnsRecords: @@ -655,7 +655,7 @@ Resources: Type: 'AWS::ServiceDiscovery::Service' # Task definition for the broker. BrokerTask: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' DependsOn: 'ContainerLogGroup' Properties: ContainerDefinitions: @@ -744,7 +744,7 @@ Resources: Type: 'AWS::ECS::TaskDefinition' # Security group for clients of the broker's WebSocket server. BrokerWsClientSecurityGroup: - Condition: 'MaybeDeployBroker' + Condition: 'DeployBroker' Properties: GroupDescription: !Ref 'AWS::StackName' SecurityGroupEgress: @@ -754,11 +754,11 @@ Resources: Type: 'AWS::EC2::SecurityGroup' # Cluster for running ECS containers. ContainerCluster: - Condition: 'MaybeDeployContainers' + Condition: 'DeployContainers' Type: 'AWS::ECS::Cluster' # Log group for ECS task logging. ContainerLogGroup: - Condition: 'MaybeDeployContainers' + Condition: 'DeployContainers' Properties: LogGroupName: !Join - '' @@ -773,7 +773,7 @@ Resources: Type: 'AWS::Logs::LogGroup' # Role with assorted permissions required to run containers. ContainerRole: - Condition: 'MaybeDeployContainers' + Condition: 'DeployContainers' Properties: AssumeRolePolicyDocument: Statement: @@ -867,7 +867,7 @@ Resources: Type: 'AWS::IAM::Role' # Security group for ECS containers. ContainerSecurityGroup: - Condition: 'MaybeDeployContainers' + Condition: 'DeployContainers' Properties: GroupDescription: !Ref 'AWS::StackName' # Allow all outbound traffic, to other resources and to Docker Hub. @@ -878,7 +878,7 @@ Resources: Type: 'AWS::EC2::SecurityGroup' # Database cluster. DbCluster: - Condition: 'MaybeDeployDb' + Condition: 'DeployDb' Metadata: cfn-lint: config: @@ -913,7 +913,7 @@ Resources: Type: 'AWS::RDS::DBCluster' # Primary (writer) database instance. DbInstancePrimary: - Condition: 'MaybeDeployDb' + Condition: 'DeployDb' Properties: DBClusterIdentifier: !Ref 'DbCluster' DBInstanceClass: 'db.serverless' @@ -921,7 +921,7 @@ Resources: Type: 'AWS::RDS::DBInstance' # Replica (reader) database instance. DbInstanceReplica: - Condition: 'MaybeDeployDb' + Condition: 'DeployDb' Properties: DBClusterIdentifier: !Ref 'DbCluster' DBInstanceClass: 'db.serverless' @@ -929,7 +929,7 @@ Resources: Type: 'AWS::RDS::DBInstance' # Security group for the database itself. DbSecurityGroup: - Condition: 'MaybeDeployDb' + Condition: 'DeployDb' Properties: GroupDescription: !Ref 'AWS::StackName' VpcId: !Ref 'Vpc' @@ -937,7 +937,7 @@ Resources: # Ingress policy for the database's security group, separated to eliminate # circular dependencies with security group for users of the database. DbSecurityGroupIngress: - Condition: 'MaybeDeployDb' + Condition: 'DeployDb' Properties: GroupId: !Ref 'DbSecurityGroup' IpProtocol: -1 @@ -945,7 +945,7 @@ Resources: Type: 'AWS::EC2::SecurityGroupIngress' # Database subnet group. DbSubnetGroup: - Condition: 'MaybeDeployDb' + Condition: 'DeployDb' Properties: DBSubnetGroupDescription: !Ref 'AWS::StackName' SubnetIds: @@ -955,7 +955,7 @@ Resources: Type: 'AWS::RDS::DBSubnetGroup' # Security group for users of the database. DbUserSecurityGroup: - Condition: 'MaybeDeployDb' + Condition: 'DeployDb' Properties: GroupDescription: !Ref 'AWS::StackName' SecurityGroupEgress: @@ -978,7 +978,7 @@ Resources: - Identifier: !Ref 'Identifier' # ForEach transforms require that condition is second key or later. # yamllint disable-line rule:key-ordering - Condition: 'MaybeDeployRouteTables' + Condition: 'DeployRouteTables' Type: 'AWS::EC2::SubnetRouteTableAssociation' # A private subnet for each of the database availability zones. Fn::ForEach::PrivateSubnet: @@ -1001,7 +1001,7 @@ Resources: MapPublicIpOnLaunch: false VpcId: !Ref 'Vpc' # ForEach transforms require that condition is second key or later. - Condition: 'MaybeDeployVpc' # yamllint disable-line rule:key-ordering + Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::Subnet' # Association for public route table with each public subnet. Fn::ForEach::PublicRouteTableAssociation: @@ -1018,7 +1018,7 @@ Resources: - Identifier: !Ref 'Identifier' # ForEach transforms require that condition is second key or later. # yamllint disable-line rule:key-ordering - Condition: 'MaybeDeployRouteTables' + Condition: 'DeployRouteTables' Type: 'AWS::EC2::SubnetRouteTableAssociation' # A public subnet for each availability zone. Fn::ForEach::PublicSubnet: @@ -1041,35 +1041,35 @@ Resources: MapPublicIpOnLaunch: true VpcId: !Ref 'Vpc' # ForEach transforms require that condition is second key or later. - Condition: 'MaybeDeployVpc' # yamllint disable-line rule:key-ordering + Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::Subnet' # Internet gateway for the virtual private cloud. InternetGateway: - Condition: 'MaybeDeployVpc' + Condition: 'DeployVpc' Type: 'AWS::EC2::InternetGateway' # Attachment of internet gateway to the virtual private cloud. InternetGatewayAttachment: - Condition: 'MaybeDeployVpc' + Condition: 'DeployVpc' Properties: InternetGatewayId: !Ref 'InternetGateway' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::VPCGatewayAttachment' # Network address translation gateway for the virtual private cloud. NatGateway: - Condition: 'MaybeDeployVpc' + Condition: 'DeployVpc' Properties: AllocationId: !GetAtt 'NatGatewayEip.AllocationId' SubnetId: !Ref 'PublicSubnetA' Type: 'AWS::EC2::NatGateway' # Elastic IP address for the network address translation gateway. NatGatewayEip: - Condition: 'MaybeDeployVpc' + Condition: 'DeployVpc' Properties: Domain: 'vpc' Type: 'AWS::EC2::EIP' # Private network load balancer. Nlb: - Condition: 'MaybeDeployNlb' + Condition: 'DeployNlb' Properties: LoadBalancerAttributes: - Key: 'load_balancing.cross_zone.enabled' @@ -1078,11 +1078,11 @@ Resources: SecurityGroups: - !Ref 'NlbSecurityGroup' - !If - - 'MaybeDeployBroker' + - 'DeployBroker' - !Ref 'BrokerWsClientSecurityGroup' - !Ref 'AWS::NoValue' - !If - - 'MaybeDeployPostgrest' + - 'DeployPostgrest' - !Ref 'PostgrestClientSecurityGroup' - !Ref 'AWS::NoValue' Subnets: @@ -1093,7 +1093,7 @@ Resources: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' # Security group for direct clients of the network load balancer. NlbClientSecurityGroup: - Condition: 'MaybeDeployNlb' + Condition: 'DeployNlb' Properties: GroupDescription: !Ref 'AWS::StackName' SecurityGroupEgress: @@ -1103,7 +1103,7 @@ Resources: Type: 'AWS::EC2::SecurityGroup' # Security group for the network load balancer. NlbSecurityGroup: - Condition: 'MaybeDeployNlb' + Condition: 'DeployNlb' Properties: GroupDescription: !Ref 'AWS::StackName' VpcId: !Ref 'Vpc' @@ -1111,7 +1111,7 @@ Resources: # Network load balancer security group ingress policy for PostgREST traffic # over VPC link. NlbSecurityGroupIngressPostgrest: - Condition: 'MaybeDeployNlbVpcLink' + Condition: 'DeployNlbVpcLink' Properties: CidrIp: '0.0.0.0/0' FromPort: !FindInMap @@ -1127,7 +1127,7 @@ Resources: Type: 'AWS::EC2::SecurityGroupIngress' # Connection to network load balancer through VPC link. NlbVpcLink: - Condition: 'MaybeDeployNlbVpcLink' + Condition: 'DeployNlbVpcLink' Properties: Name: !Ref 'AWS::StackName' TargetArns: @@ -1135,7 +1135,7 @@ Resources: Type: 'AWS::ApiGateway::VpcLink' # Scaling policy for PostgREST service. PostgrestAutoScalingPolicy: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' Properties: PolicyName: !Sub '${AWS::StackName}-postgrest' PolicyType: 'TargetTrackingScaling' @@ -1149,7 +1149,7 @@ Resources: Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' # Scalable target for PostgREST service. PostgrestAutoScalingTarget: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' Properties: MaxCapacity: 5 MinCapacity: 1 @@ -1164,7 +1164,7 @@ Resources: Type: 'AWS::ApplicationAutoScaling::ScalableTarget' # Security group for PostgREST clients. PostgrestClientSecurityGroup: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' Properties: GroupDescription: !Ref 'AWS::StackName' SecurityGroupEgress: @@ -1174,7 +1174,7 @@ Resources: Type: 'AWS::EC2::SecurityGroup' # Listener for network load balancer traffic to PostgREST. PostgrestNlbListener: - Condition: 'MaybeDeployNlb' + Condition: 'DeployNlb' Properties: DefaultActions: - TargetGroupArn: !Ref 'PostgrestNlbTargetGroup' @@ -1188,7 +1188,7 @@ Resources: Type: 'AWS::ElasticLoadBalancingV2::Listener' # Target group for network load balancer traffic to PostgREST. PostgrestNlbTargetGroup: - Condition: 'MaybeDeployNlb' + Condition: 'DeployNlb' DependsOn: 'Nlb' Properties: HealthCheckIntervalSeconds: 5 @@ -1217,7 +1217,7 @@ Resources: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' # Service for running PostgREST. PostgrestRunner: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' DependsOn: # Wait for both database instances to ensure cluster is fully available. - 'DbInstancePrimary' @@ -1236,7 +1236,7 @@ Resources: # network load balancer. Metadata: ConditionalDependencyProxy: !If - - 'MaybeDeployNlb' + - 'DeployNlb' - !Ref 'PostgrestNlbListener' - '' Properties: @@ -1247,7 +1247,7 @@ Resources: Rollback: true LaunchType: 'FARGATE' LoadBalancers: !If - - 'MaybeDeployNlb' + - 'DeployNlb' - - ContainerName: !Sub '${AWS::StackName}-postgrest' ContainerPort: !FindInMap - 'Constants' @@ -1271,14 +1271,14 @@ Resources: Type: 'AWS::ECS::Service' # Security group for the PostgREST server. PostgrestServerSecurityGroup: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' Properties: GroupDescription: !Ref 'AWS::StackName' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' # Ingress policy for the PostgREST server security group. PostgrestServerSecurityGroupIngress: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' Properties: GroupId: !Ref 'PostgrestServerSecurityGroup' IpProtocol: -1 @@ -1286,7 +1286,7 @@ Resources: Type: 'AWS::EC2::SecurityGroupIngress' # Service discovery for PostgREST. PostgrestServiceDiscovery: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' Properties: DnsConfig: DnsRecords: @@ -1300,7 +1300,7 @@ Resources: Type: 'AWS::ServiceDiscovery::Service' # Task definition for PostgREST. PostgrestTask: - Condition: 'MaybeDeployPostgrest' + Condition: 'DeployPostgrest' DependsOn: 'ContainerLogGroup' Properties: ContainerDefinitions: @@ -1396,13 +1396,13 @@ Resources: Type: 'AWS::ECS::TaskDefinition' # Route table for private subnets in the virtual private cloud. PrivateRouteTable: - Condition: 'MaybeDeployRouteTables' + Condition: 'DeployRouteTables' Properties: VpcId: !Ref 'Vpc' Type: 'AWS::EC2::RouteTable' # Route from private subnets through network address translation gateway. PrivateRouteThroughNatGateway: - Condition: 'MaybeDeployRouteTables' + Condition: 'DeployRouteTables' Properties: DestinationCidrBlock: '0.0.0.0/0' NatGatewayId: !Ref 'NatGateway' @@ -1410,7 +1410,7 @@ Resources: Type: 'AWS::EC2::Route' # Private DNS namespace for internal service discovery. PrivateServiceDiscoveryNamespace: - Condition: 'MaybeDeployVpc' + Condition: 'DeployVpc' Properties: Description: !Ref 'AWS::StackName' Name: !Ref 'AWS::StackName' @@ -1418,14 +1418,14 @@ Resources: Type: 'AWS::ServiceDiscovery::PrivateDnsNamespace' # Security group for the processor's WebSocket server. ProcessorPublisherSecurityGroup: - Condition: 'MaybeDeployVpc' + Condition: 'DeployVpc' Properties: GroupDescription: !Ref 'AWS::StackName' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' # Ingress policy for the processor's WebSocket server security group. ProcessorPublisherSecurityGroupIngress: - Condition: 'MaybeDeployProcessor' + Condition: 'DeployProcessor' Properties: GroupId: !Ref 'ProcessorPublisherSecurityGroup' IpProtocol: -1 @@ -1433,7 +1433,7 @@ Resources: Type: 'AWS::EC2::SecurityGroupIngress' # Service for running the processor. ProcessorRunner: - Condition: 'MaybeDeployProcessor' + Condition: 'DeployProcessor' DependsOn: # Pending instance creation, the cluster endpoint is defined but not # available, so the service will not be able to connect to the database. @@ -1471,7 +1471,7 @@ Resources: Type: 'AWS::ECS::Service' # Service discovery for the processor. ProcessorServiceDiscovery: - Condition: 'MaybeDeployProcessor' + Condition: 'DeployProcessor' Properties: DnsConfig: DnsRecords: @@ -1485,7 +1485,7 @@ Resources: Type: 'AWS::ServiceDiscovery::Service' # Task definition for processor. ProcessorTask: - Condition: 'MaybeDeployProcessor' + Condition: 'DeployProcessor' DependsOn: 'ContainerLogGroup' Properties: ContainerDefinitions: @@ -1598,7 +1598,7 @@ Resources: Type: 'AWS::ECS::TaskDefinition' # Security group for clients of the processor's WebSocket server. ProcessorWsClientSecurityGroup: - Condition: 'MaybeDeployProcessor' + Condition: 'DeployProcessor' Properties: GroupDescription: !Ref 'AWS::StackName' SecurityGroupEgress: @@ -1608,13 +1608,13 @@ Resources: Type: 'AWS::EC2::SecurityGroup' # Route table for public subnets in the virtual private cloud. PublicRouteTable: - Condition: 'MaybeDeployRouteTables' + Condition: 'DeployRouteTables' Properties: VpcId: !Ref 'Vpc' Type: 'AWS::EC2::RouteTable' # Route from public subnets through the internet gateway. PublicRouteToInternet: - Condition: 'MaybeDeployRouteTables' + Condition: 'DeployRouteTables' Properties: DestinationCidrBlock: '0.0.0.0/0' GatewayId: !Ref 'InternetGateway' @@ -1622,7 +1622,7 @@ Resources: Type: 'AWS::EC2::Route' # REST API endpoint. RestApi: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: ApiKeySourceType: 'HEADER' EndpointConfiguration: @@ -1632,7 +1632,7 @@ Resources: Type: 'AWS::ApiGateway::RestApi' # DNS certificate for the REST API endpoint. RestApiCertificate: - Condition: 'MaybeDeployRestApiDnsRecord' + Condition: 'DeployRestApiDnsRecord' Properties: DomainName: !Sub - '${Environment}.${RootDomain}' @@ -1655,7 +1655,7 @@ Resources: Type: 'AWS::CertificateManager::Certificate' # Deployment for the REST API endpoint. RestApiDeployment: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' DependsOn: - 'RestApiMethodGeneral' - 'RestApiMethodRoot' @@ -1664,7 +1664,7 @@ Resources: Type: 'AWS::ApiGateway::Deployment' # DNS record for the REST API. RestApiDnsRecord: - Condition: 'MaybeDeployRestApiDnsRecord' + Condition: 'DeployRestApiDnsRecord' Properties: AliasTarget: DNSName: !GetAtt 'RestApiDomainName.RegionalDomainName' @@ -1683,7 +1683,7 @@ Resources: Type: 'AWS::Route53::RecordSet' # Domain mapping for the REST API endpoint. RestApiDomainMapping: - Condition: 'MaybeDeployRestApiDnsRecord' + Condition: 'DeployRestApiDnsRecord' Properties: DomainName: !Ref 'RestApiDomainName' RestApiId: !Ref 'RestApi' @@ -1691,7 +1691,7 @@ Resources: Type: 'AWS::ApiGateway::BasePathMapping' # Custom domain name for the REST API endpoint. RestApiDomainName: - Condition: 'MaybeDeployRestApiDnsRecord' + Condition: 'DeployRestApiDnsRecord' Properties: DomainName: !Sub - '${Environment}.${RootDomain}' @@ -1707,7 +1707,7 @@ Resources: Type: 'AWS::ApiGateway::DomainName' # API key for the REST API endpoint. RestApiKey: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: Enabled: true StageKeys: @@ -1716,7 +1716,7 @@ Resources: Type: 'AWS::ApiGateway::ApiKey' # Rest API endpoint general method. RestApiMethodGeneral: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: ApiKeyRequired: true AuthorizationType: 'NONE' @@ -1742,7 +1742,7 @@ Resources: Type: 'AWS::ApiGateway::Method' # Rest API endpoint root method. RestApiMethodRoot: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: ApiKeyRequired: true AuthorizationType: 'NONE' @@ -1768,7 +1768,7 @@ Resources: Type: 'AWS::ApiGateway::Method' # Proxy resource for the REST API endpoint. RestApiProxyResource: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: ParentId: !GetAtt 'RestApi.RootResourceId' PathPart: '{proxy+}' @@ -1776,7 +1776,7 @@ Resources: Type: 'AWS::ApiGateway::Resource' # Stage for the REST API endpoint. RestApiStage: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: CacheClusterEnabled: 'true' CacheClusterSize: '0.5' @@ -1804,7 +1804,7 @@ Resources: Type: 'AWS::ApiGateway::Stage' # REST API usage plan. RestApiUsagePlan: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: ApiStages: - ApiId: !Ref 'RestApi' @@ -1812,7 +1812,7 @@ Resources: Type: 'AWS::ApiGateway::UsagePlan' # Association of API key with usage plan. RestApiUsagePlanKeyAssociation: - Condition: 'MaybeDeployRestApi' + Condition: 'DeployRestApi' Properties: KeyId: !Ref 'RestApiKey' KeyType: 'API_KEY' @@ -1820,7 +1820,7 @@ Resources: Type: 'AWS::ApiGateway::UsagePlanKey' # Virtual private cloud for internal networking. Vpc: - Condition: 'MaybeDeployVpc' + Condition: 'DeployVpc' Properties: CidrBlock: !FindInMap - 'Constants' @@ -1831,7 +1831,7 @@ Resources: Type: 'AWS::EC2::VPC' # Web application firewall. Waf: - Condition: 'MaybeDeployWaf' + Condition: 'DeployWaf' Properties: # Allow all all traffic by default unless blocked per below rules. DefaultAction: @@ -1953,14 +1953,14 @@ Resources: Type: 'AWS::WAFv2::WebACL' # Web application firewall for the broker application load balancer. WafAlb: - Condition: 'MaybeDeployWaf' + Condition: 'DeployWaf' Properties: ResourceArn: !Ref 'Alb' WebACLArn: !GetAtt 'Waf.Arn' Type: 'AWS::WAFv2::WebACLAssociation' # Web application firewall for the REST API. WafRestApi: - Condition: 'MaybeDeployWaf' + Condition: 'DeployWaf' Properties: ResourceArn: !Sub - 'arn:aws:apigateway:${Region}::/restapis/${ApiId}/stages/${StageName}' @@ -1973,102 +1973,102 @@ Rules: DeployAlb: Assertions: - Assert: !Or - - !Condition 'MaybeDeployVpc' + - !Condition 'DeployVpc' - Fn::Not: - - !Condition 'MaybeDeployAlb' + - !Condition 'DeployAlb' DeployAlbDnsRecord: Assertions: - Assert: !Or - - !Condition 'MaybeDeployAlb' + - !Condition 'DeployAlb' - Fn::Not: - - !Condition 'MaybeDeployAlbDnsRecord' + - !Condition 'DeployAlbDnsRecord' DeployBastionHost: Assertions: - Assert: !Or - - !Condition 'MaybeDeployDb' + - !Condition 'DeployDb' - Fn::Not: - - !Condition 'MaybeDeployBastionHost' + - !Condition 'DeployBastionHost' DeployBroker: Assertions: - Assert: !Or - Fn::And: - - !Condition 'MaybeDeployContainers' - - !Condition 'MaybeDeployProcessor' + - !Condition 'DeployContainers' + - !Condition 'DeployProcessor' - Fn::Not: - - !Condition 'MaybeDeployBroker' + - !Condition 'DeployBroker' DeployContainers: Assertions: - Assert: !Or - Fn::And: - - !Condition 'MaybeDeployDb' - - !Condition 'MaybeDeployRouteTables' + - !Condition 'DeployDb' + - !Condition 'DeployRouteTables' - Fn::Not: - - !Condition 'MaybeDeployContainers' + - !Condition 'DeployContainers' DeployDb: Assertions: - Assert: !Or - - !Condition 'MaybeDeployVpc' + - !Condition 'DeployVpc' - Fn::Not: - - !Condition 'MaybeDeployDb' + - !Condition 'DeployDb' DeployNlb: Assertions: - Assert: !Or - - !Condition 'MaybeDeployVpc' + - !Condition 'DeployVpc' - Fn::Not: - - !Condition 'MaybeDeployNlb' + - !Condition 'DeployNlb' DeployNlbVpcLink: Assertions: - Assert: !Or - - !Condition 'MaybeDeployNlb' + - !Condition 'DeployNlb' - Fn::Not: - - !Condition 'MaybeDeployNlbVpcLink' + - !Condition 'DeployNlbVpcLink' DeployPostgrest: Assertions: - Assert: !Or - Fn::And: - - !Condition 'MaybeDeployContainers' - - !Condition 'MaybeDeployProcessor' + - !Condition 'DeployContainers' + - !Condition 'DeployProcessor' - Fn::Not: - - !Condition 'MaybeDeployPostgrest' + - !Condition 'DeployPostgrest' DeployProcessor: Assertions: - Assert: !Or - - !Condition 'MaybeDeployContainers' + - !Condition 'DeployContainers' - Fn::Not: - - !Condition 'MaybeDeployProcessor' + - !Condition 'DeployProcessor' DeployRestApi: Assertions: - Assert: !Or - Fn::And: - - !Condition 'MaybeDeployNlbVpcLink' - - !Condition 'MaybeDeployPostgrest' + - !Condition 'DeployNlbVpcLink' + - !Condition 'DeployPostgrest' - Fn::Not: - - !Condition 'MaybeDeployRestApi' + - !Condition 'DeployRestApi' DeployRestApiDnsRecord: Assertions: - Assert: !Or - - !Condition 'MaybeDeployRestApi' + - !Condition 'DeployRestApi' - Fn::Not: - - !Condition 'MaybeDeployRestApiDnsRecord' + - !Condition 'DeployRestApiDnsRecord' DeployRouteTables: Assertions: - Assert: !Or - - !Condition 'MaybeDeployVpc' + - !Condition 'DeployVpc' - Fn::Not: - - !Condition 'MaybeDeployRouteTables' + - !Condition 'DeployRouteTables' DeployVpc: Assertions: - Assert: !Or - - !Condition 'MaybeDeployStack' + - !Condition 'DeployStack' - Fn::Not: - - !Condition 'MaybeDeployVpc' + - !Condition 'DeployVpc' DeployWaf: Assertions: - Assert: !Or - Fn::And: - - !Condition 'MaybeDeployAlb' - - !Condition 'MaybeDeployRestApi' + - !Condition 'DeployAlb' + - !Condition 'DeployRestApi' - Fn::Not: - - !Condition 'MaybeDeployWaf' + - !Condition 'DeployWaf' Transform: 'AWS::LanguageExtensions' ... From 00373a6956435cc5838090a2bd429e3089607426 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:49:39 -0700 Subject: [PATCH 04/62] Update deploy file for rule changes --- src/cloud-formation/deploy-dev.yaml | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index f5581c082..1f28f181d 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,26 +1,26 @@ --- parameters: BrokerImageVersion: '0.7.0' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' + DeployBastionHost: 'true' + DeployBroker: 'true' + DeployContainers: 'true' + DeployDb: 'true' + DeployNlb: 'true' + DeployNlbVpcLink: 'true' + DeployPostgrest: 'true' + DeployProcessor: 'true' + DeployRestApi: 'true' + DeployRestApiDnsRecord: 'true' + DeployRouteTables: 'true' + DeployStack: 'true' + DeployVpc: 'true' + DeployWaf: 'true' EnableWafRulesGeneral: 'true' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' Environment: 'dev' - MaybeDeployAlb: 'true' - MaybeDeployAlbDnsRecord: 'true' - MaybeDeployBastionHost: 'true' - MaybeDeployBroker: 'true' - MaybeDeployContainers: 'true' - MaybeDeployDb: 'true' - MaybeDeployNlb: 'true' - MaybeDeployNlbVpcLink: 'true' - MaybeDeployPostgrest: 'true' - MaybeDeployProcessor: 'true' - MaybeDeployRestApi: 'true' - MaybeDeployRestApiDnsRecord: 'true' - MaybeDeployRouteTables: 'true' - MaybeDeployStack: 'true' - MaybeDeployVpc: 'true' - MaybeDeployWaf: 'true' Network: 'testnet' ProcessorImageVersion: '0.5.0' tags: null From 37442f15cd44aeac40391c170aab5ce5753d70db Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:54:28 -0700 Subject: [PATCH 05/62] Update README in prep for rules checking --- src/cloud-formation/README.md | 45 +++++++++-------------------- src/cloud-formation/deploy-dev.yaml | 2 +- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/cloud-formation/README.md b/src/cloud-formation/README.md index bf3a11bb1..58e3ae6fe 100644 --- a/src/cloud-formation/README.md +++ b/src/cloud-formation/README.md @@ -22,37 +22,18 @@ under a root domain you provide, for an environment name of your choosing: ## Template parameters -`indexer.cfn.yaml` contains assorted [parameters] of the form `MaybeDeploy*` -that can be used to selectively provision and de-provision [resources]. For a -concise list of such parameters, see a [stack deployment file] at -`deploy-*.yaml`. See the template [conditions] section for associated -dependencies. - -Note that even if a parameter is passed as `true`, the resources that directly -depend on it will not be created unless the condition's dependencies are also -met. All resources are eventually conditional on `MaybeDeployStack`, which can -be used to toggle provisioning and de-provisioning of all resources. - -In practice this means that even if a `MaybeDeploy*` parameter is passed as -`true`, the corresponding resource(s) might not be created. For example if -`MaybeDeployStack` is `false`, then even if `MaybeDeployVpc` is `true`, -virtual private network resources won't be created because `MaybeDeployVpc` -is conditional on `MaybeDeployStack`. - -In theory [rules] could be used to enforce parametric dependencies, thus -generating an error in the case that a hypothetical `DeployVpc` is passed -`true` but a hypothetical `DeployStack` is passed `false`, however rules have -several prohibitive issues in practice: - -1. [`cfn-lint` issue #3630]. - -1. If a rule assertion fails, rather than reporting an assertion error, the - [GitSync status dashboard] instead simply halts the update with - [GitSync event] type `CHANGESET_CREATION_FAILED` and following event message, - misleadingly reporting that no changes are present when in fact the update - failure was a result of failed rule assertions: - - > Changeset creation failed. The reason was No updates are to be performed.. +`indexer.cfn.yaml` contains assorted [parameters] of the form `Deploy*` that can +be used to [conditionally][conditions] provision and de-provision [resources]. +For a concise list of such parameters, see a [stack deployment file] at +`deploy-*.yaml`. See the template [rules] section for associated dependencies. + +Note that if a rule assertion fails, rather than reporting an assertion error, +the [GitSync status dashboard] instead simply halts the update with +[GitSync event] type `CHANGESET_CREATION_FAILED` and following event message, +misleadingly reporting that no changes are present when in fact the update +failure was a result of failed rule assertions: + +> Changeset creation failed. The reason was No updates are to be performed.. ## Setup @@ -306,7 +287,7 @@ deployment environment: ### Bastion host connections Before you try connecting to the bastion host, verify that the -`MaybeDeployBastionHost` [condition][conditions] evaluates to `true`. Note +`DeployBastionHost` [condition][conditions] evaluates to `true`. Note too that if you have been provisioning and de-provisioning other resources, you might want to de-provision then provision the bastion host before running the below commands, in order to refresh the bastion host [user data] that stores the diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 1f28f181d..e6f48002d 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -14,7 +14,7 @@ parameters: DeployRestApi: 'true' DeployRestApiDnsRecord: 'true' DeployRouteTables: 'true' - DeployStack: 'true' + DeployStack: 'false' DeployVpc: 'true' DeployWaf: 'true' EnableWafRulesGeneral: 'true' From 7ec0cdf0c33955a5f18244f8ffcb9a44e33b5d7f Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:57:28 -0700 Subject: [PATCH 06/62] Update env for deploy --- src/cloud-formation/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index e6f48002d..e1896ebeb 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -20,7 +20,7 @@ parameters: EnableWafRulesGeneral: 'true' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' - Environment: 'dev' + Environment: 'ECO-2188' Network: 'testnet' ProcessorImageVersion: '0.5.0' tags: null From ac4d8b0885f30c1f1b0b83ab91f4d1da2dbf6d37 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:17:20 -0700 Subject: [PATCH 07/62] Switch condition style --- cfg/pre-commit-config.yaml | 2 +- src/cloud-formation/indexer.cfn.yaml | 165 ++++++++++++++++++--------- 2 files changed, 111 insertions(+), 56 deletions(-) diff --git a/cfg/pre-commit-config.yaml b/cfg/pre-commit-config.yaml index db4819455..78c44062f 100644 --- a/cfg/pre-commit-config.yaml +++ b/cfg/pre-commit-config.yaml @@ -231,7 +231,7 @@ repos: files: '.*\.cfn\.yaml' id: 'cfn-lint' repo: 'https://github.com/aws-cloudformation/cfn-lint' - rev: 'v1.14.2' + rev: 'v1.15.0' - hooks: - diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 5e50bd8b5..682b4bd94 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -1973,102 +1973,157 @@ Rules: DeployAlb: Assertions: - Assert: !Or - - !Condition 'DeployVpc' - - Fn::Not: - - !Condition 'DeployAlb' + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployAlb' + - 'false' DeployAlbDnsRecord: Assertions: - Assert: !Or - - !Condition 'DeployAlb' - - Fn::Not: - - !Condition 'DeployAlbDnsRecord' + - !Equals + - !Ref 'DeployAlb' + - 'true' + - !Equals + - !Ref 'DeployAlbDnsRecord' + - 'false' DeployBastionHost: Assertions: - Assert: !Or - - !Condition 'DeployDb' - - Fn::Not: - - !Condition 'DeployBastionHost' + - !Equals + - !Ref 'DeployDb' + - 'true' + - !Equals + - !Ref 'DeployBastionHost' + - 'false' DeployBroker: Assertions: - Assert: !Or - - Fn::And: - - !Condition 'DeployContainers' - - !Condition 'DeployProcessor' - - Fn::Not: - - !Condition 'DeployBroker' + - !And + - !Equals + - !Ref 'DeployContainers' + - 'true' + - !Equals + - !Ref 'DeployProcessor' + - 'true' + - !Equals + - !Ref 'DeployBroker' + - 'false' DeployContainers: Assertions: - Assert: !Or - - Fn::And: - - !Condition 'DeployDb' - - !Condition 'DeployRouteTables' - - Fn::Not: - - !Condition 'DeployContainers' + - !And + - !Equals + - !Ref 'DeployDb' + - 'true' + - !Equals + - !Ref 'DeployRouteTables' + - 'true' + - !Equals + - !Ref 'DeployContainers' + - 'false' DeployDb: Assertions: - Assert: !Or - - !Condition 'DeployVpc' - - Fn::Not: - - !Condition 'DeployDb' + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployDb' + - 'false' DeployNlb: Assertions: - Assert: !Or - - !Condition 'DeployVpc' - - Fn::Not: - - !Condition 'DeployNlb' + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployNlb' + - 'false' DeployNlbVpcLink: Assertions: - Assert: !Or - - !Condition 'DeployNlb' - - Fn::Not: - - !Condition 'DeployNlbVpcLink' + - !Equals + - !Ref 'DeployNlb' + - 'true' + - !Equals + - !Ref 'DeployNlbVpcLink' + - 'false' DeployPostgrest: Assertions: - Assert: !Or - - Fn::And: - - !Condition 'DeployContainers' - - !Condition 'DeployProcessor' - - Fn::Not: - - !Condition 'DeployPostgrest' + - !And + - !Equals + - !Ref 'DeployContainers' + - 'true' + - !Equals + - !Ref 'DeployProcessor' + - 'true' + - !Equals + - !Ref 'DeployPostgrest' + - 'false' DeployProcessor: Assertions: - Assert: !Or - - !Condition 'DeployContainers' - - Fn::Not: - - !Condition 'DeployProcessor' + - !Equals + - !Ref 'DeployContainers' + - 'true' + - !Equals + - !Ref 'DeployProcessor' + - 'false' DeployRestApi: Assertions: - Assert: !Or - - Fn::And: - - !Condition 'DeployNlbVpcLink' - - !Condition 'DeployPostgrest' - - Fn::Not: - - !Condition 'DeployRestApi' + - !And + - !Equals + - !Ref 'DeployNlbVpcLink' + - 'true' + - !Equals + - !Ref 'DeployPostgrest' + - 'true' + - !Equals + - !Ref 'DeployRestApi' + - 'false' DeployRestApiDnsRecord: Assertions: - Assert: !Or - - !Condition 'DeployRestApi' - - Fn::Not: - - !Condition 'DeployRestApiDnsRecord' + - !Equals + - !Ref 'DeployRestApi' + - 'true' + - !Equals + - !Ref 'DeployRestApiDnsRecord' + - 'false' DeployRouteTables: Assertions: - Assert: !Or - - !Condition 'DeployVpc' - - Fn::Not: - - !Condition 'DeployRouteTables' + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployRouteTables' + - 'false' DeployVpc: Assertions: - Assert: !Or - - !Condition 'DeployStack' - - Fn::Not: - - !Condition 'DeployVpc' + - !Equals + - 'DeployStack' + - 'true' + - !Equals + - !Ref 'DeployVpc' + - 'false' DeployWaf: Assertions: - Assert: !Or - - Fn::And: - - !Condition 'DeployAlb' - - !Condition 'DeployRestApi' - - Fn::Not: - - !Condition 'DeployWaf' + - !And + - !Equals + - !Ref 'DeployAlb' + - 'true' + - !Equals + - !Ref 'DeployRestApi' + - 'true' + - !Equals + - !Ref 'DeployWaf' + - 'false' Transform: 'AWS::LanguageExtensions' ... From da5c56d50e23d4b24cb7d5e818b8312c1a308489 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:46:19 -0700 Subject: [PATCH 08/62] Try deploying repro template --- src/cloud-formation/deploy-dev.yaml | 49 +- src/cloud-formation/indexer.cfn.yaml | 2087 +------------------------- 2 files changed, 37 insertions(+), 2099 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index e1896ebeb..4466a5cfb 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,28 +1,31 @@ --- parameters: - BrokerImageVersion: '0.7.0' - DeployAlb: 'true' - DeployAlbDnsRecord: 'true' - DeployBastionHost: 'true' - DeployBroker: 'true' - DeployContainers: 'true' - DeployDb: 'true' - DeployNlb: 'true' - DeployNlbVpcLink: 'true' - DeployPostgrest: 'true' - DeployProcessor: 'true' - DeployRestApi: 'true' - DeployRestApiDnsRecord: 'true' - DeployRouteTables: 'true' - DeployStack: 'false' - DeployVpc: 'true' - DeployWaf: 'true' - EnableWafRulesGeneral: 'true' - EnableWafRulesRestApi: 'false' - EnableWafRulesWebSocket: 'false' - Environment: 'ECO-2188' - Network: 'testnet' - ProcessorImageVersion: '0.5.0' + # BrokerImageVersion: '0.7.0' + # DeployAlb: 'true' + # DeployAlbDnsRecord: 'true' + # DeployBastionHost: 'true' + # DeployBroker: 'true' + # DeployContainers: 'true' + # DeployDb: 'true' + # DeployNlb: 'true' + # DeployNlbVpcLink: 'true' + # DeployPostgrest: 'true' + # DeployProcessor: 'true' + # DeployRestApi: 'true' + # DeployRestApiDnsRecord: 'true' + # DeployRouteTables: 'true' + # DeployStack: 'false' + # DeployVpc: 'true' + # DeployWaf: 'true' + # EnableWafRulesGeneral: 'true' + # EnableWafRulesRestApi: 'false' + # EnableWafRulesWebSocket: 'false' + # Environment: 'ECO-2188' + # Network: 'testnet' + # ProcessorImageVersion: '0.5.0' + DeployAnything: 'false' + DeployGateway: 'true' + DeployVpc: 'false' tags: null template-file-path: 'src/cloud-formation/indexer.cfn.yaml' ... diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 682b4bd94..dcbefd14b 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -1,220 +1,23 @@ -# cspell:word aarch -# cspell:word awslogs -# cspell:word awsvpc -# cspell:word fargate -# cspell:word multivalue -# cspell:word pullthroughcache -# cspell:word restapis --- Conditions: - DeployAlb: !Equals - - !Ref 'DeployAlb' - - 'true' - DeployAlbDnsRecord: !Equals - - !Ref 'DeployAlbDnsRecord' - - 'true' - DeployBastionHost: !Equals - - !Ref 'DeployBastionHost' - - 'true' - DeployBroker: !Equals - - !Ref 'DeployBroker' - - 'true' - DeployContainers: !Equals - - !Ref 'DeployContainers' - - 'true' - DeployDb: !Equals - - !Ref 'DeployDb' - - 'true' - DeployNlb: !Equals - - !Ref 'DeployNlb' - - 'true' - DeployNlbVpcLink: !Equals - - !Ref 'DeployNlbVpcLink' - - 'true' - DeployPostgrest: !Equals - - !Ref 'DeployPostgrest' - - 'true' - DeployProcessor: !Equals - - !Ref 'DeployProcessor' - - 'true' - DeployRestApi: !Equals - - !Ref 'DeployRestApi' - - 'true' - DeployRestApiDnsRecord: !Equals - - !Ref 'DeployRestApiDnsRecord' - - 'true' - DeployRouteTables: !Equals - - !Ref 'DeployRouteTables' - - 'true' - DeployStack: !Equals - - !Ref 'DeployStack' + DeployGateway: !Equals + - !Ref 'DeployGateway' - 'true' DeployVpc: !Equals - !Ref 'DeployVpc' - 'true' - DeployWaf: !Equals - - !Ref 'DeployWaf' - - 'true' - EnableWafRulesGeneral: !Equals - - !Ref 'EnableWafRulesGeneral' - - 'true' - EnableWafRulesRestApi: !Equals - - !Ref 'EnableWafRulesRestApi' - - 'true' - EnableWafRulesWebSocket: !Equals - - !Ref 'EnableWafRulesWebSocket' - - 'true' -Mappings: - Constants: - # These compromised credentials are not a security risk because access to - # the database is restricted to security groups in the VPC. - DatabaseConfig: - DatabaseName: 'emojicoin' - MasterPassword: 'emojicoin' - MasterUsername: 'emojicoin' - ImageCache: - RepositoryPrefix: 'emojicoin' - Logging: - Prefix: 'emojicoin' - Networking: - BrokerPort: 3009 - DatabasePort: 5432 - DnsNameHostedZoneId: - '{{resolve:ssm:/emojicoin/indexer-dns-name/hosted-zone-id}}' - DnsNameRootDomain: - '{{resolve:ssm:/emojicoin/indexer-dns-name/root-domain}}' - PostgrestHealthCheckPort: 3001 - PostgrestPort: 3000 - ProcessorWebsocketPort: 3008 - VpcCidrBlock: '10.0.0.0/16' - Websocat: - Build: 'websocat.aarch64-unknown-linux-musl' - ReleaseUrlBase: 'https://github.com/vi/websocat/releases/download' - Version: 'v1.13.0' - PrivateSubnets: - A: - AvailabilityZone: 0 - CidrBlock: '10.0.1.0/24' - B: - AvailabilityZone: 1 - CidrBlock: '10.0.2.0/24' - C: - AvailabilityZone: 2 - CidrBlock: '10.0.3.0/24' - PublicSubnets: - A: - AvailabilityZone: 0 - CidrBlock: '10.0.4.0/24' - B: - AvailabilityZone: 1 - CidrBlock: '10.0.5.0/24' - C: - AvailabilityZone: 2 - CidrBlock: '10.0.6.0/24' Metadata: cfn-lint: config: ignore_checks: - - 'W8001' -Outputs: - BastionHostId: - Condition: 'DeployBastionHost' - Description: 'The instance ID of the bastion host.' - Value: !Ref 'BastionHost' - RestEndpoint: - Condition: 'DeployRestApiDnsRecord' - Value: !Sub - - 'https://${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - WsEndpoint: - Condition: 'DeployAlbDnsRecord' - Value: !Sub - - 'wss://ws.${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' + - 'W1001' Parameters: - BastionHostAmiId: - Default: 'ami-030cb86c12b18e236' - Type: 'AWS::EC2::Image::Id' - BrokerImageVersion: - Type: 'String' - DbMaxCapacity: - Default: 4 - Type: 'Number' - DbMinCapacity: - Default: 0.5 - Type: 'Number' - DeployAlb: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployAlbDnsRecord: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployBastionHost: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployBroker: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployContainers: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployDb: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployNlb: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployNlbVpcLink: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployPostgrest: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployProcessor: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployRestApi: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployRestApiDnsRecord: + DeployAnything: AllowedValues: - 'false' - 'true' Type: 'String' - DeployRouteTables: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - DeployStack: + DeployGateway: AllowedValues: - 'false' - 'true' @@ -224,1906 +27,38 @@ Parameters: - 'false' - 'true' Type: 'String' - DeployWaf: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - EnableWafRulesGeneral: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - EnableWafRulesRestApi: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - EnableWafRulesWebSocket: - AllowedValues: - - 'false' - - 'true' - Type: 'String' - Environment: - Type: 'String' - Network: - AllowedValues: - - 'mainnet' - - 'testnet' - Type: 'String' - PostgrestImageVersion: - Default: 'v12.2.3' - Type: 'String' - PostgrestMaxRows: - Default: 500 - Type: 'Number' - ProcessorImageVersion: - Type: 'String' Resources: - # Public application load balancer. - Alb: - Condition: 'DeployAlb' - Properties: - LoadBalancerAttributes: - - Key: 'load_balancing.cross_zone.enabled' - Value: 'true' - Scheme: 'internet-facing' - SecurityGroups: - - !Ref 'AlbSecurityGroup' - - !If - - 'DeployBroker' - - !Ref 'BrokerWsClientSecurityGroup' - - !Ref 'AWS::NoValue' - Subnets: - - !Ref 'PublicSubnetA' - - !Ref 'PublicSubnetB' - - !Ref 'PublicSubnetC' - Type: 'application' - Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' - # DNS certificate for the broker's application load balancer. - AlbCertificate: - Condition: 'DeployAlb' - Properties: - DomainName: !Sub - - 'ws.${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - DomainValidationOptions: - - DomainName: !Sub - - 'ws.${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - HostedZoneId: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameHostedZoneId' - ValidationMethod: 'DNS' - Type: 'AWS::CertificateManager::Certificate' - # DNS record for the broker's application load balancer. - AlbDnsRecord: - Condition: 'DeployAlbDnsRecord' - Properties: - AliasTarget: - DNSName: !GetAtt 'Alb.DNSName' - HostedZoneId: !GetAtt 'Alb.CanonicalHostedZoneID' - HostedZoneId: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameHostedZoneId' - Name: !Sub - - 'ws.${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - Type: 'A' - Type: 'AWS::Route53::RecordSet' - # Application load balancer listener. - AlbListener: - Condition: 'DeployAlb' - Properties: - Certificates: - - CertificateArn: !Ref 'AlbCertificate' - DefaultActions: - - TargetGroupArn: !Ref 'BrokerAlbTargetGroup' - Type: 'forward' - LoadBalancerArn: !Ref 'Alb' - Port: 443 - Protocol: 'HTTPS' - Type: 'AWS::ElasticLoadBalancingV2::Listener' - # Security group for the application load balancer. - AlbSecurityGroup: - Condition: 'DeployAlb' - Properties: - GroupDescription: !Ref 'AWS::StackName' - SecurityGroupIngress: - - CidrIp: '0.0.0.0/0' - FromPort: 443 - IpProtocol: 'tcp' - ToPort: 443 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Minimal VM for monitoring internal processes. - BastionHost: - Condition: 'DeployBastionHost' - Properties: - ImageId: !Ref 'BastionHostAmiId' - InstanceType: 't4g.nano' - NetworkInterfaces: - - DeviceIndex: 0 - GroupSet: - - !Ref 'BastionHostSecurityGroup' - - !Ref 'DbUserSecurityGroup' - - !If - - 'DeployBroker' - - !Ref 'BrokerWsClientSecurityGroup' - - !Ref 'AWS::NoValue' - - !If - - 'DeployNlb' - - !Ref 'NlbClientSecurityGroup' - - !Ref 'AWS::NoValue' - - !If - - 'DeployPostgrest' - - !Ref 'PostgrestClientSecurityGroup' - - !Ref 'AWS::NoValue' - - !If - - 'DeployProcessor' - - !Ref 'ProcessorWsClientSecurityGroup' - - !Ref 'AWS::NoValue' - SubnetId: !Ref 'PrivateSubnetA' - UserData: - # Install PostgreSQL client & websocat, set connection strings. - Fn::Base64: !Sub - - | - #!/bin/bash - yum update -y - amazon-linux-extras enable postgresql14 - yum clean metadata - yum install -y postgresql - wget ${WebsocatReleaseUrlBase}/${WebsocatVersion}/${WebsocatBuild} - chmod +x ${WebsocatBuild} - mv ${WebsocatBuild} /usr/local/bin/websocat - echo 'export DB_URL="${DatabaseUrl}"' >> /etc/profile - echo '${NlbDnsNameExport}' >> /etc/profile - echo '${BrokerWsUrlExport}' >> /etc/profile - echo '${PostgrestUrlExport}' >> /etc/profile - echo '${ProcessorWsUrlExport}' >> /etc/profile - - BrokerWsUrlExport: !If - - 'DeployBroker' - - !Join - - '' - - - 'export BROKER_WS_URL="ws://' - - !GetAtt 'BrokerServiceDiscovery.Name' - - '.' - - !Ref 'AWS::StackName' - - ':' - - !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - - '"' - - '# BROKER_WS_URL not set (broker not provisioned)' - DatabaseUrl: !Join - - '' - - - 'postgres://' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterUsername' - - ':' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterPassword' - - '@' - - !GetAtt 'DbCluster.ReadEndpoint.Address' - - '/' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'DatabaseName' - NlbDnsNameExport: !If - - 'DeployNlb' - - !Join - - '' - - - 'export NLB_DNS_NAME="' - - !GetAtt 'Nlb.DNSName' - - '"' - - '# NLB_DNS_NAME not set (NLB not provisioned)' - PostgrestUrlExport: !If - - 'DeployPostgrest' - - !Join - - '' - - - 'export POSTGREST_URL="http://' - - !GetAtt 'PostgrestServiceDiscovery.Name' - - '.' - - !Ref 'AWS::StackName' - - ':' - - !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - - '"' - - '# POSTGREST_URL not set (PostgREST not provisioned)' - ProcessorWsUrlExport: !If - - 'DeployProcessor' - - !Join - - '' - - - 'export PROCESSOR_WS_URL="ws://' - - !GetAtt 'ProcessorServiceDiscovery.Name' - - '.' - - !Ref 'AWS::StackName' - - ':' - - !FindInMap - - 'Constants' - - 'Networking' - - 'ProcessorWebsocketPort' - - '/ws"' - - '# PROCESSOR_WS_URL not set (processor not provisioned)' - WebsocatBuild: !FindInMap - - 'Constants' - - 'Websocat' - - 'Build' - WebsocatReleaseUrlBase: !FindInMap - - 'Constants' - - 'Websocat' - - 'ReleaseUrlBase' - WebsocatVersion: !FindInMap - - 'Constants' - - 'Websocat' - - 'Version' - Type: 'AWS::EC2::Instance' - # Connection endpoint allowing access to the bastion host. - BastionHostConnectionEndpoint: - Condition: 'DeployBastionHost' - Properties: - PreserveClientIp: true - SecurityGroupIds: - - !Ref 'BastionHostConnectionEndpointSecurityGroup' - SubnetId: !Ref 'PrivateSubnetA' - Type: 'AWS::EC2::InstanceConnectEndpoint' - # Security group for bastion host connection endpoint. - BastionHostConnectionEndpointSecurityGroup: - Condition: 'DeployBastionHost' - Properties: - GroupDescription: !Ref 'AWS::StackName' - SecurityGroupEgress: - - DestinationSecurityGroupId: !Ref 'BastionHostSecurityGroup' - IpProtocol: -1 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Security group for the bastion host. - BastionHostSecurityGroup: - Condition: 'DeployBastionHost' - Properties: - GroupDescription: !Ref 'AWS::StackName' - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Ingress policy for the bastion host security group, separated to eliminate - # circular dependencies with the security group for the connection endpoint. - BastionHostSecurityGroupIngress: - Condition: 'DeployBastionHost' - Properties: - GroupId: !Ref 'BastionHostSecurityGroup' - IpProtocol: -1 - SourceSecurityGroupId: - !Ref 'BastionHostConnectionEndpointSecurityGroup' - Type: 'AWS::EC2::SecurityGroupIngress' - # Target group for application load balancer traffic to the broker. - BrokerAlbTargetGroup: - Condition: 'DeployAlb' - DependsOn: 'Alb' - Properties: - HealthCheckIntervalSeconds: 5 - HealthCheckPath: '/health' - HealthCheckPort: !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - HealthCheckProtocol: 'HTTP' - HealthCheckTimeoutSeconds: 2 - HealthyThresholdCount: 2 - Name: !Sub '${AWS::StackName}-broker' - Port: !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - Protocol: 'HTTP' - ProtocolVersion: 'HTTP1' - TargetGroupAttributes: - - Key: 'deregistration_delay.timeout_seconds' - Value: '30' - - Key: 'stickiness.enabled' - Value: 'true' - - Key: 'stickiness.type' - Value: 'lb_cookie' - TargetType: 'ip' - UnhealthyThresholdCount: 2 - VpcId: !Ref 'Vpc' - Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' - # Scaling policy for broker service. - BrokerAutoScalingPolicy: - Condition: 'DeployBroker' - Properties: - PolicyName: !Sub '${AWS::StackName}-broker' - PolicyType: 'TargetTrackingScaling' - ScalingTargetId: !Ref 'BrokerAutoScalingTarget' - TargetTrackingScalingPolicyConfiguration: - PredefinedMetricSpecification: - PredefinedMetricType: 'ECSServiceAverageCPUUtilization' - ScaleInCooldown: 300 - ScaleOutCooldown: 60 - TargetValue: 70 - Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' - # Scalable target for broker service. - BrokerAutoScalingTarget: - Condition: 'DeployBroker' - Properties: - MaxCapacity: 5 - MinCapacity: 1 - ResourceId: !Join - - '/' - - - 'service' - - !Ref 'ContainerCluster' - - !GetAtt 'BrokerRunner.Name' - RoleARN: !GetAtt 'ContainerRole.Arn' - ScalableDimension: 'ecs:service:DesiredCount' - ServiceNamespace: 'ecs' - Type: 'AWS::ApplicationAutoScaling::ScalableTarget' - # Security group for the broker's WebSocket server. - BrokerPublisherSecurityGroup: - Condition: 'DeployBroker' - Properties: - GroupDescription: !Ref 'AWS::StackName' - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Ingress policy for the broker's WebSocket server security group. - BrokerPublisherSecurityGroupIngress: - Condition: 'DeployBroker' - Properties: - GroupId: !Ref 'BrokerPublisherSecurityGroup' - IpProtocol: -1 - SourceSecurityGroupId: !Ref 'BrokerWsClientSecurityGroup' - Type: 'AWS::EC2::SecurityGroupIngress' - # Service for running the broker. - BrokerRunner: - Condition: 'DeployBroker' - DependsOn: - # Wait until processor server is online. - - 'ProcessorRunner' - # Wait until there is an outbound route to get the image from the - # pull through cache. - - 'PrivateRouteTableAssociationA' - - 'PrivateRouteTableAssociationB' - - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGateway' - # Proxy for a conditional dependency on the application load balancer - # listener, which associates the broker target group with the application - # load balancer. - Metadata: - ConditionalDependencyProxy: !If - - 'DeployAlb' - - !Ref 'AlbListener' - - '' - Properties: - Cluster: !Ref 'ContainerCluster' - DeploymentConfiguration: - DeploymentCircuitBreaker: - Enable: true - Rollback: true - LaunchType: 'FARGATE' - LoadBalancers: !If - - 'DeployAlb' - - - ContainerName: !Sub '${AWS::StackName}-broker' - ContainerPort: !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - TargetGroupArn: !Ref 'BrokerAlbTargetGroup' - - !Ref 'AWS::NoValue' - NetworkConfiguration: - AwsvpcConfiguration: - SecurityGroups: - - !Ref 'BrokerPublisherSecurityGroup' - - !Ref 'ContainerSecurityGroup' - - !Ref 'ProcessorWsClientSecurityGroup' - Subnets: - - !Ref 'PrivateSubnetA' - - !Ref 'PrivateSubnetB' - - !Ref 'PrivateSubnetC' - ServiceRegistries: - - RegistryArn: !GetAtt 'BrokerServiceDiscovery.Arn' - TaskDefinition: !Ref 'BrokerTask' - Type: 'AWS::ECS::Service' - # Service discovery for the broker. - BrokerServiceDiscovery: - Condition: 'DeployBroker' - Properties: - DnsConfig: - DnsRecords: - - TTL: 10 - Type: 'A' - RoutingPolicy: 'MULTIVALUE' - HealthCheckCustomConfig: - FailureThreshold: 1 - Name: !Sub '${AWS::StackName}-broker' - NamespaceId: !Ref 'PrivateServiceDiscoveryNamespace' - Type: 'AWS::ServiceDiscovery::Service' - # Task definition for the broker. - BrokerTask: - Condition: 'DeployBroker' - DependsOn: 'ContainerLogGroup' - Properties: - ContainerDefinitions: - - Environment: - - Name: 'PROCESSOR_WS_URL' - Value: !Join - - '' - - - 'ws://' - - !GetAtt 'ProcessorServiceDiscovery.Name' - - '.' - - !Ref 'AWS::StackName' - - ':' - - !FindInMap - - 'Constants' - - 'Networking' - - 'ProcessorWebsocketPort' - - '/ws' - - Name: 'PORT' - Value: !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - - Name: 'RUST_LOG' - Value: 'info' - HealthCheck: - Command: - - 'CMD' - - 'curl' - - '--fail' - - !Sub - - 'http://localhost:${Port}/live' - - Port: !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - Interval: 5 - Retries: 1 - StartPeriod: 0 - Timeout: 2 - Image: !Join - - '' - - - !Ref 'AWS::AccountId' - - '.dkr.ecr.' - - !Ref 'AWS::Region' - - '.amazonaws.com/' - - !FindInMap - - 'Constants' - - 'ImageCache' - - 'RepositoryPrefix' - - '/econialabs/emojicoin-dot-fun-indexer-broker:' - - !Ref 'BrokerImageVersion' - LogConfiguration: - LogDriver: 'awslogs' - Options: - awslogs-group: !Join - - '' - - - '/' - - !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - - '/' - - !Ref 'AWS::StackName' - awslogs-region: !Ref 'AWS::Region' - awslogs-stream-prefix: !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - Name: !Sub '${AWS::StackName}-broker' - PortMappings: - - ContainerPort: !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - HostPort: !FindInMap - - 'Constants' - - 'Networking' - - 'BrokerPort' - Cpu: '256' - ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' - Family: !Ref 'AWS::StackName' - Memory: '512' - NetworkMode: 'awsvpc' - RequiresCompatibilities: - - 'FARGATE' - Type: 'AWS::ECS::TaskDefinition' - # Security group for clients of the broker's WebSocket server. - BrokerWsClientSecurityGroup: - Condition: 'DeployBroker' - Properties: - GroupDescription: !Ref 'AWS::StackName' - SecurityGroupEgress: - - DestinationSecurityGroupId: !Ref 'BrokerPublisherSecurityGroup' - IpProtocol: -1 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Cluster for running ECS containers. - ContainerCluster: - Condition: 'DeployContainers' - Type: 'AWS::ECS::Cluster' - # Log group for ECS task logging. - ContainerLogGroup: - Condition: 'DeployContainers' - Properties: - LogGroupName: !Join - - '' - - - '/' - - !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - - '/' - - !Ref 'AWS::StackName' - RetentionInDays: 7 - Type: 'AWS::Logs::LogGroup' - # Role with assorted permissions required to run containers. - ContainerRole: - Condition: 'DeployContainers' - Properties: - AssumeRolePolicyDocument: - Statement: - - Action: 'sts:AssumeRole' - Effect: 'Allow' - Principal: - Service: 'application-autoscaling.amazonaws.com' - - Action: 'sts:AssumeRole' - Effect: 'Allow' - Principal: - Service: 'ecs-tasks.amazonaws.com' - ManagedPolicyArns: - - !Join - - '' - - - 'arn:aws:iam::aws:policy/service-role/' - - 'AmazonEC2ContainerServiceAutoscaleRole' - Policies: - - PolicyDocument: - Statement: - # Pull through cache permissions. - - Action: - - 'ecr:BatchCheckLayerAvailability' - - 'ecr:BatchGetImage' - - 'ecr:BatchImportUpstreamImage' - - 'ecr:CompleteLayerUpload' - - 'ecr:CreateRepository' - - 'ecr:DescribeImages' - - 'ecr:DescribeRepositories' - - 'ecr:GetAuthorizationToken' - - 'ecr:GetDownloadUrlForLayer' - - 'ecr:InitiateLayerUpload' - - 'ecr:PutImage' - - 'ecr:UploadLayerPart' - Effect: 'Allow' - Resource: !Join - - '' - - - 'arn:aws:ecr:' - - !Ref 'AWS::Region' - - ':' - - !Ref 'AWS::AccountId' - - ':repository/' - - !FindInMap - - 'Constants' - - 'ImageCache' - - 'RepositoryPrefix' - - '/*' - - Action: - - 'ecr:GetAuthorizationToken' - Effect: 'Allow' - Resource: '*' - # Container logging permissions. - - Action: - - 'logs:CreateLogStream' - - 'logs:PutLogEvents' - Effect: 'Allow' - Resource: !Join - - '' - - - 'arn:aws:logs:' - - !Ref 'AWS::Region' - - ':' - - !Ref 'AWS::AccountId' - - ':log-group:/' - - !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - - '/' - - !Ref 'AWS::StackName' - - ':*' - # Secret access permissions. - - Action: - - 'secretsmanager:GetSecretValue' - Effect: 'Allow' - Resource: - - !Join - - '' - - - 'arn:aws:secretsmanager:' - - !Ref 'AWS::Region' - - ':' - - !Ref 'AWS::AccountId' - - ':secret:emojicoin/grpc-auth-token-*' - - !Join - - '' - - - 'arn:aws:secretsmanager:' - - !Ref 'AWS::Region' - - ':' - - !Ref 'AWS::AccountId' - - ':secret:ecr-pullthroughcache/emojicoin/docker-hub-*' - PolicyName: !Sub 'EmojicoinContainerRolePolicy-${AWS::StackName}' - RoleName: !Sub 'EmojicoinContainerRole-${AWS::StackName}' - Type: 'AWS::IAM::Role' - # Security group for ECS containers. - ContainerSecurityGroup: - Condition: 'DeployContainers' - Properties: - GroupDescription: !Ref 'AWS::StackName' - # Allow all outbound traffic, to other resources and to Docker Hub. - SecurityGroupEgress: - - CidrIp: '0.0.0.0/0' - IpProtocol: -1 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Database cluster. - DbCluster: - Condition: 'DeployDb' - Metadata: - cfn-lint: - config: - ignore_checks: - - 'W2501' - Properties: - DBSubnetGroupName: !Ref 'DbSubnetGroup' - DatabaseName: !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'DatabaseName' - Engine: 'aurora-postgresql' - EngineMode: 'provisioned' - EngineVersion: '16.2' - MasterUserPassword: !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterPassword' - MasterUsername: !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterUsername' - Port: !FindInMap - - 'Constants' - - 'Networking' - - 'DatabasePort' - ServerlessV2ScalingConfiguration: - MaxCapacity: !Ref 'DbMaxCapacity' - MinCapacity: !Ref 'DbMinCapacity' - VpcSecurityGroupIds: - - !Ref 'DbSecurityGroup' - Type: 'AWS::RDS::DBCluster' - # Primary (writer) database instance. - DbInstancePrimary: - Condition: 'DeployDb' - Properties: - DBClusterIdentifier: !Ref 'DbCluster' - DBInstanceClass: 'db.serverless' - Engine: 'aurora-postgresql' - Type: 'AWS::RDS::DBInstance' - # Replica (reader) database instance. - DbInstanceReplica: - Condition: 'DeployDb' - Properties: - DBClusterIdentifier: !Ref 'DbCluster' - DBInstanceClass: 'db.serverless' - Engine: 'aurora-postgresql' - Type: 'AWS::RDS::DBInstance' - # Security group for the database itself. - DbSecurityGroup: - Condition: 'DeployDb' - Properties: - GroupDescription: !Ref 'AWS::StackName' - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Ingress policy for the database's security group, separated to eliminate - # circular dependencies with security group for users of the database. - DbSecurityGroupIngress: - Condition: 'DeployDb' - Properties: - GroupId: !Ref 'DbSecurityGroup' - IpProtocol: -1 - SourceSecurityGroupId: !Ref 'DbUserSecurityGroup' - Type: 'AWS::EC2::SecurityGroupIngress' - # Database subnet group. - DbSubnetGroup: - Condition: 'DeployDb' - Properties: - DBSubnetGroupDescription: !Ref 'AWS::StackName' - SubnetIds: - - !Ref 'PrivateSubnetA' - - !Ref 'PrivateSubnetB' - - !Ref 'PrivateSubnetC' - Type: 'AWS::RDS::DBSubnetGroup' - # Security group for users of the database. - DbUserSecurityGroup: - Condition: 'DeployDb' - Properties: - GroupDescription: !Ref 'AWS::StackName' - SecurityGroupEgress: - - DestinationSecurityGroupId: !Ref 'DbSecurityGroup' - IpProtocol: -1 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Association for private route table with each private subnet. - Fn::ForEach::PrivateRouteTableAssociation: - - 'Identifier' - - - 'A' - - 'B' - - 'C' - - PrivateRouteTableAssociation${Identifier}: - Properties: - RouteTableId: !Ref 'PrivateRouteTable' - SubnetId: !Ref - Fn::Sub: - - 'PrivateSubnet${Identifier}' - - Identifier: !Ref 'Identifier' - # ForEach transforms require that condition is second key or later. - # yamllint disable-line rule:key-ordering - Condition: 'DeployRouteTables' - Type: 'AWS::EC2::SubnetRouteTableAssociation' - # A private subnet for each of the database availability zones. - Fn::ForEach::PrivateSubnet: - - 'Identifier' - - - 'A' - - 'B' - - 'C' - - PrivateSubnet${Identifier}: - Properties: - AvailabilityZone: !Select - - !FindInMap - - 'PrivateSubnets' - - Ref: 'Identifier' - - 'AvailabilityZone' - - Fn::GetAZs: !Ref 'AWS::Region' - CidrBlock: !FindInMap - - 'PrivateSubnets' - - Ref: 'Identifier' - - 'CidrBlock' - MapPublicIpOnLaunch: false - VpcId: !Ref 'Vpc' - # ForEach transforms require that condition is second key or later. - Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering - Type: 'AWS::EC2::Subnet' - # Association for public route table with each public subnet. - Fn::ForEach::PublicRouteTableAssociation: - - 'Identifier' - - - 'A' - - 'B' - - 'C' - - PublicRouteTableAssociation${Identifier}: - Properties: - RouteTableId: !Ref 'PublicRouteTable' - SubnetId: !Ref - Fn::Sub: - - 'PublicSubnet${Identifier}' - - Identifier: !Ref 'Identifier' - # ForEach transforms require that condition is second key or later. - # yamllint disable-line rule:key-ordering - Condition: 'DeployRouteTables' - Type: 'AWS::EC2::SubnetRouteTableAssociation' - # A public subnet for each availability zone. - Fn::ForEach::PublicSubnet: - - 'Identifier' - - - 'A' - - 'B' - - 'C' - - PublicSubnet${Identifier}: - Properties: - AvailabilityZone: !Select - - !FindInMap - - 'PublicSubnets' - - Ref: 'Identifier' - - 'AvailabilityZone' - - Fn::GetAZs: !Ref 'AWS::Region' - CidrBlock: !FindInMap - - 'PublicSubnets' - - Ref: 'Identifier' - - 'CidrBlock' - MapPublicIpOnLaunch: true - VpcId: !Ref 'Vpc' - # ForEach transforms require that condition is second key or later. - Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering - Type: 'AWS::EC2::Subnet' - # Internet gateway for the virtual private cloud. InternetGateway: - Condition: 'DeployVpc' + Condition: 'DeployGateway' Type: 'AWS::EC2::InternetGateway' - # Attachment of internet gateway to the virtual private cloud. InternetGatewayAttachment: Condition: 'DeployVpc' Properties: InternetGatewayId: !Ref 'InternetGateway' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::VPCGatewayAttachment' - # Network address translation gateway for the virtual private cloud. - NatGateway: - Condition: 'DeployVpc' - Properties: - AllocationId: !GetAtt 'NatGatewayEip.AllocationId' - SubnetId: !Ref 'PublicSubnetA' - Type: 'AWS::EC2::NatGateway' - # Elastic IP address for the network address translation gateway. - NatGatewayEip: - Condition: 'DeployVpc' - Properties: - Domain: 'vpc' - Type: 'AWS::EC2::EIP' - # Private network load balancer. - Nlb: - Condition: 'DeployNlb' - Properties: - LoadBalancerAttributes: - - Key: 'load_balancing.cross_zone.enabled' - Value: 'true' - Scheme: 'internal' - SecurityGroups: - - !Ref 'NlbSecurityGroup' - - !If - - 'DeployBroker' - - !Ref 'BrokerWsClientSecurityGroup' - - !Ref 'AWS::NoValue' - - !If - - 'DeployPostgrest' - - !Ref 'PostgrestClientSecurityGroup' - - !Ref 'AWS::NoValue' - Subnets: - - !Ref 'PrivateSubnetA' - - !Ref 'PrivateSubnetB' - - !Ref 'PrivateSubnetC' - Type: 'network' - Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' - # Security group for direct clients of the network load balancer. - NlbClientSecurityGroup: - Condition: 'DeployNlb' - Properties: - GroupDescription: !Ref 'AWS::StackName' - SecurityGroupEgress: - - DestinationSecurityGroupId: !Ref 'NlbSecurityGroup' - IpProtocol: -1 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Security group for the network load balancer. - NlbSecurityGroup: - Condition: 'DeployNlb' - Properties: - GroupDescription: !Ref 'AWS::StackName' - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Network load balancer security group ingress policy for PostgREST traffic - # over VPC link. - NlbSecurityGroupIngressPostgrest: - Condition: 'DeployNlbVpcLink' - Properties: - CidrIp: '0.0.0.0/0' - FromPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - GroupId: !Ref 'NlbSecurityGroup' - IpProtocol: 'tcp' - ToPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - Type: 'AWS::EC2::SecurityGroupIngress' - # Connection to network load balancer through VPC link. - NlbVpcLink: - Condition: 'DeployNlbVpcLink' - Properties: - Name: !Ref 'AWS::StackName' - TargetArns: - - !Ref 'Nlb' - Type: 'AWS::ApiGateway::VpcLink' - # Scaling policy for PostgREST service. - PostgrestAutoScalingPolicy: - Condition: 'DeployPostgrest' - Properties: - PolicyName: !Sub '${AWS::StackName}-postgrest' - PolicyType: 'TargetTrackingScaling' - ScalingTargetId: !Ref 'PostgrestAutoScalingTarget' - TargetTrackingScalingPolicyConfiguration: - PredefinedMetricSpecification: - PredefinedMetricType: 'ECSServiceAverageCPUUtilization' - ScaleInCooldown: 300 - ScaleOutCooldown: 60 - TargetValue: 70 - Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' - # Scalable target for PostgREST service. - PostgrestAutoScalingTarget: - Condition: 'DeployPostgrest' - Properties: - MaxCapacity: 5 - MinCapacity: 1 - ResourceId: !Join - - '/' - - - 'service' - - !Ref 'ContainerCluster' - - !GetAtt 'PostgrestRunner.Name' - RoleARN: !GetAtt 'ContainerRole.Arn' - ScalableDimension: 'ecs:service:DesiredCount' - ServiceNamespace: 'ecs' - Type: 'AWS::ApplicationAutoScaling::ScalableTarget' - # Security group for PostgREST clients. - PostgrestClientSecurityGroup: - Condition: 'DeployPostgrest' - Properties: - GroupDescription: !Ref 'AWS::StackName' - SecurityGroupEgress: - - DestinationSecurityGroupId: !Ref 'PostgrestServerSecurityGroup' - IpProtocol: -1 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Listener for network load balancer traffic to PostgREST. - PostgrestNlbListener: - Condition: 'DeployNlb' - Properties: - DefaultActions: - - TargetGroupArn: !Ref 'PostgrestNlbTargetGroup' - Type: 'forward' - LoadBalancerArn: !Ref 'Nlb' - Port: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - Protocol: 'TCP' - Type: 'AWS::ElasticLoadBalancingV2::Listener' - # Target group for network load balancer traffic to PostgREST. - PostgrestNlbTargetGroup: - Condition: 'DeployNlb' - DependsOn: 'Nlb' - Properties: - HealthCheckIntervalSeconds: 5 - HealthCheckPath: '/ready' - HealthCheckPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestHealthCheckPort' - HealthCheckProtocol: 'HTTP' - HealthCheckTimeoutSeconds: 2 - HealthyThresholdCount: 2 - Name: !Sub '${AWS::StackName}-postgrest' - Port: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - Protocol: 'TCP' - TargetGroupAttributes: - - Key: 'deregistration_delay.timeout_seconds' - Value: '30' - - Key: 'stickiness.enabled' - Value: 'false' - TargetType: 'ip' - UnhealthyThresholdCount: 2 - VpcId: !Ref 'Vpc' - Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' - # Service for running PostgREST. - PostgrestRunner: - Condition: 'DeployPostgrest' - DependsOn: - # Wait for both database instances to ensure cluster is fully available. - - 'DbInstancePrimary' - - 'DbInstanceReplica' - # Wait for processor to ensure that migrations have been run, so that the - # schema will be available and load balancer health checks will pass. - - 'ProcessorRunner' - # Wait until there is an outbound route to get the image from the - # pull through cache. - - 'PrivateRouteTableAssociationA' - - 'PrivateRouteTableAssociationB' - - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGateway' - # Proxy for a conditional dependency on the network load balancer listener - # for PostgREST, which associates the PostgREST target group with the - # network load balancer. - Metadata: - ConditionalDependencyProxy: !If - - 'DeployNlb' - - !Ref 'PostgrestNlbListener' - - '' - Properties: - Cluster: !Ref 'ContainerCluster' - DeploymentConfiguration: - DeploymentCircuitBreaker: - Enable: true - Rollback: true - LaunchType: 'FARGATE' - LoadBalancers: !If - - 'DeployNlb' - - - ContainerName: !Sub '${AWS::StackName}-postgrest' - ContainerPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - TargetGroupArn: !Ref 'PostgrestNlbTargetGroup' - - !Ref 'AWS::NoValue' - NetworkConfiguration: - AwsvpcConfiguration: - SecurityGroups: - - !Ref 'DbUserSecurityGroup' - - !Ref 'PostgrestServerSecurityGroup' - - !Ref 'ContainerSecurityGroup' - Subnets: - - !Ref 'PrivateSubnetA' - - !Ref 'PrivateSubnetB' - - !Ref 'PrivateSubnetC' - ServiceRegistries: - - RegistryArn: !GetAtt 'PostgrestServiceDiscovery.Arn' - TaskDefinition: !Ref 'PostgrestTask' - Type: 'AWS::ECS::Service' - # Security group for the PostgREST server. - PostgrestServerSecurityGroup: - Condition: 'DeployPostgrest' - Properties: - GroupDescription: !Ref 'AWS::StackName' - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Ingress policy for the PostgREST server security group. - PostgrestServerSecurityGroupIngress: - Condition: 'DeployPostgrest' - Properties: - GroupId: !Ref 'PostgrestServerSecurityGroup' - IpProtocol: -1 - SourceSecurityGroupId: !Ref 'PostgrestClientSecurityGroup' - Type: 'AWS::EC2::SecurityGroupIngress' - # Service discovery for PostgREST. - PostgrestServiceDiscovery: - Condition: 'DeployPostgrest' - Properties: - DnsConfig: - DnsRecords: - - TTL: 10 - Type: 'A' - RoutingPolicy: 'MULTIVALUE' - HealthCheckCustomConfig: - FailureThreshold: 1 - Name: !Sub '${AWS::StackName}-postgrest' - NamespaceId: !Ref 'PrivateServiceDiscoveryNamespace' - Type: 'AWS::ServiceDiscovery::Service' - # Task definition for PostgREST. - PostgrestTask: - Condition: 'DeployPostgrest' - DependsOn: 'ContainerLogGroup' - Properties: - ContainerDefinitions: - - Environment: - - Name: 'PGRST_ADMIN_SERVER_PORT' - Value: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestHealthCheckPort' - - Name: 'PGRST_DB_URI' - Value: !Join - - '' - - - 'postgres://' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterUsername' - - ':' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterPassword' - - '@' - # While the PostgREST endpoint is configured to support read-only - # operations, the PostgREST listener errors out when connected to - # a read replica because it relies on `LISTEN` and `NOTIFY` - # commands, which do not work on read replicas. Hence it is - # connected to the writer endpoint. - - !GetAtt 'DbCluster.Endpoint.Address' - - '/' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterUsername' - - Name: 'PGRST_DB_ANON_ROLE' - Value: 'web_anon' - - Name: 'PGRST_DB_MAX_ROWS' - Value: !Ref 'PostgrestMaxRows' - Image: !Join - - '' - - - !Ref 'AWS::AccountId' - - '.dkr.ecr.' - - !Ref 'AWS::Region' - - '.amazonaws.com/' - - !FindInMap - - 'Constants' - - 'ImageCache' - - 'RepositoryPrefix' - - '/postgrest/postgrest:' - - !Ref 'PostgrestImageVersion' - LogConfiguration: - LogDriver: 'awslogs' - Options: - awslogs-group: !Join - - '' - - - '/' - - !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - - '/' - - !Ref 'AWS::StackName' - awslogs-region: !Ref 'AWS::Region' - awslogs-stream-prefix: !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - Name: !Sub '${AWS::StackName}-postgrest' - PortMappings: - - ContainerPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - HostPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - - ContainerPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestHealthCheckPort' - HostPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestHealthCheckPort' - Cpu: '256' - ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' - Family: !Ref 'AWS::StackName' - Memory: '512' - NetworkMode: 'awsvpc' - RequiresCompatibilities: - - 'FARGATE' - Type: 'AWS::ECS::TaskDefinition' - # Route table for private subnets in the virtual private cloud. - PrivateRouteTable: - Condition: 'DeployRouteTables' - Properties: - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::RouteTable' - # Route from private subnets through network address translation gateway. - PrivateRouteThroughNatGateway: - Condition: 'DeployRouteTables' - Properties: - DestinationCidrBlock: '0.0.0.0/0' - NatGatewayId: !Ref 'NatGateway' - RouteTableId: !Ref 'PrivateRouteTable' - Type: 'AWS::EC2::Route' - # Private DNS namespace for internal service discovery. - PrivateServiceDiscoveryNamespace: - Condition: 'DeployVpc' - Properties: - Description: !Ref 'AWS::StackName' - Name: !Ref 'AWS::StackName' - Vpc: !Ref 'Vpc' - Type: 'AWS::ServiceDiscovery::PrivateDnsNamespace' - # Security group for the processor's WebSocket server. - ProcessorPublisherSecurityGroup: - Condition: 'DeployVpc' - Properties: - GroupDescription: !Ref 'AWS::StackName' - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Ingress policy for the processor's WebSocket server security group. - ProcessorPublisherSecurityGroupIngress: - Condition: 'DeployProcessor' - Properties: - GroupId: !Ref 'ProcessorPublisherSecurityGroup' - IpProtocol: -1 - SourceSecurityGroupId: !Ref 'ProcessorWsClientSecurityGroup' - Type: 'AWS::EC2::SecurityGroupIngress' - # Service for running the processor. - ProcessorRunner: - Condition: 'DeployProcessor' - DependsOn: - # Pending instance creation, the cluster endpoint is defined but not - # available, so the service will not be able to connect to the database. - - 'DbInstancePrimary' - - 'DbInstanceReplica' - # Wait until there is an outbound route to get the image from the - # pull through cache. - - 'PrivateRouteTableAssociationA' - - 'PrivateRouteTableAssociationB' - - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGateway' - Properties: - Cluster: !Ref 'ContainerCluster' - DeploymentConfiguration: - DeploymentCircuitBreaker: - Enable: true - Rollback: true - MaximumPercent: 100 - MinimumHealthyPercent: 0 - DesiredCount: 1 - LaunchType: 'FARGATE' - NetworkConfiguration: - AwsvpcConfiguration: - SecurityGroups: - - !Ref 'ContainerSecurityGroup' - - !Ref 'DbUserSecurityGroup' - - !Ref 'ProcessorPublisherSecurityGroup' - Subnets: - - !Ref 'PrivateSubnetA' - - !Ref 'PrivateSubnetB' - - !Ref 'PrivateSubnetC' - ServiceRegistries: - - RegistryArn: !GetAtt 'ProcessorServiceDiscovery.Arn' - TaskDefinition: !Ref 'ProcessorTask' - Type: 'AWS::ECS::Service' - # Service discovery for the processor. - ProcessorServiceDiscovery: - Condition: 'DeployProcessor' - Properties: - DnsConfig: - DnsRecords: - - TTL: 10 - Type: 'A' - RoutingPolicy: 'MULTIVALUE' - HealthCheckCustomConfig: - FailureThreshold: 1 - Name: !Sub '${AWS::StackName}-processor' - NamespaceId: !Ref 'PrivateServiceDiscoveryNamespace' - Type: 'AWS::ServiceDiscovery::Service' - # Task definition for processor. - ProcessorTask: - Condition: 'DeployProcessor' - DependsOn: 'ContainerLogGroup' - Properties: - ContainerDefinitions: - - Environment: - - Name: 'DATABASE_URL' - Value: !Join - - '' - - - 'postgres://' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterUsername' - - ':' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterPassword' - - '@' - - !GetAtt 'DbCluster.Endpoint.Address' - - '/' - - !FindInMap - - 'Constants' - - 'DatabaseConfig' - - 'MasterUsername' - - Name: 'GRPC_DATA_SERVICE_URL' - Value: !Sub - - '{{resolve:ssm:/emojicoin/grpc-data-service-url/${Network}}}' - - Network: !Ref 'Network' - - Name: 'MINIMUM_STARTING_VERSION' - Value: !Sub - - '{{resolve:ssm:/emojicoin/minimum-starting-version/${Network}}}' - - Network: !Ref 'Network' - - Name: 'EMOJICOIN_MODULE_ADDRESS' - Value: !Sub - - '{{resolve:ssm:/emojicoin/package-address/${Network}}}' - - Network: !Ref 'Network' - - Name: 'WS_PORT' - Value: !FindInMap - - 'Constants' - - 'Networking' - - 'ProcessorWebsocketPort' - HealthCheck: - Command: - - 'CMD' - - 'curl' - - '--fail' - - !Sub - - 'http://localhost:${Port}/' - - Port: !FindInMap - - 'Constants' - - 'Networking' - - 'ProcessorWebsocketPort' - Interval: 5 - Retries: 1 - StartPeriod: 0 - Timeout: 2 - Image: !Join - - '' - - - !Ref 'AWS::AccountId' - - '.dkr.ecr.' - - !Ref 'AWS::Region' - - '.amazonaws.com/' - - !FindInMap - - 'Constants' - - 'ImageCache' - - 'RepositoryPrefix' - - '/econialabs/emojicoin-dot-fun-indexer-processor:' - - !Ref 'ProcessorImageVersion' - LogConfiguration: - LogDriver: 'awslogs' - Options: - awslogs-group: !Join - - '' - - - '/' - - !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - - '/' - - !Ref 'AWS::StackName' - awslogs-region: !Ref 'AWS::Region' - awslogs-stream-prefix: !FindInMap - - 'Constants' - - 'Logging' - - 'Prefix' - Name: !Sub '${AWS::StackName}-processor' - PortMappings: - - ContainerPort: !FindInMap - - 'Constants' - - 'Networking' - - 'ProcessorWebsocketPort' - HostPort: !FindInMap - - 'Constants' - - 'Networking' - - 'ProcessorWebsocketPort' - Secrets: - - Name: 'GRPC_AUTH_TOKEN' - ValueFrom: !Sub - - 'arn:aws:secretsmanager:${Region}:${Account}:secret:${Secret}' - - Account: !Ref 'AWS::AccountId' - Region: !Ref 'AWS::Region' - Secret: 'emojicoin/grpc-auth-token' - Cpu: '256' - ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' - Family: !Ref 'AWS::StackName' - Memory: '512' - NetworkMode: 'awsvpc' - RequiresCompatibilities: - - 'FARGATE' - Type: 'AWS::ECS::TaskDefinition' - # Security group for clients of the processor's WebSocket server. - ProcessorWsClientSecurityGroup: - Condition: 'DeployProcessor' - Properties: - GroupDescription: !Ref 'AWS::StackName' - SecurityGroupEgress: - - DestinationSecurityGroupId: !Ref 'ProcessorPublisherSecurityGroup' - IpProtocol: -1 - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::SecurityGroup' - # Route table for public subnets in the virtual private cloud. - PublicRouteTable: - Condition: 'DeployRouteTables' - Properties: - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::RouteTable' - # Route from public subnets through the internet gateway. - PublicRouteToInternet: - Condition: 'DeployRouteTables' - Properties: - DestinationCidrBlock: '0.0.0.0/0' - GatewayId: !Ref 'InternetGateway' - RouteTableId: !Ref 'PublicRouteTable' - Type: 'AWS::EC2::Route' - # REST API endpoint. - RestApi: - Condition: 'DeployRestApi' - Properties: - ApiKeySourceType: 'HEADER' - EndpointConfiguration: - Types: - - 'REGIONAL' - Name: !Ref 'AWS::StackName' - Type: 'AWS::ApiGateway::RestApi' - # DNS certificate for the REST API endpoint. - RestApiCertificate: - Condition: 'DeployRestApiDnsRecord' - Properties: - DomainName: !Sub - - '${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - DomainValidationOptions: - - DomainName: !Sub - - '${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - HostedZoneId: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameHostedZoneId' - ValidationMethod: 'DNS' - Type: 'AWS::CertificateManager::Certificate' - # Deployment for the REST API endpoint. - RestApiDeployment: - Condition: 'DeployRestApi' - DependsOn: - - 'RestApiMethodGeneral' - - 'RestApiMethodRoot' - Properties: - RestApiId: !Ref 'RestApi' - Type: 'AWS::ApiGateway::Deployment' - # DNS record for the REST API. - RestApiDnsRecord: - Condition: 'DeployRestApiDnsRecord' - Properties: - AliasTarget: - DNSName: !GetAtt 'RestApiDomainName.RegionalDomainName' - HostedZoneId: !GetAtt 'RestApiDomainName.RegionalHostedZoneId' - HostedZoneId: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameHostedZoneId' - Name: !Sub - - '${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - Type: 'A' - Type: 'AWS::Route53::RecordSet' - # Domain mapping for the REST API endpoint. - RestApiDomainMapping: - Condition: 'DeployRestApiDnsRecord' - Properties: - DomainName: !Ref 'RestApiDomainName' - RestApiId: !Ref 'RestApi' - Stage: !Ref 'RestApiStage' - Type: 'AWS::ApiGateway::BasePathMapping' - # Custom domain name for the REST API endpoint. - RestApiDomainName: - Condition: 'DeployRestApiDnsRecord' - Properties: - DomainName: !Sub - - '${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - EndpointConfiguration: - Types: - - 'REGIONAL' - RegionalCertificateArn: !Ref 'RestApiCertificate' - SecurityPolicy: 'TLS_1_2' - Type: 'AWS::ApiGateway::DomainName' - # API key for the REST API endpoint. - RestApiKey: - Condition: 'DeployRestApi' - Properties: - Enabled: true - StageKeys: - - RestApiId: !Ref 'RestApi' - StageName: !Ref 'RestApiStage' - Type: 'AWS::ApiGateway::ApiKey' - # Rest API endpoint general method. - RestApiMethodGeneral: - Condition: 'DeployRestApi' - Properties: - ApiKeyRequired: true - AuthorizationType: 'NONE' - HttpMethod: 'GET' - Integration: - ConnectionId: !Ref 'NlbVpcLink' - ConnectionType: 'VPC_LINK' - IntegrationHttpMethod: 'GET' - RequestParameters: - integration.request.path.proxy: 'method.request.path.proxy' - Type: 'HTTP_PROXY' - Uri: !Sub - - 'http://${NlbDnsName}:${PostgrestPort}/{proxy}' - - NlbDnsName: !GetAtt 'Nlb.DNSName' - PostgrestPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - RequestParameters: - method.request.path.proxy: true - ResourceId: !Ref 'RestApiProxyResource' - RestApiId: !Ref 'RestApi' - Type: 'AWS::ApiGateway::Method' - # Rest API endpoint root method. - RestApiMethodRoot: - Condition: 'DeployRestApi' - Properties: - ApiKeyRequired: true - AuthorizationType: 'NONE' - HttpMethod: 'GET' - Integration: - ConnectionId: !Ref 'NlbVpcLink' - ConnectionType: 'VPC_LINK' - IntegrationHttpMethod: 'GET' - RequestParameters: - integration.request.path.proxy: 'method.request.path.proxy' - Type: 'HTTP_PROXY' - Uri: !Sub - - 'http://${NlbDnsName}:${PostgrestPort}/' - - NlbDnsName: !GetAtt 'Nlb.DNSName' - PostgrestPort: !FindInMap - - 'Constants' - - 'Networking' - - 'PostgrestPort' - RequestParameters: - method.request.path.proxy: true - ResourceId: !GetAtt 'RestApi.RootResourceId' - RestApiId: !Ref 'RestApi' - Type: 'AWS::ApiGateway::Method' - # Proxy resource for the REST API endpoint. - RestApiProxyResource: - Condition: 'DeployRestApi' - Properties: - ParentId: !GetAtt 'RestApi.RootResourceId' - PathPart: '{proxy+}' - RestApiId: !Ref 'RestApi' - Type: 'AWS::ApiGateway::Resource' - # Stage for the REST API endpoint. - RestApiStage: - Condition: 'DeployRestApi' - Properties: - CacheClusterEnabled: 'true' - CacheClusterSize: '0.5' - DeploymentId: !Ref 'RestApiDeployment' - MethodSettings: - # Cache general data for one second, with permissive throttling. - - CacheDataEncrypted: false - CacheTtlInSeconds: 1 - CachingEnabled: true - HttpMethod: '*' - ResourcePath: '/*' - ThrottlingBurstLimit: 1000 - ThrottlingRateLimit: 500 - # Use a long cache time and restrictive throttling on the schema at the - # root, which is large and not required for normal operations. - - CacheDataEncrypted: false - CacheTtlInSeconds: 3600 - CachingEnabled: true - HttpMethod: 'GET' - ResourcePath: '/' - ThrottlingBurstLimit: 100 - ThrottlingRateLimit: 50 - RestApiId: !Ref 'RestApi' - StageName: !Ref 'AWS::StackName' - Type: 'AWS::ApiGateway::Stage' - # REST API usage plan. - RestApiUsagePlan: - Condition: 'DeployRestApi' - Properties: - ApiStages: - - ApiId: !Ref 'RestApi' - Stage: !Ref 'RestApiStage' - Type: 'AWS::ApiGateway::UsagePlan' - # Association of API key with usage plan. - RestApiUsagePlanKeyAssociation: - Condition: 'DeployRestApi' - Properties: - KeyId: !Ref 'RestApiKey' - KeyType: 'API_KEY' - UsagePlanId: !Ref 'RestApiUsagePlan' - Type: 'AWS::ApiGateway::UsagePlanKey' - # Virtual private cloud for internal networking. Vpc: Condition: 'DeployVpc' Properties: - CidrBlock: !FindInMap - - 'Constants' - - 'Networking' - - 'VpcCidrBlock' - EnableDnsHostnames: true - EnableDnsSupport: true + CidrBlock: '0.0.0.0/16' Type: 'AWS::EC2::VPC' - # Web application firewall. - Waf: - Condition: 'DeployWaf' - Properties: - # Allow all all traffic by default unless blocked per below rules. - DefaultAction: - # CloudFormation prohibits both null and empty values. - Allow: {} # yamllint disable-line rule:braces - Name: !Ref 'AWS::StackName' - Rules: - # Use AWS common rule set. - - Name: !Sub '${AWS::StackName}-AWSManagedRulesCommonRuleSet' - OverrideAction: !If - - 'EnableWafRulesGeneral' - - None: {} # yamllint disable-line rule:braces - - Count: {} # yamllint disable-line rule:braces - Priority: 1 - Statement: - ManagedRuleGroupStatement: - Name: 'AWSManagedRulesCommonRuleSet' - VendorName: 'AWS' - VisibilityConfig: - CloudWatchMetricsEnabled: true - MetricName: !Sub '${AWS::StackName}-AWSManagedRulesCommonRuleSet' - SampledRequestsEnabled: true - # Block VPNs and otherwise obfuscated IP addresses. - - Name: !Sub '${AWS::StackName}-AWSManagedRulesAnonymousIpList' - OverrideAction: !If - - 'EnableWafRulesGeneral' - - None: {} # yamllint disable-line rule:braces - - Count: {} # yamllint disable-line rule:braces - Priority: 2 - Statement: - ManagedRuleGroupStatement: - Name: 'AWSManagedRulesAnonymousIpList' - VendorName: 'AWS' - VisibilityConfig: - CloudWatchMetricsEnabled: true - MetricName: !Sub '${AWS::StackName}-AWSManagedRulesAnonymousIpList' - SampledRequestsEnabled: true - # Block known IP threats. - - Name: !Sub '${AWS::StackName}-AWSManagedRulesAmazonIpReputationList' - OverrideAction: !If - - 'EnableWafRulesGeneral' - - None: {} # yamllint disable-line rule:braces - - Count: {} # yamllint disable-line rule:braces - Priority: 3 - Statement: - ManagedRuleGroupStatement: - Name: 'AWSManagedRulesAmazonIpReputationList' - VendorName: 'AWS' - VisibilityConfig: - CloudWatchMetricsEnabled: true - MetricName: !Sub - '${AWS::StackName}-AWSManagedRulesAmazonIpReputationList' - SampledRequestsEnabled: true - # Rate limit by IP for the REST API. - - Action: !If - - 'EnableWafRulesRestApi' - - Block: {} # yamllint disable-line rule:braces - - Count: {} # yamllint disable-line rule:braces - Name: !Sub '${AWS::StackName}-RestApiRateLimit' - Priority: 4 - Statement: - RateBasedStatement: - AggregateKeyType: 'IP' - Limit: 200 - ScopeDownStatement: - ByteMatchStatement: - FieldToMatch: - SingleHeader: - Name: 'host' - PositionalConstraint: 'CONTAINS' - SearchString: !Sub - - '${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - TextTransformations: - - Priority: 0 - Type: 'LOWERCASE' - VisibilityConfig: - CloudWatchMetricsEnabled: true - MetricName: !Sub '${AWS::StackName}-RestApiRateLimit' - SampledRequestsEnabled: true - # Limit WebSocket messages by IP via rate limit. - - Action: !If - - 'EnableWafRulesWebSocket' - - Block: {} # yamllint disable-line rule:braces - - Count: {} # yamllint disable-line rule:braces - Name: !Sub '${AWS::StackName}-WebSocketMessageRateLimit' - Priority: 5 - Statement: - RateBasedStatement: - AggregateKeyType: 'IP' - Limit: 100 - ScopeDownStatement: - ByteMatchStatement: - FieldToMatch: - SingleHeader: - Name: 'host' - PositionalConstraint: 'CONTAINS' - SearchString: !Sub - - 'ws.${Environment}.${RootDomain}' - - RootDomain: !FindInMap - - 'Constants' - - 'Networking' - - 'DnsNameRootDomain' - TextTransformations: - - Priority: 0 - Type: 'LOWERCASE' - VisibilityConfig: - CloudWatchMetricsEnabled: true - MetricName: !Sub '${AWS::StackName}-WebSocketMessageRateLimit' - SampledRequestsEnabled: true - Scope: 'REGIONAL' - VisibilityConfig: - CloudWatchMetricsEnabled: true - MetricName: !Ref 'AWS::StackName' - SampledRequestsEnabled: true - Type: 'AWS::WAFv2::WebACL' - # Web application firewall for the broker application load balancer. - WafAlb: - Condition: 'DeployWaf' - Properties: - ResourceArn: !Ref 'Alb' - WebACLArn: !GetAtt 'Waf.Arn' - Type: 'AWS::WAFv2::WebACLAssociation' - # Web application firewall for the REST API. - WafRestApi: - Condition: 'DeployWaf' - Properties: - ResourceArn: !Sub - - 'arn:aws:apigateway:${Region}::/restapis/${ApiId}/stages/${StageName}' - - ApiId: !Ref 'RestApi' - Region: !Ref 'AWS::Region' - StageName: !Ref 'RestApiStage' - WebACLArn: !GetAtt 'Waf.Arn' - Type: 'AWS::WAFv2::WebACLAssociation' Rules: - DeployAlb: + DeployGateway: Assertions: - Assert: !Or - !Equals - - !Ref 'DeployVpc' - - 'true' - - !Equals - - !Ref 'DeployAlb' - - 'false' - DeployAlbDnsRecord: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployAlb' + - !Ref 'DeployAnything' - 'true' - !Equals - - !Ref 'DeployAlbDnsRecord' - - 'false' - DeployBastionHost: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployDb' - - 'true' - - !Equals - - !Ref 'DeployBastionHost' - - 'false' - DeployBroker: - Assertions: - - Assert: !Or - - !And - - !Equals - - !Ref 'DeployContainers' - - 'true' - - !Equals - - !Ref 'DeployProcessor' - - 'true' - - !Equals - - !Ref 'DeployBroker' - - 'false' - DeployContainers: - Assertions: - - Assert: !Or - - !And - - !Equals - - !Ref 'DeployDb' - - 'true' - - !Equals - - !Ref 'DeployRouteTables' - - 'true' - - !Equals - - !Ref 'DeployContainers' - - 'false' - DeployDb: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployVpc' - - 'true' - - !Equals - - !Ref 'DeployDb' - - 'false' - DeployNlb: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployVpc' - - 'true' - - !Equals - - !Ref 'DeployNlb' - - 'false' - DeployNlbVpcLink: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployNlb' - - 'true' - - !Equals - - !Ref 'DeployNlbVpcLink' - - 'false' - DeployPostgrest: - Assertions: - - Assert: !Or - - !And - - !Equals - - !Ref 'DeployContainers' - - 'true' - - !Equals - - !Ref 'DeployProcessor' - - 'true' - - !Equals - - !Ref 'DeployPostgrest' - - 'false' - DeployProcessor: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployContainers' - - 'true' - - !Equals - - !Ref 'DeployProcessor' - - 'false' - DeployRestApi: - Assertions: - - Assert: !Or - - !And - - !Equals - - !Ref 'DeployNlbVpcLink' - - 'true' - - !Equals - - !Ref 'DeployPostgrest' - - 'true' - - !Equals - - !Ref 'DeployRestApi' - - 'false' - DeployRestApiDnsRecord: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployRestApi' - - 'true' - - !Equals - - !Ref 'DeployRestApiDnsRecord' - - 'false' - DeployRouteTables: - Assertions: - - Assert: !Or - - !Equals - - !Ref 'DeployVpc' - - 'true' - - !Equals - - !Ref 'DeployRouteTables' + - !Ref 'DeployGateway' - 'false' DeployVpc: Assertions: - Assert: !Or - !Equals - - 'DeployStack' + - !Ref 'DeployGateway' - 'true' - !Equals - !Ref 'DeployVpc' - 'false' - DeployWaf: - Assertions: - - Assert: !Or - - !And - - !Equals - - !Ref 'DeployAlb' - - 'true' - - !Equals - - !Ref 'DeployRestApi' - - 'true' - - !Equals - - !Ref 'DeployWaf' - - 'false' -Transform: 'AWS::LanguageExtensions' ... From 546892f825fce1748c7a2445404ef22f88c11305 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:53:15 -0700 Subject: [PATCH 09/62] Update README --- src/cloud-formation/README.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/cloud-formation/README.md b/src/cloud-formation/README.md index 58e3ae6fe..30c2d15c3 100644 --- a/src/cloud-formation/README.md +++ b/src/cloud-formation/README.md @@ -27,14 +27,6 @@ be used to [conditionally][conditions] provision and de-provision [resources]. For a concise list of such parameters, see a [stack deployment file] at `deploy-*.yaml`. See the template [rules] section for associated dependencies. -Note that if a rule assertion fails, rather than reporting an assertion error, -the [GitSync status dashboard] instead simply halts the update with -[GitSync event] type `CHANGESET_CREATION_FAILED` and following event message, -misleadingly reporting that no changes are present when in fact the update -failure was a result of failed rule assertions: - -> Changeset creation failed. The reason was No updates are to be performed.. - ## Setup 1. [Make Route 53 the DNS service for a domain you own], which will @@ -201,7 +193,8 @@ failure was a result of failed rule assertions: 1. Create a [stack deployment file] (see `deploy-*.yml`) with appropriate [template parameters](#template-parameters). -1. [Create the stack with GitSync]. +1. [Create the stack with GitSync], then monitor [GitSync events][gitsync event] + in the [GitSync status dashboard]. ## Querying endpoints @@ -457,5 +450,4 @@ See the [Web ACL traffic overview dashboards] to monitor rules. [user data]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html [web acl traffic overview dashboards]: https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-dashboards.html [web application firewall]: https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html -[`cfn-lint` issue #3630]: https://github.com/aws-cloudformation/cfn-lint/issues/3630 [`ecr::getauthorizationtoken`]: https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_GetAuthorizationToken.html From 5206dc92645ea21e49b522cf6f7ec8cac76daf60 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:56:31 -0700 Subject: [PATCH 10/62] Try provisioning VPC with new condition style --- src/cloud-formation/deploy-dev.yaml | 49 +- src/cloud-formation/indexer.cfn.yaml | 2083 +++++++++++++++++++++++++- 2 files changed, 2096 insertions(+), 36 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 4466a5cfb..d2a7faf79 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,31 +1,28 @@ --- parameters: - # BrokerImageVersion: '0.7.0' - # DeployAlb: 'true' - # DeployAlbDnsRecord: 'true' - # DeployBastionHost: 'true' - # DeployBroker: 'true' - # DeployContainers: 'true' - # DeployDb: 'true' - # DeployNlb: 'true' - # DeployNlbVpcLink: 'true' - # DeployPostgrest: 'true' - # DeployProcessor: 'true' - # DeployRestApi: 'true' - # DeployRestApiDnsRecord: 'true' - # DeployRouteTables: 'true' - # DeployStack: 'false' - # DeployVpc: 'true' - # DeployWaf: 'true' - # EnableWafRulesGeneral: 'true' - # EnableWafRulesRestApi: 'false' - # EnableWafRulesWebSocket: 'false' - # Environment: 'ECO-2188' - # Network: 'testnet' - # ProcessorImageVersion: '0.5.0' - DeployAnything: 'false' - DeployGateway: 'true' - DeployVpc: 'false' + BrokerImageVersion: '0.7.0' + DeployAlb: 'false' + DeployAlbDnsRecord: 'false' + DeployBastionHost: 'false' + DeployBroker: 'false' + DeployContainers: 'false' + DeployDb: 'false' + DeployNlb: 'false' + DeployNlbVpcLink: 'false' + DeployPostgrest: 'false' + DeployProcessor: 'false' + DeployRestApi: 'false' + DeployRestApiDnsRecord: 'false' + DeployRouteTables: 'false' + DeployStack: 'true' + DeployVpc: 'true' + DeployWaf: 'false' + EnableWafRulesGeneral: 'false' + EnableWafRulesRestApi: 'false' + EnableWafRulesWebSocket: 'false' + Environment: 'ECO-2188' + Network: 'testnet' + ProcessorImageVersion: '0.5.0' tags: null template-file-path: 'src/cloud-formation/indexer.cfn.yaml' ... diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index dcbefd14b..c8ad143ea 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -1,23 +1,218 @@ +# cspell:word aarch +# cspell:word awslogs +# cspell:word awsvpc +# cspell:word fargate +# cspell:word multivalue +# cspell:word pullthroughcache +# cspell:word restapis --- Conditions: - DeployGateway: !Equals - - !Ref 'DeployGateway' + DeployAlb: !Equals + - !Ref 'DeployAlb' + - 'true' + DeployAlbDnsRecord: !Equals + - !Ref 'DeployAlbDnsRecord' + - 'true' + DeployBastionHost: !Equals + - !Ref 'DeployBastionHost' + - 'true' + DeployBroker: !Equals + - !Ref 'DeployBroker' + - 'true' + DeployContainers: !Equals + - !Ref 'DeployContainers' + - 'true' + DeployDb: !Equals + - !Ref 'DeployDb' + - 'true' + DeployNlb: !Equals + - !Ref 'DeployNlb' + - 'true' + DeployNlbVpcLink: !Equals + - !Ref 'DeployNlbVpcLink' + - 'true' + DeployPostgrest: !Equals + - !Ref 'DeployPostgrest' + - 'true' + DeployProcessor: !Equals + - !Ref 'DeployProcessor' + - 'true' + DeployRestApi: !Equals + - !Ref 'DeployRestApi' + - 'true' + DeployRestApiDnsRecord: !Equals + - !Ref 'DeployRestApiDnsRecord' + - 'true' + DeployRouteTables: !Equals + - !Ref 'DeployRouteTables' - 'true' DeployVpc: !Equals - !Ref 'DeployVpc' - 'true' + DeployWaf: !Equals + - !Ref 'DeployWaf' + - 'true' + EnableWafRulesGeneral: !Equals + - !Ref 'EnableWafRulesGeneral' + - 'true' + EnableWafRulesRestApi: !Equals + - !Ref 'EnableWafRulesRestApi' + - 'true' + EnableWafRulesWebSocket: !Equals + - !Ref 'EnableWafRulesWebSocket' + - 'true' +Mappings: + Constants: + # These compromised credentials are not a security risk because access to + # the database is restricted to security groups in the VPC. + DatabaseConfig: + DatabaseName: 'emojicoin' + MasterPassword: 'emojicoin' + MasterUsername: 'emojicoin' + ImageCache: + RepositoryPrefix: 'emojicoin' + Logging: + Prefix: 'emojicoin' + Networking: + BrokerPort: 3009 + DatabasePort: 5432 + DnsNameHostedZoneId: + '{{resolve:ssm:/emojicoin/indexer-dns-name/hosted-zone-id}}' + DnsNameRootDomain: + '{{resolve:ssm:/emojicoin/indexer-dns-name/root-domain}}' + PostgrestHealthCheckPort: 3001 + PostgrestPort: 3000 + ProcessorWebsocketPort: 3008 + VpcCidrBlock: '10.0.0.0/16' + Websocat: + Build: 'websocat.aarch64-unknown-linux-musl' + ReleaseUrlBase: 'https://github.com/vi/websocat/releases/download' + Version: 'v1.13.0' + PrivateSubnets: + A: + AvailabilityZone: 0 + CidrBlock: '10.0.1.0/24' + B: + AvailabilityZone: 1 + CidrBlock: '10.0.2.0/24' + C: + AvailabilityZone: 2 + CidrBlock: '10.0.3.0/24' + PublicSubnets: + A: + AvailabilityZone: 0 + CidrBlock: '10.0.4.0/24' + B: + AvailabilityZone: 1 + CidrBlock: '10.0.5.0/24' + C: + AvailabilityZone: 2 + CidrBlock: '10.0.6.0/24' Metadata: cfn-lint: config: ignore_checks: + - 'E3005' - 'W1001' +Outputs: + BastionHostId: + Condition: 'DeployBastionHost' + Description: 'The instance ID of the bastion host.' + Value: !Ref 'BastionHost' + RestEndpoint: + Condition: 'DeployRestApiDnsRecord' + Value: !Sub + - 'https://${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + WsEndpoint: + Condition: 'DeployAlbDnsRecord' + Value: !Sub + - 'wss://ws.${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' Parameters: - DeployAnything: + BastionHostAmiId: + Default: 'ami-030cb86c12b18e236' + Type: 'AWS::EC2::Image::Id' + BrokerImageVersion: + Type: 'String' + DbMaxCapacity: + Default: 4 + Type: 'Number' + DbMinCapacity: + Default: 0.5 + Type: 'Number' + DeployAlb: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployAlbDnsRecord: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployBastionHost: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployBroker: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployContainers: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployDb: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployNlb: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployNlbVpcLink: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployPostgrest: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployProcessor: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployRestApi: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployRestApiDnsRecord: AllowedValues: - 'false' - 'true' Type: 'String' - DeployGateway: + DeployRouteTables: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + DeployStack: AllowedValues: - 'false' - 'true' @@ -27,38 +222,1906 @@ Parameters: - 'false' - 'true' Type: 'String' + DeployWaf: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + EnableWafRulesGeneral: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + EnableWafRulesRestApi: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + EnableWafRulesWebSocket: + AllowedValues: + - 'false' + - 'true' + Type: 'String' + Environment: + Type: 'String' + Network: + AllowedValues: + - 'mainnet' + - 'testnet' + Type: 'String' + PostgrestImageVersion: + Default: 'v12.2.3' + Type: 'String' + PostgrestMaxRows: + Default: 500 + Type: 'Number' + ProcessorImageVersion: + Type: 'String' Resources: + # Public application load balancer. + Alb: + Condition: 'DeployAlb' + Properties: + LoadBalancerAttributes: + - Key: 'load_balancing.cross_zone.enabled' + Value: 'true' + Scheme: 'internet-facing' + SecurityGroups: + - !Ref 'AlbSecurityGroup' + - !If + - 'DeployBroker' + - !Ref 'BrokerWsClientSecurityGroup' + - !Ref 'AWS::NoValue' + Subnets: + - !Ref 'PublicSubnetA' + - !Ref 'PublicSubnetB' + - !Ref 'PublicSubnetC' + Type: 'application' + Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' + # DNS certificate for the broker's application load balancer. + AlbCertificate: + Condition: 'DeployAlb' + Properties: + DomainName: !Sub + - 'ws.${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + DomainValidationOptions: + - DomainName: !Sub + - 'ws.${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + HostedZoneId: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameHostedZoneId' + ValidationMethod: 'DNS' + Type: 'AWS::CertificateManager::Certificate' + # DNS record for the broker's application load balancer. + AlbDnsRecord: + Condition: 'DeployAlbDnsRecord' + Properties: + AliasTarget: + DNSName: !GetAtt 'Alb.DNSName' + HostedZoneId: !GetAtt 'Alb.CanonicalHostedZoneID' + HostedZoneId: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameHostedZoneId' + Name: !Sub + - 'ws.${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + Type: 'A' + Type: 'AWS::Route53::RecordSet' + # Application load balancer listener. + AlbListener: + Condition: 'DeployAlb' + Properties: + Certificates: + - CertificateArn: !Ref 'AlbCertificate' + DefaultActions: + - TargetGroupArn: !Ref 'BrokerAlbTargetGroup' + Type: 'forward' + LoadBalancerArn: !Ref 'Alb' + Port: 443 + Protocol: 'HTTPS' + Type: 'AWS::ElasticLoadBalancingV2::Listener' + # Security group for the application load balancer. + AlbSecurityGroup: + Condition: 'DeployAlb' + Properties: + GroupDescription: !Ref 'AWS::StackName' + SecurityGroupIngress: + - CidrIp: '0.0.0.0/0' + FromPort: 443 + IpProtocol: 'tcp' + ToPort: 443 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Minimal VM for monitoring internal processes. + BastionHost: + Condition: 'DeployBastionHost' + Properties: + ImageId: !Ref 'BastionHostAmiId' + InstanceType: 't4g.nano' + NetworkInterfaces: + - DeviceIndex: 0 + GroupSet: + - !Ref 'BastionHostSecurityGroup' + - !Ref 'DbUserSecurityGroup' + - !If + - 'DeployBroker' + - !Ref 'BrokerWsClientSecurityGroup' + - !Ref 'AWS::NoValue' + - !If + - 'DeployNlb' + - !Ref 'NlbClientSecurityGroup' + - !Ref 'AWS::NoValue' + - !If + - 'DeployPostgrest' + - !Ref 'PostgrestClientSecurityGroup' + - !Ref 'AWS::NoValue' + - !If + - 'DeployProcessor' + - !Ref 'ProcessorWsClientSecurityGroup' + - !Ref 'AWS::NoValue' + SubnetId: !Ref 'PrivateSubnetA' + UserData: + # Install PostgreSQL client & websocat, set connection strings. + Fn::Base64: !Sub + - | + #!/bin/bash + yum update -y + amazon-linux-extras enable postgresql14 + yum clean metadata + yum install -y postgresql + wget ${WebsocatReleaseUrlBase}/${WebsocatVersion}/${WebsocatBuild} + chmod +x ${WebsocatBuild} + mv ${WebsocatBuild} /usr/local/bin/websocat + echo 'export DB_URL="${DatabaseUrl}"' >> /etc/profile + echo '${NlbDnsNameExport}' >> /etc/profile + echo '${BrokerWsUrlExport}' >> /etc/profile + echo '${PostgrestUrlExport}' >> /etc/profile + echo '${ProcessorWsUrlExport}' >> /etc/profile + - BrokerWsUrlExport: !If + - 'DeployBroker' + - !Join + - '' + - - 'export BROKER_WS_URL="ws://' + - !GetAtt 'BrokerServiceDiscovery.Name' + - '.' + - !Ref 'AWS::StackName' + - ':' + - !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + - '"' + - '# BROKER_WS_URL not set (broker not provisioned)' + DatabaseUrl: !Join + - '' + - - 'postgres://' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterUsername' + - ':' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterPassword' + - '@' + - !GetAtt 'DbCluster.ReadEndpoint.Address' + - '/' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'DatabaseName' + NlbDnsNameExport: !If + - 'DeployNlb' + - !Join + - '' + - - 'export NLB_DNS_NAME="' + - !GetAtt 'Nlb.DNSName' + - '"' + - '# NLB_DNS_NAME not set (NLB not provisioned)' + PostgrestUrlExport: !If + - 'DeployPostgrest' + - !Join + - '' + - - 'export POSTGREST_URL="http://' + - !GetAtt 'PostgrestServiceDiscovery.Name' + - '.' + - !Ref 'AWS::StackName' + - ':' + - !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + - '"' + - '# POSTGREST_URL not set (PostgREST not provisioned)' + ProcessorWsUrlExport: !If + - 'DeployProcessor' + - !Join + - '' + - - 'export PROCESSOR_WS_URL="ws://' + - !GetAtt 'ProcessorServiceDiscovery.Name' + - '.' + - !Ref 'AWS::StackName' + - ':' + - !FindInMap + - 'Constants' + - 'Networking' + - 'ProcessorWebsocketPort' + - '/ws"' + - '# PROCESSOR_WS_URL not set (processor not provisioned)' + WebsocatBuild: !FindInMap + - 'Constants' + - 'Websocat' + - 'Build' + WebsocatReleaseUrlBase: !FindInMap + - 'Constants' + - 'Websocat' + - 'ReleaseUrlBase' + WebsocatVersion: !FindInMap + - 'Constants' + - 'Websocat' + - 'Version' + Type: 'AWS::EC2::Instance' + # Connection endpoint allowing access to the bastion host. + BastionHostConnectionEndpoint: + Condition: 'DeployBastionHost' + Properties: + PreserveClientIp: true + SecurityGroupIds: + - !Ref 'BastionHostConnectionEndpointSecurityGroup' + SubnetId: !Ref 'PrivateSubnetA' + Type: 'AWS::EC2::InstanceConnectEndpoint' + # Security group for bastion host connection endpoint. + BastionHostConnectionEndpointSecurityGroup: + Condition: 'DeployBastionHost' + Properties: + GroupDescription: !Ref 'AWS::StackName' + SecurityGroupEgress: + - DestinationSecurityGroupId: !Ref 'BastionHostSecurityGroup' + IpProtocol: -1 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Security group for the bastion host. + BastionHostSecurityGroup: + Condition: 'DeployBastionHost' + Properties: + GroupDescription: !Ref 'AWS::StackName' + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Ingress policy for the bastion host security group, separated to eliminate + # circular dependencies with the security group for the connection endpoint. + BastionHostSecurityGroupIngress: + Condition: 'DeployBastionHost' + Properties: + GroupId: !Ref 'BastionHostSecurityGroup' + IpProtocol: -1 + SourceSecurityGroupId: + !Ref 'BastionHostConnectionEndpointSecurityGroup' + Type: 'AWS::EC2::SecurityGroupIngress' + # Target group for application load balancer traffic to the broker. + BrokerAlbTargetGroup: + Condition: 'DeployAlb' + DependsOn: 'Alb' + Properties: + HealthCheckIntervalSeconds: 5 + HealthCheckPath: '/health' + HealthCheckPort: !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + HealthCheckProtocol: 'HTTP' + HealthCheckTimeoutSeconds: 2 + HealthyThresholdCount: 2 + Name: !Sub '${AWS::StackName}-broker' + Port: !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + Protocol: 'HTTP' + ProtocolVersion: 'HTTP1' + TargetGroupAttributes: + - Key: 'deregistration_delay.timeout_seconds' + Value: '30' + - Key: 'stickiness.enabled' + Value: 'true' + - Key: 'stickiness.type' + Value: 'lb_cookie' + TargetType: 'ip' + UnhealthyThresholdCount: 2 + VpcId: !Ref 'Vpc' + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' + # Scaling policy for broker service. + BrokerAutoScalingPolicy: + Condition: 'DeployBroker' + Properties: + PolicyName: !Sub '${AWS::StackName}-broker' + PolicyType: 'TargetTrackingScaling' + ScalingTargetId: !Ref 'BrokerAutoScalingTarget' + TargetTrackingScalingPolicyConfiguration: + PredefinedMetricSpecification: + PredefinedMetricType: 'ECSServiceAverageCPUUtilization' + ScaleInCooldown: 300 + ScaleOutCooldown: 60 + TargetValue: 70 + Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' + # Scalable target for broker service. + BrokerAutoScalingTarget: + Condition: 'DeployBroker' + Properties: + MaxCapacity: 5 + MinCapacity: 1 + ResourceId: !Join + - '/' + - - 'service' + - !Ref 'ContainerCluster' + - !GetAtt 'BrokerRunner.Name' + RoleARN: !GetAtt 'ContainerRole.Arn' + ScalableDimension: 'ecs:service:DesiredCount' + ServiceNamespace: 'ecs' + Type: 'AWS::ApplicationAutoScaling::ScalableTarget' + # Security group for the broker's WebSocket server. + BrokerPublisherSecurityGroup: + Condition: 'DeployBroker' + Properties: + GroupDescription: !Ref 'AWS::StackName' + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Ingress policy for the broker's WebSocket server security group. + BrokerPublisherSecurityGroupIngress: + Condition: 'DeployBroker' + Properties: + GroupId: !Ref 'BrokerPublisherSecurityGroup' + IpProtocol: -1 + SourceSecurityGroupId: !Ref 'BrokerWsClientSecurityGroup' + Type: 'AWS::EC2::SecurityGroupIngress' + # Service for running the broker. + BrokerRunner: + Condition: 'DeployBroker' + DependsOn: + # Wait until processor server is online. + - 'ProcessorRunner' + # Wait until there is an outbound route to get the image from the + # pull through cache. + - 'PrivateRouteTableAssociationA' + - 'PrivateRouteTableAssociationB' + - 'PrivateRouteTableAssociationC' + - 'PrivateRouteThroughNatGateway' + # Proxy for a conditional dependency on the application load balancer + # listener, which associates the broker target group with the application + # load balancer. + Metadata: + ConditionalDependencyProxy: !If + - 'DeployAlb' + - !Ref 'AlbListener' + - '' + Properties: + Cluster: !Ref 'ContainerCluster' + DeploymentConfiguration: + DeploymentCircuitBreaker: + Enable: true + Rollback: true + LaunchType: 'FARGATE' + LoadBalancers: !If + - 'DeployAlb' + - - ContainerName: !Sub '${AWS::StackName}-broker' + ContainerPort: !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + TargetGroupArn: !Ref 'BrokerAlbTargetGroup' + - !Ref 'AWS::NoValue' + NetworkConfiguration: + AwsvpcConfiguration: + SecurityGroups: + - !Ref 'BrokerPublisherSecurityGroup' + - !Ref 'ContainerSecurityGroup' + - !Ref 'ProcessorWsClientSecurityGroup' + Subnets: + - !Ref 'PrivateSubnetA' + - !Ref 'PrivateSubnetB' + - !Ref 'PrivateSubnetC' + ServiceRegistries: + - RegistryArn: !GetAtt 'BrokerServiceDiscovery.Arn' + TaskDefinition: !Ref 'BrokerTask' + Type: 'AWS::ECS::Service' + # Service discovery for the broker. + BrokerServiceDiscovery: + Condition: 'DeployBroker' + Properties: + DnsConfig: + DnsRecords: + - TTL: 10 + Type: 'A' + RoutingPolicy: 'MULTIVALUE' + HealthCheckCustomConfig: + FailureThreshold: 1 + Name: !Sub '${AWS::StackName}-broker' + NamespaceId: !Ref 'PrivateServiceDiscoveryNamespace' + Type: 'AWS::ServiceDiscovery::Service' + # Task definition for the broker. + BrokerTask: + Condition: 'DeployBroker' + DependsOn: 'ContainerLogGroup' + Properties: + ContainerDefinitions: + - Environment: + - Name: 'PROCESSOR_WS_URL' + Value: !Join + - '' + - - 'ws://' + - !GetAtt 'ProcessorServiceDiscovery.Name' + - '.' + - !Ref 'AWS::StackName' + - ':' + - !FindInMap + - 'Constants' + - 'Networking' + - 'ProcessorWebsocketPort' + - '/ws' + - Name: 'PORT' + Value: !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + - Name: 'RUST_LOG' + Value: 'info' + HealthCheck: + Command: + - 'CMD' + - 'curl' + - '--fail' + - !Sub + - 'http://localhost:${Port}/live' + - Port: !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + Interval: 5 + Retries: 1 + StartPeriod: 0 + Timeout: 2 + Image: !Join + - '' + - - !Ref 'AWS::AccountId' + - '.dkr.ecr.' + - !Ref 'AWS::Region' + - '.amazonaws.com/' + - !FindInMap + - 'Constants' + - 'ImageCache' + - 'RepositoryPrefix' + - '/econialabs/emojicoin-dot-fun-indexer-broker:' + - !Ref 'BrokerImageVersion' + LogConfiguration: + LogDriver: 'awslogs' + Options: + awslogs-group: !Join + - '' + - - '/' + - !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + - '/' + - !Ref 'AWS::StackName' + awslogs-region: !Ref 'AWS::Region' + awslogs-stream-prefix: !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + Name: !Sub '${AWS::StackName}-broker' + PortMappings: + - ContainerPort: !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + HostPort: !FindInMap + - 'Constants' + - 'Networking' + - 'BrokerPort' + Cpu: '256' + ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' + Family: !Ref 'AWS::StackName' + Memory: '512' + NetworkMode: 'awsvpc' + RequiresCompatibilities: + - 'FARGATE' + Type: 'AWS::ECS::TaskDefinition' + # Security group for clients of the broker's WebSocket server. + BrokerWsClientSecurityGroup: + Condition: 'DeployBroker' + Properties: + GroupDescription: !Ref 'AWS::StackName' + SecurityGroupEgress: + - DestinationSecurityGroupId: !Ref 'BrokerPublisherSecurityGroup' + IpProtocol: -1 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Cluster for running ECS containers. + ContainerCluster: + Condition: 'DeployContainers' + Type: 'AWS::ECS::Cluster' + # Log group for ECS task logging. + ContainerLogGroup: + Condition: 'DeployContainers' + Properties: + LogGroupName: !Join + - '' + - - '/' + - !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + - '/' + - !Ref 'AWS::StackName' + RetentionInDays: 7 + Type: 'AWS::Logs::LogGroup' + # Role with assorted permissions required to run containers. + ContainerRole: + Condition: 'DeployContainers' + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: 'sts:AssumeRole' + Effect: 'Allow' + Principal: + Service: 'application-autoscaling.amazonaws.com' + - Action: 'sts:AssumeRole' + Effect: 'Allow' + Principal: + Service: 'ecs-tasks.amazonaws.com' + ManagedPolicyArns: + - !Join + - '' + - - 'arn:aws:iam::aws:policy/service-role/' + - 'AmazonEC2ContainerServiceAutoscaleRole' + Policies: + - PolicyDocument: + Statement: + # Pull through cache permissions. + - Action: + - 'ecr:BatchCheckLayerAvailability' + - 'ecr:BatchGetImage' + - 'ecr:BatchImportUpstreamImage' + - 'ecr:CompleteLayerUpload' + - 'ecr:CreateRepository' + - 'ecr:DescribeImages' + - 'ecr:DescribeRepositories' + - 'ecr:GetAuthorizationToken' + - 'ecr:GetDownloadUrlForLayer' + - 'ecr:InitiateLayerUpload' + - 'ecr:PutImage' + - 'ecr:UploadLayerPart' + Effect: 'Allow' + Resource: !Join + - '' + - - 'arn:aws:ecr:' + - !Ref 'AWS::Region' + - ':' + - !Ref 'AWS::AccountId' + - ':repository/' + - !FindInMap + - 'Constants' + - 'ImageCache' + - 'RepositoryPrefix' + - '/*' + - Action: + - 'ecr:GetAuthorizationToken' + Effect: 'Allow' + Resource: '*' + # Container logging permissions. + - Action: + - 'logs:CreateLogStream' + - 'logs:PutLogEvents' + Effect: 'Allow' + Resource: !Join + - '' + - - 'arn:aws:logs:' + - !Ref 'AWS::Region' + - ':' + - !Ref 'AWS::AccountId' + - ':log-group:/' + - !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + - '/' + - !Ref 'AWS::StackName' + - ':*' + # Secret access permissions. + - Action: + - 'secretsmanager:GetSecretValue' + Effect: 'Allow' + Resource: + - !Join + - '' + - - 'arn:aws:secretsmanager:' + - !Ref 'AWS::Region' + - ':' + - !Ref 'AWS::AccountId' + - ':secret:emojicoin/grpc-auth-token-*' + - !Join + - '' + - - 'arn:aws:secretsmanager:' + - !Ref 'AWS::Region' + - ':' + - !Ref 'AWS::AccountId' + - ':secret:ecr-pullthroughcache/emojicoin/docker-hub-*' + PolicyName: !Sub 'EmojicoinContainerRolePolicy-${AWS::StackName}' + RoleName: !Sub 'EmojicoinContainerRole-${AWS::StackName}' + Type: 'AWS::IAM::Role' + # Security group for ECS containers. + ContainerSecurityGroup: + Condition: 'DeployContainers' + Properties: + GroupDescription: !Ref 'AWS::StackName' + # Allow all outbound traffic, to other resources and to Docker Hub. + SecurityGroupEgress: + - CidrIp: '0.0.0.0/0' + IpProtocol: -1 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Database cluster. + DbCluster: + Condition: 'DeployDb' + Metadata: + cfn-lint: + config: + ignore_checks: + - 'W2501' + Properties: + DBSubnetGroupName: !Ref 'DbSubnetGroup' + DatabaseName: !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'DatabaseName' + Engine: 'aurora-postgresql' + EngineMode: 'provisioned' + EngineVersion: '16.2' + MasterUserPassword: !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterPassword' + MasterUsername: !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterUsername' + Port: !FindInMap + - 'Constants' + - 'Networking' + - 'DatabasePort' + ServerlessV2ScalingConfiguration: + MaxCapacity: !Ref 'DbMaxCapacity' + MinCapacity: !Ref 'DbMinCapacity' + VpcSecurityGroupIds: + - !Ref 'DbSecurityGroup' + Type: 'AWS::RDS::DBCluster' + # Primary (writer) database instance. + DbInstancePrimary: + Condition: 'DeployDb' + Properties: + DBClusterIdentifier: !Ref 'DbCluster' + DBInstanceClass: 'db.serverless' + Engine: 'aurora-postgresql' + Type: 'AWS::RDS::DBInstance' + # Replica (reader) database instance. + DbInstanceReplica: + Condition: 'DeployDb' + Properties: + DBClusterIdentifier: !Ref 'DbCluster' + DBInstanceClass: 'db.serverless' + Engine: 'aurora-postgresql' + Type: 'AWS::RDS::DBInstance' + # Security group for the database itself. + DbSecurityGroup: + Condition: 'DeployDb' + Properties: + GroupDescription: !Ref 'AWS::StackName' + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Ingress policy for the database's security group, separated to eliminate + # circular dependencies with security group for users of the database. + DbSecurityGroupIngress: + Condition: 'DeployDb' + Properties: + GroupId: !Ref 'DbSecurityGroup' + IpProtocol: -1 + SourceSecurityGroupId: !Ref 'DbUserSecurityGroup' + Type: 'AWS::EC2::SecurityGroupIngress' + # Database subnet group. + DbSubnetGroup: + Condition: 'DeployDb' + Properties: + DBSubnetGroupDescription: !Ref 'AWS::StackName' + SubnetIds: + - !Ref 'PrivateSubnetA' + - !Ref 'PrivateSubnetB' + - !Ref 'PrivateSubnetC' + Type: 'AWS::RDS::DBSubnetGroup' + # Security group for users of the database. + DbUserSecurityGroup: + Condition: 'DeployDb' + Properties: + GroupDescription: !Ref 'AWS::StackName' + SecurityGroupEgress: + - DestinationSecurityGroupId: !Ref 'DbSecurityGroup' + IpProtocol: -1 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Association for private route table with each private subnet. + Fn::ForEach::PrivateRouteTableAssociation: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - PrivateRouteTableAssociation${Identifier}: + Properties: + RouteTableId: !Ref 'PrivateRouteTable' + SubnetId: !Ref + Fn::Sub: + - 'PrivateSubnet${Identifier}' + - Identifier: !Ref 'Identifier' + # ForEach transforms require that condition is second key or later. + # yamllint disable-line rule:key-ordering + Condition: 'DeployRouteTables' + Type: 'AWS::EC2::SubnetRouteTableAssociation' + # A private subnet for each of the database availability zones. + Fn::ForEach::PrivateSubnet: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - PrivateSubnet${Identifier}: + Properties: + AvailabilityZone: !Select + - !FindInMap + - 'PrivateSubnets' + - Ref: 'Identifier' + - 'AvailabilityZone' + - Fn::GetAZs: !Ref 'AWS::Region' + CidrBlock: !FindInMap + - 'PrivateSubnets' + - Ref: 'Identifier' + - 'CidrBlock' + MapPublicIpOnLaunch: false + VpcId: !Ref 'Vpc' + # ForEach transforms require that condition is second key or later. + Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering + Type: 'AWS::EC2::Subnet' + # Association for public route table with each public subnet. + Fn::ForEach::PublicRouteTableAssociation: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - PublicRouteTableAssociation${Identifier}: + Properties: + RouteTableId: !Ref 'PublicRouteTable' + SubnetId: !Ref + Fn::Sub: + - 'PublicSubnet${Identifier}' + - Identifier: !Ref 'Identifier' + # ForEach transforms require that condition is second key or later. + # yamllint disable-line rule:key-ordering + Condition: 'DeployRouteTables' + Type: 'AWS::EC2::SubnetRouteTableAssociation' + # A public subnet for each availability zone. + Fn::ForEach::PublicSubnet: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - PublicSubnet${Identifier}: + Properties: + AvailabilityZone: !Select + - !FindInMap + - 'PublicSubnets' + - Ref: 'Identifier' + - 'AvailabilityZone' + - Fn::GetAZs: !Ref 'AWS::Region' + CidrBlock: !FindInMap + - 'PublicSubnets' + - Ref: 'Identifier' + - 'CidrBlock' + MapPublicIpOnLaunch: true + VpcId: !Ref 'Vpc' + # ForEach transforms require that condition is second key or later. + Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering + Type: 'AWS::EC2::Subnet' + # Internet gateway for the virtual private cloud. InternetGateway: - Condition: 'DeployGateway' + Condition: 'DeployVpc' Type: 'AWS::EC2::InternetGateway' + # Attachment of internet gateway to the virtual private cloud. InternetGatewayAttachment: Condition: 'DeployVpc' Properties: InternetGatewayId: !Ref 'InternetGateway' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::VPCGatewayAttachment' + # Network address translation gateway for the virtual private cloud. + NatGateway: + Condition: 'DeployVpc' + Properties: + AllocationId: !GetAtt 'NatGatewayEip.AllocationId' + SubnetId: !Ref 'PublicSubnetA' + Type: 'AWS::EC2::NatGateway' + # Elastic IP address for the network address translation gateway. + NatGatewayEip: + Condition: 'DeployVpc' + Properties: + Domain: 'vpc' + Type: 'AWS::EC2::EIP' + # Private network load balancer. + Nlb: + Condition: 'DeployNlb' + Properties: + LoadBalancerAttributes: + - Key: 'load_balancing.cross_zone.enabled' + Value: 'true' + Scheme: 'internal' + SecurityGroups: + - !Ref 'NlbSecurityGroup' + - !If + - 'DeployBroker' + - !Ref 'BrokerWsClientSecurityGroup' + - !Ref 'AWS::NoValue' + - !If + - 'DeployPostgrest' + - !Ref 'PostgrestClientSecurityGroup' + - !Ref 'AWS::NoValue' + Subnets: + - !Ref 'PrivateSubnetA' + - !Ref 'PrivateSubnetB' + - !Ref 'PrivateSubnetC' + Type: 'network' + Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' + # Security group for direct clients of the network load balancer. + NlbClientSecurityGroup: + Condition: 'DeployNlb' + Properties: + GroupDescription: !Ref 'AWS::StackName' + SecurityGroupEgress: + - DestinationSecurityGroupId: !Ref 'NlbSecurityGroup' + IpProtocol: -1 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Security group for the network load balancer. + NlbSecurityGroup: + Condition: 'DeployNlb' + Properties: + GroupDescription: !Ref 'AWS::StackName' + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Network load balancer security group ingress policy for PostgREST traffic + # over VPC link. + NlbSecurityGroupIngressPostgrest: + Condition: 'DeployNlbVpcLink' + Properties: + CidrIp: '0.0.0.0/0' + FromPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + GroupId: !Ref 'NlbSecurityGroup' + IpProtocol: 'tcp' + ToPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + Type: 'AWS::EC2::SecurityGroupIngress' + # Connection to network load balancer through VPC link. + NlbVpcLink: + Condition: 'DeployNlbVpcLink' + Properties: + Name: !Ref 'AWS::StackName' + TargetArns: + - !Ref 'Nlb' + Type: 'AWS::ApiGateway::VpcLink' + # Scaling policy for PostgREST service. + PostgrestAutoScalingPolicy: + Condition: 'DeployPostgrest' + Properties: + PolicyName: !Sub '${AWS::StackName}-postgrest' + PolicyType: 'TargetTrackingScaling' + ScalingTargetId: !Ref 'PostgrestAutoScalingTarget' + TargetTrackingScalingPolicyConfiguration: + PredefinedMetricSpecification: + PredefinedMetricType: 'ECSServiceAverageCPUUtilization' + ScaleInCooldown: 300 + ScaleOutCooldown: 60 + TargetValue: 70 + Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' + # Scalable target for PostgREST service. + PostgrestAutoScalingTarget: + Condition: 'DeployPostgrest' + Properties: + MaxCapacity: 5 + MinCapacity: 1 + ResourceId: !Join + - '/' + - - 'service' + - !Ref 'ContainerCluster' + - !GetAtt 'PostgrestRunner.Name' + RoleARN: !GetAtt 'ContainerRole.Arn' + ScalableDimension: 'ecs:service:DesiredCount' + ServiceNamespace: 'ecs' + Type: 'AWS::ApplicationAutoScaling::ScalableTarget' + # Security group for PostgREST clients. + PostgrestClientSecurityGroup: + Condition: 'DeployPostgrest' + Properties: + GroupDescription: !Ref 'AWS::StackName' + SecurityGroupEgress: + - DestinationSecurityGroupId: !Ref 'PostgrestServerSecurityGroup' + IpProtocol: -1 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Listener for network load balancer traffic to PostgREST. + PostgrestNlbListener: + Condition: 'DeployNlb' + Properties: + DefaultActions: + - TargetGroupArn: !Ref 'PostgrestNlbTargetGroup' + Type: 'forward' + LoadBalancerArn: !Ref 'Nlb' + Port: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + Protocol: 'TCP' + Type: 'AWS::ElasticLoadBalancingV2::Listener' + # Target group for network load balancer traffic to PostgREST. + PostgrestNlbTargetGroup: + Condition: 'DeployNlb' + DependsOn: 'Nlb' + Properties: + HealthCheckIntervalSeconds: 5 + HealthCheckPath: '/ready' + HealthCheckPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestHealthCheckPort' + HealthCheckProtocol: 'HTTP' + HealthCheckTimeoutSeconds: 2 + HealthyThresholdCount: 2 + Name: !Sub '${AWS::StackName}-postgrest' + Port: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + Protocol: 'TCP' + TargetGroupAttributes: + - Key: 'deregistration_delay.timeout_seconds' + Value: '30' + - Key: 'stickiness.enabled' + Value: 'false' + TargetType: 'ip' + UnhealthyThresholdCount: 2 + VpcId: !Ref 'Vpc' + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' + # Service for running PostgREST. + PostgrestRunner: + Condition: 'DeployPostgrest' + DependsOn: + # Wait for both database instances to ensure cluster is fully available. + - 'DbInstancePrimary' + - 'DbInstanceReplica' + # Wait for processor to ensure that migrations have been run, so that the + # schema will be available and load balancer health checks will pass. + - 'ProcessorRunner' + # Wait until there is an outbound route to get the image from the + # pull through cache. + - 'PrivateRouteTableAssociationA' + - 'PrivateRouteTableAssociationB' + - 'PrivateRouteTableAssociationC' + - 'PrivateRouteThroughNatGateway' + # Proxy for a conditional dependency on the network load balancer listener + # for PostgREST, which associates the PostgREST target group with the + # network load balancer. + Metadata: + ConditionalDependencyProxy: !If + - 'DeployNlb' + - !Ref 'PostgrestNlbListener' + - '' + Properties: + Cluster: !Ref 'ContainerCluster' + DeploymentConfiguration: + DeploymentCircuitBreaker: + Enable: true + Rollback: true + LaunchType: 'FARGATE' + LoadBalancers: !If + - 'DeployNlb' + - - ContainerName: !Sub '${AWS::StackName}-postgrest' + ContainerPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + TargetGroupArn: !Ref 'PostgrestNlbTargetGroup' + - !Ref 'AWS::NoValue' + NetworkConfiguration: + AwsvpcConfiguration: + SecurityGroups: + - !Ref 'DbUserSecurityGroup' + - !Ref 'PostgrestServerSecurityGroup' + - !Ref 'ContainerSecurityGroup' + Subnets: + - !Ref 'PrivateSubnetA' + - !Ref 'PrivateSubnetB' + - !Ref 'PrivateSubnetC' + ServiceRegistries: + - RegistryArn: !GetAtt 'PostgrestServiceDiscovery.Arn' + TaskDefinition: !Ref 'PostgrestTask' + Type: 'AWS::ECS::Service' + # Security group for the PostgREST server. + PostgrestServerSecurityGroup: + Condition: 'DeployPostgrest' + Properties: + GroupDescription: !Ref 'AWS::StackName' + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Ingress policy for the PostgREST server security group. + PostgrestServerSecurityGroupIngress: + Condition: 'DeployPostgrest' + Properties: + GroupId: !Ref 'PostgrestServerSecurityGroup' + IpProtocol: -1 + SourceSecurityGroupId: !Ref 'PostgrestClientSecurityGroup' + Type: 'AWS::EC2::SecurityGroupIngress' + # Service discovery for PostgREST. + PostgrestServiceDiscovery: + Condition: 'DeployPostgrest' + Properties: + DnsConfig: + DnsRecords: + - TTL: 10 + Type: 'A' + RoutingPolicy: 'MULTIVALUE' + HealthCheckCustomConfig: + FailureThreshold: 1 + Name: !Sub '${AWS::StackName}-postgrest' + NamespaceId: !Ref 'PrivateServiceDiscoveryNamespace' + Type: 'AWS::ServiceDiscovery::Service' + # Task definition for PostgREST. + PostgrestTask: + Condition: 'DeployPostgrest' + DependsOn: 'ContainerLogGroup' + Properties: + ContainerDefinitions: + - Environment: + - Name: 'PGRST_ADMIN_SERVER_PORT' + Value: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestHealthCheckPort' + - Name: 'PGRST_DB_URI' + Value: !Join + - '' + - - 'postgres://' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterUsername' + - ':' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterPassword' + - '@' + # While the PostgREST endpoint is configured to support read-only + # operations, the PostgREST listener errors out when connected to + # a read replica because it relies on `LISTEN` and `NOTIFY` + # commands, which do not work on read replicas. Hence it is + # connected to the writer endpoint. + - !GetAtt 'DbCluster.Endpoint.Address' + - '/' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterUsername' + - Name: 'PGRST_DB_ANON_ROLE' + Value: 'web_anon' + - Name: 'PGRST_DB_MAX_ROWS' + Value: !Ref 'PostgrestMaxRows' + Image: !Join + - '' + - - !Ref 'AWS::AccountId' + - '.dkr.ecr.' + - !Ref 'AWS::Region' + - '.amazonaws.com/' + - !FindInMap + - 'Constants' + - 'ImageCache' + - 'RepositoryPrefix' + - '/postgrest/postgrest:' + - !Ref 'PostgrestImageVersion' + LogConfiguration: + LogDriver: 'awslogs' + Options: + awslogs-group: !Join + - '' + - - '/' + - !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + - '/' + - !Ref 'AWS::StackName' + awslogs-region: !Ref 'AWS::Region' + awslogs-stream-prefix: !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + Name: !Sub '${AWS::StackName}-postgrest' + PortMappings: + - ContainerPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + HostPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + - ContainerPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestHealthCheckPort' + HostPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestHealthCheckPort' + Cpu: '256' + ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' + Family: !Ref 'AWS::StackName' + Memory: '512' + NetworkMode: 'awsvpc' + RequiresCompatibilities: + - 'FARGATE' + Type: 'AWS::ECS::TaskDefinition' + # Route table for private subnets in the virtual private cloud. + PrivateRouteTable: + Condition: 'DeployRouteTables' + Properties: + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::RouteTable' + # Route from private subnets through network address translation gateway. + PrivateRouteThroughNatGateway: + Condition: 'DeployRouteTables' + Properties: + DestinationCidrBlock: '0.0.0.0/0' + NatGatewayId: !Ref 'NatGateway' + RouteTableId: !Ref 'PrivateRouteTable' + Type: 'AWS::EC2::Route' + # Private DNS namespace for internal service discovery. + PrivateServiceDiscoveryNamespace: + Condition: 'DeployVpc' + Properties: + Description: !Ref 'AWS::StackName' + Name: !Ref 'AWS::StackName' + Vpc: !Ref 'Vpc' + Type: 'AWS::ServiceDiscovery::PrivateDnsNamespace' + # Security group for the processor's WebSocket server. + ProcessorPublisherSecurityGroup: + Condition: 'DeployVpc' + Properties: + GroupDescription: !Ref 'AWS::StackName' + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Ingress policy for the processor's WebSocket server security group. + ProcessorPublisherSecurityGroupIngress: + Condition: 'DeployProcessor' + Properties: + GroupId: !Ref 'ProcessorPublisherSecurityGroup' + IpProtocol: -1 + SourceSecurityGroupId: !Ref 'ProcessorWsClientSecurityGroup' + Type: 'AWS::EC2::SecurityGroupIngress' + # Service for running the processor. + ProcessorRunner: + Condition: 'DeployProcessor' + DependsOn: + # Pending instance creation, the cluster endpoint is defined but not + # available, so the service will not be able to connect to the database. + - 'DbInstancePrimary' + - 'DbInstanceReplica' + # Wait until there is an outbound route to get the image from the + # pull through cache. + - 'PrivateRouteTableAssociationA' + - 'PrivateRouteTableAssociationB' + - 'PrivateRouteTableAssociationC' + - 'PrivateRouteThroughNatGateway' + Properties: + Cluster: !Ref 'ContainerCluster' + DeploymentConfiguration: + DeploymentCircuitBreaker: + Enable: true + Rollback: true + MaximumPercent: 100 + MinimumHealthyPercent: 0 + DesiredCount: 1 + LaunchType: 'FARGATE' + NetworkConfiguration: + AwsvpcConfiguration: + SecurityGroups: + - !Ref 'ContainerSecurityGroup' + - !Ref 'DbUserSecurityGroup' + - !Ref 'ProcessorPublisherSecurityGroup' + Subnets: + - !Ref 'PrivateSubnetA' + - !Ref 'PrivateSubnetB' + - !Ref 'PrivateSubnetC' + ServiceRegistries: + - RegistryArn: !GetAtt 'ProcessorServiceDiscovery.Arn' + TaskDefinition: !Ref 'ProcessorTask' + Type: 'AWS::ECS::Service' + # Service discovery for the processor. + ProcessorServiceDiscovery: + Condition: 'DeployProcessor' + Properties: + DnsConfig: + DnsRecords: + - TTL: 10 + Type: 'A' + RoutingPolicy: 'MULTIVALUE' + HealthCheckCustomConfig: + FailureThreshold: 1 + Name: !Sub '${AWS::StackName}-processor' + NamespaceId: !Ref 'PrivateServiceDiscoveryNamespace' + Type: 'AWS::ServiceDiscovery::Service' + # Task definition for processor. + ProcessorTask: + Condition: 'DeployProcessor' + DependsOn: 'ContainerLogGroup' + Properties: + ContainerDefinitions: + - Environment: + - Name: 'DATABASE_URL' + Value: !Join + - '' + - - 'postgres://' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterUsername' + - ':' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterPassword' + - '@' + - !GetAtt 'DbCluster.Endpoint.Address' + - '/' + - !FindInMap + - 'Constants' + - 'DatabaseConfig' + - 'MasterUsername' + - Name: 'GRPC_DATA_SERVICE_URL' + Value: !Sub + - '{{resolve:ssm:/emojicoin/grpc-data-service-url/${Network}}}' + - Network: !Ref 'Network' + - Name: 'MINIMUM_STARTING_VERSION' + Value: !Sub + - '{{resolve:ssm:/emojicoin/minimum-starting-version/${Network}}}' + - Network: !Ref 'Network' + - Name: 'EMOJICOIN_MODULE_ADDRESS' + Value: !Sub + - '{{resolve:ssm:/emojicoin/package-address/${Network}}}' + - Network: !Ref 'Network' + - Name: 'WS_PORT' + Value: !FindInMap + - 'Constants' + - 'Networking' + - 'ProcessorWebsocketPort' + HealthCheck: + Command: + - 'CMD' + - 'curl' + - '--fail' + - !Sub + - 'http://localhost:${Port}/' + - Port: !FindInMap + - 'Constants' + - 'Networking' + - 'ProcessorWebsocketPort' + Interval: 5 + Retries: 1 + StartPeriod: 0 + Timeout: 2 + Image: !Join + - '' + - - !Ref 'AWS::AccountId' + - '.dkr.ecr.' + - !Ref 'AWS::Region' + - '.amazonaws.com/' + - !FindInMap + - 'Constants' + - 'ImageCache' + - 'RepositoryPrefix' + - '/econialabs/emojicoin-dot-fun-indexer-processor:' + - !Ref 'ProcessorImageVersion' + LogConfiguration: + LogDriver: 'awslogs' + Options: + awslogs-group: !Join + - '' + - - '/' + - !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + - '/' + - !Ref 'AWS::StackName' + awslogs-region: !Ref 'AWS::Region' + awslogs-stream-prefix: !FindInMap + - 'Constants' + - 'Logging' + - 'Prefix' + Name: !Sub '${AWS::StackName}-processor' + PortMappings: + - ContainerPort: !FindInMap + - 'Constants' + - 'Networking' + - 'ProcessorWebsocketPort' + HostPort: !FindInMap + - 'Constants' + - 'Networking' + - 'ProcessorWebsocketPort' + Secrets: + - Name: 'GRPC_AUTH_TOKEN' + ValueFrom: !Sub + - 'arn:aws:secretsmanager:${Region}:${Account}:secret:${Secret}' + - Account: !Ref 'AWS::AccountId' + Region: !Ref 'AWS::Region' + Secret: 'emojicoin/grpc-auth-token' + Cpu: '256' + ExecutionRoleArn: !GetAtt 'ContainerRole.Arn' + Family: !Ref 'AWS::StackName' + Memory: '512' + NetworkMode: 'awsvpc' + RequiresCompatibilities: + - 'FARGATE' + Type: 'AWS::ECS::TaskDefinition' + # Security group for clients of the processor's WebSocket server. + ProcessorWsClientSecurityGroup: + Condition: 'DeployProcessor' + Properties: + GroupDescription: !Ref 'AWS::StackName' + SecurityGroupEgress: + - DestinationSecurityGroupId: !Ref 'ProcessorPublisherSecurityGroup' + IpProtocol: -1 + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::SecurityGroup' + # Route table for public subnets in the virtual private cloud. + PublicRouteTable: + Condition: 'DeployRouteTables' + Properties: + VpcId: !Ref 'Vpc' + Type: 'AWS::EC2::RouteTable' + # Route from public subnets through the internet gateway. + PublicRouteToInternet: + Condition: 'DeployRouteTables' + Properties: + DestinationCidrBlock: '0.0.0.0/0' + GatewayId: !Ref 'InternetGateway' + RouteTableId: !Ref 'PublicRouteTable' + Type: 'AWS::EC2::Route' + # REST API endpoint. + RestApi: + Condition: 'DeployRestApi' + Properties: + ApiKeySourceType: 'HEADER' + EndpointConfiguration: + Types: + - 'REGIONAL' + Name: !Ref 'AWS::StackName' + Type: 'AWS::ApiGateway::RestApi' + # DNS certificate for the REST API endpoint. + RestApiCertificate: + Condition: 'DeployRestApiDnsRecord' + Properties: + DomainName: !Sub + - '${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + DomainValidationOptions: + - DomainName: !Sub + - '${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + HostedZoneId: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameHostedZoneId' + ValidationMethod: 'DNS' + Type: 'AWS::CertificateManager::Certificate' + # Deployment for the REST API endpoint. + RestApiDeployment: + Condition: 'DeployRestApi' + DependsOn: + - 'RestApiMethodGeneral' + - 'RestApiMethodRoot' + Properties: + RestApiId: !Ref 'RestApi' + Type: 'AWS::ApiGateway::Deployment' + # DNS record for the REST API. + RestApiDnsRecord: + Condition: 'DeployRestApiDnsRecord' + Properties: + AliasTarget: + DNSName: !GetAtt 'RestApiDomainName.RegionalDomainName' + HostedZoneId: !GetAtt 'RestApiDomainName.RegionalHostedZoneId' + HostedZoneId: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameHostedZoneId' + Name: !Sub + - '${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + Type: 'A' + Type: 'AWS::Route53::RecordSet' + # Domain mapping for the REST API endpoint. + RestApiDomainMapping: + Condition: 'DeployRestApiDnsRecord' + Properties: + DomainName: !Ref 'RestApiDomainName' + RestApiId: !Ref 'RestApi' + Stage: !Ref 'RestApiStage' + Type: 'AWS::ApiGateway::BasePathMapping' + # Custom domain name for the REST API endpoint. + RestApiDomainName: + Condition: 'DeployRestApiDnsRecord' + Properties: + DomainName: !Sub + - '${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + EndpointConfiguration: + Types: + - 'REGIONAL' + RegionalCertificateArn: !Ref 'RestApiCertificate' + SecurityPolicy: 'TLS_1_2' + Type: 'AWS::ApiGateway::DomainName' + # API key for the REST API endpoint. + RestApiKey: + Condition: 'DeployRestApi' + Properties: + Enabled: true + StageKeys: + - RestApiId: !Ref 'RestApi' + StageName: !Ref 'RestApiStage' + Type: 'AWS::ApiGateway::ApiKey' + # Rest API endpoint general method. + RestApiMethodGeneral: + Condition: 'DeployRestApi' + Properties: + ApiKeyRequired: true + AuthorizationType: 'NONE' + HttpMethod: 'GET' + Integration: + ConnectionId: !Ref 'NlbVpcLink' + ConnectionType: 'VPC_LINK' + IntegrationHttpMethod: 'GET' + RequestParameters: + integration.request.path.proxy: 'method.request.path.proxy' + Type: 'HTTP_PROXY' + Uri: !Sub + - 'http://${NlbDnsName}:${PostgrestPort}/{proxy}' + - NlbDnsName: !GetAtt 'Nlb.DNSName' + PostgrestPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + RequestParameters: + method.request.path.proxy: true + ResourceId: !Ref 'RestApiProxyResource' + RestApiId: !Ref 'RestApi' + Type: 'AWS::ApiGateway::Method' + # Rest API endpoint root method. + RestApiMethodRoot: + Condition: 'DeployRestApi' + Properties: + ApiKeyRequired: true + AuthorizationType: 'NONE' + HttpMethod: 'GET' + Integration: + ConnectionId: !Ref 'NlbVpcLink' + ConnectionType: 'VPC_LINK' + IntegrationHttpMethod: 'GET' + RequestParameters: + integration.request.path.proxy: 'method.request.path.proxy' + Type: 'HTTP_PROXY' + Uri: !Sub + - 'http://${NlbDnsName}:${PostgrestPort}/' + - NlbDnsName: !GetAtt 'Nlb.DNSName' + PostgrestPort: !FindInMap + - 'Constants' + - 'Networking' + - 'PostgrestPort' + RequestParameters: + method.request.path.proxy: true + ResourceId: !GetAtt 'RestApi.RootResourceId' + RestApiId: !Ref 'RestApi' + Type: 'AWS::ApiGateway::Method' + # Proxy resource for the REST API endpoint. + RestApiProxyResource: + Condition: 'DeployRestApi' + Properties: + ParentId: !GetAtt 'RestApi.RootResourceId' + PathPart: '{proxy+}' + RestApiId: !Ref 'RestApi' + Type: 'AWS::ApiGateway::Resource' + # Stage for the REST API endpoint. + RestApiStage: + Condition: 'DeployRestApi' + Properties: + CacheClusterEnabled: 'true' + CacheClusterSize: '0.5' + DeploymentId: !Ref 'RestApiDeployment' + MethodSettings: + # Cache general data for one second, with permissive throttling. + - CacheDataEncrypted: false + CacheTtlInSeconds: 1 + CachingEnabled: true + HttpMethod: '*' + ResourcePath: '/*' + ThrottlingBurstLimit: 1000 + ThrottlingRateLimit: 500 + # Use a long cache time and restrictive throttling on the schema at the + # root, which is large and not required for normal operations. + - CacheDataEncrypted: false + CacheTtlInSeconds: 3600 + CachingEnabled: true + HttpMethod: 'GET' + ResourcePath: '/' + ThrottlingBurstLimit: 100 + ThrottlingRateLimit: 50 + RestApiId: !Ref 'RestApi' + StageName: !Ref 'AWS::StackName' + Type: 'AWS::ApiGateway::Stage' + # REST API usage plan. + RestApiUsagePlan: + Condition: 'DeployRestApi' + Properties: + ApiStages: + - ApiId: !Ref 'RestApi' + Stage: !Ref 'RestApiStage' + Type: 'AWS::ApiGateway::UsagePlan' + # Association of API key with usage plan. + RestApiUsagePlanKeyAssociation: + Condition: 'DeployRestApi' + Properties: + KeyId: !Ref 'RestApiKey' + KeyType: 'API_KEY' + UsagePlanId: !Ref 'RestApiUsagePlan' + Type: 'AWS::ApiGateway::UsagePlanKey' + # Virtual private cloud for internal networking. Vpc: Condition: 'DeployVpc' Properties: - CidrBlock: '0.0.0.0/16' + CidrBlock: !FindInMap + - 'Constants' + - 'Networking' + - 'VpcCidrBlock' + EnableDnsHostnames: true + EnableDnsSupport: true Type: 'AWS::EC2::VPC' + # Web application firewall. + Waf: + Condition: 'DeployWaf' + Properties: + # Allow all all traffic by default unless blocked per below rules. + DefaultAction: + # CloudFormation prohibits both null and empty values. + Allow: {} # yamllint disable-line rule:braces + Name: !Ref 'AWS::StackName' + Rules: + # Use AWS common rule set. + - Name: !Sub '${AWS::StackName}-AWSManagedRulesCommonRuleSet' + OverrideAction: !If + - 'EnableWafRulesGeneral' + - None: {} # yamllint disable-line rule:braces + - Count: {} # yamllint disable-line rule:braces + Priority: 1 + Statement: + ManagedRuleGroupStatement: + Name: 'AWSManagedRulesCommonRuleSet' + VendorName: 'AWS' + VisibilityConfig: + CloudWatchMetricsEnabled: true + MetricName: !Sub '${AWS::StackName}-AWSManagedRulesCommonRuleSet' + SampledRequestsEnabled: true + # Block VPNs and otherwise obfuscated IP addresses. + - Name: !Sub '${AWS::StackName}-AWSManagedRulesAnonymousIpList' + OverrideAction: !If + - 'EnableWafRulesGeneral' + - None: {} # yamllint disable-line rule:braces + - Count: {} # yamllint disable-line rule:braces + Priority: 2 + Statement: + ManagedRuleGroupStatement: + Name: 'AWSManagedRulesAnonymousIpList' + VendorName: 'AWS' + VisibilityConfig: + CloudWatchMetricsEnabled: true + MetricName: !Sub '${AWS::StackName}-AWSManagedRulesAnonymousIpList' + SampledRequestsEnabled: true + # Block known IP threats. + - Name: !Sub '${AWS::StackName}-AWSManagedRulesAmazonIpReputationList' + OverrideAction: !If + - 'EnableWafRulesGeneral' + - None: {} # yamllint disable-line rule:braces + - Count: {} # yamllint disable-line rule:braces + Priority: 3 + Statement: + ManagedRuleGroupStatement: + Name: 'AWSManagedRulesAmazonIpReputationList' + VendorName: 'AWS' + VisibilityConfig: + CloudWatchMetricsEnabled: true + MetricName: !Sub + '${AWS::StackName}-AWSManagedRulesAmazonIpReputationList' + SampledRequestsEnabled: true + # Rate limit by IP for the REST API. + - Action: !If + - 'EnableWafRulesRestApi' + - Block: {} # yamllint disable-line rule:braces + - Count: {} # yamllint disable-line rule:braces + Name: !Sub '${AWS::StackName}-RestApiRateLimit' + Priority: 4 + Statement: + RateBasedStatement: + AggregateKeyType: 'IP' + Limit: 200 + ScopeDownStatement: + ByteMatchStatement: + FieldToMatch: + SingleHeader: + Name: 'host' + PositionalConstraint: 'CONTAINS' + SearchString: !Sub + - '${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + TextTransformations: + - Priority: 0 + Type: 'LOWERCASE' + VisibilityConfig: + CloudWatchMetricsEnabled: true + MetricName: !Sub '${AWS::StackName}-RestApiRateLimit' + SampledRequestsEnabled: true + # Limit WebSocket messages by IP via rate limit. + - Action: !If + - 'EnableWafRulesWebSocket' + - Block: {} # yamllint disable-line rule:braces + - Count: {} # yamllint disable-line rule:braces + Name: !Sub '${AWS::StackName}-WebSocketMessageRateLimit' + Priority: 5 + Statement: + RateBasedStatement: + AggregateKeyType: 'IP' + Limit: 100 + ScopeDownStatement: + ByteMatchStatement: + FieldToMatch: + SingleHeader: + Name: 'host' + PositionalConstraint: 'CONTAINS' + SearchString: !Sub + - 'ws.${Environment}.${RootDomain}' + - RootDomain: !FindInMap + - 'Constants' + - 'Networking' + - 'DnsNameRootDomain' + TextTransformations: + - Priority: 0 + Type: 'LOWERCASE' + VisibilityConfig: + CloudWatchMetricsEnabled: true + MetricName: !Sub '${AWS::StackName}-WebSocketMessageRateLimit' + SampledRequestsEnabled: true + Scope: 'REGIONAL' + VisibilityConfig: + CloudWatchMetricsEnabled: true + MetricName: !Ref 'AWS::StackName' + SampledRequestsEnabled: true + Type: 'AWS::WAFv2::WebACL' + # Web application firewall for the broker application load balancer. + WafAlb: + Condition: 'DeployWaf' + Properties: + ResourceArn: !Ref 'Alb' + WebACLArn: !GetAtt 'Waf.Arn' + Type: 'AWS::WAFv2::WebACLAssociation' + # Web application firewall for the REST API. + WafRestApi: + Condition: 'DeployWaf' + Properties: + ResourceArn: !Sub + - 'arn:aws:apigateway:${Region}::/restapis/${ApiId}/stages/${StageName}' + - ApiId: !Ref 'RestApi' + Region: !Ref 'AWS::Region' + StageName: !Ref 'RestApiStage' + WebACLArn: !GetAtt 'Waf.Arn' + Type: 'AWS::WAFv2::WebACLAssociation' Rules: - DeployGateway: + DeployAlb: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployAlb' + - 'false' + DeployAlbDnsRecord: Assertions: - Assert: !Or - !Equals - - !Ref 'DeployAnything' + - !Ref 'DeployAlb' - 'true' - !Equals - - !Ref 'DeployGateway' + - !Ref 'DeployAlbDnsRecord' + - 'false' + DeployBastionHost: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployDb' + - 'true' + - !Equals + - !Ref 'DeployBastionHost' + - 'false' + DeployBroker: + Assertions: + - Assert: !Or + - !And + - !Equals + - !Ref 'DeployContainers' + - 'true' + - !Equals + - !Ref 'DeployProcessor' + - 'true' + - !Equals + - !Ref 'DeployBroker' + - 'false' + DeployContainers: + Assertions: + - Assert: !Or + - !And + - !Equals + - !Ref 'DeployDb' + - 'true' + - !Equals + - !Ref 'DeployRouteTables' + - 'true' + - !Equals + - !Ref 'DeployContainers' + - 'false' + DeployDb: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployDb' + - 'false' + DeployNlb: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployNlb' + - 'false' + DeployNlbVpcLink: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployNlb' + - 'true' + - !Equals + - !Ref 'DeployNlbVpcLink' + - 'false' + DeployPostgrest: + Assertions: + - Assert: !Or + - !And + - !Equals + - !Ref 'DeployContainers' + - 'true' + - !Equals + - !Ref 'DeployProcessor' + - 'true' + - !Equals + - !Ref 'DeployPostgrest' + - 'false' + DeployProcessor: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployContainers' + - 'true' + - !Equals + - !Ref 'DeployProcessor' + - 'false' + DeployRestApi: + Assertions: + - Assert: !Or + - !And + - !Equals + - !Ref 'DeployNlbVpcLink' + - 'true' + - !Equals + - !Ref 'DeployPostgrest' + - 'true' + - !Equals + - !Ref 'DeployRestApi' + - 'false' + DeployRestApiDnsRecord: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployRestApi' + - 'true' + - !Equals + - !Ref 'DeployRestApiDnsRecord' + - 'false' + DeployRouteTables: + Assertions: + - Assert: !Or + - !Equals + - !Ref 'DeployVpc' + - 'true' + - !Equals + - !Ref 'DeployRouteTables' - 'false' DeployVpc: Assertions: - Assert: !Or - !Equals - - !Ref 'DeployGateway' + - 'DeployStack' - 'true' - !Equals - !Ref 'DeployVpc' - 'false' + DeployWaf: + Assertions: + - Assert: !Or + - !And + - !Equals + - !Ref 'DeployAlb' + - 'true' + - !Equals + - !Ref 'DeployRestApi' + - 'true' + - !Equals + - !Ref 'DeployWaf' + - 'false' +Transform: 'AWS::LanguageExtensions' ... From 01a7ae088d8435c2af1432944c3c32d70cd32056 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 23 Sep 2024 18:03:20 -0700 Subject: [PATCH 11/62] Add missing `!Ref` --- src/cloud-formation/indexer.cfn.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index c8ad143ea..85d3c7fbf 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -2105,7 +2105,7 @@ Rules: Assertions: - Assert: !Or - !Equals - - 'DeployStack' + - !Ref 'DeployStack' - 'true' - !Equals - !Ref 'DeployVpc' From 06f5564479a3f467e5cb3a2b21ec38c1b6f0f765 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:26:00 -0700 Subject: [PATCH 12/62] Bump cfn-lint, rules logic --- cfg/pre-commit-config.yaml | 2 +- src/cloud-formation/indexer.cfn.yaml | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/cfg/pre-commit-config.yaml b/cfg/pre-commit-config.yaml index 78c44062f..e1322c277 100644 --- a/cfg/pre-commit-config.yaml +++ b/cfg/pre-commit-config.yaml @@ -231,7 +231,7 @@ repos: files: '.*\.cfn\.yaml' id: 'cfn-lint' repo: 'https://github.com/aws-cloudformation/cfn-lint' - rev: 'v1.15.0' + rev: 'v1.15.1' - hooks: - diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 85d3c7fbf..117c72db6 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -108,12 +108,6 @@ Mappings: C: AvailabilityZone: 2 CidrBlock: '10.0.6.0/24' -Metadata: - cfn-lint: - config: - ignore_checks: - - 'E3005' - - 'W1001' Outputs: BastionHostId: Condition: 'DeployBastionHost' From 86eec30662caa6da137ee53425ca4f252dbe9653 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:38:39 -0700 Subject: [PATCH 13/62] Kill stack --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index d2a7faf79..c503995d7 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -14,8 +14,8 @@ parameters: DeployRestApi: 'false' DeployRestApiDnsRecord: 'false' DeployRouteTables: 'false' - DeployStack: 'true' - DeployVpc: 'true' + DeployStack: 'false' + DeployVpc: 'false' DeployWaf: 'false' EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' From f2d30b3e73c468038e30a93968ab3d90c0f3317c Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:34:59 -0700 Subject: [PATCH 14/62] Try primary/fallback NAT gateway --- src/cloud-formation/indexer.cfn.yaml | 53 +++++++++++++++++++++------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 117c72db6..b3feea1ff 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -592,7 +592,8 @@ Resources: - 'PrivateRouteTableAssociationA' - 'PrivateRouteTableAssociationB' - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGateway' + - 'PrivateRouteThroughNatGatewayFallback' + - 'PrivateRouteThroughNatGatewayPrimary' # Proxy for a conditional dependency on the application load balancer # listener, which associates the broker target group with the application # load balancer. @@ -1046,19 +1047,33 @@ Resources: InternetGatewayId: !Ref 'InternetGateway' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::VPCGatewayAttachment' - # Network address translation gateway for the virtual private cloud. - NatGateway: + # Elastic IP address for the fallback network address translation gateway. + NatGatewayEipFallback: Condition: 'DeployVpc' Properties: - AllocationId: !GetAtt 'NatGatewayEip.AllocationId' - SubnetId: !Ref 'PublicSubnetA' - Type: 'AWS::EC2::NatGateway' - # Elastic IP address for the network address translation gateway. - NatGatewayEip: + Domain: 'vpc' + Type: 'AWS::EC2::EIP' + # Elastic IP address for the primary network address translation gateway. + NatGatewayEipPrimary: Condition: 'DeployVpc' Properties: Domain: 'vpc' Type: 'AWS::EC2::EIP' + # Fallback network address translation gateway for the virtual private cloud. + NatGatewayFallback: + Condition: 'DeployVpc' + DependsOn: 'NatGatewayPrimary' + Properties: + AllocationId: !GetAtt 'NatGatewayEipFallback.AllocationId' + SubnetId: !Ref 'PublicSubnetB' + Type: 'AWS::EC2::NatGateway' + # Primary network address translation gateway for the virtual private cloud. + NatGatewayPrimary: + Condition: 'DeployVpc' + Properties: + AllocationId: !GetAtt 'NatGatewayEipPrimary.AllocationId' + SubnetId: !Ref 'PublicSubnetA' + Type: 'AWS::EC2::NatGateway' # Private network load balancer. Nlb: Condition: 'DeployNlb' @@ -1222,7 +1237,8 @@ Resources: - 'PrivateRouteTableAssociationA' - 'PrivateRouteTableAssociationB' - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGateway' + - 'PrivateRouteThroughNatGatewayFallback' + - 'PrivateRouteThroughNatGatewayPrimary' # Proxy for a conditional dependency on the network load balancer listener # for PostgREST, which associates the PostgREST target group with the # network load balancer. @@ -1392,12 +1408,22 @@ Resources: Properties: VpcId: !Ref 'Vpc' Type: 'AWS::EC2::RouteTable' - # Route from private subnets through network address translation gateway. - PrivateRouteThroughNatGateway: + # Private subnet route through fallback network address translation gateway. + PrivateRouteThroughNatGatewayFallback: + Condition: 'DeployRouteTables' + # Create after creating the primary route. + DependsOn: 'PrivateRouteThroughNatGatewayPrimary' + Properties: + DestinationCidrBlock: '0.0.0.0/0' + NatGatewayId: !Ref 'NatGatewayFallback' + RouteTableId: !Ref 'PrivateRouteTable' + Type: 'AWS::EC2::Route' + # Private subnet route through primary network address translation gateway. + PrivateRouteThroughNatGatewayPrimary: Condition: 'DeployRouteTables' Properties: DestinationCidrBlock: '0.0.0.0/0' - NatGatewayId: !Ref 'NatGateway' + NatGatewayId: !Ref 'NatGatewayPrimary' RouteTableId: !Ref 'PrivateRouteTable' Type: 'AWS::EC2::Route' # Private DNS namespace for internal service discovery. @@ -1436,7 +1462,8 @@ Resources: - 'PrivateRouteTableAssociationA' - 'PrivateRouteTableAssociationB' - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGateway' + - 'PrivateRouteThroughNatGatewayFallback' + - 'PrivateRouteThroughNatGatewayPrimary' Properties: Cluster: !Ref 'ContainerCluster' DeploymentConfiguration: From abcecf4a8bd0309dae43e75dc68f56b24b2ba213 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:06:52 -0700 Subject: [PATCH 15/62] Start making NAT gateway for each AZ --- src/cloud-formation/deploy-dev.yaml | 4 ++-- src/cloud-formation/indexer.cfn.yaml | 34 ++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index c503995d7..d2a7faf79 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -14,8 +14,8 @@ parameters: DeployRestApi: 'false' DeployRestApiDnsRecord: 'false' DeployRouteTables: 'false' - DeployStack: 'false' - DeployVpc: 'false' + DeployStack: 'true' + DeployVpc: 'true' DeployWaf: 'false' EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index b3feea1ff..4ca8d9a7f 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -956,6 +956,34 @@ Resources: IpProtocol: -1 VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' + # Network address translation gateway for each public subnet. + Fn::ForEach::NatGateway: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - NatGateway${Identifier}: + Properties: + AllocationId: !GetAtt + - !Sub 'NatGatewayEip${Identifier}' + - 'AllocationId' + SubnetId: !Ref + Fn::Sub: 'PublicSubnet${Identifier}' + # ForEach transforms require that condition is second key or later. + Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering + Type: 'AWS::EC2::NatGateway' + # Elastic IP address for each network address translation gateway. + Fn::ForEach::NatGatewayEip: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - NatGatewayEip${Identifier}: + Properties: + Domain: 'vpc' + # ForEach transforms require that condition is second key or later. + Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering + Type: 'AWS::EC2::EIP' # Association for private route table with each private subnet. Fn::ForEach::PrivateRouteTableAssociation: - 'Identifier' @@ -970,8 +998,7 @@ Resources: - 'PrivateSubnet${Identifier}' - Identifier: !Ref 'Identifier' # ForEach transforms require that condition is second key or later. - # yamllint disable-line rule:key-ordering - Condition: 'DeployRouteTables' + Condition: 'DeployRouteTables' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::SubnetRouteTableAssociation' # A private subnet for each of the database availability zones. Fn::ForEach::PrivateSubnet: @@ -1010,8 +1037,7 @@ Resources: - 'PublicSubnet${Identifier}' - Identifier: !Ref 'Identifier' # ForEach transforms require that condition is second key or later. - # yamllint disable-line rule:key-ordering - Condition: 'DeployRouteTables' + Condition: 'DeployRouteTables' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::SubnetRouteTableAssociation' # A public subnet for each availability zone. Fn::ForEach::PublicSubnet: From 76270ccbda11ca12773091be8d2d934298217561 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:12:08 -0700 Subject: [PATCH 16/62] Simplify use of Fn::Sub --- src/cloud-formation/indexer.cfn.yaml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 4ca8d9a7f..37f631435 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -994,9 +994,7 @@ Resources: Properties: RouteTableId: !Ref 'PrivateRouteTable' SubnetId: !Ref - Fn::Sub: - - 'PrivateSubnet${Identifier}' - - Identifier: !Ref 'Identifier' + Fn::Sub: 'PrivateSubnet${Identifier}' # ForEach transforms require that condition is second key or later. Condition: 'DeployRouteTables' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::SubnetRouteTableAssociation' @@ -1033,9 +1031,7 @@ Resources: Properties: RouteTableId: !Ref 'PublicRouteTable' SubnetId: !Ref - Fn::Sub: - - 'PublicSubnet${Identifier}' - - Identifier: !Ref 'Identifier' + Fn::Sub: 'PublicSubnet${Identifier}' # ForEach transforms require that condition is second key or later. Condition: 'DeployRouteTables' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::SubnetRouteTableAssociation' @@ -1557,16 +1553,13 @@ Resources: - 'MasterUsername' - Name: 'GRPC_DATA_SERVICE_URL' Value: !Sub - - '{{resolve:ssm:/emojicoin/grpc-data-service-url/${Network}}}' - - Network: !Ref 'Network' + '{{resolve:ssm:/emojicoin/grpc-data-service-url/${Network}}}' - Name: 'MINIMUM_STARTING_VERSION' Value: !Sub - - '{{resolve:ssm:/emojicoin/minimum-starting-version/${Network}}}' - - Network: !Ref 'Network' + '{{resolve:ssm:/emojicoin/minimum-starting-version/${Network}}}' - Name: 'EMOJICOIN_MODULE_ADDRESS' Value: !Sub - - '{{resolve:ssm:/emojicoin/package-address/${Network}}}' - - Network: !Ref 'Network' + '{{resolve:ssm:/emojicoin/package-address/${Network}}}' - Name: 'WS_PORT' Value: !FindInMap - 'Constants' From 6de2e05041a560d069e1ed1c0b5c1bf5a18085fc Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:22:34 -0700 Subject: [PATCH 17/62] Add redudant NAT gateways, kill stack --- src/cloud-formation/deploy-dev.yaml | 4 +- src/cloud-formation/indexer.cfn.yaml | 99 +++++++++++----------------- 2 files changed, 42 insertions(+), 61 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index d2a7faf79..c503995d7 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -14,8 +14,8 @@ parameters: DeployRestApi: 'false' DeployRestApiDnsRecord: 'false' DeployRouteTables: 'false' - DeployStack: 'true' - DeployVpc: 'true' + DeployStack: 'false' + DeployVpc: 'false' DeployWaf: 'false' EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 37f631435..fa47c5a13 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -592,8 +592,9 @@ Resources: - 'PrivateRouteTableAssociationA' - 'PrivateRouteTableAssociationB' - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGatewayFallback' - - 'PrivateRouteThroughNatGatewayPrimary' + - 'PrivateRouteThroughNatGatewayA' + - 'PrivateRouteThroughNatGatewayB' + - 'PrivateRouteThroughNatGatewayC' # Proxy for a conditional dependency on the application load balancer # listener, which associates the broker target group with the application # load balancer. @@ -984,7 +985,19 @@ Resources: # ForEach transforms require that condition is second key or later. Condition: 'DeployVpc' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::EIP' - # Association for private route table with each private subnet. + # Route table for each private subnet. + Fn::ForEach::PrivateRouteTable: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - PrivateRouteTable${Identifier}: + Properties: + VpcId: !Ref 'Vpc' + # ForEach transforms require that condition is second key or later. + Condition: 'DeployRouteTables' # yamllint disable-line rule:key-ordering + Type: 'AWS::EC2::RouteTable' + # Association for each private route table within each private subnet. Fn::ForEach::PrivateRouteTableAssociation: - 'Identifier' - - 'A' @@ -992,12 +1005,29 @@ Resources: - 'C' - PrivateRouteTableAssociation${Identifier}: Properties: - RouteTableId: !Ref 'PrivateRouteTable' + RouteTableId: !Ref + Fn::Sub: 'PrivateRouteTable${Identifier}' SubnetId: !Ref Fn::Sub: 'PrivateSubnet${Identifier}' # ForEach transforms require that condition is second key or later. Condition: 'DeployRouteTables' # yamllint disable-line rule:key-ordering Type: 'AWS::EC2::SubnetRouteTableAssociation' + # Route through network address translation gateway for each private subnet. + Fn::ForEach::PrivateRouteThroughNatGateway: + - 'Identifier' + - - 'A' + - 'B' + - 'C' + - PrivateRouteThroughNatGateway${Identifier}: + Properties: + DestinationCidrBlock: '0.0.0.0/0' + NatGatewayId: !Ref + Fn::Sub: 'NatGateway${Identifier}' + RouteTableId: !Ref + Fn::Sub: 'PrivateRouteTable${Identifier}' + # ForEach transforms require that condition is second key or later. + Condition: 'DeployRouteTables' # yamllint disable-line rule:key-ordering + Type: 'AWS::EC2::Route' # A private subnet for each of the database availability zones. Fn::ForEach::PrivateSubnet: - 'Identifier' @@ -1069,33 +1099,6 @@ Resources: InternetGatewayId: !Ref 'InternetGateway' VpcId: !Ref 'Vpc' Type: 'AWS::EC2::VPCGatewayAttachment' - # Elastic IP address for the fallback network address translation gateway. - NatGatewayEipFallback: - Condition: 'DeployVpc' - Properties: - Domain: 'vpc' - Type: 'AWS::EC2::EIP' - # Elastic IP address for the primary network address translation gateway. - NatGatewayEipPrimary: - Condition: 'DeployVpc' - Properties: - Domain: 'vpc' - Type: 'AWS::EC2::EIP' - # Fallback network address translation gateway for the virtual private cloud. - NatGatewayFallback: - Condition: 'DeployVpc' - DependsOn: 'NatGatewayPrimary' - Properties: - AllocationId: !GetAtt 'NatGatewayEipFallback.AllocationId' - SubnetId: !Ref 'PublicSubnetB' - Type: 'AWS::EC2::NatGateway' - # Primary network address translation gateway for the virtual private cloud. - NatGatewayPrimary: - Condition: 'DeployVpc' - Properties: - AllocationId: !GetAtt 'NatGatewayEipPrimary.AllocationId' - SubnetId: !Ref 'PublicSubnetA' - Type: 'AWS::EC2::NatGateway' # Private network load balancer. Nlb: Condition: 'DeployNlb' @@ -1259,8 +1262,9 @@ Resources: - 'PrivateRouteTableAssociationA' - 'PrivateRouteTableAssociationB' - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGatewayFallback' - - 'PrivateRouteThroughNatGatewayPrimary' + - 'PrivateRouteThroughNatGatewayA' + - 'PrivateRouteThroughNatGatewayB' + - 'PrivateRouteThroughNatGatewayC' # Proxy for a conditional dependency on the network load balancer listener # for PostgREST, which associates the PostgREST target group with the # network load balancer. @@ -1424,30 +1428,6 @@ Resources: RequiresCompatibilities: - 'FARGATE' Type: 'AWS::ECS::TaskDefinition' - # Route table for private subnets in the virtual private cloud. - PrivateRouteTable: - Condition: 'DeployRouteTables' - Properties: - VpcId: !Ref 'Vpc' - Type: 'AWS::EC2::RouteTable' - # Private subnet route through fallback network address translation gateway. - PrivateRouteThroughNatGatewayFallback: - Condition: 'DeployRouteTables' - # Create after creating the primary route. - DependsOn: 'PrivateRouteThroughNatGatewayPrimary' - Properties: - DestinationCidrBlock: '0.0.0.0/0' - NatGatewayId: !Ref 'NatGatewayFallback' - RouteTableId: !Ref 'PrivateRouteTable' - Type: 'AWS::EC2::Route' - # Private subnet route through primary network address translation gateway. - PrivateRouteThroughNatGatewayPrimary: - Condition: 'DeployRouteTables' - Properties: - DestinationCidrBlock: '0.0.0.0/0' - NatGatewayId: !Ref 'NatGatewayPrimary' - RouteTableId: !Ref 'PrivateRouteTable' - Type: 'AWS::EC2::Route' # Private DNS namespace for internal service discovery. PrivateServiceDiscoveryNamespace: Condition: 'DeployVpc' @@ -1484,8 +1464,9 @@ Resources: - 'PrivateRouteTableAssociationA' - 'PrivateRouteTableAssociationB' - 'PrivateRouteTableAssociationC' - - 'PrivateRouteThroughNatGatewayFallback' - - 'PrivateRouteThroughNatGatewayPrimary' + - 'PrivateRouteThroughNatGatewayA' + - 'PrivateRouteThroughNatGatewayB' + - 'PrivateRouteThroughNatGatewayC' Properties: Cluster: !Ref 'ContainerCluster' DeploymentConfiguration: From 2d7fbcc6f5c809ac17ada7fe5773c36e8ac46dda Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:26:46 -0700 Subject: [PATCH 18/62] Add multi-NAT gateway design note --- src/cloud-formation/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cloud-formation/README.md b/src/cloud-formation/README.md index 30c2d15c3..7b2388921 100644 --- a/src/cloud-formation/README.md +++ b/src/cloud-formation/README.md @@ -373,6 +373,10 @@ The indexer database uses [Aurora PostgreSQL] on a [high availability][high availability for aurora] with [fault tolerant replica promotion] and [autoscaling][aurora autoscaling]. +### NAT gateway redundancy + +The indexer uses [a NAT gateway in each availability zone] for high resilience. + ### Permissions The `ContainerRole` [ECS task execution IAM role] provides @@ -399,6 +403,7 @@ toggle [rule actions] between `Block` and `Count`. See the [Web ACL traffic overview dashboards] to monitor rules. +[a nat gateway in each availability zone]: https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-basics.html [amazonec2containerserviceautoscalerole]: https://docs.aws.amazon.com/autoscaling/application/userguide/security-iam-awsmanpol.html#ecs-policy [application autoscaling iam access]: https://docs.aws.amazon.com/autoscaling/application/userguide/security_iam_service-with-iam.html [aptos labs grpc endpoint]: https://aptos.dev/en/build/indexer/txn-stream/aptos-hosted-txn-stream#endpoints From daeec1f58b00536ad307b7a0c30575e649c7cbd9 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:27:46 -0700 Subject: [PATCH 19/62] Provision entire stack --- src/cloud-formation/deploy-dev.yaml | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index c503995d7..237558b0e 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,25 +1,25 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'false' - DeployAlbDnsRecord: 'false' - DeployBastionHost: 'false' - DeployBroker: 'false' - DeployContainers: 'false' - DeployDb: 'false' - DeployNlb: 'false' - DeployNlbVpcLink: 'false' - DeployPostgrest: 'false' - DeployProcessor: 'false' - DeployRestApi: 'false' - DeployRestApiDnsRecord: 'false' - DeployRouteTables: 'false' - DeployStack: 'false' - DeployVpc: 'false' - DeployWaf: 'false' - EnableWafRulesGeneral: 'false' - EnableWafRulesRestApi: 'false' - EnableWafRulesWebSocket: 'false' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' + DeployBastionHost: 'true' + DeployBroker: 'true' + DeployContainers: 'true' + DeployDb: 'true' + DeployNlb: 'true' + DeployNlbVpcLink: 'true' + DeployPostgrest: 'true' + DeployProcessor: 'true' + DeployRestApi: 'true' + DeployRestApiDnsRecord: 'true' + DeployRouteTables: 'true' + DeployStack: 'true' + DeployVpc: 'true' + DeployWaf: 'true' + EnableWafRulesGeneral: 'true' + EnableWafRulesRestApi: 'true' + EnableWafRulesWebSocket: 'true' Environment: 'ECO-2188' Network: 'testnet' ProcessorImageVersion: '0.5.0' From 4c22c743f92d2facbc793a8bea47bc4435d3bf78 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:50:17 -0700 Subject: [PATCH 20/62] Kill WAF rules for REST, WS --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 237558b0e..15092555f 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -18,8 +18,8 @@ parameters: DeployVpc: 'true' DeployWaf: 'true' EnableWafRulesGeneral: 'true' - EnableWafRulesRestApi: 'true' - EnableWafRulesWebSocket: 'true' + EnableWafRulesRestApi: 'false' + EnableWafRulesWebSocket: 'false' Environment: 'ECO-2188' Network: 'testnet' ProcessorImageVersion: '0.5.0' From eec18d7adf82f63fcda1bb3d0d28ac3e7a60f788 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:53:38 -0700 Subject: [PATCH 21/62] Kill WAF --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 15092555f..b7a939204 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -16,8 +16,8 @@ parameters: DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' - DeployWaf: 'true' - EnableWafRulesGeneral: 'true' + DeployWaf: 'false' + EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' Environment: 'ECO-2188' From 9de9ea14b4436aecb4144806a809e6baaecb3e62 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:56:43 -0700 Subject: [PATCH 22/62] Provision WAF --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index b7a939204..15092555f 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -16,8 +16,8 @@ parameters: DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' - DeployWaf: 'false' - EnableWafRulesGeneral: 'false' + DeployWaf: 'true' + EnableWafRulesGeneral: 'true' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' Environment: 'ECO-2188' From 20853e958fbcb0a3f9dfc7babe517a2c9953213f Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:28:09 -0700 Subject: [PATCH 23/62] Add alarm overload --- src/cloud-formation/indexer.cfn.yaml | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index fa47c5a13..60c18fc71 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -957,6 +957,35 @@ Resources: IpProtocol: -1 VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' + # Overload for default low CPU usage alarm on autoscaled containers, + # designed to effectively disable the alarm since containers have low CPU + # utilization at idle even on the smallest instance possible. + Fn::ForEach::LowCpuAlarmOverload: + - 'Identifier' + - - 'Broker' + - 'Postgrest' + - LowCpuAlarmOverload${Identifier}: + Properties: + ActionsEnabled: 'false' + AlarmName: !Sub '${AWS::StackName}-${Identifier}-low-cpu-overload' + ComparisonOperator: 'LessThanThreshold' + Dimensions: + - Name: 'ClusterName' + Value: !Ref 'ContainerCluster' + - Name: 'ServiceName' + Value: !GetAtt + - !Sub '${Identifier}Runner' + - 'Name' + EvaluationPeriods: 3 + MetricName: 'CPUUtilization' + Namespace: 'AWS/ECS/ManagedScaling' + Period: 10 + Statistic: 'Maximum' + Threshold: 1 + # ForEach transforms require that condition is second key or later. + # yamllint disable-line rule:key-ordering + Condition: !Sub 'Deploy${Identifier}' + Type: 'AWS::CloudWatch::Alarm' # Network address translation gateway for each public subnet. Fn::ForEach::NatGateway: - 'Identifier' From c2971eb88194097cf69fa2f7d8ba208d5354489b Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:40:23 -0700 Subject: [PATCH 24/62] Match original alarm exactly --- src/cloud-formation/indexer.cfn.yaml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 60c18fc71..cfbb32a36 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -957,9 +957,10 @@ Resources: IpProtocol: -1 VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' - # Overload for default low CPU usage alarm on autoscaled containers, - # designed to effectively disable the alarm since containers have low CPU - # utilization at idle even on the smallest instance possible. + # Overload for default low CPU usage alarm on autoscaled containers, which + # replicates the default alarm values but disables actions. Designed to + # effectively disable the alarm since containers have low CPU utilization at + # idle even on the smallest instance possible. Fn::ForEach::LowCpuAlarmOverload: - 'Identifier' - - 'Broker' @@ -976,12 +977,13 @@ Resources: Value: !GetAtt - !Sub '${Identifier}Runner' - 'Name' - EvaluationPeriods: 3 + EvaluationPeriods: 15 MetricName: 'CPUUtilization' - Namespace: 'AWS/ECS/ManagedScaling' - Period: 10 - Statistic: 'Maximum' - Threshold: 1 + Namespace: 'AWS/ECS' + Period: 60 + Statistic: 'Average' + Threshold: 63.0 + Unit: 'Percent' # ForEach transforms require that condition is second key or later. # yamllint disable-line rule:key-ordering Condition: !Sub 'Deploy${Identifier}' From f8b8ef04cd3fce0017198e7afe2d5144b018872b Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:49:22 -0700 Subject: [PATCH 25/62] Use cusom metric spec --- src/cloud-formation/indexer.cfn.yaml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index cfbb32a36..0882cd73a 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -545,8 +545,14 @@ Resources: PolicyType: 'TargetTrackingScaling' ScalingTargetId: !Ref 'BrokerAutoScalingTarget' TargetTrackingScalingPolicyConfiguration: - PredefinedMetricSpecification: - PredefinedMetricType: 'ECSServiceAverageCPUUtilization' + CustomizedMetricSpecification: + Dimensions: + - Name: 'ClusterName' + Value: !Ref 'ContainerCluster' + - Name: 'ServiceName' + Value: !Ref 'BrokerRunner' + MetricName: 'CPUUtilization' + Namespace: 'AWS/ECS' ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 @@ -1204,8 +1210,14 @@ Resources: PolicyType: 'TargetTrackingScaling' ScalingTargetId: !Ref 'PostgrestAutoScalingTarget' TargetTrackingScalingPolicyConfiguration: - PredefinedMetricSpecification: - PredefinedMetricType: 'ECSServiceAverageCPUUtilization' + CustomizedMetricSpecification: + Dimensions: + - Name: 'ClusterName' + Value: !Ref 'ContainerCluster' + - Name: 'ServiceName' + Value: !Ref 'PostgrestRunner' + MetricName: 'CPUUtilization' + Namespace: 'AWS/ECS' ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 From 6c92c90bc4c3044d04c630f31c334a065be26502 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:50:43 -0700 Subject: [PATCH 26/62] Specify statistic --- src/cloud-formation/indexer.cfn.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 0882cd73a..84d22ad66 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -553,6 +553,7 @@ Resources: Value: !Ref 'BrokerRunner' MetricName: 'CPUUtilization' Namespace: 'AWS/ECS' + Statistic: 'Average' ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 @@ -1218,6 +1219,7 @@ Resources: Value: !Ref 'PostgrestRunner' MetricName: 'CPUUtilization' Namespace: 'AWS/ECS' + Statistic: 'Average' ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 From b8ead83cb887b79d8a430c947b117d7698d2d678 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:51:44 -0700 Subject: [PATCH 27/62] Pin unit --- src/cloud-formation/indexer.cfn.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 84d22ad66..c6e5bc16b 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -554,6 +554,7 @@ Resources: MetricName: 'CPUUtilization' Namespace: 'AWS/ECS' Statistic: 'Average' + Unit: 'Percent' ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 @@ -1220,6 +1221,7 @@ Resources: MetricName: 'CPUUtilization' Namespace: 'AWS/ECS' Statistic: 'Average' + Unit: 'Percent' ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 From 8484e7139af5995d31eccd1d975c10465ba8e73a Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:07:28 -0700 Subject: [PATCH 28/62] Abstract autoscaling policy, targets --- src/cloud-formation/indexer.cfn.yaml | 127 +++++++++++---------------- 1 file changed, 53 insertions(+), 74 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index c6e5bc16b..ddcc3749f 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -537,43 +537,6 @@ Resources: UnhealthyThresholdCount: 2 VpcId: !Ref 'Vpc' Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' - # Scaling policy for broker service. - BrokerAutoScalingPolicy: - Condition: 'DeployBroker' - Properties: - PolicyName: !Sub '${AWS::StackName}-broker' - PolicyType: 'TargetTrackingScaling' - ScalingTargetId: !Ref 'BrokerAutoScalingTarget' - TargetTrackingScalingPolicyConfiguration: - CustomizedMetricSpecification: - Dimensions: - - Name: 'ClusterName' - Value: !Ref 'ContainerCluster' - - Name: 'ServiceName' - Value: !Ref 'BrokerRunner' - MetricName: 'CPUUtilization' - Namespace: 'AWS/ECS' - Statistic: 'Average' - Unit: 'Percent' - ScaleInCooldown: 300 - ScaleOutCooldown: 60 - TargetValue: 70 - Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' - # Scalable target for broker service. - BrokerAutoScalingTarget: - Condition: 'DeployBroker' - Properties: - MaxCapacity: 5 - MinCapacity: 1 - ResourceId: !Join - - '/' - - - 'service' - - !Ref 'ContainerCluster' - - !GetAtt 'BrokerRunner.Name' - RoleARN: !GetAtt 'ContainerRole.Arn' - ScalableDimension: 'ecs:service:DesiredCount' - ServiceNamespace: 'ecs' - Type: 'AWS::ApplicationAutoScaling::ScalableTarget' # Security group for the broker's WebSocket server. BrokerPublisherSecurityGroup: Condition: 'DeployBroker' @@ -965,6 +928,59 @@ Resources: IpProtocol: -1 VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' + # Autoscaling policy for broker and for PostgREST. + Fn::ForEach::AutoScalingPolicy: + - 'Identifier' + - - 'Broker' + - 'Postgrest' + - AutoScalingPolicy${Identifier}: + Properties: + PolicyName: !Sub '${AWS::StackName}-${Identifier}' + PolicyType: 'TargetTrackingScaling' + ScalingTargetId: !Ref + Fn::Sub: 'AutoScalingTarget${Identifier}' + TargetTrackingScalingPolicyConfiguration: + CustomizedMetricSpecification: + Dimensions: + - Name: 'ClusterName' + Value: !Ref 'ContainerCluster' + - Name: 'ServiceName' + Value: !Ref + Fn::Sub: '${Identifier}Runner' + MetricName: 'CPUUtilization' + Namespace: 'AWS/ECS' + Statistic: 'Average' + Unit: 'Percent' + ScaleInCooldown: 300 + ScaleOutCooldown: 60 + TargetValue: 70 + # ForEach transforms require that condition is second key or later. + # yamllint disable-line rule:key-ordering + Condition: !Sub 'Deploy${Identifier}' + Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' + # Autoscaling target for broker and for PostgREST. + Fn::ForEach::AutoScalingTarget: + - 'Identifier' + - - 'Broker' + - 'Postgrest' + - AutoScalingTarget${Identifier}: + Properties: + MaxCapacity: 5 + MinCapacity: 1 + ResourceId: !Join + - '/' + - - 'service' + - !Ref 'ContainerCluster' + - !GetAtt + - !Sub '${Identifier}Runner' + - 'Name' + RoleARN: !GetAtt 'ContainerRole.Arn' + ScalableDimension: 'ecs:service:DesiredCount' + ServiceNamespace: 'ecs' + # ForEach transforms require that condition is second key or later. + # yamllint disable-line rule:key-ordering + Condition: !Sub 'Deploy${Identifier}' + Type: 'AWS::ApplicationAutoScaling::ScalableTarget' # Overload for default low CPU usage alarm on autoscaled containers, which # replicates the default alarm values but disables actions. Designed to # effectively disable the alarm since containers have low CPU utilization at @@ -1204,43 +1220,6 @@ Resources: TargetArns: - !Ref 'Nlb' Type: 'AWS::ApiGateway::VpcLink' - # Scaling policy for PostgREST service. - PostgrestAutoScalingPolicy: - Condition: 'DeployPostgrest' - Properties: - PolicyName: !Sub '${AWS::StackName}-postgrest' - PolicyType: 'TargetTrackingScaling' - ScalingTargetId: !Ref 'PostgrestAutoScalingTarget' - TargetTrackingScalingPolicyConfiguration: - CustomizedMetricSpecification: - Dimensions: - - Name: 'ClusterName' - Value: !Ref 'ContainerCluster' - - Name: 'ServiceName' - Value: !Ref 'PostgrestRunner' - MetricName: 'CPUUtilization' - Namespace: 'AWS/ECS' - Statistic: 'Average' - Unit: 'Percent' - ScaleInCooldown: 300 - ScaleOutCooldown: 60 - TargetValue: 70 - Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' - # Scalable target for PostgREST service. - PostgrestAutoScalingTarget: - Condition: 'DeployPostgrest' - Properties: - MaxCapacity: 5 - MinCapacity: 1 - ResourceId: !Join - - '/' - - - 'service' - - !Ref 'ContainerCluster' - - !GetAtt 'PostgrestRunner.Name' - RoleARN: !GetAtt 'ContainerRole.Arn' - ScalableDimension: 'ecs:service:DesiredCount' - ServiceNamespace: 'ecs' - Type: 'AWS::ApplicationAutoScaling::ScalableTarget' # Security group for PostgREST clients. PostgrestClientSecurityGroup: Condition: 'DeployPostgrest' From 2b3130b4c7468c520440ab1bf7fef299a049fdb8 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:27:41 -0700 Subject: [PATCH 29/62] Add scale up and scale down alarms --- src/cloud-formation/indexer.cfn.yaml | 77 ++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index ddcc3749f..aa8787c20 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -981,19 +981,73 @@ Resources: # yamllint disable-line rule:key-ordering Condition: !Sub 'Deploy${Identifier}' Type: 'AWS::ApplicationAutoScaling::ScalableTarget' - # Overload for default low CPU usage alarm on autoscaled containers, which - # replicates the default alarm values but disables actions. Designed to - # effectively disable the alarm since containers have low CPU utilization at - # idle even on the smallest instance possible. - Fn::ForEach::LowCpuAlarmOverload: + # Scale down trigger for broker and PostgREST. + Fn::ForEach::AutoScalingTriggerScaleDown: - 'Identifier' - - 'Broker' - 'Postgrest' - - LowCpuAlarmOverload${Identifier}: + - AutoScalingTriggerScaleDown${Identifier}: Properties: - ActionsEnabled: 'false' - AlarmName: !Sub '${AWS::StackName}-${Identifier}-low-cpu-overload' + ActionsEnabled: 'true' + AlarmName: !Sub '${AWS::StackName}-${Identifier}-scale-down' ComparisonOperator: 'LessThanThreshold' + DatapointsToAlarm: 15 + EvaluationPeriods: 15 + Metrics: + - Id: 'cpuUtilization' + MetricStat: + Metric: + Dimensions: + - Name: 'ClusterName' + Value: !Ref 'ContainerCluster' + - Name: 'ServiceName' + Value: !GetAtt + - !Sub '${Identifier}Runner' + - 'Name' + MetricName: 'CPUUtilization' + Namespace: 'AWS/ECS' + Period: 60 + Stat: 'Average' + Unit: 'Percent' + ReturnData: 'false' + - Id: 'runningTaskCount' + MetricStat: + Metric: + Dimensions: + - Name: 'ClusterName' + Value: !Ref 'ContainerCluster' + - Name: 'ServiceName' + Value: !GetAtt + - !Sub '${Identifier}Runner' + - 'Name' + MetricName: 'RunningTaskCount' + Namespace: 'ECS/ContainerInsights' + Period: 60 + Stat: 'Minimum' + ReturnData: false + # If only one instance is running, return 100% utilization so that + # scaledown alarm isn't triggered. + - Expression: 'IF(runningTaskCount > 1, cpuUtilization, 100)' + Id: 'expression' + Label: 'CPU Utilization when more than one task is running' + ReturnData: true + Threshold: 60.0 + TreatMissingData: 'notBreaching' + # ForEach transforms require that condition is second key or later. + # yamllint disable-line rule:key-ordering + Condition: !Sub 'Deploy${Identifier}' + Type: 'AWS::CloudWatch::Alarm' + # Scale up trigger for broker and PostgREST. + Fn::ForEach::AutoScalingTriggerScaleUp: + - 'Identifier' + - - 'Broker' + - 'Postgrest' + - AutoScalingTriggerScaleUp${Identifier}: + Properties: + ActionsEnabled: 'true' + AlarmName: !Sub '${AWS::StackName}-${Identifier}-scale-up' + ComparisonOperator: 'GreaterThanThreshold' + DatapointsToAlarm: 3 Dimensions: - Name: 'ClusterName' Value: !Ref 'ContainerCluster' @@ -1001,12 +1055,13 @@ Resources: Value: !GetAtt - !Sub '${Identifier}Runner' - 'Name' - EvaluationPeriods: 15 + EvaluationPeriods: 3 MetricName: 'CPUUtilization' Namespace: 'AWS/ECS' - Period: 60 + Period: 10 Statistic: 'Average' - Threshold: 63.0 + Threshold: 70.0 + TreatMissingData: 'notBreaching' Unit: 'Percent' # ForEach transforms require that condition is second key or later. # yamllint disable-line rule:key-ordering From 494194d7ad9402b26d04fa2ab3a3061815d64756 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:32:01 -0700 Subject: [PATCH 30/62] Kill broker, postgrest --- src/cloud-formation/deploy-dev.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 15092555f..13f23fc07 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -4,14 +4,14 @@ parameters: DeployAlb: 'true' DeployAlbDnsRecord: 'true' DeployBastionHost: 'true' - DeployBroker: 'true' + DeployBroker: 'false' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' DeployNlbVpcLink: 'true' - DeployPostgrest: 'true' + DeployPostgrest: 'false' DeployProcessor: 'true' - DeployRestApi: 'true' + DeployRestApi: 'false' DeployRestApiDnsRecord: 'true' DeployRouteTables: 'true' DeployStack: 'true' From 3d409338a2a66f7b300f5d697ef3611271ae6851 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:34:39 -0700 Subject: [PATCH 31/62] Use predefined metric for scaling policy --- src/cloud-formation/indexer.cfn.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index aa8787c20..8ba12ddb8 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -940,17 +940,8 @@ Resources: ScalingTargetId: !Ref Fn::Sub: 'AutoScalingTarget${Identifier}' TargetTrackingScalingPolicyConfiguration: - CustomizedMetricSpecification: - Dimensions: - - Name: 'ClusterName' - Value: !Ref 'ContainerCluster' - - Name: 'ServiceName' - Value: !Ref - Fn::Sub: '${Identifier}Runner' - MetricName: 'CPUUtilization' - Namespace: 'AWS/ECS' - Statistic: 'Average' - Unit: 'Percent' + PredefinedMetricSpecification: + PredefinedMetricType: 'ECSServiceAverageCPUUtilization' ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 From bbe28fdadefa4f651c42bcd3db1ae5da5dd86b9e Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:38:54 -0700 Subject: [PATCH 32/62] Kill more REST dependents --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 13f23fc07..77c4f9135 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -12,11 +12,11 @@ parameters: DeployPostgrest: 'false' DeployProcessor: 'true' DeployRestApi: 'false' - DeployRestApiDnsRecord: 'true' + DeployRestApiDnsRecord: 'false' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' - DeployWaf: 'true' + DeployWaf: 'false' EnableWafRulesGeneral: 'true' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' From f535b3b5135edd0a864684a45622936a3d2bee8c Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:44:51 -0700 Subject: [PATCH 33/62] Re-deploy entire stack --- src/cloud-formation/deploy-dev.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 77c4f9135..15092555f 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -4,19 +4,19 @@ parameters: DeployAlb: 'true' DeployAlbDnsRecord: 'true' DeployBastionHost: 'true' - DeployBroker: 'false' + DeployBroker: 'true' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' DeployNlbVpcLink: 'true' - DeployPostgrest: 'false' + DeployPostgrest: 'true' DeployProcessor: 'true' - DeployRestApi: 'false' - DeployRestApiDnsRecord: 'false' + DeployRestApi: 'true' + DeployRestApiDnsRecord: 'true' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' - DeployWaf: 'false' + DeployWaf: 'true' EnableWafRulesGeneral: 'true' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' From 3b46bb6e0dda2f44aa3ae27e4e5d6c7cbbfda52e Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:53:43 -0700 Subject: [PATCH 34/62] Kill WAF rules --- src/cloud-formation/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 15092555f..3527287b2 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -17,7 +17,7 @@ parameters: DeployStack: 'true' DeployVpc: 'true' DeployWaf: 'true' - EnableWafRulesGeneral: 'true' + EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' Environment: 'ECO-2188' From 0283329a12759fff8cbb1ef8d978e4432adc79ac Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:53:55 -0700 Subject: [PATCH 35/62] Kill WAF entirely --- src/cloud-formation/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 3527287b2..b7a939204 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -16,7 +16,7 @@ parameters: DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' - DeployWaf: 'true' + DeployWaf: 'false' EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' From 2c38371f9102d50f36fdcaa19eee4ce71f767582 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:55:52 -0700 Subject: [PATCH 36/62] Remove typo --- src/cloud-formation/indexer.cfn.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 8ba12ddb8..961327d4c 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -1924,7 +1924,7 @@ Resources: Waf: Condition: 'DeployWaf' Properties: - # Allow all all traffic by default unless blocked per below rules. + # Allow all traffic by default unless blocked per below rules. DefaultAction: # CloudFormation prohibits both null and empty values. Allow: {} # yamllint disable-line rule:braces From f89d2393b8bf6937b7f516ae55f4ddf460dd636e Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:17:25 -0700 Subject: [PATCH 37/62] Update bastion README --- src/cloud-formation/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/cloud-formation/README.md b/src/cloud-formation/README.md index 7b2388921..4a74fea5c 100644 --- a/src/cloud-formation/README.md +++ b/src/cloud-formation/README.md @@ -292,11 +292,16 @@ URLs of other resources in the stack. pip install ec2instanceconnectcli ``` -1. Connect to the bastion host over the [EC2 Instance Connect Endpoint] using - your stack name, for example `emoji-dev`: +1. Set your stack name: + + ```sh + STACK_NAME= + echo $STACK_NAME + ``` + +1. Connect to the bastion host over the [EC2 Instance Connect Endpoint]: ```sh - STACK_NAME=emoji-dev INSTANCE_ID=$(aws cloudformation describe-stacks \ --output text \ --query 'Stacks[0].Outputs[?OutputKey==`BastionHostId`].OutputValue' \ From bfe775d6fa42b7d751956e1d425291da235e3bbf Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:21:52 -0700 Subject: [PATCH 38/62] Kill broker, ALB, ALB DNS cert --- src/cloud-formation/deploy-dev.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index b7a939204..1c67ee352 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,10 +1,10 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'true' - DeployAlbDnsRecord: 'true' + DeployAlb: 'false' + DeployAlbDnsRecord: 'false' DeployBastionHost: 'true' - DeployBroker: 'true' + DeployBroker: 'false' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' From 61864554e66c0a889d106c8b1a28d4845c4f0a28 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:34:45 -0700 Subject: [PATCH 39/62] Use step scale-in --- src/cloud-formation/indexer.cfn.yaml | 67 +++++++++++++--------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 961327d4c..1d0ba753c 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -928,21 +928,45 @@ Resources: IpProtocol: -1 VpcId: !Ref 'Vpc' Type: 'AWS::EC2::SecurityGroup' - # Autoscaling policy for broker and for PostgREST. - Fn::ForEach::AutoScalingPolicy: + # Autoscaling policy for scaling in broker and PostgREST. + Fn::ForEach::AutoScalingPolicyScaleIn: + - 'Identifier' + - - 'Broker' + - 'Postgrest' + - AutoScalingPolicyScaleIn${Identifier}: + Properties: + PolicyName: !Sub '${AWS::StackName}-${Identifier}-scale-in' + PolicyType: 'TargetTrackingScaling' + ScalingTargetId: !Ref + Fn::Sub: 'AutoScalingTarget${Identifier}' + StepScalingPolicyConfiguration: + AdjustmentType: 'ChangeInCapacity' + Cooldown: 300 + MetricAggregationType: 'Average' + StepAdjustments: + - MetricIntervalUpperBound: 0 + ScalingAdjustment: -1 + # ForEach transforms require that condition is second key or later. + # yamllint disable-line rule:key-ordering + Condition: !Sub 'Deploy${Identifier}' + Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' + # Autoscaling policy for scaling out broker and PostgREST. + Fn::ForEach::AutoScalingPolicyScaleOut: - 'Identifier' - - 'Broker' - 'Postgrest' - AutoScalingPolicy${Identifier}: Properties: - PolicyName: !Sub '${AWS::StackName}-${Identifier}' + PolicyName: !Sub '${AWS::StackName}-${Identifier}-scale-out' PolicyType: 'TargetTrackingScaling' ScalingTargetId: !Ref Fn::Sub: 'AutoScalingTarget${Identifier}' + # Use a default policy but disable automatic scale-in to prevent the + # triggering of scale-in alarms when only one instance is running. TargetTrackingScalingPolicyConfiguration: + DisableScaleIn: 'true' PredefinedMetricSpecification: PredefinedMetricType: 'ECSServiceAverageCPUUtilization' - ScaleInCooldown: 300 ScaleOutCooldown: 60 TargetValue: 70 # ForEach transforms require that condition is second key or later. @@ -980,7 +1004,10 @@ Resources: - AutoScalingTriggerScaleDown${Identifier}: Properties: ActionsEnabled: 'true' - AlarmName: !Sub '${AWS::StackName}-${Identifier}-scale-down' + AlarmActions: + - !Ref + Fn::Sub: 'AutoScalingPolicyScaleIn${Identifier}' + AlarmName: !Sub '${AWS::StackName}-${Identifier}-scale-in' ComparisonOperator: 'LessThanThreshold' DatapointsToAlarm: 15 EvaluationPeriods: 15 @@ -1028,36 +1055,6 @@ Resources: # yamllint disable-line rule:key-ordering Condition: !Sub 'Deploy${Identifier}' Type: 'AWS::CloudWatch::Alarm' - # Scale up trigger for broker and PostgREST. - Fn::ForEach::AutoScalingTriggerScaleUp: - - 'Identifier' - - - 'Broker' - - 'Postgrest' - - AutoScalingTriggerScaleUp${Identifier}: - Properties: - ActionsEnabled: 'true' - AlarmName: !Sub '${AWS::StackName}-${Identifier}-scale-up' - ComparisonOperator: 'GreaterThanThreshold' - DatapointsToAlarm: 3 - Dimensions: - - Name: 'ClusterName' - Value: !Ref 'ContainerCluster' - - Name: 'ServiceName' - Value: !GetAtt - - !Sub '${Identifier}Runner' - - 'Name' - EvaluationPeriods: 3 - MetricName: 'CPUUtilization' - Namespace: 'AWS/ECS' - Period: 10 - Statistic: 'Average' - Threshold: 70.0 - TreatMissingData: 'notBreaching' - Unit: 'Percent' - # ForEach transforms require that condition is second key or later. - # yamllint disable-line rule:key-ordering - Condition: !Sub 'Deploy${Identifier}' - Type: 'AWS::CloudWatch::Alarm' # Network address translation gateway for each public subnet. Fn::ForEach::NatGateway: - 'Identifier' From 8a9bd36f3d9189f7daffb29a4fc2f336ba671589 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:35:47 -0700 Subject: [PATCH 40/62] Re-provision broker --- src/cloud-formation/deploy-dev.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 1c67ee352..b7a939204 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,10 +1,10 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'false' - DeployAlbDnsRecord: 'false' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' DeployBastionHost: 'true' - DeployBroker: 'false' + DeployBroker: 'true' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' From 5d12e4e973e4a935612efcc2ad9071357beb1db5 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:43:00 -0700 Subject: [PATCH 41/62] Make name consistent --- src/cloud-formation/indexer.cfn.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 1d0ba753c..cb6f613fb 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -955,7 +955,7 @@ Resources: - 'Identifier' - - 'Broker' - 'Postgrest' - - AutoScalingPolicy${Identifier}: + - AutoScalingPolicyScaleOut${Identifier}: Properties: PolicyName: !Sub '${AWS::StackName}-${Identifier}-scale-out' PolicyType: 'TargetTrackingScaling' From ba590bfcf7b7ff1a9c0656a1b7532ca58d6a3d49 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:46:38 -0700 Subject: [PATCH 42/62] SPecify step scaling --- src/cloud-formation/indexer.cfn.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index cb6f613fb..5f89f9c75 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -936,7 +936,7 @@ Resources: - AutoScalingPolicyScaleIn${Identifier}: Properties: PolicyName: !Sub '${AWS::StackName}-${Identifier}-scale-in' - PolicyType: 'TargetTrackingScaling' + PolicyType: 'StepScaling' ScalingTargetId: !Ref Fn::Sub: 'AutoScalingTarget${Identifier}' StepScalingPolicyConfiguration: From a6dfdce63eeb99da4b667c45d80348448c9b4b27 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:48:41 -0700 Subject: [PATCH 43/62] Try killing all containers --- src/cloud-formation/deploy-dev.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index b7a939204..068c82f09 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,18 +1,18 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'true' - DeployAlbDnsRecord: 'true' + DeployAlb: 'false' + DeployAlbDnsRecord: 'false' DeployBastionHost: 'true' - DeployBroker: 'true' + DeployBroker: 'false' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' DeployNlbVpcLink: 'true' - DeployPostgrest: 'true' + DeployPostgrest: 'false' DeployProcessor: 'true' - DeployRestApi: 'true' - DeployRestApiDnsRecord: 'true' + DeployRestApi: 'false' + DeployRestApiDnsRecord: 'false' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' From a9363d666f653713a4857ad1df9e2bfb56866e19 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:57:26 -0700 Subject: [PATCH 44/62] Deploy API containers --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 068c82f09..954645f42 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -4,12 +4,12 @@ parameters: DeployAlb: 'false' DeployAlbDnsRecord: 'false' DeployBastionHost: 'true' - DeployBroker: 'false' + DeployBroker: 'true' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' DeployNlbVpcLink: 'true' - DeployPostgrest: 'false' + DeployPostgrest: 'true' DeployProcessor: 'true' DeployRestApi: 'false' DeployRestApiDnsRecord: 'false' From 4b5efbd2d1eefdfc04af2956ee1020874e41cd03 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:15:45 -0700 Subject: [PATCH 45/62] Re-provision container extensions --- src/cloud-formation/deploy-dev.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 954645f42..b7a939204 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,8 +1,8 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'false' - DeployAlbDnsRecord: 'false' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' DeployBastionHost: 'true' DeployBroker: 'true' DeployContainers: 'true' @@ -11,8 +11,8 @@ parameters: DeployNlbVpcLink: 'true' DeployPostgrest: 'true' DeployProcessor: 'true' - DeployRestApi: 'false' - DeployRestApiDnsRecord: 'false' + DeployRestApi: 'true' + DeployRestApiDnsRecord: 'true' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' From 20c5dc6bf027a0f73dc5ae61bd2c059d46609d6a Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:08:10 -0700 Subject: [PATCH 46/62] Kill ALB --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index b7a939204..41d55af26 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,8 +1,8 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'true' - DeployAlbDnsRecord: 'true' + DeployAlb: 'false' + DeployAlbDnsRecord: 'false' DeployBastionHost: 'true' DeployBroker: 'true' DeployContainers: 'true' From fe50b1b56acfba8f5ee940e9c464c2a6bf768588 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:13:08 -0700 Subject: [PATCH 47/62] Kill broker --- src/cloud-formation/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 41d55af26..1c67ee352 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -4,7 +4,7 @@ parameters: DeployAlb: 'false' DeployAlbDnsRecord: 'false' DeployBastionHost: 'true' - DeployBroker: 'true' + DeployBroker: 'false' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' From 4cd0ab3d0fdb3cc137abf133186c85f622ab92b8 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:17:43 -0700 Subject: [PATCH 48/62] Redeploy broker, ALB --- src/cloud-formation/deploy-dev.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 1c67ee352..b7a939204 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,10 +1,10 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'false' - DeployAlbDnsRecord: 'false' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' DeployBastionHost: 'true' - DeployBroker: 'false' + DeployBroker: 'true' DeployContainers: 'true' DeployDb: 'true' DeployNlb: 'true' From 4108536b4146e3cd374d4b0f0bd2ef98bae057a8 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:36:57 -0700 Subject: [PATCH 49/62] Kill all but DB, processor --- src/cloud-formation/deploy-dev.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index b7a939204..efed384f3 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,18 +1,18 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'true' - DeployAlbDnsRecord: 'true' - DeployBastionHost: 'true' - DeployBroker: 'true' + DeployAlb: 'false' + DeployAlbDnsRecord: 'false' + DeployBastionHost: 'false' + DeployBroker: 'false' DeployContainers: 'true' DeployDb: 'true' - DeployNlb: 'true' - DeployNlbVpcLink: 'true' - DeployPostgrest: 'true' + DeployNlb: 'false' + DeployNlbVpcLink: 'false' + DeployPostgrest: 'false' DeployProcessor: 'true' - DeployRestApi: 'true' - DeployRestApiDnsRecord: 'true' + DeployRestApi: 'false' + DeployRestApiDnsRecord: 'false' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' From ef14b83ae4cf0d1e805ea9f52ba9d53876598c09 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:45:17 -0700 Subject: [PATCH 50/62] Deploy all except WAF --- src/cloud-formation/deploy-dev.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index efed384f3..b7a939204 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,18 +1,18 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'false' - DeployAlbDnsRecord: 'false' - DeployBastionHost: 'false' - DeployBroker: 'false' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' + DeployBastionHost: 'true' + DeployBroker: 'true' DeployContainers: 'true' DeployDb: 'true' - DeployNlb: 'false' - DeployNlbVpcLink: 'false' - DeployPostgrest: 'false' + DeployNlb: 'true' + DeployNlbVpcLink: 'true' + DeployPostgrest: 'true' DeployProcessor: 'true' - DeployRestApi: 'false' - DeployRestApiDnsRecord: 'false' + DeployRestApi: 'true' + DeployRestApiDnsRecord: 'true' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' From a9f8a6a431249b367c4b908e005c26bcd3d46f62 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:06:03 -0700 Subject: [PATCH 51/62] Rename down to in --- src/cloud-formation/indexer.cfn.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 5f89f9c75..c65e47c22 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -997,11 +997,11 @@ Resources: Condition: !Sub 'Deploy${Identifier}' Type: 'AWS::ApplicationAutoScaling::ScalableTarget' # Scale down trigger for broker and PostgREST. - Fn::ForEach::AutoScalingTriggerScaleDown: + Fn::ForEach::AutoScalingTriggerScaleIn: - 'Identifier' - - 'Broker' - 'Postgrest' - - AutoScalingTriggerScaleDown${Identifier}: + - AutoScalingTriggerScaleIn${Identifier}: Properties: ActionsEnabled: 'true' AlarmActions: @@ -1044,7 +1044,7 @@ Resources: Stat: 'Minimum' ReturnData: false # If only one instance is running, return 100% utilization so that - # scaledown alarm isn't triggered. + # scale in alarm isn't triggered. - Expression: 'IF(runningTaskCount > 1, cpuUtilization, 100)' Id: 'expression' Label: 'CPU Utilization when more than one task is running' From af743632793760802408f06c828b78f225d1c394 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:07:41 -0700 Subject: [PATCH 52/62] Kill again --- src/cloud-formation/deploy-dev.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index b7a939204..efed384f3 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,18 +1,18 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'true' - DeployAlbDnsRecord: 'true' - DeployBastionHost: 'true' - DeployBroker: 'true' + DeployAlb: 'false' + DeployAlbDnsRecord: 'false' + DeployBastionHost: 'false' + DeployBroker: 'false' DeployContainers: 'true' DeployDb: 'true' - DeployNlb: 'true' - DeployNlbVpcLink: 'true' - DeployPostgrest: 'true' + DeployNlb: 'false' + DeployNlbVpcLink: 'false' + DeployPostgrest: 'false' DeployProcessor: 'true' - DeployRestApi: 'true' - DeployRestApiDnsRecord: 'true' + DeployRestApi: 'false' + DeployRestApiDnsRecord: 'false' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' From ecc56fa5469b7fbc919feb9feebd8db25bad7bed Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:13:52 -0700 Subject: [PATCH 53/62] Add autoscaling architecture notes --- src/cloud-formation/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/cloud-formation/README.md b/src/cloud-formation/README.md index 4a74fea5c..8bdb0e036 100644 --- a/src/cloud-formation/README.md +++ b/src/cloud-formation/README.md @@ -408,6 +408,18 @@ toggle [rule actions] between `Block` and `Count`. See the [Web ACL traffic overview dashboards] to monitor rules. +### Container scaling + +[Container autoscaling] for both REST and WebSocket endpoints relies on a +mixture of [target tracking] for scaling out and [step scaling] for scaling in. + +Scaling in uses a custom [step scale CloudWatch alarm] that only fires when more +than one instance is active, to prevent alarms from triggering when only one +instance is live and at idle. + +This design ensures that at least one server container is always live for both +REST and WebSocket endpoints. + [a nat gateway in each availability zone]: https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-basics.html [amazonec2containerserviceautoscalerole]: https://docs.aws.amazon.com/autoscaling/application/userguide/security-iam-awsmanpol.html#ecs-policy [application autoscaling iam access]: https://docs.aws.amazon.com/autoscaling/application/userguide/security_iam_service-with-iam.html @@ -421,6 +433,7 @@ See the [Web ACL traffic overview dashboards] to monitor rules. [aws cloudformation]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html [cloudformation service role]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-servicerole.html [conditions]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html +[container autoscaling]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-auto-scaling.html [container logging permissions]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html#ec2-considerations [create the stack with gitsync]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/git-sync-walkthrough.html [ec2 instance connect cli]: https://github.com/aws/aws-ec2-instance-connect-cli @@ -452,7 +465,10 @@ See the [Web ACL traffic overview dashboards] to monitor rules. [secrets manager secrets]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html [stack]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html [stack deployment file]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/git-sync-concepts-terms.html#git-sync-concepts-terms-depoyment-file +[step scale cloudwatch alarm]: https://docs.aws.amazon.com/autoscaling/application/userguide/step-scaling-policy-overview.html#step-scaling-how-it-works +[step scaling]: https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-step-scaling-policies.html [systems manager parameters]: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html +[target tracking]: https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-target-tracking.html [template file]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/gettingstarted.templatebasics.html [template outputs section]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html [the upstream repository credentials docs]: https://docs.aws.amazon.com/AmazonECR/latest/userguide/pull-through-cache-creating-secret.html From 100546476798dc75c5e8d8284eefd396c05a7f3e Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:14:21 -0700 Subject: [PATCH 54/62] Redeploy all except WAF --- src/cloud-formation/deploy-dev.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index efed384f3..b7a939204 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,18 +1,18 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'false' - DeployAlbDnsRecord: 'false' - DeployBastionHost: 'false' - DeployBroker: 'false' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' + DeployBastionHost: 'true' + DeployBroker: 'true' DeployContainers: 'true' DeployDb: 'true' - DeployNlb: 'false' - DeployNlbVpcLink: 'false' - DeployPostgrest: 'false' + DeployNlb: 'true' + DeployNlbVpcLink: 'true' + DeployPostgrest: 'true' DeployProcessor: 'true' - DeployRestApi: 'false' - DeployRestApiDnsRecord: 'false' + DeployRestApi: 'true' + DeployRestApiDnsRecord: 'true' DeployRouteTables: 'true' DeployStack: 'true' DeployVpc: 'true' From 53944a82dd70dcc5d9304d65e152002443bb1a37 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:19:40 -0700 Subject: [PATCH 55/62] Update bastion host line breaking --- src/cloud-formation/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cloud-formation/README.md b/src/cloud-formation/README.md index 8bdb0e036..9388a9f75 100644 --- a/src/cloud-formation/README.md +++ b/src/cloud-formation/README.md @@ -280,11 +280,11 @@ deployment environment: ### Bastion host connections Before you try connecting to the bastion host, verify that the -`DeployBastionHost` [condition][conditions] evaluates to `true`. Note -too that if you have been provisioning and de-provisioning other resources, you -might want to de-provision then provision the bastion host before running the -below commands, in order to refresh the bastion host [user data] that stores the -URLs of other resources in the stack. +`DeployBastionHost` [condition][conditions] evaluates to `true`. Note too that +if you have been provisioning and de-provisioning other resources, you might +want to de-provision then provision the bastion host before running the below +commands, in order to refresh the bastion host [user data] that stores the URLs +of other resources in the stack. 1. Install the [EC2 Instance Connect CLI]: From 623bd2e3f07f4603e9a004fedd5bf969dd3dad5d Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:28:45 -0700 Subject: [PATCH 56/62] Address cspell --- src/cloud-formation/indexer.cfn.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index c65e47c22..77f71527f 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -1,6 +1,7 @@ # cspell:word aarch # cspell:word awslogs # cspell:word awsvpc +# cspell:word datapoints # cspell:word fargate # cspell:word multivalue # cspell:word pullthroughcache From 4e07697e126effa64547466782c30ed621f67d04 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:07:47 -0700 Subject: [PATCH 57/62] Kill stack to update SQL db --- src/cloud-formation/deploy-dev.yaml | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index b7a939204..c503995d7 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,21 +1,21 @@ --- parameters: BrokerImageVersion: '0.7.0' - DeployAlb: 'true' - DeployAlbDnsRecord: 'true' - DeployBastionHost: 'true' - DeployBroker: 'true' - DeployContainers: 'true' - DeployDb: 'true' - DeployNlb: 'true' - DeployNlbVpcLink: 'true' - DeployPostgrest: 'true' - DeployProcessor: 'true' - DeployRestApi: 'true' - DeployRestApiDnsRecord: 'true' - DeployRouteTables: 'true' - DeployStack: 'true' - DeployVpc: 'true' + DeployAlb: 'false' + DeployAlbDnsRecord: 'false' + DeployBastionHost: 'false' + DeployBroker: 'false' + DeployContainers: 'false' + DeployDb: 'false' + DeployNlb: 'false' + DeployNlbVpcLink: 'false' + DeployPostgrest: 'false' + DeployProcessor: 'false' + DeployRestApi: 'false' + DeployRestApiDnsRecord: 'false' + DeployRouteTables: 'false' + DeployStack: 'false' + DeployVpc: 'false' DeployWaf: 'false' EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' From 74c71eef9e44c3466c27fc0bc9dc3748610fdba6 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:11:57 -0700 Subject: [PATCH 58/62] Bump binary versions --- src/cloud-formation/deploy-dev.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index c503995d7..7f010a5b7 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,6 +1,6 @@ --- parameters: - BrokerImageVersion: '0.7.0' + BrokerImageVersion: '0.8.0' DeployAlb: 'false' DeployAlbDnsRecord: 'false' DeployBastionHost: 'false' @@ -22,7 +22,7 @@ parameters: EnableWafRulesWebSocket: 'false' Environment: 'ECO-2188' Network: 'testnet' - ProcessorImageVersion: '0.5.0' + ProcessorImageVersion: '0.5.9' tags: null template-file-path: 'src/cloud-formation/indexer.cfn.yaml' ... From 1644c62618439edc433f65b56810610f315de4d5 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:12:39 -0700 Subject: [PATCH 59/62] Re-enable stack --- src/cloud-formation/deploy-dev.yaml | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 7f010a5b7..a5a4800c3 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -1,21 +1,21 @@ --- parameters: BrokerImageVersion: '0.8.0' - DeployAlb: 'false' - DeployAlbDnsRecord: 'false' - DeployBastionHost: 'false' - DeployBroker: 'false' - DeployContainers: 'false' - DeployDb: 'false' - DeployNlb: 'false' - DeployNlbVpcLink: 'false' - DeployPostgrest: 'false' - DeployProcessor: 'false' - DeployRestApi: 'false' - DeployRestApiDnsRecord: 'false' - DeployRouteTables: 'false' - DeployStack: 'false' - DeployVpc: 'false' + DeployAlb: 'true' + DeployAlbDnsRecord: 'true' + DeployBastionHost: 'true' + DeployBroker: 'true' + DeployContainers: 'true' + DeployDb: 'true' + DeployNlb: 'true' + DeployNlbVpcLink: 'true' + DeployPostgrest: 'true' + DeployProcessor: 'true' + DeployRestApi: 'true' + DeployRestApiDnsRecord: 'true' + DeployRouteTables: 'true' + DeployStack: 'true' + DeployVpc: 'true' DeployWaf: 'false' EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' From 9a895c27deb4ff8f0e0c511fc30a9402fb5581ed Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 30 Sep 2024 10:10:08 -0700 Subject: [PATCH 60/62] Remove generalized cache with wildcard error --- src/cloud-formation/indexer.cfn.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/cloud-formation/indexer.cfn.yaml b/src/cloud-formation/indexer.cfn.yaml index 77f71527f..d314fe479 100644 --- a/src/cloud-formation/indexer.cfn.yaml +++ b/src/cloud-formation/indexer.cfn.yaml @@ -1871,14 +1871,6 @@ Resources: CacheClusterSize: '0.5' DeploymentId: !Ref 'RestApiDeployment' MethodSettings: - # Cache general data for one second, with permissive throttling. - - CacheDataEncrypted: false - CacheTtlInSeconds: 1 - CachingEnabled: true - HttpMethod: '*' - ResourcePath: '/*' - ThrottlingBurstLimit: 1000 - ThrottlingRateLimit: 500 # Use a long cache time and restrictive throttling on the schema at the # root, which is large and not required for normal operations. - CacheDataEncrypted: false From de6957d0366222454d6177e919b2f39b60364219 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:22:44 -0700 Subject: [PATCH 61/62] Incorporate processor bump suggestion Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/cloud-formation/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index a5a4800c3..5d03cdb57 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -22,7 +22,7 @@ parameters: EnableWafRulesWebSocket: 'false' Environment: 'ECO-2188' Network: 'testnet' - ProcessorImageVersion: '0.5.9' + ProcessorImageVersion: '0.6.0' tags: null template-file-path: 'src/cloud-formation/indexer.cfn.yaml' ... From d97df24cf84485b6268ff949989524114cd786d0 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:31:17 -0700 Subject: [PATCH 62/62] Revert environment --- src/cloud-formation/deploy-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud-formation/deploy-dev.yaml b/src/cloud-formation/deploy-dev.yaml index 5d03cdb57..220eb9a26 100644 --- a/src/cloud-formation/deploy-dev.yaml +++ b/src/cloud-formation/deploy-dev.yaml @@ -20,7 +20,7 @@ parameters: EnableWafRulesGeneral: 'false' EnableWafRulesRestApi: 'false' EnableWafRulesWebSocket: 'false' - Environment: 'ECO-2188' + Environment: 'dev' Network: 'testnet' ProcessorImageVersion: '0.6.0' tags: null