diff --git a/.dockerignore b/.dockerignore index 4635812..5cf72d5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,14 +1,24 @@ **/.DS_Store -.dockerignore -.env.example -*.env -.git -/deploy-templates -/log/* -/tmp/* /node_modules -/.bundle -/public/assets /_*/ + +/.bundle +/.circleci +/.dockerignore +/.elasticbeanstalk +/.env* +/.git +/.gitignore +/.rspec +/.rubocop* +/.qa_server_app + /coverage/* +/db/*.sqlite3 +/log/*.log +/public/assets +/spec/* +/tmp/* +/vendor/* +/docker-compose.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 154a6b7..d042b90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +Release v2.0.0 (2022-04-22) + +* adjust styling for check status selection and padding in tables +* use .dockerignore to not include files that aren't required for the image to run +* remove deploy templates (moved to LD4P/qa_server_aws_deploy) +* log release version in deploy log for debugging + Release v1.0.2 (2022-04-15) * add ability to manually build and push images to ECR diff --git a/VERSION b/VERSION index 570c796..46b105a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.0.2 +v2.0.0 diff --git a/app/assets/stylesheets/qa_server_container.css b/app/assets/stylesheets/qa_server_container.css index f574cea..d842b87 100644 --- a/app/assets/stylesheets/qa_server_container.css +++ b/app/assets/stylesheets/qa_server_container.css @@ -184,10 +184,6 @@ table{ border-spacing:0 } -td,th{ - padding:0 -} - /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{ *,*:before,*:after{ background:transparent !important; @@ -9632,4 +9628,6 @@ h2{ } - +select#authority { + width: auto; +} diff --git a/bin/docker-entrypoint.sh b/bin/docker-entrypoint.sh index 5d58ec0..c540e26 100755 --- a/bin/docker-entrypoint.sh +++ b/bin/docker-entrypoint.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash # ! /bin/sh -echo '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' -echo '----------------------- RUNNING ENTRY POINT -----------------------' +echo '=====================================================================' +echo ' RUNNING ENTRY POINT' +echo '---------------------------------------------------------------------' +echo "From qa_server_container image: $(< ./VERSION)" echo '=====================================================================' set -e diff --git a/deploy-templates/README.md b/deploy-templates/README.md deleted file mode 100644 index 9a62908..0000000 --- a/deploy-templates/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Deployment Templates for Amazon Web Services (AWS) - -This deployment has three basic pieces: -* The application, running within a container that is hosted in Amazon Elastic Container Service (ECS) -* An environment file, residing in a S3 bucket that the container has permission to access -* A filesystem containing two volumes that the container can mount. This filesystem can be implemented either as an Elastic Block Store (EBS) volume or an Elastic File System (EFS). - -Two templates are provided, a CloudFormation template and a standalone ECS task definition. Both require customization to your environment by populating various values; in the CloudFormation version these values can be passed in as parameters when the stack is created or updated. - -# Prerequisites - -This deployment assumes that you have an AWS account, and an Identity and Access Management (IAM) account within your AWS account that allows enough access to create, update, and destroy all the necessary resources. If your IAM user lacks sufficient privileges, your deployment will stop with an error message telling you so. - -To use this deployment, you will need the following pieces of infrastructure in place in your AWS account before you begin: -1. A repo containing a successfully built Docker image. This can be an Elastic Container Registry (ECR) repository or a public or private repo hosted elsewhere. Building the application image and pushing it to the repo should already be done, and you will need the full path to the image, including the repository URL and image tag (usually :latest). Full documentation on this process is available at . -2. A Simple Storage Service (S3) bucket containing an environment file that has been populated with valid values. The file contains secrets and other parameters necessary for the application to run, an .env.example file has been provided as a template. The S3 bucket must contain this file, and the bucket permissions must be configured to allow access from the Amazon ECS task execution IAM role, but it should not be open to the public. Full documentation on setup is available at https://docs.aws.amazon.com/AmazonECS/latest/developerguide/taskdef-envfiles.html. -3. A filesystem that the containers can mount. At this point, only EFS volumes are supported, EBS will be supported in the future. This deployment assumes one EFS filesystem with two separate access points. The first access point contains the authority files, and it should be pre-populated with the files you want to use. The second will contain the database storage files, and this should provision itself on the first run. -While it's not strictly required, you should also have control over a domain space where you are able to create and update DNS records. - -# Files and Templates - -This folder contains several files you can use to deploy the qa-server container into ECS after the above prerequisites have been met. The most complete solution can be found in the aws-cloudformation.yaml file, which will create all the necessary resources including an EC2 cluster, an ECS task definition, and an ECS service with a public-facing load balancer. To use this template, you will need to make a copy of parameters.env.example and rename it to parameters.env, then populate it with the values appropriate to your environment. CloudFormation will read those values from the file if you pass it on the command line when you create or update the stack. Instructions on the individual parameters can be found in the CloudFormation template. - -For those who may not want to create all of the resources from scratch, you can simply edit the CloudFormation template to exclude the unwanted resources; if you have an existing infrastructure and you only want the ECS task definition, that is provided in the form of the task-definition.json file. The task definition file also requires you to fill in values appropriate to your environment, however, as a simple JSON document it does not allow for variable substitution (or even comments). The variables have been indicated with angle brackets, and they have been named consistent with the variables in the CloudFormation template, so you can use the instructions in the latter to help with filling them in. Once the values are in place, the JSON file can be used to create an ECS task definition capable of running the QA server container. - -# Prerequisites - -Something about the template here - -When the prerequisites template runs successfully, it will provision the necessary resources, and it will output four pieces of information needed for the next stage: the name of the S3 bucket, the id of the EFS filesystem, and the ids of the two EFS access points. Before you proceed with the next template, you need to upload a parameters file to the S3 bucket, and you need to place at least one authority file to the EFS access point dedicated to the authority files. - -To upload authority files to the authority EFS access point, use the following commands to mount the authority EFS access point as a directory on an EC2 Linux server. -``` -sudo mkdir /qa-server -sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport *EFSFilesystemId*.efs.*aws-region*.amazonaws.com:/authorities /qa-server -``` -Once the authority EFS access point is mounted, simply copy one or more authority files to the filesystem. - -The next step is to upload an environment file to the S3 bucket. A template for the environment file is provided in this repo as .env.example; make a copy and rename it, and populate the values with ones appropriate to your environment. For the purposes of this deployment, the AUTHORITIES_PATH variable should simply be `authorities`. \ No newline at end of file diff --git a/deploy-templates/aws-cloudformation.yaml b/deploy-templates/aws-cloudformation.yaml deleted file mode 100644 index 7a130fe..0000000 --- a/deploy-templates/aws-cloudformation.yaml +++ /dev/null @@ -1,453 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Description: > - 1. Make a copy of parameters.env.example and rename it to parameters.env, then populate it with the values appropriate to your environment. CloudFormation will read those values from the file if you pass it on the command line when you create or update the stack. See the parameter definitions below for full instructions. - 2. Navigate to the directory that contains these files and run the following aws cli command: - aws cloudformation create-stack --stack-name qa-server \ - --template-body file://./aws-cloudformation.yaml \ - --parameters file://./parameters-copy.env --capabilities CAPABILITY_NAMED_IAM \ - --profile qa-useer --role-arn arn:aws:iam:::role/qa-role - 3. You will need to validate the ACM certificate after the stack has been created, by creating the DNS record that will appear in the CloudFormation and ACM consoles. - -Parameters: - AWSVpcId: - Type: String - Default: '' - Description: Your AWS VPC ID. It should look like vpc-1a2b3c4d. Required for load balancer configuration and security group rules. - SubnetIds: - Type: List - Description: Select two subnets in your selected VPC. The values must be separated by commas. - TaskDefinitionName: - Type: String - Default: '' - Description: Name of the task definition and the task definition family. This can be anything you like, but should not contain spaces. It is reused in naming the load balancer and the log groups. - S3BucketName: - Type: String - Default: '' - Description: Name of the S3 bucket where the environment file resides. If you ran the prerequisites template, this value should match the one you used in that template. - EnvFileName: - Type: String - Default: '' - Description: Name of the environment file. The environment file must exist prior to running this template, or else the application will fail to launch. You can use a copy of the parameters.env.example file, populated with values appropriate to your environment. - ImageLocation: - Type: String - Default: '' - Description: Full path to the container image for the qa-server application, including repository URL and tag. This must be a repo within Amazon ECR, or a publicly accessible repo. Private repo authentication is not available at this time. - VolumeId: - Type: String - Default: '' - Description: ID of the volume where the database and authority files are stored. This is an EFS volume that must exist for the application to launch. The EFS volume must contain two access points that are specified in the next two parameters. - AuthorityAccessPointId: - Type: String - Default: '' - Description: EFS Access Point ID of the volume where the authority files are stored. This will map to the application's authority file volume, and it should contain at least one authority file. If no authority files are found, the application will still launch, and static portions will display, but the Authorities, Check Status, and Monitor Status pages may behave unpredictably. - DatabaseAccessPointId: - Type: String - Default: '' - Description: EFS Access Point ID of the volume where the database is stored. This can be empty at first launch, and it should be reused in subsequent launches if persistence of the data is desired. - DomainName: - Type: String - Default: '' - Description: Domain name to use for the public facing service. This is only used to provision the ACM certificate for the load balancer to serve secure connections. If this is not desired, the certificate resource can be removed from this template, along with the load balancer listener for port 443 that requires the certificate. If this domain is hosted in Route 53, nothing need be done to validate the domain. If it is not hosted in Route 53, you will need to validate the domain by creating a CNAME record with a unique value. You will find the value in the output of this template. - InstanceType: - Description: EC2 instance type for the cluster. This is recommended to be a type with at least 2 CPUs and 8 GiB of RAM, but you can change it here if you need to. - Type: String - Default: m5.large -Mappings: - AWSRegionToAMI: - Description: This list of ECS-optimized AMIs will change over time and will need to be maintained. You can get the most recent AMI for your current region with the command `aws ssm get-parameters --names /aws/service/ecs/optimized-ami/amazon-linux-2/recommended`. If your region is known and does not change, the others can be ignored or removed safely. - us-east-1: - AMIID: ami-08a29dcf20b8fea61 - us-east-2: - AMIID: ami-0a9e12068cb98a01d - us-west-1: - AMIID: ami-0fa6c8d131a220017 - us-west-2: - AMIID: ami-078c97cf1cefd1b38 - eu-west-1: - AMIID: ami-0c9ef930279337028 - eu-central-1: - AMIID: ami-065c1e34da68f2b02 - ap-northeast-1: - AMIID: ami-02265963d1614d04d - ap-southeast-1: - AMIID: ami-0b68661b29b9e058c - ap-southeast-2: - AMIID: ami-00e4b147599c13588 -Resources: - TaskDefinition: - Type: AWS::ECS::TaskDefinition - Properties: - Family: !Ref TaskDefinitionName - ContainerDefinitions: - - Name: 'qa-server-app' - Cpu: 0 - EnvironmentFiles: - - Type: s3 - Value: !Join ["", ["arn:aws:s3:::", !Ref S3BucketName, "/", !Ref EnvFileName]] - Essential: True - Image: !Ref ImageLocation - Links: - - qa-mariadb - LogConfiguration: - LogDriver: awslogs - Options: - awslogs-group: !Join ["", ["/ecs/", !Ref AWS::StackName, "-lb"]] - awslogs-region: us-east-1 - awslogs-stream-prefix: ecs - MemoryReservation: 512 - MountPoints: - - SourceVolume: qa-server-authorities - ContainerPath: /app/ld4p/qa_server-webapp/config/authorities - PortMappings: - - HostPort: 3000 - Protocol: tcp - ContainerPort: 3000 - - Name: 'qa-mariadb' - Cpu: 0 - EnvironmentFiles: - - Type: s3 - Value: !Join ["", ["arn:aws:s3:::", !Ref S3BucketName, "/", !Ref EnvFileName]] - Essential: True - Image: mariadb:latest - LogConfiguration: - LogDriver: awslogs - Options: - awslogs-group: !Join ["", ["/ecs/", !Ref AWS::StackName, "-lb"]] - awslogs-region: us-east-1 - awslogs-stream-prefix: ecs - MemoryReservation: 512 - MountPoints: - - SourceVolume: db-mysql-data - ContainerPath: /var/lib/mysql/data - PortMappings: - - HostPort: 3306 - Protocol: tcp - ContainerPort: 3306 - Cpu: 1024 - ExecutionRoleArn: !GetAtt TaskRole.Arn - Memory: 1024 - NetworkMode: bridge - RequiresCompatibilities: - - EC2 - TaskRoleArn: !GetAtt TaskRole.Arn - Volumes: - - Name: qa-server-authorities - EFSVolumeConfiguration: - FilesystemId: !Ref VolumeId - AuthorizationConfig: - AccessPointId: !Ref AuthorityAccessPointId - RootDirectory: "/" - TransitEncryption: ENABLED - - Name: db-mysql-data - EFSVolumeConfiguration: - FilesystemId: !Ref VolumeId - AuthorizationConfig: - AccessPointId: !Ref DatabaseAccessPointId - RootDirectory: "/" - TransitEncryption: ENABLED - CloudwatchLogGroup: - Type: AWS::Logs::LogGroup - Properties: - LogGroupName: !Join ["", ["/ecs/", !Ref AWS::StackName, "-lb"]] - RetentionInDays: 7 - EcsCluster: - Type: AWS::ECS::Cluster - Properties: - ClusterName: !Join ["", [!Ref AWS::StackName, "-cluster"]] - Service: - Type: AWS::ECS::Service - DependsOn: - - Listener443 - - Listener80 - Properties: - Cluster: !Join ["", [!Ref AWS::StackName, "-cluster"]] - DeploymentConfiguration: - MaximumPercent: 200 - MinimumHealthyPercent: 100 - DesiredCount: 2 - EnableECSManagedTags: true - HealthCheckGracePeriodSeconds: 0 - LaunchType: "EC2" - LoadBalancers: - - ContainerName: "qa-server-app" - ContainerPort: 3000 - TargetGroupArn: !Ref TargetGroup3000 - PlacementStrategies: - - Field: "attribute:ecs.availability-zone" - Type: "spread" - - Field: "instanceId" - Type: "spread" - SchedulingStrategy: "REPLICA" - ServiceName: "qa-server-service-2" - TaskDefinition: !Ref TaskDefinitionName - ACMCert: - Type: AWS::CertificateManager::Certificate - Properties: - DomainName: !Join ["", ["*.", !Ref DomainName]] - SubjectAlternativeNames: - - !Ref DomainName - ValidationMethod: DNS - LoadBalancer: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - IpAddressType: ipv4 - Name: !Join ["", [!Ref AWS::StackName, "-lb"]] - Scheme: internet-facing - SecurityGroups: - - !Ref SecurityGroupLB - Subnets: !Ref SubnetIds - Tags: - - Key: Name - Value: - Ref: AWS::StackName - Type: application - SecurityGroupLB: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security Group to attach to the service load balancer - GroupName: !Join ["", [!Ref AWS::StackName, "-service-group"]] - VpcId: !Ref AWSVpcId - SecurityGroupHTTPingress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !Ref 'SecurityGroupLB' - IpProtocol: tcp - FromPort: '80' - ToPort: '80' - CidrIp: 0.0.0.0/0 - SecurityGroupHTTPSingress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !Ref 'SecurityGroupLB' - IpProtocol: tcp - FromPort: '443' - ToPort: '443' - CidrIp: 0.0.0.0/0 - SecurityGroupCI: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security Group to attach to the container instances - GroupName: !Join ["", [!Ref AWS::StackName, "-instance-group"]] - VpcId: !Ref AWSVpcId - SecurityGroupEgressCI: - Type: AWS::EC2::SecurityGroupEgress - Properties: - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - GroupId: !Ref SecurityGroupCI - DestinationSecurityGroupId: !GetAtt SecurityGroupLB.GroupId - SecurityGroupIngressCI: - Type: AWS::EC2::SecurityGroupIngress - Properties: - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - GroupId: !Ref SecurityGroupCI - SourceSecurityGroupId: !GetAtt SecurityGroupLB.GroupId - SecurityGroupEgressLB: - Type: AWS::EC2::SecurityGroupEgress - Properties: - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - GroupId: !Ref SecurityGroupLB - DestinationSecurityGroupId: !GetAtt SecurityGroupCI.GroupId - SecurityGroupIngressLB: - Type: AWS::EC2::SecurityGroupIngress - Properties: - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - GroupId: !Ref SecurityGroupLB - SourceSecurityGroupId: !GetAtt SecurityGroupCI.GroupId - SecurityGroup3000inbound: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !Ref SecurityGroupCI - IpProtocol: tcp - FromPort: '3000' - ToPort: '3000' - CidrIp: 0.0.0.0/0 - SecurityGroupEgress: - Type: AWS::EC2::SecurityGroupEgress - Properties: - GroupId: !Ref SecurityGroupCI - IpProtocol: -1 - FromPort: -1 - ToPort: -1 - CidrIp: 0.0.0.0/0 - TargetGroup3000: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - DependsOn: LoadBalancer - Properties: - HealthCheckEnabled: true - HealthCheckIntervalSeconds: 30 - HealthCheckPath: / - HealthCheckPort: traffic-port - HealthCheckProtocol: HTTP - HealthCheckTimeoutSeconds: 5 - HealthyThresholdCount: 5 - Matcher: - HttpCode: 200 - Name: !Join ["", [!Ref AWS::StackName, "-3000"]] - Port: 3000 - Protocol: HTTP - UnhealthyThresholdCount: 2 - VpcId: !Ref AWSVpcId - Listener80: - Type: 'AWS::ElasticLoadBalancingV2::Listener' - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref TargetGroup3000 - LoadBalancerArn: !Ref LoadBalancer - Port: 80 - Protocol: "HTTP" - Listener443: - Type: 'AWS::ElasticLoadBalancingV2::Listener' - DependsOn: ECSServiceRole - Properties: - Certificates: - - CertificateArn: !Ref ACMCert - DefaultActions: - - Type: forward - TargetGroupArn: !Ref TargetGroup3000 - LoadBalancerArn: !Ref LoadBalancer - Port: 443 - Protocol: "HTTPS" - SslPolicy: "ELBSecurityPolicy-2016-08" - ECSAutoScalingGroup: - Type: AWS::AutoScaling::AutoScalingGroup - Properties: - VPCZoneIdentifier: !Ref 'SubnetIds' - LaunchConfigurationName: !Ref 'ContainerInstances' - MinSize: '1' - MaxSize: '2' - DesiredCapacity: '2' - CreationPolicy: - ResourceSignal: - Timeout: PT15M - UpdatePolicy: - AutoScalingReplacingUpdate: - WillReplace: 'true' - ContainerInstances: - Type: AWS::AutoScaling::LaunchConfiguration - Properties: - ImageId: !FindInMap [AWSRegionToAMI, !Ref 'AWS::Region', AMIID] - SecurityGroups: [!Ref 'SecurityGroupCI', !Ref 'SecurityGroupLB'] - InstanceType: !Ref 'InstanceType' - IamInstanceProfile: !Ref 'EC2InstanceProfile' - UserData: - Fn::Base64: !Sub | - #!/bin/bash -xe - echo ECS_CLUSTER=${EcsCluster} >> /etc/ecs/ecs.config;echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config - sudo yum install -y aws-cfn-bootstrap - /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region} - EC2Role: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - - Effect: Allow - Principal: - Service: [ec2.amazonaws.com] - Action: ['sts:AssumeRole'] - Policies: - - PolicyName: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, "-qa-ecs-service-role-policy"]]} - PolicyDocument: - Statement: - - Effect: Allow - Action: ['ec2:DescribeTags','ecs:CreateCluster', - 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint', - 'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession', - 'ecs:UpdateContainerInstancesState', 'ecs:Submit*', 'ecr:GetAuthorizationToken', - 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', - 'ecr:BatchGetImage', 'logs:CreateLogStream', 'logs:PutLogEvents'] - Resource: '*' -# If you would rather use the AWS managed policy than the above custom policy, use this: -# ManagedPolicyArns: -# - 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role' - AutoscalingRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - - Effect: Allow - Principal: - Service: [application-autoscaling.amazonaws.com] - Action: ['sts:AssumeRole'] - Path: / - Policies: - - PolicyName: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, "-qa-service-autoscaling-role-policy"]]} - PolicyDocument: - Statement: - - Effect: Allow - Action: ['application-autoscaling:*', 'cloudwatch:DescribeAlarms', - 'cloudwatch:PutMetricAlarm', 'cloudwatch:DeleteAlarms', - 'ecs:DescribeServices', 'ecs:UpdateService'] - Resource: '*' - TaskRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - - Effect: Allow - Principal: - Service: ['ecs-tasks.amazonaws.com','ecs.amazonaws.com'] - Action: ['sts:AssumeRole'] - Path: / - Policies: - - PolicyName: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, "-qa-service-task-role-policy"]]} - PolicyDocument: - Statement: - - Effect: Allow - Action: ['ecr:GetAuthorizationToken', 'ecr:BatchCheckLayerAvailability', - 'ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage', 'logs:CreateLogStream', - 'logs:PutLogEvents'] - Resource: '*' - - PolicyName: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, "-qa-service-bucket-policy"]]} - PolicyDocument: - Statement: - - Effect: Allow - Action: ['s3:GetObject'] - Resource: {"Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "S3BucketName"}, "/*"]]} - - Effect: Allow - Action: ['s3:GetBucketLocation'] - Resource: {"Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "S3BucketName"}, ""]]} - ECSServiceRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - - Effect: Allow - Principal: - Service: [ecs.amazonaws.com] - Action: ['sts:AssumeRole'] - Path: / - Policies: - - PolicyName: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, "-qa-service-ecs-service-policy"]]} - PolicyDocument: - Statement: - - Effect: Allow - Action: ['elasticloadbalancing:DeregisterInstancesFromLoadBalancer', - 'elasticloadbalancing:DeregisterTargets', 'elasticloadbalancing:Describe*', - 'elasticloadbalancing:RegisterInstancesWithLoadBalancer', - 'elasticloadbalancing:RegisterTargets', 'ec2:Describe*', - 'ec2:AuthorizeSecurityGroupIngress'] - Resource: '*' - EC2InstanceProfile: - Type: AWS::IAM::InstanceProfile - Properties: - Path: / - Roles: [!Ref 'EC2Role'] -Outputs: - Service: - Value: !Ref 'Service' - Cluster: - Value: !Ref 'EcsCluster' - LoadBalancer: - Description: Your Load Balancer's URL. Create a DNS record for {!Ref DomainName} that points to this location. - Value: !Join ['', [!GetAtt [LoadBalancer, DNSName]]] - TaskDefinition: - Value: !Ref 'TaskDefinition' diff --git a/deploy-templates/aws-permissions.md b/deploy-templates/aws-permissions.md deleted file mode 100644 index 1da9fa7..0000000 --- a/deploy-templates/aws-permissions.md +++ /dev/null @@ -1,153 +0,0 @@ -# Permissions for Amazon Web Services (AWS) - -The CloudFormation templates provided here will run perfectly when launched by a user with full admin access. However, it is not a security best practice to allow an admin user to run CloudFormation templates, because of the potential damage that a poorly written or malicious template can do to your account. The proper way to run CloudFormation templates is to launch them with a user that has access to the CloudFormation service, and the privilege to pass the operations through a role that CloudFormation assumes on their behalf. This role then carries the least amount of privileges necessary to create and/or destroy the resources in the template. The following IAM policies are intended to provide this permissions scheme. - -# 1. Create User and Add Permissions - -Use the web console to create an IAM user that will be used to manage the stacks for the QA server. Click the "Add user" button and name the user. You can name this user whatever you like, but for this example we will be naming the user `qa-user`. This user can be a web console user, but it is highly recommended that you create a programmatic access user, for use with the AWS CLI. Click on "Next:Permissions" to continue. - -Select "Attach existing policies directly" and then click "Create policy". Click the JSON tab and paste in the following policy document: - -``` -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "cloudformation:CreateStack", - "cloudformation:DescribeStacks", - "cloudformation:DeleteStack", - "cloudformation:DescribeStackEvents", - "cloudformation:UpdateStack", - "iam:PassRole" - ], - "Resource": "*" - } - ] - } -``` -This policy will allow the user to launch, update, and delete the CloudFormation stacks that are defined by the templates in this repository. Click "Next:Tags" and then "Next:Review" to create the policy, giving the policy an appropriate name. Return to your IAM user screen and find the policy you have just created, and select it to assign the policy to your user. Then click "Next:Tags", then "Next:Review", and finally "Create user". - -Be sure to download your *access key ID* and *secret access key* and install them in your AWS CLI tool credentials file, creating a named profile if necessary. - -Once the user `qa-user` is installed in your AWS CLI tool, you should be able to launch the template with a command resembling: - -`aws cloudformation create-stack --stack-name qa-server --profile qa-user` - -However, the template will immediately fail with a permissions error, because the user does not have permissions to create any of the resources within the stacks. That is accomplished by creating a role that has all of those permissions, and the user will pass that role to the service. - -# 2. Create CloudFormation Role and Add Permissions - -Use the web console to create an IAM role that the service will assume. First, click on the "Create role" button and select "AWS Service", and from the list of services select CloudFormation. Click "Next: Permissions" and then click "Create policy". Click on the JSON tab and paste in the following policy: -``` -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "ec2:DescribeSubnets", - "ec2:DescribeSecurityGroups", - "ec2:CreateSecurityGroup", - "ec2:RevokeSecurityGroupEgress", - "ec2:RevokeSecurityGroupIngress", - "ec2:DeleteSecurityGroup", - "ec2:AuthorizeSecurityGroupEgress", - "ec2:AuthorizeSecurityGroupIngress", - "ec2:DescribeInstances", - "ec2:CreateNetworkInterface", - "ec2:CreateNetworkInterfacePermission", - "ec2:DeleteNetworkInterface", - "ec2:DeleteNetworkInterfacePermission", - "ec2:DescribeNetworkInterfaces", - "ec2:DescribeNetworkInterfacePermissions", - "ec2:DescribeNetworkInterfaceAttribute", - "ec2:DetachNetworkInterface", - "ecs:DescribeClusters", - "ecs:CreateCluster", - "ecs:DeleteCluster", - "ecs:RegisterTaskDefinition", - "ecs:DeregisterTaskDefinition", - "ecs:DescribeServices", - "ecs:CreateService", - "ecs:DeleteService", - "elasticfilesystem:CreateFileSystem", - "elasticfilesystem:ModifyMountTargetSecurityGroups", - "elasticfilesystem:DeleteFileSystem", - "elasticfilesystem:DescribeFileSystems", - "elasticfilesystem:CreateMountTarget", - "elasticfilesystem:DeleteMountTarget", - "elasticfilesystem:CreateAccessPoint", - "elasticfilesystem:DeleteAccessPoint", - "elasticfilesystem:DescribeMountTargets", - "elasticfilesystem:DescribeMountTargetSecurityGroups", - "elasticfilesystem:DescribeAccessPoints", - "elasticloadbalancing:DescribeLoadBalancers", - "elasticloadbalancing:CreateLoadBalancer", - "elasticloadbalancing:DeleteLoadBalancer", - "elasticloadbalancing:DescribeTargetGroups", - "elasticloadbalancing:CreateTargetGroup", - "elasticloadbalancing:DeleteTargetGroup", - "elasticloadbalancing:DescribeListeners", - "elasticloadbalancing:CreateListener", - "elasticloadbalancing:DeleteListener", - "autoscaling:CreateLaunchConfiguration", - "autoscaling:DeleteLaunchConfiguration", - "autoscaling:CreateAutoScalingGroup", - "autoscaling:DeleteAutoScalingGroup", - "autoscaling:DescribeAutoScalingGroups", - "autoscaling:UpdateAutoScalingGroup", - "autoscaling:DescribeScalingActivities", - "autoscaling:DescribeLaunchConfigurations", - "autoscaling:DescribeAutoScalingInstances", - "acm:RequestCertificate", - "acm:DescribeCertificate", - "acm:DeleteCertificate", - "iam:CreateRole", - "iam:ListRoleTags", - "iam:getRolePolicy", - "iam:PutRolePolicy", - "iam:DeleteRolePolicy", - "iam:DeleteRole", - "iam:GetRole", - "iam:PassRole", - "iam:CreateInstanceProfile", - "iam:DeleteInstanceProfile", - "iam:RemoveRoleFromInstanceProfile", - "iam:AddRoleToInstanceProfile", - "logs:CreateLogGroup", - "logs:PutRetentionPolicy", - "logs:DeleteLogGroup", - "s3:CreateBucket", - "s3:DeleteBucket", - "s3:GetBucketLocation", - "s3:ListBucket", - "s3:PutBucketPublicAccessBlock", - "datasync:CreateLocationEfs", - "datasync:CreateLocationS3", - "datasync:CreateTask", - "datasync:DeleteLocation", - "datasync:DescribeLocationS3", - "datasync:DescribeLocationEfs", - "datasync:DescribeTask", - "datasync:ListTagsForResource", - "datasync:DeleteTask" - ], - "Resource": "*" - } - ] - } -``` -Click on "Next:Tags" and then "Next:Review" to create the policy, giving the policy an appropriate name. - -Return to the IAM role screen and find the policy you have just created, and select it to assign the policy to your role. Then click "Next:Tags", then "Next:Review", and finally "Create role". You can name this role whatever you like, but for this example we will be naming the role `qa-role`. - -Once your role has been created, click on it to view the role summary page, and the first item will be the "Role ARN", copy it. You should now be able to pass the arn of this role from `qa-user` to CloudFormation using the command line, like this: - -`aws cloudformation create-stack --stack-name qa-server --profile qa-user --role-arn arn:aws:iam:::role/qa-role` - -You should also use this user and role to make any updates to the stack, or to delete the stack. - - - diff --git a/deploy-templates/aws-prerequisites.yaml b/deploy-templates/aws-prerequisites.yaml deleted file mode 100644 index fd43f3c..0000000 --- a/deploy-templates/aws-prerequisites.yaml +++ /dev/null @@ -1,161 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Description: > - 1. Review the parameters below and ensure that they are appropriate to your environment. Then deploy with this aws cli command: - aws cloudformation create-stack --stack-name qa-server-prerequisites \ - --template-body file://./aws-prerequisites.yaml \ - --parameters file://./prerequisites.env - 2. The outputs of this template file will be used by the aws-cloudformation.yaml template. -Parameters: - AWSVpcId: - Type: String - Default: '' - Description: Your AWS VPC ID. It should look like vpc-4d4bec2b. Required for load balancer configuration and security group rules. - SubnetIds: - Type: List - Description: Select two subnets in your selected VPC. The values must be separated by commas. - S3BucketName: - Type: String - Default: '' - Description: The name you wish your S3 bucket to have. This bucket name must be unique within the entire AWS region, so you will have to change the value provided here to one that is not already in use. - EFSName: - Type: String - Description: The name you wish your EFS filesystem to have. This name must be unique within your account. - EFSAuthoritiesAccessPoint: - Type: String - Default: '' - Description: The name you wish your EFS authorities access point to have. This will create an access point where your service's authorities files will live. After it is created, you will need to connect to this access point to upload at least one authority file. - EFSDatabaseAccessPoint: - Type: String - Default: '' - Description: The name you wish your EFS database access point to have. This will create an access point that the database will use to store its datafiles. -Resources: - Bucket: - Type: AWS::S3::Bucket - Properties: - BucketName: !Ref S3BucketName - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - Filesystem: - Type: AWS::EFS::FileSystem - Properties: - FileSystemTags: - - Key: "Name" - Value: !Ref EFSName - EFSMountTarget1: - Type: AWS::EFS::MountTarget - Properties: - FileSystemId: !Ref Filesystem - SecurityGroups: - - !Ref NFSSecurityGroup - SubnetId: !Select [ 0, !Ref SubnetIds ] - EFSMountTarget2: - Type: AWS::EFS::MountTarget - Properties: - FileSystemId: !Ref Filesystem - SecurityGroups: - - !Ref NFSSecurityGroup - SubnetId: !Select [ 1, !Ref SubnetIds ] - NFSSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: "Permit access over port 2049" - GroupName: !Ref EFSName - SecurityGroupEgress: - - CidrIp: 0.0.0.0/0 - Description: Wide open to the world - FromPort: 2049 - IpProtocol: tcp - ToPort: 2049 - SecurityGroupIngress: - - CidrIp: 0.0.0.0/0 - Description: Wide open to the world - FromPort: 2049 - IpProtocol: tcp - ToPort: 2049 - VpcId: !Ref AWSVpcId - AuthorityAccessPoint: - Type: AWS::EFS::AccessPoint - Properties: - AccessPointTags: - - Key: "Name" - Value: !Ref EFSAuthoritiesAccessPoint - FileSystemId: !Ref Filesystem - PosixUser: - Gid: 0 - Uid: 0 - RootDirectory: - Path: "/authorities" - DatabaseAccessPoint: - Type: AWS::EFS::AccessPoint - Properties: - AccessPointTags: - - Key: "Name" - Value: !Ref EFSDatabaseAccessPoint - FileSystemId: !Ref Filesystem - PosixUser: - Gid: 0 - Uid: 0 - RootDirectory: - Path: "/db-mysql-data" - EfsLocation: - Type: AWS::DataSync::LocationEFS - Properties: - EfsFilesystemArn: !Sub arn:${AWS::Partition}:elasticfilesystem:${AWS::Region}:${AWS::AccountId}:file-system/${Filesystem} - Ec2Config: - SecurityGroupArns: - - !Sub arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/${NFSSecurityGroup} - SubnetArn: !Sub arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${EFSMountTarget2.SubnetId} - S3Location: - Type: AWS::DataSync::LocationS3 - Properties: - S3BucketArn: !GetAtt Bucket.Arn - S3Config: - BucketAccessRoleArn: !GetAtt DataSyncRole.Arn - S3StorageClass: STANDARD - DataSyncRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - - Effect: Allow - Principal: - Service: [datasync.amazonaws.com] - Action: ['sts:AssumeRole'] - Policies: - - PolicyName: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, "-qa-datasync-role-policy"]]} - PolicyDocument: - Statement: - - Effect: Allow - Action: ['s3:GetBucketLocation', 's3:ListBucket', 's3:ListBucketMultipartUploads'] - Resource: !Sub arn:${AWS::Partition}:s3:::${S3BucketName} - - Effect: Allow - Action: ['s3:AbortMultipartUpload', 's3:DeleteObject', 's3:GetObject', 's3:ListMultipartUploadParts', 's3:PutObjectTagging', 's3:GetObjectTagging', 's3:PutObject'] - Resource: !Sub arn:${AWS::Partition}:s3:::${S3BucketName} - S3ToEfsTask: - Type: AWS::DataSync::Task - Properties: - Name: 'Copy S3 to EFS' - SourceLocationArn: !Ref S3Location - DestinationLocationArn: !Ref EfsLocation - Schedule: - ScheduleExpression: cron(30 * * * ? *) - Options: - OverwriteMode: 'ALWAYS' - PreserveDeletedFiles: 'REMOVE' - TransferMode: 'CHANGED' - -Outputs: - S3BucketName: - Value: !Ref Bucket - VolumeId: - Value: !Ref Filesystem - AuthorityAccessPointId: - Value: !Ref AuthorityAccessPoint - DatabaseAccessPointId: - Value: !Ref DatabaseAccessPoint - S3ToEfsTask: - Value: !Ref S3ToEfsTask - \ No newline at end of file diff --git a/deploy-templates/parameters.env.example b/deploy-templates/parameters.env.example deleted file mode 100644 index 469e446..0000000 --- a/deploy-templates/parameters.env.example +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "ParameterKey": "AWSVpcId", - "ParameterValue": "" - }, - { - "ParameterKey": "SubnetIds", - "ParameterValue": "," - }, - { - "ParameterKey": "TaskDefinitionName", - "ParameterValue": "" - }, - { - "ParameterKey": "S3BucketName", - "ParameterValue": "" - }, - { - "ParameterKey": "EnvFileName", - "ParameterValue": "" - }, - { - "ParameterKey": "DomainName", - "ParameterValue": "" - }, - { - "ParameterKey": "ImageLocation", - "ParameterValue": "" - }, - { - "ParameterKey": "VolumeId", - "ParameterValue": "" - }, - { - "ParameterKey": "AuthorityAccessPointId", - "ParameterValue": "" - }, - { - "ParameterKey": "DatabaseAccessPointId", - "ParameterValue": "" - } -] diff --git a/deploy-templates/prerequisites.env.example b/deploy-templates/prerequisites.env.example deleted file mode 100644 index c2b9695..0000000 --- a/deploy-templates/prerequisites.env.example +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "ParameterKey": "AWSVpcId", - "ParameterValue": "" - }, - { - "ParameterKey": "SubnetIds", - "ParameterValue": "," - }, - { - "ParameterKey": "S3BucketName", - "ParameterValue": "" - }, - { - "ParameterKey": "EFSDatabaseAccessPoint", - "ParameterValue": "" - }, - { - "ParameterKey": "EFSAuthoritiesAccessPoint", - "ParameterValue": "" - }, - { - "ParameterKey": "EFSName", - "ParameterValue": "" - } -] diff --git a/deploy-templates/task-definition.json b/deploy-templates/task-definition.json deleted file mode 100644 index 8ce8df7..0000000 --- a/deploy-templates/task-definition.json +++ /dev/null @@ -1,270 +0,0 @@ -{ - "ipcMode": null, - "executionRoleArn": "arn:aws:iam::092831676293:role/qa-server-ld4p3-TaskRole-K1GW1QWSUADY", - "containerDefinitions": [ - { - "dnsSearchDomains": [], - "environmentFiles": [ - { - "value": "arn:aws:s3:::ld4l-qa-server/qa-server-container-with-db.env", - "type": "s3" - } - ], - "logConfiguration": { - "logDriver": "awslogs", - "secretOptions": null, - "options": { - "awslogs-group": "/ecs/qa-server-ld4p3-lb", - "awslogs-region": "us-east-1", - "awslogs-stream-prefix": "ecs" - } - }, - "entryPoint": [], - "portMappings": [ - { - "hostPort": 3000, - "protocol": "tcp", - "containerPort": 3000 - } - ], - "command": [], - "linuxParameters": null, - "cpu": 0, - "environment": [], - "resourceRequirements": null, - "ulimits": null, - "dnsServers": [], - "mountPoints": [ - { - "readOnly": null, - "containerPath": "/app/ld4p/qa_server-webapp/config/authorities", - "sourceVolume": "qa-server-authorities" - } - ], - "workingDirectory": null, - "secrets": null, - "dockerSecurityOptions": [], - "memory": null, - "memoryReservation": 512, - "volumesFrom": [], - "stopTimeout": null, - "image": "092831676293.dkr.ecr.us-east-1.amazonaws.com/qa-server/qa-server-app:latest", - "startTimeout": null, - "firelensConfiguration": null, - "dependsOn": null, - "disableNetworking": null, - "interactive": null, - "healthCheck": null, - "essential": true, - "links": [ - "qa-mariadb" - ], - "hostname": null, - "extraHosts": null, - "pseudoTerminal": null, - "user": null, - "readonlyRootFilesystem": null, - "dockerLabels": null, - "systemControls": [], - "privileged": null, - "name": "qa-server-app" - }, - { - "dnsSearchDomains": [], - "environmentFiles": [ - { - "value": "arn:aws:s3:::ld4l-qa-server/qa-server-container-with-db.env", - "type": "s3" - } - ], - "logConfiguration": { - "logDriver": "awslogs", - "secretOptions": null, - "options": { - "awslogs-group": "/ecs/qa-server-ld4p3-lb", - "awslogs-region": "us-east-1", - "awslogs-stream-prefix": "ecs" - } - }, - "entryPoint": [], - "portMappings": [ - { - "hostPort": 3306, - "protocol": "tcp", - "containerPort": 3306 - } - ], - "command": [], - "linuxParameters": null, - "cpu": 0, - "environment": [], - "resourceRequirements": null, - "ulimits": null, - "dnsServers": [], - "mountPoints": [ - { - "readOnly": null, - "containerPath": "/var/lib/mysql/data", - "sourceVolume": "db-mysql-data" - } - ], - "workingDirectory": null, - "secrets": null, - "dockerSecurityOptions": [], - "memory": null, - "memoryReservation": 512, - "volumesFrom": [], - "stopTimeout": null, - "image": "mariadb:latest", - "startTimeout": null, - "firelensConfiguration": null, - "dependsOn": null, - "disableNetworking": null, - "interactive": null, - "healthCheck": null, - "essential": true, - "links": [], - "hostname": null, - "extraHosts": null, - "pseudoTerminal": null, - "user": null, - "readonlyRootFilesystem": null, - "dockerLabels": {}, - "systemControls": [], - "privileged": null, - "name": "qa-mariadb" - } - ], - "placementConstraints": [], - "memory": "1024", - "taskRoleArn": "arn:aws:iam::092831676293:role/qa-server-ld4p3-TaskRole-K1GW1QWSUADY", - "compatibilities": [ - "EC2" - ], - "taskDefinitionArn": "arn:aws:ecs:us-east-1:092831676293:task-definition/qa-server-with-cluster:25", - "family": "qa-server-with-cluster", - "requiresAttributes": [ - { - "targetId": null, - "targetType": null, - "value": null, - "name": "ecs.capability.execution-role-awslogs" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.ecr-auth" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.21" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.task-iam-role" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "ecs.capability.execution-role-ecr-pull" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "ecs.capability.efsAuth" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "ecs.capability.efs" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "ecs.capability.env-files.s3" - }, - { - "targetId": null, - "targetType": null, - "value": null, - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.25" - } - ], - "pidMode": null, - "requiresCompatibilities": [ - "EC2" - ], - "networkMode": "bridge", - "cpu": "1024", - "revision": 25, - "status": "ACTIVE", - "inferenceAccelerators": null, - "proxyConfiguration": null, - "volumes": [ - { - "fsxWindowsFileServerVolumeConfiguration": null, - "efsVolumeConfiguration": { - "transitEncryptionPort": null, - "fileSystemId": "fs-402461c2", - "authorizationConfig": { - "iam": "DISABLED", - "accessPointId": "fsap-01817d5defea7264d" - }, - "transitEncryption": "ENABLED", - "rootDirectory": "/" - }, - "name": "qa-server-authorities", - "host": null, - "dockerVolumeConfiguration": null - }, - { - "fsxWindowsFileServerVolumeConfiguration": null, - "efsVolumeConfiguration": { - "transitEncryptionPort": null, - "fileSystemId": "fs-402461c2", - "authorizationConfig": { - "iam": "DISABLED", - "accessPointId": "fsap-0861798cebbd27cbe" - }, - "transitEncryption": "ENABLED", - "rootDirectory": "/" - }, - "name": "db-mysql-data", - "host": null, - "dockerVolumeConfiguration": null - } - ] -} \ No newline at end of file