From 35c34a215572a229387ebf35ab37ed7a6d3d18e7 Mon Sep 17 00:00:00 2001 From: Joseph Manley Date: Sat, 23 May 2020 23:51:34 -0400 Subject: [PATCH] Intial commit --- .github/workflows/build_stage.yml | 38 ++++++++ cloudformation/dns.yaml | 44 ++++++++++ cloudformation/load_balancing.yaml | 136 +++++++++++++++++++++++++++++ cloudformation/task.yaml | 41 +++++++++ cloudformation/top.yaml | 112 ++++++++++++++++++++++++ 5 files changed, 371 insertions(+) create mode 100644 .github/workflows/build_stage.yml create mode 100644 cloudformation/dns.yaml create mode 100644 cloudformation/load_balancing.yaml create mode 100644 cloudformation/task.yaml create mode 100644 cloudformation/top.yaml diff --git a/.github/workflows/build_stage.yml b/.github/workflows/build_stage.yml new file mode 100644 index 0000000..6e42e80 --- /dev/null +++ b/.github/workflows/build_stage.yml @@ -0,0 +1,38 @@ + +name: Deploy Environment + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v1 + - name: Ship to S3 + uses: jakejarvis/s3-sync-action@master + with: + args: --follow-symlinks --delete + env: + SOURCE_DIR: "./" + AWS_REGION: "us-east-1" + DEST_DIR: jenkins/production + AWS_S3_BUCKET: ${{ secrets.DEPLOY_BUCKET }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + - name: Deploy to AWS CloudFormation + uses: aws-actions/aws-cloudformation-github-deploy@v1 + with: + name: ecs-jenkins + template: cloudformation/top.yaml + capabilities: "CAPABILITY_NAMED_IAM,CAPABILITY_IAM" + parameter-overrides: VpcId=${{ secrets.VPC_ID }},PublicSubnets=${{ secrets.SUBNET_IDS }},EcsCluster=${{ secrets.CLUSTER }},PortalCertificate=${{ secrets.CERTIFICATE }},Domain=${{ secrets.DOMAIN }} \ No newline at end of file diff --git a/cloudformation/dns.yaml b/cloudformation/dns.yaml new file mode 100644 index 0000000..c1d79c3 --- /dev/null +++ b/cloudformation/dns.yaml @@ -0,0 +1,44 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: Jenkins DNS stack +Parameters: + #------------------------ + # Deployment Information + #------------------------ + environment: + Type: String + Description: Name of the environment + Default: production + + #----------------------- + # Route53 Configuration + #----------------------- + Domain: + Type: String + Description: The HostedZoneName to create the endpoint on + SubDomain: + Type: String + Description: The subdomain to be used by jenkins + + #----------- + # Resources + #----------- + JenkinsDns: + Type: String + Description: Load balancer DNS endpoint for Jenkins + +Resources: + JenkinsEndpoint: + Type: AWS::Route53::RecordSet + Properties: + HostedZoneName: !Sub "${Domain}." + Comment: 'DNS name for jenkins' + Name: !Sub "${SubDomain}.${Domain}." + Type: CNAME + TTL: '300' + ResourceRecords: + - !Ref JenkinsDns + +Outputs: + JenkinsEndpoint: + Description: 'DNS name for Jenkins' + Value: !Sub "${SubDomain}.${Domain}." \ No newline at end of file diff --git a/cloudformation/load_balancing.yaml b/cloudformation/load_balancing.yaml new file mode 100644 index 0000000..4b7f76d --- /dev/null +++ b/cloudformation/load_balancing.yaml @@ -0,0 +1,136 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: Jenkins load balancing stack +Parameters: + environment: + Type: String + Description: Name of the environment + Default: production + release: + Type: String + Description: Name of the release name of the stack version to use. + Default: production + AllowedValues: ['develop', 'production'] + ConstraintDescription: "Must be a possible release version." + PublicSubnets: + Description: The public subnets for the ALB to run in. + Type: String + PortalCertificate: + Description: Arn of AWS Certificate + Type: String + VpcId: + Description: ID of the VPC + Type: AWS::EC2::VPC::Id + +Resources: + + #-- Application Load Balancer --# + PublicALB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Type: application + LoadBalancerAttributes: + - Key: deletion_protection.enabled + Value: false + - Key: idle_timeout.timeout_seconds + Value: 60 + Scheme: internet-facing + SecurityGroups: + - !Ref AlbSecurityGroup + Subnets: !Split [",", !Ref PublicSubnets] + Tags: + - Key: Name + Value: !Sub "Jenkins-${environment}-ALB" + - Key: environment + Value: !Ref environment + + AlbSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: ECS Allowed Ports + VpcId: !Ref VpcId + SecurityGroupIngress: + - IpProtocol: icmp + FromPort: "-1" + ToPort: "-1" + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: "443" + ToPort: "443" + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: "80" + ToPort: "80" + CidrIp: 0.0.0.0/0 + SecurityGroupEgress: + - IpProtocol: icmp + FromPort: "-1" + ToPort: "-1" + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: "0" + ToPort: "65535" + CidrIp: 0.0.0.0/0 + - IpProtocol: udp + FromPort: "0" + ToPort: "65535" + CidrIp: 0.0.0.0/0 + + # Target group for admin portal port + TargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + HealthCheckIntervalSeconds: 30 + HealthCheckProtocol: HTTP + HealthCheckTimeoutSeconds: 15 + HealthyThresholdCount: 2 + UnhealthyThresholdCount: 2 + Matcher: + HttpCode: '200' + HealthCheckPath: '/api/health' + Port: 3000 + Protocol: HTTP + TargetGroupAttributes: + - Key: deregistration_delay.timeout_seconds + Value: '20' + VpcId: !Ref 'VpcId' + Tags: + - Key: Name + Value: !Sub 'jenkins-${release}' + + # HTTPS for Admin Portal + AlbListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + Certificates: + - CertificateArn: !Ref PortalCertificate + DefaultActions: + - Type: forward + TargetGroupArn: !Ref TargetGroup + LoadBalancerArn: !Ref PublicALB + Port: 443 + Protocol: HTTPS + + # Redirect HTTP -> HTTPS + RedirectAlbListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: redirect + RedirectConfig: + Protocol: HTTPS + Port: 443 + Host: '#{host}' + Path: '/#{path}' + Query: '#{query}' + StatusCode: HTTP_301 + LoadBalancerArn: !Ref PublicALB + Port: 80 + Protocol: HTTP + +Outputs: + AdminPortalTargetGroup: + Description: "" + Value: !Ref TargetGroup + PublicAlbDnsName: + Description: "" + Value: !GetAtt PublicALB.DNSName \ No newline at end of file diff --git a/cloudformation/task.yaml b/cloudformation/task.yaml new file mode 100644 index 0000000..e16a1ef --- /dev/null +++ b/cloudformation/task.yaml @@ -0,0 +1,41 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Jenkins ECS Task +Parameters: + LogGroupName: + Type: String + Description: The AWS CloudWatch log group to output logs to. + Default: "/ecs/jenkins" + +Resources: + + + LogGroup: + Type: AWS::Logs::LogGroup + Properties: + RetentionInDays: 7 + LogGroupName: !Ref LogGroupName + + TaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + ContainerDefinitions: + - Name: jenkins + Essential: 'true' + Image: "jenkins" + MemoryReservation: 800 + PortMappings: + - HostPort: 0 + ContainerPort: 8080 + - HostPort: 0 + ContainerPort: 50000 + LogConfiguration: + LogDriver: awslogs + Options: + awslogs-region: + Ref: AWS::Region + awslogs-group: + Ref: LogGroup +Outputs: + TaskArn: + Description: ARN of the TaskDefinition + Value: !Ref TaskDefinition \ No newline at end of file diff --git a/cloudformation/top.yaml b/cloudformation/top.yaml new file mode 100644 index 0000000..a7b0df0 --- /dev/null +++ b/cloudformation/top.yaml @@ -0,0 +1,112 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Jenkins ECS Service +Parameters: + #------------------------ + # Deployment Information + #------------------------ + environment: + Type: String + Description: Name of the environment to use in naming. + Default: production + release: + Type: String + Description: Name of the release name of the stack version to use. + Default: production + AllowedValues: ['develop', 'production'] + ConstraintDescription: "Must be a possible release version." + VpcId: + Description: ID of the VPC + Type: AWS::EC2::VPC::Id + + #------------------- + # ECS Configuration + #------------------- + EcsCluster: + Type: String + Description: The cluster to run the Jenkins service on. + + #----------------- + # Load Balancing + #----------------- + PublicSubnets: + Description: The public subnets for the ALB to run in. + Type: String + PortalCertificate: + Description: Arn of AWS Certificate + Type: String + + #----- + # DNS + #----- + Domain: + Type: String + Description: The domain to create the endpoint on (Must have an existing hosted zone ex. `example.com`) Leave blank to skip DNS. + Default: "" + SubDomain: + Type: String + Description: The subdomain to be used by jenkins. (ex. `jenkins.example.com`) + Default: jenkins + + #------------ + # CloudWatch + #------------ + LogGroup: + Type: String + Description: The AWS CloudWatch log group to output logs to. + Default: "/ecs/jenkins" + +Conditions: + CreateDns: !Not [!Equals [!Ref Domain, ""]] + +Resources: + + #----- + # DNS + #----- + DnsRecords: + Condition: CreateDns + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub 'https://s3.us-east-1.amazonaws.com/sumu-stacks/jenkins/${release}/cloudformation/jenkins/dns.yaml' + Parameters: + environment: !Ref environment + Domain: !Ref Domain + SubDomain: !Ref SubDomain + JenkinsDns: !GetAtt LoadBalancing.Outputs.PublicAlbDnsName + + #----------------- + # Load Balancing + #----------------- + LoadBalancing: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub 'https://s3.us-east-1.amazonaws.com/sumu-stacks/jenkins/${release}/cloudformation/jenkins/load_balancing.yaml' + Parameters: + environment: !Ref environment + release: !Ref release + VpcId: !Ref VpcId + PublicSubnets: !Ref PublicSubnets + PortalCertificate: !Ref PortalCertificate + + #------------------- + # ECS Task & Service + #------------------- + TaskDefinition: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub 'https://s3.us-east-1.amazonaws.com/sumu-stacks/jenkins/${release}/cloudformation/jenkins/task.yaml' + Parameters: + LogGroupName: !Ref LogGroup + + + EcsService: + DependsOn: LoadBalancing + Type: AWS::ECS::Service + Properties: + Cluster: !Ref EcsCluster + DesiredCount: 1 + TaskDefinition: !GetAtt TaskDefinition.Outputs.TaskArn + LoadBalancers: + - ContainerName: jenkins + ContainerPort: 8080 + TargetGroupArn: !GetAtt LoadBalancing.Outputs.AdminPortalTargetGroup \ No newline at end of file