Created
December 2, 2025 22:29
-
-
Save timmatheson/d40728b659b1ee4f130d2ff60ae99148 to your computer and use it in GitHub Desktop.
AWS Optimized Load Balancer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| AWSTemplateFormatVersion: '2010-09-09' | |
| Description: Production-grade ALB for CAB Backend (HTTPS redirect, access logs, secure defaults) | |
| Parameters: | |
| Environment: | |
| Type: String | |
| AllowedPattern: ^(dev|staging|prod)$ | |
| Description: Environment name (dev/staging/prod) | |
| VpcId: | |
| Type: AWS::EC2::VPC::Id | |
| Description: VPC for the ALB | |
| PublicSubnetIds: | |
| Type: List<AWS::EC2::Subnet::Id> | |
| Description: At least two public subnets in different AZs | |
| LoadBalancerSecurityGroupId: | |
| Type: AWS::EC2::SecurityGroup::Id | |
| Description: SG allowing 80/443 inbound to ALB | |
| CertificateArn: | |
| Type: String | |
| Default: "" | |
| Description: ACM certificate ARN (leave empty to disable HTTPS) | |
| AccessLogsBucket: | |
| Type: String | |
| Default: "" | |
| Description: S3 bucket for ALB access logs (e.g. cab-alb-logs-prod). Leave empty to disable. | |
| EnableWaf: | |
| Type: String | |
| AllowedValues: ['true', 'false'] | |
| Default: 'false' | |
| Description: Attach AWS Managed Rules WAF (recommended in prod) | |
| Conditions: | |
| HasCertificate: !Not [!Equals [!Ref CertificateArn, ""]] | |
| EnableAccessLogs: !Not [!Equals [!Ref AccessLogsBucket, ""]] | |
| AttachWaf: !Equals [!Ref EnableWaf, 'true'] | |
| Resources: | |
| # Application Load Balancer | |
| LoadBalancer: | |
| Type: AWS::ElasticLoadBalancingV2::LoadBalancer | |
| Properties: | |
| Name: !Sub ${Environment}-cab | |
| Type: application | |
| Scheme: internet-facing | |
| IpAddressType: ipv4 | |
| Subnets: !Ref PublicSubnetIds | |
| SecurityGroups: | |
| - !Ref LoadBalancerSecurityGroupId | |
| LoadBalancerAttributes: | |
| - Key: access_logs.s3.enabled | |
| Value: !If [EnableAccessLogs, 'true', 'false'] | |
| - Key: access_logs.s3.bucket | |
| Value: !If [EnableAccessLogs, !Ref AccessLogsBucket, !Ref "AWS::NoValue"] | |
| - Key: access_logs.s3.prefix | |
| Value: !If [EnableAccessLogs, !Sub alb/${Environment}, !Ref "AWS::NoValue"] | |
| - Key: deletion_protection.enabled | |
| Value: 'true' | |
| - Key: routing.http2.enabled | |
| Value: 'true' | |
| - Key: routing.http.drop_invalid_header_fields.enabled | |
| Value: 'true' | |
| - Key: idle_timeout.timeout_seconds | |
| Value: '350' # Critical for WebSocket/gRPC/Fargate | |
| - Key: load_balancing.cross_zone.enabled | |
| Value: 'true' | |
| Tags: | |
| - Key: Name | |
| Value: !Sub ${Environment}-cab-alb | |
| - Key: Environment | |
| Value: !Ref Environment | |
| - Key: Service | |
| Value: cab-backend | |
| # Target Group - IP targets (perfect for Fargate / ECS) | |
| TargetGroup: | |
| Type: AWS::ElasticLoadBalancingV2::TargetGroup | |
| Properties: | |
| Name: !Sub ${Environment}-cab | |
| Port: 8080 | |
| Protocol: HTTP | |
| ProtocolVersion: HTTP1 # Use HTTP/1.1 (better for streaming) | |
| TargetType: ip | |
| VpcId: !Ref VpcId | |
| HealthCheckEnabled: true | |
| HealthCheckPath: /health | |
| HealthCheckProtocol: HTTP | |
| HealthCheckPort: traffic-port | |
| HealthCheckIntervalSeconds: 30 | |
| HealthCheckTimeoutSeconds: 10 | |
| HealthyThresholdCount: 3 | |
| UnhealthyThresholdCount: 3 | |
| Matcher: | |
| HttpCode: 200-299 # Accept 201, etc. | |
| TargetGroupAttributes: | |
| - Key: deregistration_delay.timeout_seconds | |
| Value: '30' | |
| # Removed sticky sessions - they hurt horizontal scaling | |
| # If you really need them, add conditionally | |
| Tags: | |
| - Key: Name | |
| Value: !Sub ${Environment}-cab-tg | |
| - Key: Environment | |
| Value: !Ref Environment | |
| # HTTP → HTTPS Redirect Listener (always created) | |
| HTTPListener: | |
| Type: AWS::ElasticLoadBalancingV2::Listener | |
| Properties: | |
| LoadBalancerArn: !Ref LoadBalancer | |
| Port: 80 | |
| Protocol: HTTP | |
| DefaultActions: | |
| - Type: redirect | |
| RedirectConfig: | |
| Protocol: !If [HasCertificate, HTTPS, HTTP] | |
| Port: !If [HasCertificate, '443', '80'] | |
| Host: '#{host}' | |
| Path: '/#{path}' | |
| Query: '#{query}' | |
| StatusCode: HTTP_301 | |
| # HTTPS Listener (only if cert provided) | |
| HTTPSListener: | |
| Type: AWS::ElasticLoadBalancingV2::Listener | |
| Condition: HasCertificate | |
| Properties: | |
| LoadBalancerArn: !Ref LoadBalancer | |
| Port: 443 | |
| Protocol: HTTPS | |
| Certificates: | |
| - CertificateArn: !Ref CertificateArn | |
| SslPolicy: ELBSecurityPolicy-TLS13-1-3-2021-06 # Strongest available | |
| DefaultActions: | |
| - Type: forward | |
| TargetGroupArn: !Ref TargetGroup | |
| # Optional: Attach WAFv2 (AWS Managed Rules) | |
| WafAssociation: | |
| Type: AWS::WAFv2::WebACLAssociation | |
| Condition: AttachWaf | |
| Properties: | |
| ResourceArn: !Ref LoadBalancer | |
| WebACLArn: !ImportValue AWSManagedRulesCommonRuleSet-WebACL # Or your own | |
| Outputs: | |
| LoadBalancerDnsName: | |
| Description: ALB DNS name | |
| Value: !GetAtt LoadBalancer.DNSName | |
| Export: | |
| Name: !Sub ${Environment}-cab-alb-dns | |
| LoadBalancerCanonicalHostedZoneId: | |
| Description: ALB canonical hosted zone ID (for Route53 alias) | |
| Value: !GetAtt LoadBalancer.CanonicalHostedZoneID | |
| LoadBalancerArn: | |
| Description: ALB ARN | |
| Value: !Ref LoadBalancer | |
| TargetGroupArn: | |
| Description: Target Group ARN (for ECS service) | |
| Value: !Ref TargetGroup | |
| Export: | |
| Name: !Sub ${Environment}-cab-tg-arn | |
| LoadBalancerUrl: | |
| Description: Primary URL to access the service | |
| Value: !If | |
| - HasCertificate | |
| - !Sub https://${LoadBalancer.DNSName} | |
| - !Sub http://${LoadBalancer.DNSName} | |
| LoadBalancerFullUrlWithPath: | |
| Description: Example full URL | |
| Value: !If | |
| - HasCertificate | |
| - !Sub https://${LoadBalancer.DNSName}/api/health | |
| - !Sub http://${LoadBalancer.DNSName}/api/health |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment