AWSTemplateFormatVersion: 2010-09-09 Description: Deploys a AWX Cluster Parameters: QSS3BucketName: AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" ConstraintDescription: Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Default: aws-quickstart Description: S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Type: String QSS3KeyPrefix: AllowedPattern: ^[0-9a-zA-Z-/]*$ ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Default: quickstart-awx/ Description: S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Type: String Cluster: Description: Name of ECS cluster to deploy awx service to Type: String AWXWebRegistry: Description: Name of ECR containing the awx_web image Type: String AWXTaskRegistry: Description: Name of ECR containing the awx_task Type: String RabbitMQRegistry: Description: Name of ECR containing the rabbitmq image Type: String MemcachedRegistry: Description: Name of ECR containing the memcached image Type: String SidecarRegistry: Description: Name of ECR containing the SideCar image Type: String AWXVersion: Description: Which version of AWX to use Type: String Default: 6.1.0 AllowedValues: - 4.0.0 - 5.0.0 - 6.0.0 - 6.1.0 AWXGitHubRepo: Description: Which github should we use as the source for the build Type: String Default: https://github.com/ansible/awx.git MasterUserPassword: Description: Master user database Password. Only applicable if DatabaseEndpoint is blank Type: String NoEcho: 'true' MasterUsername: Description: Master database Username. Only applicable if DatabaseEndpoint is blank Type: String AWXAdminPassword: Description: Password for the AWX admin user. Required is blank Type: String NoEcho: 'true' AWXAdminUsername: Description: AWX Admin user name Type: String Default: admin DatabaseEndpoint: Description: Endpoint for postgres database Type: String ALBARN: Description: Arn for ALB Type: String VPC: Description: VPCID Type: AWS::EC2::VPC::Id Conditions: GovCloudCondition: !Equals [!Ref 'AWS::Region', us-gov-west-1] Resources: mockresource: Type: AWS::S3::Bucket CodeBuildRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: codebuild.amazonaws.com Policies: - PolicyName: ecs-service PolicyDocument: Statement: - Effect: Allow Resource: - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*' - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*:*' Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents - logs:DescribeLogStreams - Effect: Allow Resource: - !Sub 'arn:aws:s3:::${QSS3BucketName}/scripts/dummy.zip' Action: - s3:GetObject - s3:GetObjectVersion - Effect: Allow Resource: - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${MemcachedRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${RabbitMQRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${AWXTaskRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${AWXWebRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${SidecarRegistry}' Action: - ecr:DescribeImages - ecr:ListImages - ecr:PutImage - ecr:BatchCheckLayerAvailability - ecr:BatchGetImage - ecr:CompleteLayerUpload - ecr:GetDownloadUrlForLayer - ecr:GetRepositoryPolicy - ecr:InitiateLayerUpload - ecr:UploadLayerPart - Effect: Allow Resource: "*" Action: - ecr:GetAuthorizationToken AWXCodeBuildProject: DependsOn: [ CopyZips ] Type: AWS::CodeBuild::Project Properties: Name: !Sub 'awx-build-${AWS::StackName}' Description: Builds the AWX container images to ECR ServiceRole: !GetAtt 'CodeBuildRole.Arn' Artifacts: Type: NO_ARTIFACTS Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: docker.io/amazonlinux:2017.09 PrivilegedMode: True EnvironmentVariables: - Name: AWX_TASK_REPO Value: !Ref 'AWXTaskRegistry' - Name: AWX_WEB_REPO Value: !Ref 'AWXWebRegistry' - Name: MEMCACHED_REPO Value: !Ref 'MemcachedRegistry' - Name: RABBITMQ_REPO Value: !Ref 'RabbitMQRegistry' - Name: SIDECAR_REPO Value: !Ref 'SidecarRegistry' - Name: AWX_VERSION Value: !Ref 'AWXVersion' - Name: pg_hostname Value: !Ref 'DatabaseEndpoint' - Name: pg_password Value: !Ref MasterUserPassword - Name: pg_username Value: !Ref MasterUsername - Name: awx_username Value: !Ref AWXAdminUsername - Name: awx_password Value: !Ref AWXAdminPassword - Name: cfn_signal_url Value: 'placeholder' - Name: cfn_stack_id Value: 'placeholder' - Name: cfn_logical_resource_id Value: 'placeholder' - Name: cfn_request_id Value: 'placeholder' Source: Location: !Join - '#' - - !Ref 'AWXGitHubRepo' - !Ref 'AWXVersion' Type: GITHUB BuildSpec: | version: 0.2 phases: install: commands: - yum install -y git gcc docker python27-pip python27-devel libffi-devel openssl-devel curl util-linux - /usr/bin/pip install -U docker docker-compose ansible awscli - service docker start - $(aws ecr get-login --no-include-email) - set build: commands: - git checkout $AWX_VERSION - cd installer/ - sed -i "s/^dockerhub_version=latest/dockerhub_version=$AWS_VERSION/g" inventory - echo ansible-playbook -i inventory -e awx_version="$AWX_VERSION" -e pg_hostname="$pg_hostname" -e pg_username="$pg_username" -e pg_password="$pg_password" -e pg_database="awx" -e pg_port=5432 -e default_admin_user="$awx_username" -e default_admin_password="$awx_password" -e admin_user="$awx_username" -e admin_password="$awx_password" install.yml - ansible-playbook -v -i inventory -e awx_version="$AWX_VERSION" -e pg_hostname="$pg_hostname" -e pg_username="$pg_username" -e pg_password="$pg_password" -e pg_database="awx" -e pg_port=5432 -e default_admin_user="$awx_username" -e default_admin_password="$awx_password" -e admin_user="$awx_username" -e admin_password="$awx_password" install.yml # Allow time for the DB Setup Scripts to complete - sleep 240 # Get container tagging info - export ACCOUNT_ID=$(echo ${CODEBUILD_BUILD_ARN} | awk -F':' '{print $5}') - export RABBITMQ_TAG=$(docker image ls --all | grep rabbitmq | awk '{print $2}' | cut -d ':' -f 2) - export RABBITMQ_IMAGE=$(docker image ls --all | grep rabbitmq | awk '{print $1}') - export MEMCACHED_TAG=$(docker image ls --all | grep memcached | awk '{print $2}' | cut -d ':' -f 2) - export MEMCACHED_IMAGE=$(docker image ls --all | grep memcached | awk '{print $1}') # Make a Sidecar container to inject created files for mounting - mkdir sidecar - mkdir sidecar/tmp - cp -drpu /tmp/awxcompose/ sidecar/tmp/ - echo "changes" - echo "FROM ansible/awx_web:${AWX_VERSION}" > sidecar/Dockerfile - echo "VOLUME /etc/tower/" >> sidecar/Dockerfile - echo "COPY --chown=1000 /tmp/awxcompose/credentials.py /etc/tower/conf.d/credentials.py" >> sidecar/Dockerfile - echo "COPY --chown=1000 /tmp/awxcompose/SECRET_KEY /etc/tower/SECRET_KEY" >> sidecar/Dockerfile - echo "COPY --chown=1000 /tmp/awxcompose/environment.sh /etc/tower/conf.d/environment.sh" >> sidecar/Dockerfile - | echo 'CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"' >> sidecar/Dockerfile - docker build -t sidecar:${AWX_VERSION} sidecar # Debug info # Contextual info for other debug data - docker container ls --all - docker image ls --all # Full logs of each container #- for i in $(docker ps -q); do docker logs $i; done # Docker inspect #- for i in $(docker ps -q); do echo "CONTAINER $i\\n========="; docker inspect $i; echo "========="; done # Push containers to their respective repositories - > docker tag ansible/awx_task:${AWX_VERSION} ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWX_TASK_REPO}:${AWX_VERSION} && docker push ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWX_TASK_REPO}:${AWX_VERSION} - > docker tag ansible/awx_web:${AWX_VERSION} ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWX_WEB_REPO}:${AWX_VERSION} && docker push ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWX_WEB_REPO}:${AWX_VERSION} - > docker tag ${MEMCACHED_IMAGE}:${MEMCACHED_TAG} ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${MEMCACHED_REPO}:${AWX_VERSION} && docker push ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${MEMCACHED_REPO}:${AWX_VERSION} - > docker tag ${RABBITMQ_IMAGE}:${RABBITMQ_TAG} ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${RABBITMQ_REPO}:${AWX_VERSION} && docker push ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${RABBITMQ_REPO}:${AWX_VERSION} - > docker tag sidecar:${AWX_VERSION} ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${SIDECAR_REPO}:${AWX_VERSION} && docker push ${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${SIDECAR_REPO}:${AWX_VERSION} post_build: commands: # FIXME: Need to trap and only execute this if the Build is passing. - echo "Signal back if we have gotten this far" # FIXME: Wanted to add a UUID but then I have an issue with storing these somewhere. - export UUID=1233244324 - | if [ $CODEBUILD_BUILD_SUCCEEDING -eq 1 ] then curl -X PUT -H 'Content-Type:' -d "{\"StackId\":\"$cfn_stack_id\", \"RequestId\":\"$cfn_request_id\", \"LogicalResourceId\":\"$cfn_logical_resource_id\", \"PhysicalResourceId\":\"$UUID\", \"Status\":\"SUCCESS\"}" "$cfn_signal_url" else # Add Reason here and test. curl -X PUT -H 'Content-Type:' -d "{\"StackId\":\"$cfn_stack_id\", \"RequestId\":\"$cfn_request_id\", \"LogicalResourceId\":\"$cfn_logical_resource_id\", \"PhysicalResourceId\":\"$UUID\", \"Status\":\"FAILED\"}" "$cfn_signal_url" fi # FIXME: get this close to the actual time things take with 5 minute margin added for safety TimeoutInMinutes: 60 CopyZipsRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Path: / Policies: - PolicyName: lambda-copier PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:GetObject Resource: - !Sub 'arn:aws:s3:::${QSS3BucketName}/${QSS3KeyPrefix}*' - Effect: Allow Action: - s3:PutObject - s3:DeleteObject Resource: - !Sub 'arn:aws:s3:::${LambdaZipsBucket}/${QSS3KeyPrefix}*' CopyZipsFunction: Type: AWS::Lambda::Function Properties: Description: Copies objects from a source S3 bucket to a destination Handler: index.handler Runtime: python2.7 Role: !GetAtt 'CopyZipsRole.Arn' Timeout: 240 Code: ZipFile: !Join - "\n" - - import json - import logging - import threading - import boto3 - import cfnresponse - '' - '' - 'def copy_objects(source_bucket, dest_bucket, prefix, objects):' - ' s3 = boto3.client(''s3'')' - ' for o in objects:' - ' key = prefix + o' - ' copy_source = {' - ' ''Bucket'': source_bucket,' - ' ''Key'': key' - ' }' - ' print(''copy_source: %s'' % copy_source)' - ' print(''dest_bucket = %s''%dest_bucket)' - ' print(''key = %s'' %key)' - ' s3.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=key)' - '' - '' - 'def delete_objects(bucket, prefix, objects):' - ' s3 = boto3.client(''s3'')' - ' objects = {''Objects'': [{''Key'': prefix + o} for o in objects]}' - ' s3.delete_objects(Bucket=bucket, Delete=objects)' - '' - '' - 'def timeout(event, context):' - ' logging.error(''Execution is about to time out, sending failure response to CloudFormation'')' - ' cfnresponse.send(event, context, cfnresponse.FAILED, {}, None)' - '' - '' - 'def handler(event, context):' - ' # make sure we send a failure to CloudFormation if the function is going to timeout' - ' timer = threading.Timer((context.get_remaining_time_in_millis() / 1000.00) - 0.5, timeout, args=[event, context])' - ' timer.start()' - '' - ' print(''Received event: %s'' % json.dumps(event))' - ' status = cfnresponse.SUCCESS' - ' try:' - ' source_bucket = event[''ResourceProperties''][''SourceBucket'']' - ' dest_bucket = event[''ResourceProperties''][''DestBucket'']' - ' prefix = event[''ResourceProperties''][''Prefix'']' - ' objects = event[''ResourceProperties''][''Objects'']' - ' if event[''RequestType''] == ''Delete'':' - ' delete_objects(dest_bucket, prefix, objects)' - ' else:' - ' copy_objects(source_bucket, dest_bucket, prefix, objects)' - ' except Exception as e:' - ' logging.error(''Exception: %s'' % e, exc_info=True)' - ' status = cfnresponse.FAILED' - ' finally:' - ' timer.cancel()' - ' cfnresponse.send(event, context, status, {}, None)' - '' LambdaZipsBucket: Type: AWS::S3::Bucket Properties: Tags: [] CopyZips: Type: AWS::CloudFormation::CustomResource Properties: ServiceToken: !GetAtt 'CopyZipsFunction.Arn' DestBucket: !Ref 'LambdaZipsBucket' SourceBucket: !Ref 'QSS3BucketName' Prefix: !Ref 'QSS3KeyPrefix' Objects: - scripts/lambda_codebuild.zip BuildContainers: Type: Custom::BuildContainers Properties: # Different version cause a rebuild. UpdateMe: !Ref AWXVersion ServiceToken: !GetAtt BuildContainersLambda.Arn BuildProjectName: !Ref AWXCodeBuildProject # Logging loglevel: 'debug' # For deletion of the container images. AWXTaskRegistry: !Ref AWXTaskRegistry AWXWebRegistry: !Ref AWXWebRegistry MemcachedRegistry: !Ref MemcachedRegistry RabbitMQRegistry: !Ref RabbitMQRegistry SidecarRegistry: !Ref SidecarRegistry # cfn_signal_url: is part of the event by default hence missing here # cfn_stack_id: is part of the event by default # cfn_request_id: also part of the event # cfn_logical_resource_id: also part of the event # Lambda function passes the above into the build project when invoking StartBuild BuildContainersLambda: DependsOn: [ CopyZips ] Type: AWS::Lambda::Function Properties: Code: S3Bucket: !Ref LambdaZipsBucket S3Key: Fn::Join: - "" - - !Ref QSS3KeyPrefix - "scripts/lambda_codebuild.zip" Handler: !Sub "lambda_codebuild.lambda_handler" Runtime: python3.6 Timeout: 300 Role: !GetAtt BuildContainersLambdaExecutionRole.Arn BuildContainersLambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - codebuild:StartBuild Resource: !GetAtt AWXCodeBuildProject.Arn - Effect: Allow Resource: - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${MemcachedRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${RabbitMQRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${AWXTaskRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${AWXWebRegistry}' - !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${SidecarRegistry}' Action: - ecr:DescribeImages - ecr:ListImages - ecr:BatchGetImage - ecr:BatchDeleteImage - Effect: Allow Resource: - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*' Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents ECSServiceRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: ecs.amazonaws.com Policies: - PolicyName: ecs-service PolicyDocument: Statement: - Effect: Allow Action: - ec2:AuthorizeSecurityGroupIngres - ec2:Describe* - elasticloadbalancing:DeregisterInstancesFromLoadBalancer - elasticloadbalancing:DeregisterTargets - elasticloadbalancing:Describe* - elasticloadbalancing:RegisterInstancesWithLoadBalancer - elasticloadbalancing:RegisterTargets Resource: '*' AWXWebService: Type: AWS::ECS::Service DependsOn: ListenerRule Properties: Cluster: !Ref Cluster Role: !Ref ECSServiceRole DesiredCount: 1 TaskDefinition: !Ref AWXWebTaskDefinition LoadBalancers: - ContainerName: awxweb ContainerPort: 8052 TargetGroupArn: !Ref TargetGroup ContainerLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub ${AWS::StackName}-awx- AWXWebTaskDefinition: Type: AWS::ECS::TaskDefinition DependsOn: BuildContainers Properties: ContainerDefinitions: - Name: awxweb Hostname: awxweb Essential: true Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${AWXWebRegistry}:${AWXVersion} Memory: 1024 PortMappings: - ContainerPort: 8052 HostPort: 80 LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ContainerLogGroup awslogs-region: !Sub ${AWS::Region} awslogs-stream-prefix: awxweb Links: - "awx:awx" - "rabbitmq:rabbitmq" - "memcached:memcached" VolumesFrom: - SourceContainer: "sidecar" Environment: - Name: "http_proxy" Value: "" - Name: "https_proxy" Value: "" - Name: no_proxy Value: "" - Name: SECRET_KEY Value: "awxsecret" - Name: DATABASE_NAME Value: "awx" - Name: DATABASE_USER Value: !Ref MasterUsername - Name: DATABASE_PASSWORD Value: !Ref MasterUserPassword - Name: DATABASE_PORT Value: "5432" - Name: DATABASE_HOST Value: !Ref 'DatabaseEndpoint' - Name: RABBITMQ_USER Value: "guest" - Name: RABBITMQ_PASSWORD Value: "awxpass" - Name: RABBITMQ_HOST Value: "rabbitmq" - Name: RABBITMQ_PORT Value: "5672" - Name: RABBITMQ_VHOST Value: "awx" - Name: MEMCACHED_HOST Value: "memcached" - Name: MEMCACHED_PORT Value: "11211" - Name: AWX_ADMIN_USER Value: !Ref AWXAdminUsername - Name: AWX_ADMIN_PASSWORD Value: !Ref AWXAdminPassword - Name: rabbitmq Hostname: rabbitmq Essential: true Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${RabbitMQRegistry}:${AWXVersion} Memory: 1024 PortMappings: - ContainerPort: 4369 LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ContainerLogGroup awslogs-region: !Sub ${AWS::Region} awslogs-stream-prefix: rabbitmq Environment: - Name: "RABBITMQ_DEFAULT_VHOST" Value: "awx" - Name: "RABBITMQ_DEFAULT_USER" Value: "guest" - Name: "RABBITMQ_DEFAULT_PASS" Value: "awxpass" - Name: "RABBITMQ_ERLANG_COOKIE" Value: "cookiemonster" - Name: awx Hostname: awx Essential: true Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${AWXTaskRegistry}:${AWXVersion} Memory: 2048 PortMappings: - ContainerPort: 8052 Links: - "rabbitmq:rabbitmq" - "memcached:memcached" LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ContainerLogGroup awslogs-region: !Sub ${AWS::Region} awslogs-stream-prefix: awxtask VolumesFrom: - SourceContainer: "sidecar" Environment: - Name: "http_proxy" Value: "" - Name: "https_proxy" Value: "" - Name: no_proxy Value: "" - Name: SECRET_KEY Value: "awxsecret" - Name: DATABASE_NAME Value: "awx" - Name: DATABASE_USER Value: !Ref MasterUsername - Name: DATABASE_PASSWORD Value: !Ref MasterUserPassword - Name: DATABASE_PORT Value: "5432" - Name: DATABASE_HOST Value: !Ref 'DatabaseEndpoint' - Name: RABBITMQ_USER Value: "guest" - Name: RABBITMQ_PASSWORD Value: "awxpass" - Name: RABBITMQ_HOST Value: "rabbitmq" - Name: RABBITMQ_PORT Value: "5672" - Name: RABBITMQ_VHOST Value: "awx" - Name: MEMCACHED_HOST Value: "memcached" - Name: MEMCACHED_PORT Value: "11211" - Name: AWX_ADMIN_USER Value: !Ref AWXAdminUsername - Name: AWX_ADMIN_PASSWORD Value: !Ref AWXAdminPassword - Name: memcached Hostname: memcached Essential: true Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${MemcachedRegistry}:${AWXVersion} Memory: 2048 PortMappings: - ContainerPort: 11211 LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ContainerLogGroup awslogs-region: !Sub ${AWS::Region} awslogs-stream-prefix: memcached - Name: sidecar Hostname: sidecar Essential: true Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${SidecarRegistry}:${AWXVersion} Memory: 512 LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ContainerLogGroup awslogs-region: !Sub ${AWS::Region} awslogs-stream-prefix: sidecar TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: VpcId: !Ref VPC Port: 80 Protocol: HTTP Matcher: HttpCode: 200-499 HealthCheckIntervalSeconds: 10 HealthCheckPath: / HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 ListenerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: ListenerArn: !Ref Listener Priority: 1 Conditions: - Field: path-pattern Values: - "/" Actions: - Type: forward TargetGroupArn: Ref: TargetGroup Listener: Type: AWS::ElasticLoadBalancingV2::Listener DependsOn: BuildContainers Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref 'TargetGroup' LoadBalancerArn: !Ref 'ALBARN' Port: 80 Protocol: HTTP