diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f443b4b..d8ed70c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,5 +77,5 @@ jobs: uses: aws-actions/aws-cloudformation-github-deploy@v1 with: name: xt-fiddle - template: cloudformation/deploy.yml - parameter-overrides: "HostedZoneId=${{ secrets.HOSTED_ZONE_ID }},HostedZoneName=${{ secrets.HOSTED_ZONE_NAME }},HostedZoneName=${{ steps.vars.outputs.tag }}" + template: cloudformation/service.yml + parameter-overrides: "DockerTag=${{ steps.vars.outputs.tag }}" diff --git a/cloudformation/deploy.yml b/cloudformation/deploy.yml index 749bdd4..357ca40 100644 --- a/cloudformation/deploy.yml +++ b/cloudformation/deploy.yml @@ -8,14 +8,9 @@ Parameters: HostedZoneName: Type: 'String' Description: 'The zone name for the hosted zone' - DockerTag: - Type: 'String' - Default: 'latest' - Description: 'The zone name for the hosted zone' Resources: # >> VPC - # TODO: Use sub-stacks? VPC: Type: AWS::EC2::VPC @@ -24,6 +19,13 @@ Resources: EnableDnsSupport: true EnableDnsHostnames: true + SSMVPC: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: 'xt-fiddle_vpc-id' + Value: !Ref VPC + InternetGateway: Type: AWS::EC2::InternetGateway @@ -57,6 +59,13 @@ Resources: CidrBlock: 10.0.20.0/24 MapPublicIpOnLaunch: false + SSMPrivateSubnetOne: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: 'xt-fiddle_private-subnet-1' + Value: !Ref PrivateSubnetOne + PrivateSubnetTwo: Type: AWS::EC2::Subnet Properties: @@ -65,6 +74,13 @@ Resources: CidrBlock: 10.0.21.0/24 MapPublicIpOnLaunch: false + SSMPrivateSubnetTwo: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: 'xt-fiddle_private-subnet-2' + Value: !Ref PrivateSubnetTwo + PublicRouteTable: Type: AWS::EC2::RouteTable Properties: @@ -215,6 +231,13 @@ Resources: HealthCheckPath: /status HealthCheckTimeoutSeconds: 5 + SSMECSTargetGroup: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: 'xt-fiddle_target-group-arn' + Value: !Ref ECSTargetGroup + # Redirect all HTTP traffic to HTTPS HTTPALBListener: Type: 'AWS::ElasticLoadBalancingV2::Listener' @@ -245,186 +268,3 @@ Resources: DefaultActions: - Type: forward TargetGroupArn: !Ref ECSTargetGroup - - - # >> ECS Deployment - - ECSCluster: - Type: AWS::ECS::Cluster - Properties: - ClusterName: 'xt-fiddle' - - LogGroup: - Type: AWS::Logs::LogGroup - Properties: - LogGroupName: 'xt-fiddle' - RetentionInDays: 365 - - TaskExecutionRole: - Type: 'AWS::IAM::Role' - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: 'Allow' - Principal: - Service: 'ecs-tasks.amazonaws.com' - Action: 'sts:AssumeRole' - ManagedPolicyArns: - - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' - - TaskRole: - Type: 'AWS::IAM::Role' - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: 'Allow' - Principal: - Service: 'ecs-tasks.amazonaws.com' - Action: 'sts:AssumeRole' - - ContainerRepo: - Type: AWS::ECR::Repository - Properties: - RepositoryName: xt-fiddle - - # TODO: Add scaling - TaskDefinition: - Type: 'AWS::ECS::TaskDefinition' - Properties: - Family: 'xt-fiddle' - RequiresCompatibilities: - - 'FARGATE' - NetworkMode: 'awsvpc' - - # TODO: Scale up/down? - Cpu: '1024' - Memory: '4096' - ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn - TaskRoleArn: !GetAtt TaskRole.Arn - - ContainerDefinitions: - - Name: 'xt-fiddle' - Image: !Join [":", [!GetAtt ContainerRepo.RepositoryUri, !Ref DockerTag]] - Essential: true - PortMappings: - - ContainerPort: 8000 - LogConfiguration: - LogDriver: awslogs - Options: - awslogs-group: !Ref LogGroup - awslogs-region: !Ref 'AWS::Region' - awslogs-stream-prefix: xt-fiddle - - TaskSecurityGroup: - Type: 'AWS::EC2::SecurityGroup' - Properties: - VpcId: !Ref VPC - GroupDescription: Security group allowing access to container - SecurityGroupIngress: - - CidrIp: 0.0.0.0/0 - IpProtocol: tcp - FromPort: 8000 - ToPort: 8000 - SecurityGroupEgress: - - CidrIp: 0.0.0.0/0 - IpProtocol: -1 - - ECSService: - Type: 'AWS::ECS::Service' - Properties: - Cluster: !Ref ECSCluster - TaskDefinition: !Ref TaskDefinition - LaunchType: 'FARGATE' - - DesiredCount: 1 - HealthCheckGracePeriodSeconds: 60 - - NetworkConfiguration: - AwsvpcConfiguration: - AssignPublicIp: 'DISABLED' - SecurityGroups: - - !Ref TaskSecurityGroup - Subnets: - - !Ref PrivateSubnetOne - - !Ref PrivateSubnetTwo - - LoadBalancers: - - ContainerName: xt-fiddle - ContainerPort: '8000' - TargetGroupArn: !Ref ECSTargetGroup - - - # >> Github deploy IAM Role - - GithubDeployUser: - Type: 'AWS::IAM::User' - Properties: - Policies: - # ECR Push - - PolicyName: ecr-allow-push - PolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Action: - - ecr:CompleteLayerUpload - - ecr:GetAuthorizationToken - - ecr:UploadLayerPart - - ecr:InitiateLayerUpload - - ecr:BatchCheckLayerAvailability - - ecr:PutImage - - ecr:BatchGetImage - Resource: "*" - # Resource: - # - !GetAtt ContainerRepo.Arn - # https://github.com/aws-actions/amazon-ecs-deploy-task-definition?tab=readme-ov-file#permissions - - PolicyName: task-definition-update - PolicyDocument: - Version: '2012-10-17' - Statement: - - Sid: RegisterTaskDefinition - Effect: Allow - Action: - - ecs:RegisterTaskDefinition - Resource: "*" - - Sid: PassRolesInTaskDefinition - Effect: Allow - Action: - - iam:PassRole - Resource: - - !GetAtt TaskRole.Arn - - !GetAtt TaskExecutionRole.Arn - - Sid: DeployService - Effect: Allow - Action: - - ecs:UpdateService - - ecs:DescribeServices - Resource: - - !GetAtt ECSCluster.Arn - # https://github.com/aws-actions/aws-cloudformation-github-deploy?tab=readme-ov-file#permissions - - PolicyName: cloudformation-deploy - PolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Action: - - cloudformation:CreateStack - - cloudformation:DescribeStacks - - cloudformation:CreateChangeSet - - cloudformation:DescribeChangeSet - - cloudformation:DeleteChangeSet - - cloudformation:ExecuteChangeSet - Resource: "*" - - GithubDeployUserAccessKey: - Type: 'AWS::IAM::AccessKey' - Properties: - UserName: !Ref GithubDeployUser - -Outputs: - GithubDeployAccessKeyId: - Value: !Ref GithubDeployUserAccessKey - GithubDeploySecretAccessKey: - Value: !GetAtt GithubDeployUserAccessKey.SecretAccessKey diff --git a/cloudformation/github-keys.yml b/cloudformation/github-keys.yml new file mode 100644 index 0000000..83b80b0 --- /dev/null +++ b/cloudformation/github-keys.yml @@ -0,0 +1,76 @@ +--- +AWSTemplateFormatVersion: '2010-09-09' + +Resources: + # >> Github deploy IAM Role + + GithubDeployUser: + Type: 'AWS::IAM::User' + Properties: + Policies: + # ECR Push + - PolicyName: ecr-allow-push + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - ecr:CompleteLayerUpload + - ecr:GetAuthorizationToken + - ecr:UploadLayerPart + - ecr:InitiateLayerUpload + - ecr:BatchCheckLayerAvailability + - ecr:PutImage + - ecr:BatchGetImage + Resource: "*" + # Resource: + # - !GetAtt ContainerRepo.Arn + # https://github.com/aws-actions/amazon-ecs-deploy-task-definition?tab=readme-ov-file#permissions + - PolicyName: task-definition-update + PolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: RegisterTaskDefinition + Effect: Allow + Action: + - ecs:RegisterTaskDefinition + Resource: "*" + - Sid: PassRolesInTaskDefinition + Effect: Allow + Action: + - iam:PassRole + Resource: + - '{{resolve:ssm:xt-fiddle_task-role-arn}}' + - '{{resolve:ssm:xt-fiddle_task-execution-role-arn}}' + - Sid: DeployService + Effect: Allow + Action: + - ecs:UpdateService + - ecs:DescribeServices + Resource: + - '{{resolve:ssm:xt-fiddle_ecs-cluster-arn}}' + # https://github.com/aws-actions/aws-cloudformation-github-deploy?tab=readme-ov-file#permissions + - PolicyName: cloudformation-deploy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - cloudformation:CreateStack + - cloudformation:DescribeStacks + - cloudformation:CreateChangeSet + - cloudformation:DescribeChangeSet + - cloudformation:DeleteChangeSet + - cloudformation:ExecuteChangeSet + Resource: "*" + + GithubDeployUserAccessKey: + Type: 'AWS::IAM::AccessKey' + Properties: + UserName: !Ref GithubDeployUser + +Outputs: + GithubDeployAccessKeyId: + Value: !Ref GithubDeployUserAccessKey + GithubDeploySecretAccessKey: + Value: !GetAtt GithubDeployUserAccessKey.SecretAccessKey diff --git a/cloudformation/service.yml b/cloudformation/service.yml new file mode 100644 index 0000000..331b1af --- /dev/null +++ b/cloudformation/service.yml @@ -0,0 +1,135 @@ +--- +AWSTemplateFormatVersion: '2010-09-09' + +Parameters: + DockerTag: + Type: 'String' + Default: 'latest' + Description: 'The zone name for the hosted zone' + +Resources: + ECSCluster: + Type: AWS::ECS::Cluster + Properties: + ClusterName: 'xt-fiddle' + + SSMECSCluster: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: 'xt-fiddle_ecs-cluster-arn' + Value: !GetAtt ECSCluster.Arn + + LogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: 'xt-fiddle' + RetentionInDays: 365 + + TaskExecutionRole: + Type: 'AWS::IAM::Role' + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: 'Allow' + Principal: + Service: 'ecs-tasks.amazonaws.com' + Action: 'sts:AssumeRole' + ManagedPolicyArns: + - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' + + SSMTaskExecutionRole: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: 'xt-fiddle_task-execution-role-arn' + Value: !GetAtt TaskExecutionRole.Arn + + TaskRole: + Type: 'AWS::IAM::Role' + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: 'Allow' + Principal: + Service: 'ecs-tasks.amazonaws.com' + Action: 'sts:AssumeRole' + + SSMTaskRole: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: 'xt-fiddle_task-role-arn' + Value: !GetAtt TaskRole.Arn + + ContainerRepo: + Type: AWS::ECR::Repository + Properties: + RepositoryName: xt-fiddle + + # TODO: Add scaling + TaskDefinition: + Type: 'AWS::ECS::TaskDefinition' + Properties: + Family: 'xt-fiddle' + RequiresCompatibilities: + - 'FARGATE' + NetworkMode: 'awsvpc' + + # TODO: Scale up/down? + Cpu: '1024' + Memory: '4096' + ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn + TaskRoleArn: !GetAtt TaskRole.Arn + + ContainerDefinitions: + - Name: 'xt-fiddle' + Image: !Join [":", [!GetAtt ContainerRepo.RepositoryUri, !Ref DockerTag]] + Essential: true + PortMappings: + - ContainerPort: 8000 + LogConfiguration: + LogDriver: awslogs + Options: + awslogs-group: !Ref LogGroup + awslogs-region: !Ref 'AWS::Region' + awslogs-stream-prefix: xt-fiddle + + TaskSecurityGroup: + Type: 'AWS::EC2::SecurityGroup' + Properties: + VpcId: '{{resolve:ssm:xt-fiddle_vpc-id}}' + GroupDescription: Security group allowing access to container + SecurityGroupIngress: + - CidrIp: 0.0.0.0/0 + IpProtocol: tcp + FromPort: 8000 + ToPort: 8000 + SecurityGroupEgress: + - CidrIp: 0.0.0.0/0 + IpProtocol: -1 + + ECSService: + Type: 'AWS::ECS::Service' + Properties: + Cluster: !Ref ECSCluster + TaskDefinition: !Ref TaskDefinition + LaunchType: 'FARGATE' + + DesiredCount: 1 + HealthCheckGracePeriodSeconds: 60 + + NetworkConfiguration: + AwsvpcConfiguration: + AssignPublicIp: 'DISABLED' + SecurityGroups: + - !Ref TaskSecurityGroup + Subnets: + - '{{resolve:ssm:xt-fiddle_private-subnet-1}}' + - '{{resolve:ssm:xt-fiddle_private-subnet-2}}' + LoadBalancers: + - ContainerName: xt-fiddle + ContainerPort: '8000' + TargetGroupArn: '{{resolve:ssm:xt-fiddle_target-group-arn}}'