From 5373ac6c5e9062ea08d19ed7eec9a6a9b7c8d66b Mon Sep 17 00:00:00 2001 From: Parisa Tork Date: Mon, 7 Aug 2023 17:04:14 +0100 Subject: [PATCH 01/42] Initial internal lb migration implementation Co-authored-by: DanielCliftonGuardian 110032454+DanielCliftonGuardian@users.noreply.github.com Co-authored-by: Charlotte charlotte.emms@theguardian.com --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 36 +++++++++ dotcom-rendering/cloudformation.yml | 81 ++++++++++---------- 2 files changed, 78 insertions(+), 39 deletions(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index ac976ad02e7..4171e3441d1 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -3,7 +3,10 @@ import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; import type { App } from 'aws-cdk-lib'; +import { Duration } from "aws-cdk-lib"; +import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import { Peer } from 'aws-cdk-lib/aws-ec2'; +import { LoadBalancingProtocol } from "aws-cdk-lib/aws-elasticloadbalancing"; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; interface DCRProps extends GuStackProps { @@ -70,6 +73,38 @@ export class DotcomRendering extends GuStack { reason: 'Retaining a stateful resource previously defined in YAML', }); + const lb = new GuClassicLoadBalancer(this, 'InternalLoadBalancer', { + app: props.app, + vpc: vpc, + listeners: [{ + internalProtocol: LoadBalancingProtocol.HTTP, + internalPort: 80, + externalProtocol: LoadBalancingProtocol.HTTP, + externalPort: 9000, + }], + healthCheck: { + port: 9000, + path: '/_healthcheck', + healthyThreshold: 2, + unhealthyThreshold: 10, + interval: Duration.seconds(30), + timeout: Duration.seconds(10), + }, + subnetSelection: { subnets: vpc.publicSubnets }, + crossZone: true, + accessLoggingPolicy: { + enabled: true, + emitInterval: 5, + s3BucketName: 'gu-elb-logs', + s3BucketPrefix: `ELBLogs/${props.stack}/${props.app}/${props.stage}`, + }, + }); + + this.overrideLogicalId(lb, { + logicalId: 'InternalLoadBalancer', + reason: 'Retaining a stateful resource previously defined in YAML', + }); + const yamlTemplateFilePath = join( __dirname, '../..', @@ -83,6 +118,7 @@ export class DotcomRendering extends GuStack { VPCIpBlock: vpc.vpcCidrBlock, InternalLoadBalancerSecurityGroup: lbSecurityGroup.securityGroupId, InstanceSecurityGroup: instanceSecurityGroup.securityGroupId, + InternalLoadBalancer: lb.idWithApp, } }); } diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index 31987589b7b..d4ed7a0ae6c 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -64,6 +64,9 @@ Parameters: InstanceSecurityGroup: Description: Id of instance security group Type: String + InternalLoadBalancer: + Description: Id of internal load balancer + Type: String Conditions: HasLatencyScalingAlarm: !Equals [!Ref Stage, 'PROD'] @@ -143,45 +146,45 @@ Resources: Roles: - Ref: InstanceRole - InternalLoadBalancer: - Type: AWS::ElasticLoadBalancing::LoadBalancer - Properties: - Scheme: internal - LoadBalancerName: !Sub '${Stack}-${Stage}-${App}-ELB' - Listeners: - - { LoadBalancerPort: 80, InstancePort: 9000, Protocol: HTTP } - CrossZone: true - HealthCheck: - Target: HTTP:9000/_healthcheck - HealthyThreshold: 2 - UnhealthyThreshold: 10 - Interval: 30 - Timeout: 10 - Subnets: - Ref: Subnets - SecurityGroups: - - Ref: InternalLoadBalancerSecurityGroup - AccessLoggingPolicy: - EmitInterval: 5 - Enabled: true - S3BucketName: gu-elb-logs - S3BucketPrefix: - Fn::Join: - - '/' - - - ELBLogs - - Fn::FindInMap: [Constants, Stack, Value] - - Fn::FindInMap: [Constants, App, Value] - - Ref: Stage - Tags: - - Key: Stage - Value: - Ref: Stage - - Key: Stack - Value: - Fn::FindInMap: [Constants, Stack, Value] - - Key: App - Value: - Fn::FindInMap: [Constants, App, Value] + # InternalLoadBalancer: + # Type: AWS::ElasticLoadBalancing::LoadBalancer + # Properties: + # Scheme: internal + # LoadBalancerName: !Sub '${Stack}-${Stage}-${App}-ELB' + # Listeners: + # - { LoadBalancerPort: 80, InstancePort: 9000, Protocol: HTTP } + # CrossZone: true + # HealthCheck: + # Target: HTTP:9000/_healthcheck + # HealthyThreshold: 2 + # UnhealthyThreshold: 10 + # Interval: 30 + # Timeout: 10 + # Subnets: + # Ref: Subnets + # SecurityGroups: + # - Ref: InternalLoadBalancerSecurityGroup + # AccessLoggingPolicy: + # EmitInterval: 5 + # Enabled: true + # S3BucketName: gu-elb-logs + # S3BucketPrefix: + # Fn::Join: + # - '/' + # - - ELBLogs + # - Fn::FindInMap: [Constants, Stack, Value] + # - Fn::FindInMap: [Constants, App, Value] + # - Ref: Stage + # Tags: + # - Key: Stage + # Value: + # Ref: Stage + # - Key: Stack + # Value: + # Fn::FindInMap: [Constants, Stack, Value] + # - Key: App + # Value: + # Fn::FindInMap: [Constants, App, Value] LaunchConfig: Type: AWS::AutoScaling::LaunchConfiguration From d03f2cd6b162580d81fc84d23599f4efe7137b21 Mon Sep 17 00:00:00 2001 From: Parisa Tork <47482049+ParisaTork@users.noreply.github.com> Date: Tue, 8 Aug 2023 09:43:21 +0100 Subject: [PATCH 02/42] Appease GHA linter with order of imports --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 4171e3441d1..632677add37 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -2,9 +2,9 @@ import { join } from 'node:path'; import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; +import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import type { App } from 'aws-cdk-lib'; import { Duration } from "aws-cdk-lib"; -import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import { Peer } from 'aws-cdk-lib/aws-ec2'; import { LoadBalancingProtocol } from "aws-cdk-lib/aws-elasticloadbalancing"; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; From 4cbfdaa390caeb04e4312fd341d9845d7c1cbcb6 Mon Sep 17 00:00:00 2001 From: Parisa Tork <47482049+ParisaTork@users.noreply.github.com> Date: Tue, 8 Aug 2023 10:55:19 +0100 Subject: [PATCH 03/42] Appease GHA linter by using property shorthand for vpc --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 632677add37..9568d647028 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -75,7 +75,7 @@ export class DotcomRendering extends GuStack { const lb = new GuClassicLoadBalancer(this, 'InternalLoadBalancer', { app: props.app, - vpc: vpc, + vpc, listeners: [{ internalProtocol: LoadBalancingProtocol.HTTP, internalPort: 80, From 6c8db53d40356a76315666386ed2e5e36a87249a Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 11:00:06 +0100 Subject: [PATCH 04/42] infra: migrate asg to cdk and cleanup cloudformation template --- dotcom-rendering/cdk/bin/cdk.ts | 19 +- .../dotcom-rendering.test.ts.snap | 640 +++++++++++++++--- .../cdk/lib/dotcom-rendering.test.ts | 5 + dotcom-rendering/cdk/lib/dotcom-rendering.ts | 92 ++- dotcom-rendering/cdk/lib/types.ts | 29 + dotcom-rendering/cloudformation.yml | 90 +-- 6 files changed, 665 insertions(+), 210 deletions(-) create mode 100644 dotcom-rendering/cdk/lib/types.ts diff --git a/dotcom-rendering/cdk/bin/cdk.ts b/dotcom-rendering/cdk/bin/cdk.ts index 3b70082ae26..f01813645e0 100644 --- a/dotcom-rendering/cdk/bin/cdk.ts +++ b/dotcom-rendering/cdk/bin/cdk.ts @@ -3,13 +3,26 @@ import { App } from 'aws-cdk-lib'; import { DotcomRendering } from '../lib/dotcom-rendering'; const app = new App(); -new DotcomRendering(app, 'DotcomRendering-PROD', { + +const sharedProps = { app: 'rendering', stack: 'frontend', + region: 'eu-west-1', + amiRecipe: 'dotcom-rendering-ARM-jammy-node-18.17.0', +}; + +new DotcomRendering(app, 'DotcomRendering-PROD', { stage: 'PROD', + minCapacity: 15, + maxCapacity: 60, + instanceType: 't4g.small', + ...sharedProps, }); + new DotcomRendering(app, 'DotcomRendering-CODE', { - app: 'rendering', - stack: 'frontend', stage: 'CODE', + minCapacity: 1, + maxCapacity: 4, + instanceType: 't4g.micro', + ...sharedProps, }); diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 3346cc87463..d30e49a64fe 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -31,22 +31,24 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Value": "frontend", }, }, - "StageMap": { - "CODE": { - "MaxCapacity": 4, - "MinCapacity": 1, - }, - "PROD": { - "MaxCapacity": 60, - "MinCapacity": 15, - }, - }, }, "Metadata": { "gu:cdk:constructs": [ "GuVpcParameter", "GuSecurityGroup", "GuSecurityGroup", + "GuStringParameter", + "GuAmiParameter", + "GuInstanceRole", + "GuDescribeEC2Policy", + "GuLoggingStreamNameParameter", + "GuLogShippingPolicy", + "GuDistributionBucketParameter", + "GuGetDistributablePolicy", + "GuParameterStoreReadPolicy", + "GuHttpsEgressSecurityGroup", + "GuWazuhAccess", + "GuAutoScalingGroup", ], "gu:cdk:version": "TEST", }, @@ -61,8 +63,8 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, }, "Parameters": { - "AMI": { - "Description": "AMI to use for instances", + "AMIRendering": { + "Description": "Amazon Machine Image ID for the app rendering. Use this in conjunction with AMIgo to keep AMIs up to date.", "Type": "AWS::EC2::Image::Id", }, "App": { @@ -85,9 +87,14 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Description": "(Optional) Number of consecutive periods the threshold needs to be reached before 5XX alarm is triggered", "Type": "String", }, - "ELKStream": { - "Description": "name of the kinesis stream to use to send logs to the central ELK stack", - "Type": "String", + "DistributionBucketName": { + "Default": "/account/services/artifact.bucket", + "Description": "SSM parameter containing the S3 bucket name holding distribution artifacts", + "Type": "AWS::SSM::Parameter::Value", + }, + "ELKStreamId": { + "Default": "/frontend/test/logstash.stream.name", + "Type": "AWS::SSM::Parameter::Value", }, "FrontendConfigKey": { "Description": "Parameter store KMS key", @@ -97,6 +104,11 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Description": "EC2 Instance Type to use for dotcom-rendering", "Type": "String", }, + "LoggingStreamName": { + "Default": "/account/services/logging.stream.name", + "Description": "SSM parameter containing the Name (not ARN) on the kinesis stream", + "Type": "AWS::SSM::Parameter::Value", + }, "NotificationAlarmAction": { "Default": "", "Description": "(Optional) ARN of action to execute when notification alarms change state", @@ -129,48 +141,26 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Resources": { "AutoscalingGroup": { "Properties": { - "AvailabilityZones": { - "Fn::GetAZs": "", - }, "HealthCheckGracePeriod": 120, "HealthCheckType": "ELB", - "LaunchConfigurationName": { - "Ref": "LaunchConfig", - }, - "LoadBalancerNames": [ - { - "Ref": "InternalLoadBalancer", + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "frontendTESTrenderingF3FD9600", + }, + "Version": { + "Fn::GetAtt": [ + "frontendTESTrenderingF3FD9600", + "LatestVersionNumber", + ], }, - ], - "MaxSize": { - "Fn::FindInMap": [ - "StageMap", - { - "Ref": "Stage", - }, - "MaxCapacity", - ], - }, - "MinSize": { - "Fn::FindInMap": [ - "StageMap", - { - "Ref": "Stage", - }, - "MinCapacity", - ], }, + "MaxSize": "4", + "MinSize": "1", "Tags": [ { "Key": "App", "PropagateAtLaunch": true, - "Value": { - "Fn::FindInMap": [ - "Constants", - "App", - "Value", - ], - }, + "Value": "rendering", }, { "Key": "gu:cdk:version", @@ -193,9 +183,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Value": "TEST", }, ], - "VPCZoneIdentifier": { - "Ref": "Subnets", - }, + "VPCZoneIdentifier": [], }, "Type": "AWS::AutoScaling::AutoScalingGroup", }, @@ -236,6 +224,146 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Type": "AWS::CloudWatch::Alarm", }, + "DescribeEC2PolicyFF5F9295": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "autoscaling:DescribeAutoScalingInstances", + "autoscaling:DescribeAutoScalingGroups", + "ec2:DescribeTags", + "ec2:DescribeInstances", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "describe-ec2-policy", + "Roles": [ + { + "Ref": "InstanceRoleRenderingB06D827E", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "GetDistributablePolicyRenderingCA2FF5C7": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "DistributionBucketName", + }, + "/frontend/TEST/rendering/*", + ], + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "GetDistributablePolicyRenderingCA2FF5C7", + "Roles": [ + { + "Ref": "InstanceRoleRenderingB06D827E", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "GuHttpsEgressSecurityGroupRenderingE17B2710": { + "Properties": { + "GroupDescription": "Allow all outbound HTTPS traffic", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound HTTPS traffic", + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443, + }, + ], + "Tags": [ + { + "Key": "App", + "Value": "rendering", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "VpcId": { + "Ref": "VpcId", + }, + }, + "Type": "AWS::EC2::SecurityGroup", + }, + "GuLogShippingPolicy981BFE5A": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "kinesis:Describe*", + "kinesis:Put*", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:kinesis:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":stream/", + { + "Ref": "LoggingStreamName", + }, + ], + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "GuLogShippingPolicy981BFE5A", + "Roles": [ + { + "Ref": "InstanceRoleRenderingB06D827E", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "InstanceProfile": { "Properties": { "Path": "/", @@ -355,6 +483,60 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Type": "AWS::IAM::Role", }, + "InstanceRoleRenderingB06D827E": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/AmazonSSMManagedInstanceCore", + ], + ], + }, + ], + "Path": "/", + "Tags": [ + { + "Key": "App", + "Value": "rendering", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + }, + "Type": "AWS::IAM::Role", + }, "InstanceSecurityGroup": { "Properties": { "GroupDescription": "rendering instance", @@ -584,63 +766,90 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Type": "AWS::CloudWatch::Alarm", }, - "LaunchConfig": { + "ParameterStoreReadRenderingFDC91AAA": { "Properties": { - "AssociatePublicIpAddress": true, - "IamInstanceProfile": { - "Ref": "InstanceProfile", - }, - "ImageId": { - "Ref": "AMI", - }, - "InstanceType": { - "Ref": "InstanceType", + "PolicyDocument": { + "Statement": [ + { + "Action": "ssm:GetParametersByPath", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":parameter/TEST/frontend/rendering", + ], + ], + }, + }, + { + "Action": [ + "ssm:GetParameters", + "ssm:GetParameter", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":parameter/TEST/frontend/rendering/*", + ], + ], + }, + }, + ], + "Version": "2012-10-17", }, - "SecurityGroups": [ + "PolicyName": "parameter-store-read-policy", + "Roles": [ { - "Fn::GetAtt": [ - "InstanceSecurityGroup", - "GroupId", - ], + "Ref": "InstanceRoleRenderingB06D827E", }, ], - "UserData": { - "Fn::Base64": { - "Fn::Sub": "#!/bin/bash -ev - -groupadd frontend -useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering -usermod -a -G frontend aws-kinesis-agent-user -cd /home/dotcom-rendering - -aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/\${Stage}/\${App}/\${App}.zip ./ -unzip -q \${App}.zip -d \${App} - -chown -R dotcom-rendering:frontend \${App} - -cd \${App} - -export TERM=xterm-256color -export NODE_ENV=production -export GU_STAGE=\${Stage} - -mkdir /var/log/dotcom-rendering -chown -R dotcom-rendering:frontend /var/log/dotcom-rendering - -make start-prod - -/opt/aws-kinesis-agent/configure-aws-kinesis-agent \${AWS::Region} \${ELKStream} /var/log/dotcom-rendering/dotcom-rendering.log -", - }, - }, }, - "Type": "AWS::AutoScaling::LaunchConfiguration", + "Type": "AWS::IAM::Policy", }, "ScaleDownPolicy": { "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { - "Ref": "AutoscalingGroup", + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":autoscaling:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":autoScalingGroup:*:autoScalingGroupName/", + { + "Ref": "AutoscalingGroup", + }, + ], + ], }, "Cooldown": "120", "ScalingAdjustment": -1, @@ -651,13 +860,246 @@ make start-prod "Properties": { "AdjustmentType": "PercentChangeInCapacity", "AutoScalingGroupName": { - "Ref": "AutoscalingGroup", + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":autoscaling:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":autoScalingGroup:*:autoScalingGroupName/", + { + "Ref": "AutoscalingGroup", + }, + ], + ], }, "Cooldown": "600", "ScalingAdjustment": 100, }, "Type": "AWS::AutoScaling::ScalingPolicy", }, + "WazuhSecurityGroup": { + "Properties": { + "GroupDescription": "Allow outbound traffic from wazuh agent to manager", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Wazuh event logging", + "FromPort": 1514, + "IpProtocol": "tcp", + "ToPort": 1514, + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "Wazuh agent registration", + "FromPort": 1515, + "IpProtocol": "tcp", + "ToPort": 1515, + }, + ], + "Tags": [ + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "VpcId": { + "Ref": "VpcId", + }, + }, + "Type": "AWS::EC2::SecurityGroup", + }, + "frontendTESTrenderingF3FD9600": { + "Properties": { + "LaunchTemplateData": { + "IamInstanceProfile": { + "Arn": { + "Fn::GetAtt": [ + "frontendTESTrenderingProfileA2D2425E", + "Arn", + ], + }, + }, + "ImageId": { + "Ref": "AMIRendering", + }, + "InstanceType": "t4g.micro", + "MetadataOptions": { + "HttpTokens": "required", + }, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "GuHttpsEgressSecurityGroupRenderingE17B2710", + "GroupId", + ], + }, + { + "Fn::GetAtt": [ + "WazuhSecurityGroup", + "GroupId", + ], + }, + { + "Fn::GetAtt": [ + "InstanceSecurityGroup", + "GroupId", + ], + }, + ], + "TagSpecifications": [ + { + "ResourceType": "instance", + "Tags": [ + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Name", + "Value": "DotcomRendering/frontend-TEST-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + }, + { + "ResourceType": "volume", + "Tags": [ + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Name", + "Value": "DotcomRendering/frontend-TEST-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + }, + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + " + #!/bin/bash -ev + + groupadd frontend + useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering + usermod -a -G frontend aws-kinesis-agent-user + cd /home/dotcom-rendering + + aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/TEST/rendering/rendering.zip ./ + unzip -q rendering.zip -d rendering + + chown -R dotcom-rendering:frontend rendering + + cd rendering + + export TERM=xterm-256color + export NODE_ENV=production + export GU_STAGE=TEST + + mkdir /var/log/dotcom-rendering + chown -R dotcom-rendering:frontend /var/log/dotcom-rendering + + make start-prod + + /opt/aws-kinesis-agent/configure-aws-kinesis-agent eu-west-1 ", + { + "Ref": "ELKStreamId", + }, + " /var/log/dotcom-rendering/dotcom-rendering.log + ", + ], + ], + }, + }, + }, + "TagSpecifications": [ + { + "ResourceType": "launch-template", + "Tags": [ + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Name", + "Value": "DotcomRendering/frontend-TEST-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + }, + ], + }, + "Type": "AWS::EC2::LaunchTemplate", + }, + "frontendTESTrenderingProfileA2D2425E": { + "Properties": { + "Roles": [ + { + "Ref": "InstanceRoleRenderingB06D827E", + }, + ], + }, + "Type": "AWS::IAM::InstanceProfile", + }, }, } `; diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.test.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.test.ts index cfc75c78361..46667771358 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.test.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.test.ts @@ -14,6 +14,11 @@ describe('The DotcomRendering stack', () => { stack: 'frontend', stage: 'TEST', app: 'rendering', + minCapacity: 1, + maxCapacity: 4, + instanceType: 't4g.micro', + region: 'eu-west-1', + amiRecipe: 'amiRecipe', }); const template = Template.fromStack(stack); expect(template.toJSON()).toMatchSnapshot(); diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index ac976ad02e7..1ec385cec10 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -1,19 +1,20 @@ import { join } from 'node:path'; -import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; -import { GuStack } from '@guardian/cdk/lib/constructs/core'; +import { GuAutoScalingGroup } from '@guardian/cdk/lib/constructs/autoscaling'; +import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; import type { App } from 'aws-cdk-lib'; -import { Peer } from 'aws-cdk-lib/aws-ec2'; +import { Duration } from 'aws-cdk-lib'; +import { HealthCheck } from 'aws-cdk-lib/aws-autoscaling'; +import { InstanceType, Peer } from 'aws-cdk-lib/aws-ec2'; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; - -interface DCRProps extends GuStackProps { - app: string; -} +import type { DCRProps } from './types'; export class DotcomRendering extends GuStack { constructor(scope: App, id: string, props: DCRProps) { super(scope, id, props); + const { app, region, stack, stage } = props; + // This fetches the VPC using the SSM parameter defined for this account // and specifies the CIDR block to use with it here const vpc = GuVpc.fromIdParameter(this, 'vpc', { @@ -24,7 +25,7 @@ export class DotcomRendering extends GuStack { this, 'InternalLoadBalancerSecurityGroup', { - app: props.app, + app, description: 'Allows HTTP and HTTPS inbound connections from within the VPC', vpc, @@ -42,18 +43,13 @@ export class DotcomRendering extends GuStack { ], }, ); - this.overrideLogicalId(lbSecurityGroup, { - logicalId: 'InternalLoadBalancerSecurityGroup', - reason: 'Retaining a stateful resource previously defined in YAML', - }); const instanceSecurityGroup = new GuSecurityGroup( this, 'InstanceSecurityGroup', { - app: props.app, - description: - 'rendering instance', + app, + description: 'rendering instance', vpc, ingresses: [ { @@ -65,10 +61,67 @@ export class DotcomRendering extends GuStack { }, ); + const elkStream = new GuStringParameter(this, 'ELKStreamId', { + fromSSM: true, + default: `/${stack}/${stage.toLowerCase()}/logstash.stream.name`, + }); + + const userData = ` + #!/bin/bash -ev + + groupadd frontend + useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering + usermod -a -G frontend aws-kinesis-agent-user + cd /home/dotcom-rendering + + aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${stage}/${app}/${app}.zip ./ + unzip -q ${app}.zip -d ${app} + + chown -R dotcom-rendering:frontend ${app} + + cd ${app} + + export TERM=xterm-256color + export NODE_ENV=production + export GU_STAGE=${stage} + + mkdir /var/log/dotcom-rendering + chown -R dotcom-rendering:frontend /var/log/dotcom-rendering + + make start-prod + + /opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStream.valueAsString} /var/log/dotcom-rendering/dotcom-rendering.log + `; + + const asg = new GuAutoScalingGroup(this, 'AutoscalingGroup', { + app, + vpc, + instanceType: new InstanceType(props.instanceType), + minimumInstances: props.minCapacity, + maximumInstances: props.maxCapacity, + healthCheck: HealthCheck.elb({ grace: Duration.minutes(2) }), + userData, + imageRecipe: props.amiRecipe, + // role: instanceRole, + additionalSecurityGroups: [instanceSecurityGroup], + vpcSubnets: { subnets: vpc.publicSubnets }, + }); + + // ----------------------------------------------------------------- // + // Temporarily overriding logical IDs during CDK migration + this.overrideLogicalId(lbSecurityGroup, { + logicalId: 'InternalLoadBalancerSecurityGroup', + reason: 'Retaining a stateful resource previously defined in YAML', + }); this.overrideLogicalId(instanceSecurityGroup, { logicalId: 'InstanceSecurityGroup', reason: 'Retaining a stateful resource previously defined in YAML', }); + this.overrideLogicalId(asg, { + logicalId: 'AutoscalingGroup', + reason: 'Retaining a stateful resource previously defined in YAML', + }); + // ----------------------------------------------------------------- // const yamlTemplateFilePath = join( __dirname, @@ -79,11 +132,10 @@ export class DotcomRendering extends GuStack { new CfnInclude(this, 'YamlTemplate', { templateFile: yamlTemplateFilePath, parameters: { - VpcId: vpc.vpcId, - VPCIpBlock: vpc.vpcCidrBlock, - InternalLoadBalancerSecurityGroup: lbSecurityGroup.securityGroupId, - InstanceSecurityGroup: instanceSecurityGroup.securityGroupId, - } + InternalLoadBalancerSecurityGroup: + lbSecurityGroup.securityGroupId, + AutoscalingGroup: asg.autoScalingGroupArn, + }, }); } } diff --git a/dotcom-rendering/cdk/lib/types.ts b/dotcom-rendering/cdk/lib/types.ts new file mode 100644 index 00000000000..6633e602017 --- /dev/null +++ b/dotcom-rendering/cdk/lib/types.ts @@ -0,0 +1,29 @@ +import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; + +export interface DCRProps extends GuStackProps { + /** + * The name of the application + */ + app: string; + /** + * The minimum number of instances in the autoscaling group + */ + minCapacity: number; + /** + * The maximum number of instances in the autoscaling group. + * Defaults to twice the minimum capacity if not specified + */ + maxCapacity?: number; + /** + * EC2 Instance Type to use for dotcom-rendering + */ + instanceType: string; + /** + * The region in AWS where this app will run + */ + region: string; + /** + * AMI Recipe to use + */ + amiRecipe: string; +} diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index 31987589b7b..aa62bb259e5 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -8,9 +8,6 @@ Parameters: Subnets: Description: The subnets where rendering instances will run Type: List - VpcId: - Description: The VPC in which rendering instances will run - Type: AWS::EC2::VPC::Id App: Description: Application name Type: String @@ -26,9 +23,6 @@ Parameters: Description: Stack name Type: String Default: frontend - AMI: - Description: AMI to use for instances - Type: AWS::EC2::Image::Id NotificationAlarmAction: Type: CommaDelimitedList Description: (Optional) ARN of action to execute when notification alarms change state @@ -48,21 +42,14 @@ Parameters: InstanceType: Type: String Description: EC2 Instance Type to use for dotcom-rendering - VPCIpBlock: - Type: String - Description: CIDR block for IP addresses inside this VPC - Default: 10.248.136.0/22 - ELKStream: - Type: String - Description: name of the kinesis stream to use to send logs to the central ELK stack FrontendConfigKey: Description: Parameter store KMS key Type: String InternalLoadBalancerSecurityGroup: Description: Id of load balancer security group Type: String - InstanceSecurityGroup: - Description: Id of instance security group + AutoscalingGroup: + Description: ARN of the Autoscaling group Type: String Conditions: @@ -75,13 +62,6 @@ Mappings: Value: frontend App: Value: rendering - StageMap: - PROD: - MinCapacity: 15 - MaxCapacity: 60 - CODE: - MinCapacity: 1 - MaxCapacity: 4 Resources: InstanceRole: @@ -183,72 +163,6 @@ Resources: Value: Fn::FindInMap: [Constants, App, Value] - LaunchConfig: - Type: AWS::AutoScaling::LaunchConfiguration - Properties: - ImageId: - Ref: AMI - SecurityGroups: - - Ref: InstanceSecurityGroup - InstanceType: !Ref InstanceType - IamInstanceProfile: - Ref: InstanceProfile - AssociatePublicIpAddress: true - UserData: - 'Fn::Base64': !Sub | - #!/bin/bash -ev - - groupadd frontend - useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering - usermod -a -G frontend aws-kinesis-agent-user - cd /home/dotcom-rendering - - aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${Stage}/${App}/${App}.zip ./ - unzip -q ${App}.zip -d ${App} - - chown -R dotcom-rendering:frontend ${App} - - cd ${App} - - export TERM=xterm-256color - export NODE_ENV=production - export GU_STAGE=${Stage} - - mkdir /var/log/dotcom-rendering - chown -R dotcom-rendering:frontend /var/log/dotcom-rendering - - make start-prod - - /opt/aws-kinesis-agent/configure-aws-kinesis-agent ${AWS::Region} ${ELKStream} /var/log/dotcom-rendering/dotcom-rendering.log - - AutoscalingGroup: - Type: AWS::AutoScaling::AutoScalingGroup - Properties: - AvailabilityZones: !GetAZs '' - VPCZoneIdentifier: - Ref: Subnets - LaunchConfigurationName: - Ref: LaunchConfig - MinSize: !FindInMap [StageMap, !Ref Stage, MinCapacity] - MaxSize: !FindInMap [StageMap, !Ref Stage, MaxCapacity] - HealthCheckType: ELB - HealthCheckGracePeriod: 120 - LoadBalancerNames: - - Ref: InternalLoadBalancer - Tags: - - Key: Stage - Value: - Ref: Stage - PropagateAtLaunch: true - - Key: Stack - Value: - Fn::FindInMap: [Constants, Stack, Value] - PropagateAtLaunch: true - - Key: App - Value: - Fn::FindInMap: [Constants, App, Value] - PropagateAtLaunch: true - ScaleDownPolicy: Type: AWS::AutoScaling::ScalingPolicy Properties: From 7c1ab57a48cae717ef053b913560a6d6e72b006c Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 11:38:23 +0100 Subject: [PATCH 05/42] refactor: set CIDR block outside vpc def --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 1ec385cec10..5df3787a097 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -15,10 +15,12 @@ export class DotcomRendering extends GuStack { const { app, region, stack, stage } = props; + const vpcCidrBlock = '10.248.136.0/22'; + // This fetches the VPC using the SSM parameter defined for this account // and specifies the CIDR block to use with it here const vpc = GuVpc.fromIdParameter(this, 'vpc', { - vpcCidrBlock: '10.248.136.0/22', + vpcCidrBlock, }); const lbSecurityGroup = new GuSecurityGroup( @@ -31,12 +33,12 @@ export class DotcomRendering extends GuStack { vpc, ingresses: [ { - range: Peer.ipv4(vpc.vpcCidrBlock), + range: Peer.ipv4(vpcCidrBlock), port: 80, description: 'TCP 80 ingress', }, { - range: Peer.ipv4(vpc.vpcCidrBlock), + range: Peer.ipv4(vpcCidrBlock), port: 443, description: 'TCP 443 ingress', }, @@ -53,7 +55,7 @@ export class DotcomRendering extends GuStack { vpc, ingresses: [ { - range: Peer.ipv4(vpc.vpcCidrBlock), + range: Peer.ipv4(vpcCidrBlock), port: 9000, description: 'TCP 9000 ingress', }, From c5a95f7af9b940c5032b7fe3ced7376643c550d5 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 11:48:31 +0100 Subject: [PATCH 06/42] refactor: remove unnecessary repetition of reason in overriding ids --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 5df3787a097..9e45bfb853f 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -111,17 +111,19 @@ export class DotcomRendering extends GuStack { // ----------------------------------------------------------------- // // Temporarily overriding logical IDs during CDK migration + const reason = + 'Retaining a stateful resource previously defined in YAML'; this.overrideLogicalId(lbSecurityGroup, { logicalId: 'InternalLoadBalancerSecurityGroup', - reason: 'Retaining a stateful resource previously defined in YAML', + reason, }); this.overrideLogicalId(instanceSecurityGroup, { logicalId: 'InstanceSecurityGroup', - reason: 'Retaining a stateful resource previously defined in YAML', + reason, }); this.overrideLogicalId(asg, { logicalId: 'AutoscalingGroup', - reason: 'Retaining a stateful resource previously defined in YAML', + reason, }); // ----------------------------------------------------------------- // From 3b0e188cd7e382d270f1fcf5efdfd38dfc88d168 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 11:54:47 +0100 Subject: [PATCH 07/42] refactor: use destructured region from props --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 8d6c55d0552..3d3f784b951 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -91,14 +91,14 @@ export class DotcomRendering extends GuStack { new GuAllowPolicy(this, 'AllowPolicyDescribeDecryptKms', { actions: ['kms:Decrypt', 'kms:DescribeKey'], resources: [ - `arn:aws:kms:${this.region}:${this.account}:FrontendConfigKey`, + `arn:aws:kms:${region}:${this.account}:FrontendConfigKey`, ], }), new GuAllowPolicy(this, 'AllowPolicyGetSsmParamsByPath', { actions: ['ssm:GetParametersByPath', 'ssm:GetParameter'], resources: [ - `arn:aws:ssm:${props.region}:${this.account}:parameter/frontend/*`, - `arn:aws:ssm:${props.region}:${this.account}:parameter/dotcom/*`, + `arn:aws:ssm:${region}:${this.account}:parameter/frontend/*`, + `arn:aws:ssm:${region}:${this.account}:parameter/dotcom/*`, ], }), ], From cfb7f7a5dc919ced7cbfb3e7c12f884ad8a05d87 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 12:08:05 +0100 Subject: [PATCH 08/42] refactor: move userdata definition to separate file --- .../dotcom-rendering.test.ts.snap | 8 +--- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 35 ++++------------ dotcom-rendering/cdk/lib/launch-config.ts | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 dotcom-rendering/cdk/lib/launch-config.ts diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 0bbed30afdf..523c09c4ec6 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -182,11 +182,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Fn::Join": [ "", [ - "arn:aws:kms:", - { - "Ref": "AWS::Region", - }, - ":", + "arn:aws:kms:eu-west-1:", { "Ref": "AWS::AccountId", }, @@ -1110,7 +1106,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Ref": "ELKStreamId", }, " /var/log/dotcom-rendering/dotcom-rendering.log - ", + ", ], ], }, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 3d3f784b951..beabb3ccfe7 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -11,6 +11,7 @@ import { Duration } from 'aws-cdk-lib'; import { HealthCheck } from 'aws-cdk-lib/aws-autoscaling'; import { InstanceType, Peer } from 'aws-cdk-lib/aws-ec2'; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; +import { getUserData } from './launch-config'; import type { DCRProps } from './types'; export class DotcomRendering extends GuStack { @@ -109,33 +110,6 @@ export class DotcomRendering extends GuStack { default: `/${stack}/${stage.toLowerCase()}/logstash.stream.name`, }); - const userData = ` - #!/bin/bash -ev - - groupadd frontend - useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering - usermod -a -G frontend aws-kinesis-agent-user - cd /home/dotcom-rendering - - aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${stage}/${app}/${app}.zip ./ - unzip -q ${app}.zip -d ${app} - - chown -R dotcom-rendering:frontend ${app} - - cd ${app} - - export TERM=xterm-256color - export NODE_ENV=production - export GU_STAGE=${stage} - - mkdir /var/log/dotcom-rendering - chown -R dotcom-rendering:frontend /var/log/dotcom-rendering - - make start-prod - - /opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStream.valueAsString} /var/log/dotcom-rendering/dotcom-rendering.log - `; - const asg = new GuAutoScalingGroup(this, 'AutoscalingGroup', { app, vpc, @@ -143,7 +117,12 @@ export class DotcomRendering extends GuStack { minimumInstances: props.minCapacity, maximumInstances: props.maxCapacity, healthCheck: HealthCheck.elb({ grace: Duration.minutes(2) }), - userData, + userData: getUserData({ + app, + region, + stage, + elkStreamId: elkStream.valueAsString, + }), imageRecipe: props.amiRecipe, role: instanceRole, additionalSecurityGroups: [instanceSecurityGroup], diff --git a/dotcom-rendering/cdk/lib/launch-config.ts b/dotcom-rendering/cdk/lib/launch-config.ts new file mode 100644 index 00000000000..ad02d353fb5 --- /dev/null +++ b/dotcom-rendering/cdk/lib/launch-config.ts @@ -0,0 +1,41 @@ +import type { DCRProps } from './types'; + +type UserDataProps = Pick & { + elkStreamId: string; +}; + +/** + * Fetches user data configuration for instances in the rendering app ASG + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html + */ +export const getUserData = ({ + app, + region, + stage, + elkStreamId, +}: UserDataProps): string => ` + #!/bin/bash -ev + + groupadd frontend + useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering + usermod -a -G frontend aws-kinesis-agent-user + cd /home/dotcom-rendering + + aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${stage}/${app}/${app}.zip ./ + unzip -q ${app}.zip -d ${app} + + chown -R dotcom-rendering:frontend ${app} + + cd ${app} + + export TERM=xterm-256color + export NODE_ENV=production + export GU_STAGE=${stage} + + mkdir /var/log/dotcom-rendering + chown -R dotcom-rendering:frontend /var/log/dotcom-rendering + + make start-prod + + /opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStreamId} /var/log/dotcom-rendering/dotcom-rendering.log + `; From d813f9538880fe4440dd03bcb6b3cf571c4e6668 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 14:37:54 +0100 Subject: [PATCH 09/42] infra: use ssm params for ami id --- .../__snapshots__/dotcom-rendering.test.ts.snap | 17 +++++++++++++++-- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 523c09c4ec6..947c0d70677 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -69,8 +69,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Parameters": { "AMIRendering": { + "Default": "/TEST/frontend/rendering/ami.imageId", "Description": "Amazon Machine Image ID for the app rendering. Use this in conjunction with AMIgo to keep AMIs up to date.", - "Type": "AWS::EC2::Image::Id", + "Type": "AWS::SSM::Parameter::Value", }, "App": { "Default": "rendering", @@ -98,7 +99,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Type": "AWS::SSM::Parameter::Value", }, "ELKStreamId": { - "Default": "/frontend/test/logstash.stream.name", + "Default": "/TEST/frontend/rendering/logstash.stream.name", "Type": "AWS::SSM::Parameter::Value", }, "FrontendConfigKey": { @@ -285,6 +286,18 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` ], ], }, + { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:eu-west-1:", + { + "Ref": "AWS::AccountId", + }, + ":parameter/TEST/frontend/*", + ], + ], + }, ], }, ], diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index beabb3ccfe7..d67b658eac0 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -1,6 +1,10 @@ import { join } from 'node:path'; import { GuAutoScalingGroup } from '@guardian/cdk/lib/constructs/autoscaling'; -import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; +import { + GuAmiParameter, + GuStack, + GuStringParameter, +} from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; import { GuAllowPolicy, @@ -100,6 +104,7 @@ export class DotcomRendering extends GuStack { resources: [ `arn:aws:ssm:${region}:${this.account}:parameter/frontend/*`, `arn:aws:ssm:${region}:${this.account}:parameter/dotcom/*`, + `arn:aws:ssm:${region}:${this.account}:parameter/${stage}/frontend/*`, ], }), ], @@ -107,7 +112,7 @@ export class DotcomRendering extends GuStack { const elkStream = new GuStringParameter(this, 'ELKStreamId', { fromSSM: true, - default: `/${stack}/${stage.toLowerCase()}/logstash.stream.name`, + default: `/${stage}/${stack}/${app}/logstash.stream.name`, }); const asg = new GuAutoScalingGroup(this, 'AutoscalingGroup', { @@ -123,6 +128,11 @@ export class DotcomRendering extends GuStack { stage, elkStreamId: elkStream.valueAsString, }), + imageId: new GuAmiParameter(this, { + app, + fromSSM: true, + default: `/${stage}/${stack}/${app}/ami.imageId`, + }), imageRecipe: props.amiRecipe, role: instanceRole, additionalSecurityGroups: [instanceSecurityGroup], From 1f0130dedb31a096ae3d80dcf14321036a42c9eb Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 15:26:01 +0100 Subject: [PATCH 10/42] update logging stream param --- .../lib/__snapshots__/dotcom-rendering.test.ts.snap | 2 +- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 947c0d70677..c0d11fef222 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -99,7 +99,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Type": "AWS::SSM::Parameter::Value", }, "ELKStreamId": { - "Default": "/TEST/frontend/rendering/logstash.stream.name", + "Default": "/TEST/frontend/rendering/logging.stream.name", "Type": "AWS::SSM::Parameter::Value", }, "FrontendConfigKey": { diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index d67b658eac0..2d4c288f7a5 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -110,11 +110,6 @@ export class DotcomRendering extends GuStack { ], }); - const elkStream = new GuStringParameter(this, 'ELKStreamId', { - fromSSM: true, - default: `/${stage}/${stack}/${app}/logstash.stream.name`, - }); - const asg = new GuAutoScalingGroup(this, 'AutoscalingGroup', { app, vpc, @@ -126,7 +121,10 @@ export class DotcomRendering extends GuStack { app, region, stage, - elkStreamId: elkStream.valueAsString, + elkStreamId: new GuStringParameter(this, 'ELKStreamId', { + fromSSM: true, + default: `/${stage}/${stack}/${app}/logging.stream.name`, + }).valueAsString, }), imageId: new GuAmiParameter(this, { app, From 0fcd13687294eb89be3734c2293d2d4b1316783a Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 8 Aug 2023 15:39:06 +0100 Subject: [PATCH 11/42] remove unused cfn params --- dotcom-rendering/cloudformation.yml | 6 ------ dotcom-rendering/scripts/deploy/riff-raff.yaml | 13 ------------- 2 files changed, 19 deletions(-) diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index cc7970c2872..b36cbb8e0c5 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -39,12 +39,6 @@ Parameters: Type: String Description: (Optional) Number of consecutive periods the threshold needs to be reached before 5XX alarm is triggered Default: '5' - InstanceType: - Type: String - Description: EC2 Instance Type to use for dotcom-rendering - FrontendConfigKey: - Description: Parameter store KMS key - Type: String InternalLoadBalancerSecurityGroup: Description: Id of load balancer security group Type: String diff --git a/dotcom-rendering/scripts/deploy/riff-raff.yaml b/dotcom-rendering/scripts/deploy/riff-raff.yaml index 7bb09057e05..857ec0c07a0 100755 --- a/dotcom-rendering/scripts/deploy/riff-raff.yaml +++ b/dotcom-rendering/scripts/deploy/riff-raff.yaml @@ -10,21 +10,8 @@ deployments: templateStagePaths: CODE: DotcomRendering-CODE.template.json PROD: DotcomRendering-PROD.template.json - templateParameters: - VpcId: "/account/vpc/primary/id" cloudFormationStackByTags: false cloudFormationStackName: rendering - templateStageParameters: - CODE: - InstanceType: t4g.micro - PROD: - InstanceType: t4g.small - amiParametersToTags: - AMI: - # Keep the Node version in sync with `.nvmrc` - Recipe: dotcom-rendering-ARM-jammy-node-18.17.0 - BuiltBy: amigo - AmigoStage: PROD rendering: type: autoscaling parameters: From 9717953f0382d93694fefadd67439f95e898d522 Mon Sep 17 00:00:00 2001 From: Parisa Tork Date: Tue, 8 Aug 2023 16:25:40 +0100 Subject: [PATCH 12/42] String param for S3 Bucket Name Co-Authored-By: Charlotte Emms --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 9568d647028..5c53b095458 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -1,6 +1,6 @@ import { join } from 'node:path'; import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; -import { GuStack } from '@guardian/cdk/lib/constructs/core'; +import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import type { App } from 'aws-cdk-lib'; @@ -95,8 +95,12 @@ export class DotcomRendering extends GuStack { accessLoggingPolicy: { enabled: true, emitInterval: 5, - s3BucketName: 'gu-elb-logs', - s3BucketPrefix: `ELBLogs/${props.stack}/${props.app}/${props.stage}`, + s3BucketName: new GuStringParameter(this, 'ELBLogsParameter', { + default: `/${props.stack}/${props.app}/${props.stage}/elb.logs.bucketName`, + fromSSM: true, + description: 'S3 Bucket Name for ELB logs', + }).valueAsString, + s3BucketPrefix: `/ELBLogs/${props.stack}/${props.app}/${props.stage}`, }, }); From 4cfc29df7ac6c280b7054b4edd52e888380264e9 Mon Sep 17 00:00:00 2001 From: Parisa Tork Date: Tue, 8 Aug 2023 16:58:04 +0100 Subject: [PATCH 13/42] cfnOutput experiment Co-Authored-By: Charlotte Emms Co-Authored-By: Daniel Clifton --- .../dotcom-rendering.test.ts.snap | 108 +++++++++++------- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 8 +- dotcom-rendering/cloudformation.yml | 12 +- yarn.lock | 2 +- 4 files changed, 79 insertions(+), 51 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index f7085d9a903..0561a2c85ab 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -47,6 +47,8 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "GuVpcParameter", "GuSecurityGroup", "GuSecurityGroup", + "GuStringParameter", + "GuClassicLoadBalancer", "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", @@ -102,6 +104,11 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Description": "SSM parameter containing the S3 bucket name holding distribution artifacts", "Type": "AWS::SSM::Parameter::Value", }, + "ELBLogsParameter": { + "Default": "/frontend/rendering/TEST/elb.logs.bucketName", + "Description": "S3 Bucket Name for ELB logs", + "Type": "AWS::SSM::Parameter::Value", + }, "ELKStream": { "Description": "name of the kinesis stream to use to send logs to the central ELK stack", "Type": "String", @@ -634,32 +641,10 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "AccessLoggingPolicy": { "EmitInterval": 5, "Enabled": true, - "S3BucketName": "gu-elb-logs", - "S3BucketPrefix": { - "Fn::Join": [ - "/", - [ - "ELBLogs", - { - "Fn::FindInMap": [ - "Constants", - "Stack", - "Value", - ], - }, - { - "Fn::FindInMap": [ - "Constants", - "App", - "Value", - ], - }, - { - "Ref": "Stage", - }, - ], - ], + "S3BucketName": { + "Ref": "ELBLogsParameter", }, + "S3BucketPrefix": "/ELBLogs/frontend/rendering/TEST", }, "CrossZone": true, "HealthCheck": { @@ -671,36 +656,26 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Listeners": [ { - "InstancePort": "9000", - "LoadBalancerPort": "80", - "Protocol": "HTTP", + "InstancePort": "80", + "InstanceProtocol": "http", + "LoadBalancerPort": "9000", + "Protocol": "http", }, ], - "LoadBalancerName": { - "Fn::Sub": "\${Stack}-\${Stage}-\${App}-ELB", - }, "Scheme": "internal", "SecurityGroups": [ { "Fn::GetAtt": [ - "InternalLoadBalancerSecurityGroup", + "InternalLoadBalancerRenderingSecurityGroup53E3F9A7", "GroupId", ], }, ], - "Subnets": { - "Ref": "Subnets", - }, + "Subnets": [], "Tags": [ { "Key": "App", - "Value": { - "Fn::FindInMap": [ - "Constants", - "App", - "Value", - ], - }, + "Value": "rendering", }, { "Key": "gu:cdk:version", @@ -722,6 +697,55 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Type": "AWS::ElasticLoadBalancing::LoadBalancer", }, + "InternalLoadBalancerRenderingSecurityGroup53E3F9A7": { + "Properties": { + "GroupDescription": "DotcomRendering/InternalLoadBalancerRendering/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "255.255.255.255/32", + "Description": "Disallow all traffic", + "FromPort": 252, + "IpProtocol": "icmp", + "ToPort": 86, + }, + ], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Default rule allow on 9000", + "FromPort": 9000, + "IpProtocol": "tcp", + "ToPort": 9000, + }, + ], + "Tags": [ + { + "Key": "App", + "Value": "rendering", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "VpcId": { + "Ref": "VpcId", + }, + }, + "Type": "AWS::EC2::SecurityGroup", + }, "InternalLoadBalancerSecurityGroup": { "Properties": { "GroupDescription": "Allows HTTP and HTTPS inbound connections from within the VPC", diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 24bdec7e715..ac4e8bbf40a 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -5,7 +5,7 @@ import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import { GuAllowPolicy, GuInstanceRole } from '@guardian/cdk/lib/constructs/iam'; import type { App } from 'aws-cdk-lib'; -import { Duration } from "aws-cdk-lib"; +import { CfnOutput, Duration } from "aws-cdk-lib"; import { Peer } from 'aws-cdk-lib/aws-ec2'; import { LoadBalancingProtocol } from "aws-cdk-lib/aws-elasticloadbalancing"; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; @@ -155,6 +155,10 @@ export class DotcomRendering extends GuStack { 'cloudformation.yml', ); + new CfnOutput(this, 'LoadBalancerUrl', { + value: lb.loadBalancerDnsName, + }) + new CfnInclude(this, 'YamlTemplate', { templateFile: yamlTemplateFilePath, parameters: { @@ -162,7 +166,7 @@ export class DotcomRendering extends GuStack { VPCIpBlock: vpc.vpcCidrBlock, InternalLoadBalancerSecurityGroup: lbSecurityGroup.securityGroupId, InstanceSecurityGroup: instanceSecurityGroup.securityGroupId, - InternalLoadBalancer: lb.idWithApp, + InternalLoadBalancer: lb.loadBalancerName, InstanceRole: instanceRole.roleName, } }); diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index b3c40dc1b76..9c22198c4dd 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -259,9 +259,9 @@ Resources: AlarmActions: !Ref NotificationAlarmAction OKActions: !Ref NotificationAlarmAction -Outputs: - LoadBalancerUrl: - Value: - 'Fn::GetAtt': - - InternalLoadBalancer - - DNSName +# Outputs: +# LoadBalancerUrl: +# Value: +# 'Fn::GetAtt': +# - InternalLoadBalancer +# - DNSName diff --git a/yarn.lock b/yarn.lock index a62a24905dd..8ecfb11fff6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15038,7 +15038,7 @@ preact@10.15.1: resolved "https://registry.yarnpkg.com/preact/-/preact-10.15.1.tgz#a1de60c9fc0c79a522d969c65dcaddc5d994eede" integrity sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g== -"prebid.js@github:guardian/prebid.js": +prebid.js@guardian/prebid.js: version "7.26.0" resolved "https://codeload.github.com/guardian/prebid.js/tar.gz/2e3b96dc57dfe14ed8c418674ee7a0a150ece7cb" dependencies: From 004ec81c7f6c28e450562f7afce55e1dc6a4b582 Mon Sep 17 00:00:00 2001 From: Parisa Tork <47482049+ParisaTork@users.noreply.github.com> Date: Tue, 8 Aug 2023 18:19:50 +0100 Subject: [PATCH 14/42] Appease GHA linter - order of imports --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index ac4e8bbf40a..7ec63e70d01 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -2,8 +2,8 @@ import { join } from 'node:path'; import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; -import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import { GuAllowPolicy, GuInstanceRole } from '@guardian/cdk/lib/constructs/iam'; +import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import type { App } from 'aws-cdk-lib'; import { CfnOutput, Duration } from "aws-cdk-lib"; import { Peer } from 'aws-cdk-lib/aws-ec2'; From 6d156d3f8fb32bdd48c5dc729ae6c8a48bdd2dfb Mon Sep 17 00:00:00 2001 From: Parisa Tork Date: Wed, 9 Aug 2023 08:06:34 +0100 Subject: [PATCH 15/42] Change S3 Bucket Name --- .../cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap | 2 +- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 0561a2c85ab..eddd7a73ef4 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -105,7 +105,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Type": "AWS::SSM::Parameter::Value", }, "ELBLogsParameter": { - "Default": "/frontend/rendering/TEST/elb.logs.bucketName", + "Default": "/TEST/frontend/rendering/elb.logs.bucketName", "Description": "S3 Bucket Name for ELB logs", "Type": "AWS::SSM::Parameter::Value", }, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index ac4e8bbf40a..5ca0f602a75 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -98,7 +98,7 @@ export class DotcomRendering extends GuStack { enabled: true, emitInterval: 5, s3BucketName: new GuStringParameter(this, 'ELBLogsParameter', { - default: `/${props.stack}/${props.app}/${props.stage}/elb.logs.bucketName`, + default: `/${props.stage}/${props.stack}/${props.app}/elb.logs.bucketName`, fromSSM: true, description: 'S3 Bucket Name for ELB logs', }).valueAsString, From 163c46b082ba573000ad6053de94c83e87bd6d24 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Wed, 9 Aug 2023 11:39:15 +0100 Subject: [PATCH 16/42] use cfnLoadBalancer Co-Authored-By: Parisa Tork <47482049+ParisaTork@users.noreply.github.com> Co-Authored-By: Charlotte Emms <43961396+cemms1@users.noreply.github.com> --- .../dotcom-rendering.test.ts.snap | 76 ++----------------- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 40 +++++----- 2 files changed, 25 insertions(+), 91 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index eddd7a73ef4..0bf160fd98c 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -48,7 +48,6 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "GuSecurityGroup", "GuSecurityGroup", "GuStringParameter", - "GuClassicLoadBalancer", "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", @@ -326,9 +325,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Ref": "LaunchConfig", }, "LoadBalancerNames": [ - { - "Ref": "InternalLoadBalancer", - }, + "InternalLoadBalancer", ], "MaxSize": { "Fn::FindInMap": [ @@ -401,9 +398,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Dimensions": [ { "Name": "LoadBalancerName", - "Value": { - "Ref": "InternalLoadBalancer", - }, + "Value": "InternalLoadBalancer", }, ], "EvaluationPeriods": { @@ -656,27 +651,23 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Listeners": [ { - "InstancePort": "80", - "InstanceProtocol": "http", - "LoadBalancerPort": "9000", - "Protocol": "http", + "InstancePort": "9000", + "LoadBalancerPort": "80", + "Protocol": "HTTP", }, ], + "LoadBalancerName": "frontend-TEST-rendering-ELB", "Scheme": "internal", "SecurityGroups": [ { "Fn::GetAtt": [ - "InternalLoadBalancerRenderingSecurityGroup53E3F9A7", + "InternalLoadBalancerSecurityGroup", "GroupId", ], }, ], "Subnets": [], "Tags": [ - { - "Key": "App", - "Value": "rendering", - }, { "Key": "gu:cdk:version", "Value": "TEST", @@ -697,55 +688,6 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Type": "AWS::ElasticLoadBalancing::LoadBalancer", }, - "InternalLoadBalancerRenderingSecurityGroup53E3F9A7": { - "Properties": { - "GroupDescription": "DotcomRendering/InternalLoadBalancerRendering/SecurityGroup", - "SecurityGroupEgress": [ - { - "CidrIp": "255.255.255.255/32", - "Description": "Disallow all traffic", - "FromPort": 252, - "IpProtocol": "icmp", - "ToPort": 86, - }, - ], - "SecurityGroupIngress": [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Default rule allow on 9000", - "FromPort": 9000, - "IpProtocol": "tcp", - "ToPort": 9000, - }, - ], - "Tags": [ - { - "Key": "App", - "Value": "rendering", - }, - { - "Key": "gu:cdk:version", - "Value": "TEST", - }, - { - "Key": "gu:repo", - "Value": "guardian/dotcom-rendering", - }, - { - "Key": "Stack", - "Value": "frontend", - }, - { - "Key": "Stage", - "Value": "TEST", - }, - ], - "VpcId": { - "Ref": "VpcId", - }, - }, - "Type": "AWS::EC2::SecurityGroup", - }, "InternalLoadBalancerSecurityGroup": { "Properties": { "GroupDescription": "Allows HTTP and HTTPS inbound connections from within the VPC", @@ -816,9 +758,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Dimensions": [ { "Name": "LoadBalancerName", - "Value": { - "Ref": "InternalLoadBalancer", - }, + "Value": "InternalLoadBalancer", }, ], "EvaluationPeriods": 1, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index f92cf3fcc14..d6820e97eeb 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -7,7 +7,7 @@ import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancin import type { App } from 'aws-cdk-lib'; import { CfnOutput, Duration } from "aws-cdk-lib"; import { Peer } from 'aws-cdk-lib/aws-ec2'; -import { LoadBalancingProtocol } from "aws-cdk-lib/aws-elasticloadbalancing"; +import { CfnLoadBalancer, LoadBalancingProtocol } from "aws-cdk-lib/aws-elasticloadbalancing"; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; interface DCRProps extends GuStackProps { @@ -75,24 +75,22 @@ export class DotcomRendering extends GuStack { reason: 'Retaining a stateful resource previously defined in YAML', }); - const lb = new GuClassicLoadBalancer(this, 'InternalLoadBalancer', { - app: props.app, - vpc, + const lb = new CfnLoadBalancer(this, 'InternalLoadBalancer', { listeners: [{ - internalProtocol: LoadBalancingProtocol.HTTP, - internalPort: 80, - externalProtocol: LoadBalancingProtocol.HTTP, - externalPort: 9000, - }], + instancePort: '9000', + protocol: 'HTTP', + loadBalancerPort: '80', + }], healthCheck: { - port: 9000, - path: '/_healthcheck', - healthyThreshold: 2, - unhealthyThreshold: 10, - interval: Duration.seconds(30), - timeout: Duration.seconds(10), + target: 'HTTP:9000/_healthcheck', + interval: '30', + timeout: '10', + unhealthyThreshold: '10', + healthyThreshold: '2', }, - subnetSelection: { subnets: vpc.publicSubnets }, + subnets: vpc.publicSubnets.map(subnet => subnet.subnetId), + scheme: 'internal', + securityGroups: [lbSecurityGroup.securityGroupId], crossZone: true, accessLoggingPolicy: { enabled: true, @@ -104,11 +102,7 @@ export class DotcomRendering extends GuStack { }).valueAsString, s3BucketPrefix: `/ELBLogs/${props.stack}/${props.app}/${props.stage}`, }, - }); - - this.overrideLogicalId(lb, { - logicalId: 'InternalLoadBalancer', - reason: 'Retaining a stateful resource previously defined in YAML', + loadBalancerName: `${props.stack}-${props.stage}-${props.app}-ELB`, }); const instanceRole = new GuInstanceRole(this, { @@ -156,7 +150,7 @@ export class DotcomRendering extends GuStack { ); new CfnOutput(this, 'LoadBalancerUrl', { - value: lb.loadBalancerDnsName, + value: lb.attrDnsName, }) new CfnInclude(this, 'YamlTemplate', { @@ -166,7 +160,7 @@ export class DotcomRendering extends GuStack { VPCIpBlock: vpc.vpcCidrBlock, InternalLoadBalancerSecurityGroup: lbSecurityGroup.securityGroupId, InstanceSecurityGroup: instanceSecurityGroup.securityGroupId, - InternalLoadBalancer: lb.loadBalancerName, + InternalLoadBalancer: lb.logicalId, InstanceRole: instanceRole.roleName, } }); From ece1faff419d9f5520803e655000fbe780a6c323 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Wed, 9 Aug 2023 11:43:59 +0100 Subject: [PATCH 17/42] Update dotcom-rendering.ts Co-Authored-By: Parisa Tork <47482049+ParisaTork@users.noreply.github.com> Co-Authored-By: Charlotte Emms <43961396+cemms1@users.noreply.github.com> --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index d6820e97eeb..b011ff01f54 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -3,11 +3,10 @@ import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; import { GuAllowPolicy, GuInstanceRole } from '@guardian/cdk/lib/constructs/iam'; -import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import type { App } from 'aws-cdk-lib'; -import { CfnOutput, Duration } from "aws-cdk-lib"; +import { CfnOutput } from "aws-cdk-lib"; import { Peer } from 'aws-cdk-lib/aws-ec2'; -import { CfnLoadBalancer, LoadBalancingProtocol } from "aws-cdk-lib/aws-elasticloadbalancing"; +import { CfnLoadBalancer } from "aws-cdk-lib/aws-elasticloadbalancing"; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; interface DCRProps extends GuStackProps { @@ -164,5 +163,5 @@ export class DotcomRendering extends GuStack { InstanceRole: instanceRole.roleName, } }); - } + } } From b05229a7e56b1ade9351a8d69d096fe2f0e8e993 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 11:52:55 +0100 Subject: [PATCH 18/42] use load balancer name instead of logical ID --- .../dotcom-rendering.test.ts.snap | 6 +- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 57 +++++++++++-------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 0bf160fd98c..acf26b7c338 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -325,7 +325,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Ref": "LaunchConfig", }, "LoadBalancerNames": [ - "InternalLoadBalancer", + "frontend-TEST-rendering-ELB", ], "MaxSize": { "Fn::FindInMap": [ @@ -398,7 +398,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Dimensions": [ { "Name": "LoadBalancerName", - "Value": "InternalLoadBalancer", + "Value": "frontend-TEST-rendering-ELB", }, ], "EvaluationPeriods": { @@ -758,7 +758,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Dimensions": [ { "Name": "LoadBalancerName", - "Value": "InternalLoadBalancer", + "Value": "frontend-TEST-rendering-ELB", }, ], "EvaluationPeriods": 1, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index b011ff01f54..ba4c486cca7 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -2,11 +2,14 @@ import { join } from 'node:path'; import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; -import { GuAllowPolicy, GuInstanceRole } from '@guardian/cdk/lib/constructs/iam'; +import { + GuAllowPolicy, + GuInstanceRole, +} from '@guardian/cdk/lib/constructs/iam'; import type { App } from 'aws-cdk-lib'; -import { CfnOutput } from "aws-cdk-lib"; +import { CfnOutput } from 'aws-cdk-lib'; import { Peer } from 'aws-cdk-lib/aws-ec2'; -import { CfnLoadBalancer } from "aws-cdk-lib/aws-elasticloadbalancing"; +import { CfnLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancing'; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; interface DCRProps extends GuStackProps { @@ -56,8 +59,7 @@ export class DotcomRendering extends GuStack { 'InstanceSecurityGroup', { app: props.app, - description: - 'rendering instance', + description: 'rendering instance', vpc, ingresses: [ { @@ -75,11 +77,13 @@ export class DotcomRendering extends GuStack { }); const lb = new CfnLoadBalancer(this, 'InternalLoadBalancer', { - listeners: [{ - instancePort: '9000', - protocol: 'HTTP', - loadBalancerPort: '80', - }], + listeners: [ + { + instancePort: '9000', + protocol: 'HTTP', + loadBalancerPort: '80', + }, + ], healthCheck: { target: 'HTTP:9000/_healthcheck', interval: '30', @@ -87,7 +91,7 @@ export class DotcomRendering extends GuStack { unhealthyThreshold: '10', healthyThreshold: '2', }, - subnets: vpc.publicSubnets.map(subnet => subnet.subnetId), + subnets: vpc.publicSubnets.map((subnet) => subnet.subnetId), scheme: 'internal', securityGroups: [lbSecurityGroup.securityGroupId], crossZone: true, @@ -110,31 +114,35 @@ export class DotcomRendering extends GuStack { //todo: do we need the first two policies? They are provided by default? new GuAllowPolicy(this, 'AllowPolicyGetArtifactsBucket', { actions: ['s3:GetObject'], - resources: ['arn:aws:s3:::aws-frontend-artifacts/*'] + resources: ['arn:aws:s3:::aws-frontend-artifacts/*'], }), new GuAllowPolicy(this, 'AllowPolicyCloudwatchLogs', { actions: ['cloudwatch:*', 'logs:*'], - resources: ['*'] + resources: ['*'], }), new GuAllowPolicy(this, 'AllowPolicyDescribeEc2Autoscaling', { actions: [ 'ec2:DescribeTags', 'ec2:DescribeInstances', 'autoscaling:DescribeAutoScalingGroups', - 'autoscaling:DescribeAutoScalingInstances' + 'autoscaling:DescribeAutoScalingInstances', ], - resources: ['*'] + resources: ['*'], }), new GuAllowPolicy(this, 'AllowPolicyDescribeDecryptKms', { actions: ['kms:Decrypt', 'kms:DescribeKey'], - resources: [`arn:aws:kms:${this.region}:${this.account}:FrontendConfigKey`], - + resources: [ + `arn:aws:kms:${this.region}:${this.account}:FrontendConfigKey`, + ], }), new GuAllowPolicy(this, 'AllowPolicyGetSsmParamsByPath', { actions: ['ssm:GetParametersByPath', 'ssm:GetParameter'], - resources: [`arn:aws:ssm:${props.region}:${this.account}:parameter/frontend/*`, `arn:aws:ssm:${props.region}:${this.account}:parameter/dotcom/*`] + resources: [ + `arn:aws:ssm:${props.region}:${this.account}:parameter/frontend/*`, + `arn:aws:ssm:${props.region}:${this.account}:parameter/dotcom/*`, + ], }), - ] + ], }); this.overrideLogicalId(instanceRole, { @@ -150,18 +158,19 @@ export class DotcomRendering extends GuStack { new CfnOutput(this, 'LoadBalancerUrl', { value: lb.attrDnsName, - }) + }); new CfnInclude(this, 'YamlTemplate', { templateFile: yamlTemplateFilePath, parameters: { VpcId: vpc.vpcId, VPCIpBlock: vpc.vpcCidrBlock, - InternalLoadBalancerSecurityGroup: lbSecurityGroup.securityGroupId, + InternalLoadBalancerSecurityGroup: + lbSecurityGroup.securityGroupId, InstanceSecurityGroup: instanceSecurityGroup.securityGroupId, - InternalLoadBalancer: lb.logicalId, + InternalLoadBalancer: lb.loadBalancerName, InstanceRole: instanceRole.roleName, - } + }, }); - } + } } From e6cc5730944045a7d67fac1294d3f0ea1c325e96 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 14:40:42 +0100 Subject: [PATCH 19/42] infa: remove first char of s3 prefix and add public subnets param --- .../__snapshots__/dotcom-rendering.test.cts.snap | 14 ++++++++++++-- dotcom-rendering/cdk/lib/dotcom-rendering.cts | 14 +++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap index acf26b7c338..fc1d88b582b 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap @@ -48,6 +48,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "GuSecurityGroup", "GuSecurityGroup", "GuStringParameter", + "GuStringParameter", "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", @@ -130,6 +131,11 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Description": "(Optional) ARN of action to execute when notification alarms change state", "Type": "CommaDelimitedList", }, + "PublicSubnets": { + "Default": "/TEST/frontend/rendering/vpc.subnets.public", + "Description": "Public subnets", + "Type": "AWS::SSM::Parameter::Value", + }, "Stack": { "Default": "frontend", "Description": "Stack name", @@ -639,7 +645,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "S3BucketName": { "Ref": "ELBLogsParameter", }, - "S3BucketPrefix": "/ELBLogs/frontend/rendering/TEST", + "S3BucketPrefix": "ELBLogs/frontend/rendering/TEST", }, "CrossZone": true, "HealthCheck": { @@ -666,7 +672,11 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` ], }, ], - "Subnets": [], + "Subnets": [ + { + "Ref": "PublicSubnets", + }, + ], "Tags": [ { "Key": "gu:cdk:version", diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.cts b/dotcom-rendering/cdk/lib/dotcom-rendering.cts index ba4c486cca7..9d33a147aef 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.cts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.cts @@ -21,6 +21,8 @@ export class DotcomRendering extends GuStack { constructor(scope: App, id: string, props: DCRProps) { super(scope, id, props); + const ssmPrefix = `/${props.stage}/${props.stack}/${props.app}`; + // This fetches the VPC using the SSM parameter defined for this account // and specifies the CIDR block to use with it here const vpc = GuVpc.fromIdParameter(this, 'vpc', { @@ -76,6 +78,12 @@ export class DotcomRendering extends GuStack { reason: 'Retaining a stateful resource previously defined in YAML', }); + const publicSubnets = new GuStringParameter(this, 'PublicSubnets', { + default: `${ssmPrefix}/vpc.subnets.public`, + fromSSM: true, + description: 'Public subnets', + }).valueAsString.split(','); + const lb = new CfnLoadBalancer(this, 'InternalLoadBalancer', { listeners: [ { @@ -91,7 +99,7 @@ export class DotcomRendering extends GuStack { unhealthyThreshold: '10', healthyThreshold: '2', }, - subnets: vpc.publicSubnets.map((subnet) => subnet.subnetId), + subnets: publicSubnets, scheme: 'internal', securityGroups: [lbSecurityGroup.securityGroupId], crossZone: true, @@ -99,11 +107,11 @@ export class DotcomRendering extends GuStack { enabled: true, emitInterval: 5, s3BucketName: new GuStringParameter(this, 'ELBLogsParameter', { - default: `/${props.stage}/${props.stack}/${props.app}/elb.logs.bucketName`, + default: `${ssmPrefix}/elb.logs.bucketName`, fromSSM: true, description: 'S3 Bucket Name for ELB logs', }).valueAsString, - s3BucketPrefix: `/ELBLogs/${props.stack}/${props.app}/${props.stage}`, + s3BucketPrefix: `ELBLogs/${props.stack}/${props.app}/${props.stage}`, }, loadBalancerName: `${props.stack}-${props.stage}-${props.app}-ELB`, }); From dd32d7f9e13a1adee2876457aa66379175dc942e Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 14:44:03 +0100 Subject: [PATCH 20/42] infra: use ref rather than name for lb in cfn --- .../lib/__snapshots__/dotcom-rendering.test.cts.snap | 12 +++++++++--- dotcom-rendering/cdk/lib/dotcom-rendering.cts | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap index fc1d88b582b..3fd627d71d8 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap @@ -331,7 +331,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Ref": "LaunchConfig", }, "LoadBalancerNames": [ - "frontend-TEST-rendering-ELB", + { + "Ref": "InternalLoadBalancer", + }, ], "MaxSize": { "Fn::FindInMap": [ @@ -404,7 +406,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Dimensions": [ { "Name": "LoadBalancerName", - "Value": "frontend-TEST-rendering-ELB", + "Value": { + "Ref": "InternalLoadBalancer", + }, }, ], "EvaluationPeriods": { @@ -768,7 +772,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Dimensions": [ { "Name": "LoadBalancerName", - "Value": "frontend-TEST-rendering-ELB", + "Value": { + "Ref": "InternalLoadBalancer", + }, }, ], "EvaluationPeriods": 1, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.cts b/dotcom-rendering/cdk/lib/dotcom-rendering.cts index 9d33a147aef..eabc3732f71 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.cts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.cts @@ -176,7 +176,7 @@ export class DotcomRendering extends GuStack { InternalLoadBalancerSecurityGroup: lbSecurityGroup.securityGroupId, InstanceSecurityGroup: instanceSecurityGroup.securityGroupId, - InternalLoadBalancer: lb.loadBalancerName, + InternalLoadBalancer: lb.ref, InstanceRole: instanceRole.roleName, }, }); From 9a77a1c029cdb563f350918b5fc6451ff3f47918 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 14:47:43 +0100 Subject: [PATCH 21/42] infra: use correct param type for subnet list --- .../lib/__snapshots__/dotcom-rendering.test.cts.snap | 12 +++++------- dotcom-rendering/cdk/lib/dotcom-rendering.cts | 10 +++++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap index 3fd627d71d8..26f0c24fc63 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.cts.snap @@ -47,7 +47,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "GuVpcParameter", "GuSecurityGroup", "GuSecurityGroup", - "GuStringParameter", + "GuSubnetListParameter", "GuStringParameter", "GuAllowPolicy", "GuAllowPolicy", @@ -134,7 +134,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "PublicSubnets": { "Default": "/TEST/frontend/rendering/vpc.subnets.public", "Description": "Public subnets", - "Type": "AWS::SSM::Parameter::Value", + "Type": "AWS::SSM::Parameter::Value>", }, "Stack": { "Default": "frontend", @@ -676,11 +676,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` ], }, ], - "Subnets": [ - { - "Ref": "PublicSubnets", - }, - ], + "Subnets": { + "Ref": "PublicSubnets", + }, "Tags": [ { "Key": "gu:cdk:version", diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.cts b/dotcom-rendering/cdk/lib/dotcom-rendering.cts index eabc3732f71..752d28ec941 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.cts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.cts @@ -1,6 +1,10 @@ import { join } from 'node:path'; import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; -import { GuStack, GuStringParameter } from '@guardian/cdk/lib/constructs/core'; +import { + GuStack, + GuStringParameter, + GuSubnetListParameter, +} from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; import { GuAllowPolicy, @@ -78,11 +82,11 @@ export class DotcomRendering extends GuStack { reason: 'Retaining a stateful resource previously defined in YAML', }); - const publicSubnets = new GuStringParameter(this, 'PublicSubnets', { + const publicSubnets = new GuSubnetListParameter(this, 'PublicSubnets', { default: `${ssmPrefix}/vpc.subnets.public`, fromSSM: true, description: 'Public subnets', - }).valueAsString.split(','); + }).valueAsList; const lb = new CfnLoadBalancer(this, 'InternalLoadBalancer', { listeners: [ From 99026c59969f430d3acee2fbc860af836838a50e Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 15:14:24 +0100 Subject: [PATCH 22/42] remove commented out yaml in cfn --- dotcom-rendering/cloudformation.yml | 46 ----------------------------- 1 file changed, 46 deletions(-) diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index 9c22198c4dd..a2d77a97a27 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -97,46 +97,6 @@ Resources: Roles: - Ref: InstanceRole - # InternalLoadBalancer: - # Type: AWS::ElasticLoadBalancing::LoadBalancer - # Properties: - # Scheme: internal - # LoadBalancerName: !Sub '${Stack}-${Stage}-${App}-ELB' - # Listeners: - # - { LoadBalancerPort: 80, InstancePort: 9000, Protocol: HTTP } - # CrossZone: true - # HealthCheck: - # Target: HTTP:9000/_healthcheck - # HealthyThreshold: 2 - # UnhealthyThreshold: 10 - # Interval: 30 - # Timeout: 10 - # Subnets: - # Ref: Subnets - # SecurityGroups: - # - Ref: InternalLoadBalancerSecurityGroup - # AccessLoggingPolicy: - # EmitInterval: 5 - # Enabled: true - # S3BucketName: gu-elb-logs - # S3BucketPrefix: - # Fn::Join: - # - '/' - # - - ELBLogs - # - Fn::FindInMap: [Constants, Stack, Value] - # - Fn::FindInMap: [Constants, App, Value] - # - Ref: Stage - # Tags: - # - Key: Stage - # Value: - # Ref: Stage - # - Key: Stack - # Value: - # Fn::FindInMap: [Constants, Stack, Value] - # - Key: App - # Value: - # Fn::FindInMap: [Constants, App, Value] - LaunchConfig: Type: AWS::AutoScaling::LaunchConfiguration Properties: @@ -259,9 +219,3 @@ Resources: AlarmActions: !Ref NotificationAlarmAction OKActions: !Ref NotificationAlarmAction -# Outputs: -# LoadBalancerUrl: -# Value: -# 'Fn::GetAtt': -# - InternalLoadBalancer -# - DNSName From aa2b64b69f201964d5f2c9615f65e67582ba17cc Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 15:32:06 +0100 Subject: [PATCH 23/42] refactor: clean up cfn and cdk code --- dotcom-rendering/cdk/bin/cdk.ts | 13 +-- .../dotcom-rendering.test.ts.snap | 6 +- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 87 ++++++++----------- dotcom-rendering/cdk/lib/types.ts | 12 +++ dotcom-rendering/cloudformation.yml | 10 --- 5 files changed, 59 insertions(+), 69 deletions(-) create mode 100644 dotcom-rendering/cdk/lib/types.ts diff --git a/dotcom-rendering/cdk/bin/cdk.ts b/dotcom-rendering/cdk/bin/cdk.ts index 94b393315ae..50db0b0f1d0 100644 --- a/dotcom-rendering/cdk/bin/cdk.ts +++ b/dotcom-rendering/cdk/bin/cdk.ts @@ -3,15 +3,18 @@ import { App } from 'aws-cdk-lib'; import { DotcomRendering } from '../lib/dotcom-rendering'; const app = new App(); -new DotcomRendering(app, 'DotcomRendering-PROD', { + +const sharedProps = { app: 'rendering', stack: 'frontend', - stage: 'PROD', region: 'eu-west-1', +}; + +new DotcomRendering(app, 'DotcomRendering-PROD', { + ...sharedProps, + stage: 'PROD', }); new DotcomRendering(app, 'DotcomRendering-CODE', { - app: 'rendering', - stack: 'frontend', + ...sharedProps, stage: 'CODE', - region: 'eu-west-1', }); diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 26f0c24fc63..6d41ce8bc62 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -46,9 +46,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "gu:cdk:constructs": [ "GuVpcParameter", "GuSecurityGroup", - "GuSecurityGroup", "GuSubnetListParameter", "GuStringParameter", + "GuSecurityGroup", "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", @@ -113,10 +113,6 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Description": "name of the kinesis stream to use to send logs to the central ELK stack", "Type": "String", }, - "FrontendConfigKey": { - "Description": "Parameter store KMS key", - "Type": "String", - }, "InstanceType": { "Description": "EC2 Instance Type to use for dotcom-rendering", "Type": "String", diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index c153dfc5fef..af96e70a827 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -1,5 +1,4 @@ import { join } from 'node:path'; -import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; import { GuStack, GuStringParameter, @@ -15,29 +14,26 @@ import { CfnOutput } from 'aws-cdk-lib'; import { Peer } from 'aws-cdk-lib/aws-ec2'; import { CfnLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancing'; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; - -interface DCRProps extends GuStackProps { - app: string; - region: string; -} +import { DCRProps } from './types'; export class DotcomRendering extends GuStack { constructor(scope: App, id: string, props: DCRProps) { super(scope, id, props); - const ssmPrefix = `/${props.stage}/${props.stack}/${props.app}`; + const { app, stack, stage } = props; + + const ssmPrefix = `/${stage}/${stack}/${app}`; // This fetches the VPC using the SSM parameter defined for this account // and specifies the CIDR block to use with it here - const vpc = GuVpc.fromIdParameter(this, 'vpc', { - vpcCidrBlock: '10.248.136.0/22', - }); + const vpcCidrBlock = '10.248.136.0/22'; + const vpc = GuVpc.fromIdParameter(this, 'vpc', { vpcCidrBlock }); const lbSecurityGroup = new GuSecurityGroup( this, 'InternalLoadBalancerSecurityGroup', { - app: props.app, + app, description: 'Allows HTTP and HTTPS inbound connections from within the VPC', vpc, @@ -60,35 +56,6 @@ export class DotcomRendering extends GuStack { reason: 'Retaining a stateful resource previously defined in YAML', }); - const instanceSecurityGroup = new GuSecurityGroup( - this, - 'InstanceSecurityGroup', - { - app: props.app, - description: - 'rendering instance', - vpc, - ingresses: [ - { - range: Peer.ipv4(vpc.vpcCidrBlock), - port: 9000, - description: 'TCP 9000 ingress', - }, - ], - }, - ); - - this.overrideLogicalId(instanceSecurityGroup, { - logicalId: 'InstanceSecurityGroup', - reason: 'Retaining a stateful resource previously defined in YAML', - }); - - const publicSubnets = new GuSubnetListParameter(this, 'PublicSubnets', { - default: `${ssmPrefix}/vpc.subnets.public`, - fromSSM: true, - description: 'Public subnets', - }).valueAsList; - const lb = new CfnLoadBalancer(this, 'InternalLoadBalancer', { listeners: [ { @@ -104,7 +71,11 @@ export class DotcomRendering extends GuStack { unhealthyThreshold: '10', healthyThreshold: '2', }, - subnets: publicSubnets, + subnets: new GuSubnetListParameter(this, 'PublicSubnets', { + default: `${ssmPrefix}/vpc.subnets.public`, + fromSSM: true, + description: 'Public subnets', + }).valueAsList, scheme: 'internal', securityGroups: [lbSecurityGroup.securityGroupId], crossZone: true, @@ -121,8 +92,29 @@ export class DotcomRendering extends GuStack { loadBalancerName: `${props.stack}-${props.stage}-${props.app}-ELB`, }); + const instanceSecurityGroup = new GuSecurityGroup( + this, + 'InstanceSecurityGroup', + { + app, + description: 'rendering instance', + vpc, + ingresses: [ + { + range: Peer.ipv4(vpcCidrBlock), + port: 9000, + description: 'TCP 9000 ingress', + }, + ], + }, + ); + this.overrideLogicalId(instanceSecurityGroup, { + logicalId: 'InstanceSecurityGroup', + reason: 'Retaining a stateful resource previously defined in YAML', + }); + const instanceRole = new GuInstanceRole(this, { - app: props.app, + app, additionalPolicies: [ //todo: do we need the first two policies? They are provided by default? new GuAllowPolicy(this, 'AllowPolicyGetArtifactsBucket', { @@ -157,7 +149,6 @@ export class DotcomRendering extends GuStack { }), ], }); - this.overrideLogicalId(instanceRole, { logicalId: 'InstanceRole', reason: 'Retaining a stateful resource previously defined in YAML', @@ -169,20 +160,18 @@ export class DotcomRendering extends GuStack { 'cloudformation.yml', ); - new CfnOutput(this, 'LoadBalancerUrl', { - value: lb.attrDnsName, - }); - new CfnInclude(this, 'YamlTemplate', { templateFile: yamlTemplateFilePath, parameters: { VpcId: vpc.vpcId, - VPCIpBlock: vpc.vpcCidrBlock, - InternalLoadBalancerSecurityGroup: lbSecurityGroup.securityGroupId, InstanceSecurityGroup: instanceSecurityGroup.securityGroupId, InternalLoadBalancer: lb.ref, InstanceRole: instanceRole.roleName, }, }); + + new CfnOutput(this, 'LoadBalancerUrl', { + value: lb.attrDnsName, + }); } } diff --git a/dotcom-rendering/cdk/lib/types.ts b/dotcom-rendering/cdk/lib/types.ts new file mode 100644 index 00000000000..8afb9f5a4e4 --- /dev/null +++ b/dotcom-rendering/cdk/lib/types.ts @@ -0,0 +1,12 @@ +import type { GuStackProps } from '@guardian/cdk/lib/constructs/core'; + +export interface DCRProps extends GuStackProps { + /** + * The name of the application + */ + app: string; + /** + * The minimum number of instances in the autoscaling group + */ + region: string; +} diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index a2d77a97a27..d2d91c91c18 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -48,19 +48,9 @@ Parameters: InstanceType: Type: String Description: EC2 Instance Type to use for dotcom-rendering - VPCIpBlock: - Type: String - Description: CIDR block for IP addresses inside this VPC - Default: 10.248.136.0/22 ELKStream: Type: String Description: name of the kinesis stream to use to send logs to the central ELK stack - FrontendConfigKey: - Description: Parameter store KMS key - Type: String - InternalLoadBalancerSecurityGroup: - Description: Id of load balancer security group - Type: String InstanceSecurityGroup: Description: Id of instance security group Type: String From 5aa57bd5c3a603037199cd4854927b78ca9f2558 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 15:36:03 +0100 Subject: [PATCH 24/42] refactor: destructure for all props --- .../lib/__snapshots__/dotcom-rendering.test.ts.snap | 6 +----- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 6d41ce8bc62..05ed1d23799 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -195,11 +195,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Fn::Join": [ "", [ - "arn:aws:kms:", - { - "Ref": "AWS::Region", - }, - ":", + "arn:aws:kms:eu-west-1:", { "Ref": "AWS::AccountId", }, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index af96e70a827..2d376d88302 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -20,7 +20,7 @@ export class DotcomRendering extends GuStack { constructor(scope: App, id: string, props: DCRProps) { super(scope, id, props); - const { app, stack, stage } = props; + const { app, region, stack, stage } = props; const ssmPrefix = `/${stage}/${stack}/${app}`; @@ -87,9 +87,9 @@ export class DotcomRendering extends GuStack { fromSSM: true, description: 'S3 Bucket Name for ELB logs', }).valueAsString, - s3BucketPrefix: `ELBLogs/${props.stack}/${props.app}/${props.stage}`, + s3BucketPrefix: `ELBLogs/${stack}/${app}/${stage}`, }, - loadBalancerName: `${props.stack}-${props.stage}-${props.app}-ELB`, + loadBalancerName: `${stack}-${stage}-${app}-ELB`, }); const instanceSecurityGroup = new GuSecurityGroup( @@ -137,14 +137,14 @@ export class DotcomRendering extends GuStack { new GuAllowPolicy(this, 'AllowPolicyDescribeDecryptKms', { actions: ['kms:Decrypt', 'kms:DescribeKey'], resources: [ - `arn:aws:kms:${this.region}:${this.account}:FrontendConfigKey`, + `arn:aws:kms:${region}:${this.account}:FrontendConfigKey`, ], }), new GuAllowPolicy(this, 'AllowPolicyGetSsmParamsByPath', { actions: ['ssm:GetParametersByPath', 'ssm:GetParameter'], resources: [ - `arn:aws:ssm:${props.region}:${this.account}:parameter/frontend/*`, - `arn:aws:ssm:${props.region}:${this.account}:parameter/dotcom/*`, + `arn:aws:ssm:${region}:${this.account}:parameter/frontend/*`, + `arn:aws:ssm:${region}:${this.account}:parameter/dotcom/*`, ], }), ], From 6ab4fb85b6717f730d2f88a76ffed067a42a4ac1 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 15:36:50 +0100 Subject: [PATCH 25/42] docs: update description for region --- dotcom-rendering/cdk/lib/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/cdk/lib/types.ts b/dotcom-rendering/cdk/lib/types.ts index 8afb9f5a4e4..4116ebb5101 100644 --- a/dotcom-rendering/cdk/lib/types.ts +++ b/dotcom-rendering/cdk/lib/types.ts @@ -6,7 +6,7 @@ export interface DCRProps extends GuStackProps { */ app: string; /** - * The minimum number of instances in the autoscaling group + * The region in AWS where the application will run */ region: string; } From 6a69b6716a9942d4d2e4ee67b12980ac5452e6ee Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 17:22:31 +0100 Subject: [PATCH 26/42] test: update snapshot --- .../cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap | 2 -- 1 file changed, 2 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index e583de78a4d..05ed1d23799 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -49,8 +49,6 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "GuSubnetListParameter", "GuStringParameter", "GuSecurityGroup", - "GuSubnetListParameter", - "GuStringParameter", "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", From 71767f97160f8318f26e85dcd18538f3d11fa9bd Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 18:07:04 +0100 Subject: [PATCH 27/42] rework public subnet definition --- .../dotcom-rendering.test.ts.snap | 54 +++---------------- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 23 ++++---- dotcom-rendering/cloudformation.yml | 2 +- 3 files changed, 23 insertions(+), 56 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 525f398ef7a..2becb0d73a0 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -35,8 +35,8 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Metadata": { "gu:cdk:constructs": [ "GuVpcParameter", - "GuSecurityGroup", "GuSubnetListParameter", + "GuSecurityGroup", "GuStringParameter", "GuSecurityGroup", "GuAllowPolicy", @@ -124,8 +124,8 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Type": "CommaDelimitedList", }, "PublicSubnets": { - "Default": "/TEST/frontend/rendering/vpc.subnets.public", - "Description": "Public subnets", + "Default": "/account/vpc/primary/subnets/public", + "Description": "A list of public subnets", "Type": "AWS::SSM::Parameter::Value>", }, "Stack": { @@ -364,7 +364,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Value": "TEST", }, ], - "VPCZoneIdentifier": [], + "VPCZoneIdentifier": { + "Ref": "PublicSubnets", + }, }, "Type": "AWS::AutoScaling::AutoScalingGroup", }, @@ -868,27 +870,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Properties": { "AdjustmentType": "ChangeInCapacity", "AutoScalingGroupName": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition", - }, - ":autoscaling:", - { - "Ref": "AWS::Region", - }, - ":", - { - "Ref": "AWS::AccountId", - }, - ":autoScalingGroup:*:autoScalingGroupName/", - { - "Ref": "AutoscalingGroup", - }, - ], - ], + "Ref": "AutoscalingGroup", }, "Cooldown": "120", "ScalingAdjustment": -1, @@ -899,27 +881,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Properties": { "AdjustmentType": "PercentChangeInCapacity", "AutoScalingGroupName": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition", - }, - ":autoscaling:", - { - "Ref": "AWS::Region", - }, - ":", - { - "Ref": "AWS::AccountId", - }, - ":autoScalingGroup:*:autoScalingGroupName/", - { - "Ref": "AutoscalingGroup", - }, - ], - ], + "Ref": "AutoscalingGroup", }, "Cooldown": "600", "ScalingAdjustment": 100, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index d5eda8cc8bb..9e92ab5577f 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -6,7 +6,11 @@ import { GuStringParameter, GuSubnetListParameter, } from '@guardian/cdk/lib/constructs/core'; -import { GuSecurityGroup, GuVpc } from '@guardian/cdk/lib/constructs/ec2'; +import { + GuSecurityGroup, + GuVpc, + SubnetType, +} from '@guardian/cdk/lib/constructs/ec2'; import { GuAllowPolicy, GuInstanceRole, @@ -31,7 +35,12 @@ export class DotcomRendering extends GuStack { // This fetches the VPC using the SSM parameter defined for this account // and specifies the CIDR block to use with it here const vpcCidrBlock = '10.248.136.0/22'; - const vpc = GuVpc.fromIdParameter(this, 'vpc', { vpcCidrBlock }); + const vpc = GuVpc.fromIdParameter(this, 'vpc', { + vpcCidrBlock, + }); + const publicSubnets = GuVpc.subnetsFromParameter(this, { + type: SubnetType.PUBLIC, + }); const lbSecurityGroup = new GuSecurityGroup( this, @@ -75,11 +84,7 @@ export class DotcomRendering extends GuStack { unhealthyThreshold: '10', healthyThreshold: '2', }, - subnets: new GuSubnetListParameter(this, 'PublicSubnets', { - default: `${ssmPrefix}/vpc.subnets.public`, - fromSSM: true, - description: 'Public subnets', - }).valueAsList, + subnets: publicSubnets.map((subnet) => subnet.subnetId), scheme: 'internal', securityGroups: [lbSecurityGroup.securityGroupId], crossZone: true, @@ -183,7 +188,7 @@ export class DotcomRendering extends GuStack { imageRecipe: props.amiRecipe, role: instanceRole, additionalSecurityGroups: [instanceSecurityGroup], - vpcSubnets: { subnets: vpc.publicSubnets }, + vpcSubnets: { subnets: publicSubnets }, }); this.overrideLogicalId(asg, { logicalId: 'AutoscalingGroup', @@ -199,7 +204,7 @@ export class DotcomRendering extends GuStack { new CfnInclude(this, 'YamlTemplate', { templateFile: yamlTemplateFilePath, parameters: { - AutoscalingGroup: asg.autoScalingGroupArn, + AutoscalingGroup: asg.autoScalingGroupName, InternalLoadBalancer: lb.ref, InstanceRole: instanceRole.roleName, }, diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index 02655c5c5a2..f0885cc6a0e 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -43,7 +43,7 @@ Parameters: Type: String Description: EC2 Instance Type to use for dotcom-rendering AutoscalingGroup: - Description: ARN of the Autoscaling group + Description: Name of the Autoscaling group Type: String InternalLoadBalancer: Description: Id of internal load balancer From 833683f9a352d4ffbe152aaef49c0503d4883eb8 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 9 Aug 2023 18:09:17 +0100 Subject: [PATCH 28/42] remove unused param --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 9e92ab5577f..06795ce04e9 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -4,7 +4,6 @@ import { GuAmiParameter, GuStack, GuStringParameter, - GuSubnetListParameter, } from '@guardian/cdk/lib/constructs/core'; import { GuSecurityGroup, From 13f81d6ff1227cc2b997e744ce345af4fac4f898 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Thu, 10 Aug 2023 10:16:51 +0100 Subject: [PATCH 29/42] refactor: use ssmPrefix in path for ssm params --- .../cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap | 2 +- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 2becb0d73a0..3f62c9dcd60 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -302,7 +302,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` { "Ref": "AWS::AccountId", }, - ":parameter/TEST/frontend/*", + ":parameter//TEST/frontend/rendering/*", ], ], }, diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 06795ce04e9..017f32c1e03 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -153,7 +153,7 @@ export class DotcomRendering extends GuStack { resources: [ `arn:aws:ssm:${region}:${this.account}:parameter/frontend/*`, `arn:aws:ssm:${region}:${this.account}:parameter/dotcom/*`, - `arn:aws:ssm:${region}:${this.account}:parameter/${stage}/frontend/*`, + `arn:aws:ssm:${region}:${this.account}:parameter/${ssmPrefix}/*`, ], }), ], @@ -176,13 +176,13 @@ export class DotcomRendering extends GuStack { stage, elkStreamId: new GuStringParameter(this, 'ELKStreamId', { fromSSM: true, - default: `/${stage}/${stack}/${app}/logging.stream.name`, + default: `${ssmPrefix}/logging.stream.name`, }).valueAsString, }), imageId: new GuAmiParameter(this, { app, fromSSM: true, - default: `/${stage}/${stack}/${app}/ami.imageId`, + default: `${ssmPrefix}/ami.imageId`, }), imageRecipe: props.amiRecipe, role: instanceRole, From fb92c5bd551204799f2cf042c1b7ac38f3a90cc1 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Thu, 10 Aug 2023 10:26:12 +0100 Subject: [PATCH 30/42] refactor: use destructured app from props --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 017f32c1e03..d7350179bf6 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -122,7 +122,7 @@ export class DotcomRendering extends GuStack { }); const instanceRole = new GuInstanceRole(this, { - app: props.app, + app, additionalPolicies: [ //todo: do we need the first two policies? They are provided by default? new GuAllowPolicy(this, 'AllowPolicyGetArtifactsBucket', { From bcd84eac1da2db021a5cac910dee35b31ccaff9c Mon Sep 17 00:00:00 2001 From: Charlotte Date: Thu, 10 Aug 2023 10:28:43 +0100 Subject: [PATCH 31/42] docs: improve docstring for getUserData --- dotcom-rendering/cdk/lib/launch-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/cdk/lib/launch-config.ts b/dotcom-rendering/cdk/lib/launch-config.ts index ad02d353fb5..e87b689e392 100644 --- a/dotcom-rendering/cdk/lib/launch-config.ts +++ b/dotcom-rendering/cdk/lib/launch-config.ts @@ -5,7 +5,7 @@ type UserDataProps = Pick & { }; /** - * Fetches user data configuration for instances in the rendering app ASG + * Returns user data configuration for instances in the rendering app * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html */ export const getUserData = ({ From df92a136a74ca9d2afbb9ab6bbbeb41f57c9e5d8 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Thu, 10 Aug 2023 15:48:55 +0100 Subject: [PATCH 32/42] chore: change location of nvmrc check for ami recipe --- scripts/check-node-versions.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/check-node-versions.mjs b/scripts/check-node-versions.mjs index c02a1f8c471..3b63ae914ff 100644 --- a/scripts/check-node-versions.mjs +++ b/scripts/check-node-versions.mjs @@ -34,8 +34,9 @@ const requiredNodeVersionMatches = pattern: /^FROM node:(.+)-alpine$/m, }, { - filepath: 'dotcom-rendering/scripts/deploy/riff-raff.yaml', - pattern: /^ +Recipe: dotcom-rendering.*-node-(\d+\.\d+\.\d+)$/m, + filepath: 'dotcom-rendering/cdk/bin/cdk.ts', + pattern: + /^.+amiRecipe: \'dotcom-rendering.*-node-(\d+\.\d+\.\d+)\'\,$/m, }, { filepath: 'apps-rendering/riff-raff.yaml', From 5eb0c018308e7fbf2db7cc5511843389fbc0d9c5 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Fri, 11 Aug 2023 08:51:17 +0100 Subject: [PATCH 33/42] refactor: rename userdata file --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 2 +- dotcom-rendering/cdk/lib/{launch-config.ts => userData.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename dotcom-rendering/cdk/lib/{launch-config.ts => userData.ts} (100%) diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 20e455aba67..c6bf07dfab3 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -20,8 +20,8 @@ import { HealthCheck } from 'aws-cdk-lib/aws-autoscaling'; import { InstanceType, Peer } from 'aws-cdk-lib/aws-ec2'; import { CfnLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancing'; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; -import { getUserData } from './launch-config'; import type { DCRProps } from './types'; +import { getUserData } from './userData'; export class DotcomRendering extends GuStack { constructor(scope: App, id: string, props: DCRProps) { diff --git a/dotcom-rendering/cdk/lib/launch-config.ts b/dotcom-rendering/cdk/lib/userData.ts similarity index 100% rename from dotcom-rendering/cdk/lib/launch-config.ts rename to dotcom-rendering/cdk/lib/userData.ts From e7f32a063f25adb3aad177954912c64c66e6145d Mon Sep 17 00:00:00 2001 From: Charlotte Date: Fri, 11 Aug 2023 08:52:00 +0100 Subject: [PATCH 34/42] chore: keep existing (unused) params in cfn in case of rollback --- dotcom-rendering/cloudformation.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index f0885cc6a0e..620ed6c52f4 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -51,6 +51,16 @@ Parameters: InstanceRole: Description: Name of instance role Type: String + # ! These params are not used but should be kept until the migration is finished + VpcId: + Description: The VPC in which rendering instances will run + Type: AWS::EC2::VPC::Id + ELKStream: + Description: name of the kinesis stream to use to send logs to the central ELK stack + Type: String + AMI: + Description: AMI to use for instances + Type: AWS::EC2::Image::Id Conditions: HasLatencyScalingAlarm: !Equals [!Ref Stage, 'PROD'] From ed5fb0a683ec3133185fc2e5c29ca5aa416c44ce Mon Sep 17 00:00:00 2001 From: Charlotte Date: Fri, 11 Aug 2023 09:01:54 +0100 Subject: [PATCH 35/42] remove vpcid param --- .../cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap | 8 ++++++++ dotcom-rendering/cloudformation.yml | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 3f62c9dcd60..f818476457e 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -70,6 +70,10 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, }, "Parameters": { + "AMI": { + "Description": "AMI to use for instances", + "Type": "AWS::EC2::Image::Id", + }, "AMIRendering": { "Default": "/TEST/frontend/rendering/ami.imageId", "Description": "Amazon Machine Image ID for the app rendering. Use this in conjunction with AMIgo to keep AMIs up to date.", @@ -105,6 +109,10 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Description": "S3 Bucket Name for ELB logs", "Type": "AWS::SSM::Parameter::Value", }, + "ELKStream": { + "Description": "name of the kinesis stream to use to send logs to the central ELK stack", + "Type": "String", + }, "ELKStreamId": { "Default": "/TEST/frontend/rendering/logging.stream.name", "Type": "AWS::SSM::Parameter::Value", diff --git a/dotcom-rendering/cloudformation.yml b/dotcom-rendering/cloudformation.yml index 620ed6c52f4..fe2fe7526b3 100644 --- a/dotcom-rendering/cloudformation.yml +++ b/dotcom-rendering/cloudformation.yml @@ -52,9 +52,6 @@ Parameters: Description: Name of instance role Type: String # ! These params are not used but should be kept until the migration is finished - VpcId: - Description: The VPC in which rendering instances will run - Type: AWS::EC2::VPC::Id ELKStream: Description: name of the kinesis stream to use to send logs to the central ELK stack Type: String From 856dfda7d285fa53c0185f6676f7ec7e9e296767 Mon Sep 17 00:00:00 2001 From: Charlotte Emms <43961396+cemms1@users.noreply.github.com> Date: Mon, 14 Aug 2023 09:49:02 +0100 Subject: [PATCH 36/42] infra: switch load balancer type (#8558) --- .../dotcom-rendering.test.ts.snap | 62 ++++++++++++- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 86 ++++++++++++------- 2 files changed, 116 insertions(+), 32 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index f818476457e..a61a6b3a292 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -38,6 +38,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "GuSubnetListParameter", "GuSecurityGroup", "GuStringParameter", + "GuClassicLoadBalancer", "GuSecurityGroup", "GuAllowPolicy", "GuAllowPolicy", @@ -343,6 +344,11 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` ], }, }, + "LoadBalancerNames": [ + { + "Ref": "InternalLoadBalancer", + }, + ], "MaxSize": "4", "MinSize": "1", "Tags": [ @@ -688,8 +694,9 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Listeners": [ { "InstancePort": "9000", + "InstanceProtocol": "http", "LoadBalancerPort": "80", - "Protocol": "HTTP", + "Protocol": "http", }, ], "LoadBalancerName": "frontend-TEST-rendering-ELB", @@ -706,6 +713,10 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Ref": "PublicSubnets", }, "Tags": [ + { + "Key": "App", + "Value": "rendering", + }, { "Key": "gu:cdk:version", "Value": "TEST", @@ -726,6 +737,55 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, "Type": "AWS::ElasticLoadBalancing::LoadBalancer", }, + "InternalLoadBalancerRenderingSecurityGroup53E3F9A7": { + "Properties": { + "GroupDescription": "DotcomRendering/InternalLoadBalancerRendering/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "255.255.255.255/32", + "Description": "Disallow all traffic", + "FromPort": 252, + "IpProtocol": "icmp", + "ToPort": 86, + }, + ], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Default rule allow on 80", + "FromPort": 80, + "IpProtocol": "tcp", + "ToPort": 80, + }, + ], + "Tags": [ + { + "Key": "App", + "Value": "rendering", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "VpcId": { + "Ref": "VpcId", + }, + }, + "Type": "AWS::EC2::SecurityGroup", + }, "InternalLoadBalancerSecurityGroup": { "Properties": { "GroupDescription": "Allows HTTP and HTTPS inbound connections from within the VPC", diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index c6bf07dfab3..48a2babb799 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -14,11 +14,12 @@ import { GuAllowPolicy, GuInstanceRole, } from '@guardian/cdk/lib/constructs/iam'; +import { GuClassicLoadBalancer } from '@guardian/cdk/lib/constructs/loadbalancing'; import type { App } from 'aws-cdk-lib'; import { CfnOutput, Duration } from 'aws-cdk-lib'; import { HealthCheck } from 'aws-cdk-lib/aws-autoscaling'; import { InstanceType, Peer } from 'aws-cdk-lib/aws-ec2'; -import { CfnLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancing'; +import { LoadBalancingProtocol } from 'aws-cdk-lib/aws-elasticloadbalancing'; import { CfnInclude } from 'aws-cdk-lib/cloudformation-include'; import type { DCRProps } from './types'; import { getUserData } from './userData'; @@ -66,36 +67,57 @@ export class DotcomRendering extends GuStack { reason: 'Retaining a stateful resource previously defined in YAML', }); - const lb = new CfnLoadBalancer(this, 'InternalLoadBalancer', { - listeners: [ - { - instancePort: '9000', - protocol: 'HTTP', - loadBalancerPort: '80', + const loadBalancer = new GuClassicLoadBalancer( + this, + 'InternalLoadBalancer', + { + app, + vpc, + accessLoggingPolicy: { + enabled: true, + s3BucketName: new GuStringParameter( + this, + 'ELBLogsParameter', + { + default: `${ssmPrefix}/elb.logs.bucketName`, + fromSSM: true, + description: 'S3 Bucket Name for ELB logs', + }, + ).valueAsString, + emitInterval: 5, + s3BucketPrefix: `ELBLogs/${stack}/${app}/${stage}`, + }, + crossZone: true, + healthCheck: { + interval: Duration.seconds(30), + port: 9000, + protocol: LoadBalancingProtocol.HTTP, + timeout: Duration.seconds(10), + healthyThreshold: 2, + unhealthyThreshold: 10, + path: '/_healthcheck', + }, + listeners: [ + { + externalPort: 80, + externalProtocol: LoadBalancingProtocol.HTTP, + internalPort: 9000, + internalProtocol: LoadBalancingProtocol.HTTP, + }, + ], + subnetSelection: { subnets: publicSubnets }, + propertiesToOverride: { + LoadBalancerName: `${stack}-${stage}-${app}-ELB`, + // Note: this does not prevent the GuClassicLoadBalancer + // from creating a default security group, though it does + // override which one is used/associated with the load balancer + SecurityGroups: [lbSecurityGroup.securityGroupId], }, - ], - healthCheck: { - target: 'HTTP:9000/_healthcheck', - interval: '30', - timeout: '10', - unhealthyThreshold: '10', - healthyThreshold: '2', - }, - subnets: publicSubnets.map((subnet) => subnet.subnetId), - scheme: 'internal', - securityGroups: [lbSecurityGroup.securityGroupId], - crossZone: true, - accessLoggingPolicy: { - enabled: true, - emitInterval: 5, - s3BucketName: new GuStringParameter(this, 'ELBLogsParameter', { - default: `${ssmPrefix}/elb.logs.bucketName`, - fromSSM: true, - description: 'S3 Bucket Name for ELB logs', - }).valueAsString, - s3BucketPrefix: `ELBLogs/${stack}/${app}/${stage}`, }, - loadBalancerName: `${stack}-${stage}-${app}-ELB`, + ); + this.overrideLogicalId(loadBalancer, { + logicalId: 'InternalLoadBalancer', + reason: 'Retaining a stateful resource previously defined in YAML', }); const instanceSecurityGroup = new GuSecurityGroup( @@ -192,6 +214,8 @@ export class DotcomRendering extends GuStack { reason: 'Retaining a stateful resource previously defined in YAML', }); + asg.attachToClassicLB(loadBalancer); + const yamlTemplateFilePath = join( __dirname, '../..', @@ -202,13 +226,13 @@ export class DotcomRendering extends GuStack { templateFile: yamlTemplateFilePath, parameters: { AutoscalingGroup: asg.autoScalingGroupName, - InternalLoadBalancer: lb.ref, + InternalLoadBalancer: loadBalancer.loadBalancerName, InstanceRole: instanceRole.roleName, }, }); new CfnOutput(this, 'LoadBalancerUrl', { - value: lb.attrDnsName, + value: loadBalancer.loadBalancerDnsName, }); } } From 960cde01f551178aaf770462dfb6ae7d0c43bd5d Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 15 Aug 2023 12:58:45 +0100 Subject: [PATCH 37/42] remove whitespace from start of userdata --- .../cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap | 3 +-- dotcom-rendering/cdk/lib/userData.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index a61a6b3a292..8330bb9fbc5 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -1094,8 +1094,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Fn::Join": [ "", [ - " - #!/bin/bash -ev + "#!/bin/bash -ev groupadd frontend useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering diff --git a/dotcom-rendering/cdk/lib/userData.ts b/dotcom-rendering/cdk/lib/userData.ts index e87b689e392..05ed9937e7e 100644 --- a/dotcom-rendering/cdk/lib/userData.ts +++ b/dotcom-rendering/cdk/lib/userData.ts @@ -13,8 +13,7 @@ export const getUserData = ({ region, stage, elkStreamId, -}: UserDataProps): string => ` - #!/bin/bash -ev +}: UserDataProps): string => `#!/bin/bash -ev groupadd frontend useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering From eb5190ad0abc6a98e7e47696391064aedaf8f769 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 15 Aug 2023 13:05:23 +0100 Subject: [PATCH 38/42] remove whitespace in userdata --- .../dotcom-rendering.test.ts.snap | 32 +++++++++---------- dotcom-rendering/cdk/lib/userData.ts | 32 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 8330bb9fbc5..dce3d80d025 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -1096,33 +1096,33 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` [ "#!/bin/bash -ev - groupadd frontend - useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering - usermod -a -G frontend aws-kinesis-agent-user - cd /home/dotcom-rendering +groupadd frontend +useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering +usermod -a -G frontend aws-kinesis-agent-user +cd /home/dotcom-rendering - aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/TEST/rendering/rendering.zip ./ - unzip -q rendering.zip -d rendering +aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/TEST/rendering/rendering.zip ./ +unzip -q rendering.zip -d rendering - chown -R dotcom-rendering:frontend rendering +chown -R dotcom-rendering:frontend rendering - cd rendering +cd rendering - export TERM=xterm-256color - export NODE_ENV=production - export GU_STAGE=TEST +export TERM=xterm-256color +export NODE_ENV=production +export GU_STAGE=TEST - mkdir /var/log/dotcom-rendering - chown -R dotcom-rendering:frontend /var/log/dotcom-rendering +mkdir /var/log/dotcom-rendering +chown -R dotcom-rendering:frontend /var/log/dotcom-rendering - make start-prod +make start-prod - /opt/aws-kinesis-agent/configure-aws-kinesis-agent eu-west-1 ", +/opt/aws-kinesis-agent/configure-aws-kinesis-agent eu-west-1 ", { "Ref": "ELKStreamId", }, " /var/log/dotcom-rendering/dotcom-rendering.log - ", +", ], ], }, diff --git a/dotcom-rendering/cdk/lib/userData.ts b/dotcom-rendering/cdk/lib/userData.ts index 05ed9937e7e..e6e3d320e4d 100644 --- a/dotcom-rendering/cdk/lib/userData.ts +++ b/dotcom-rendering/cdk/lib/userData.ts @@ -15,26 +15,26 @@ export const getUserData = ({ elkStreamId, }: UserDataProps): string => `#!/bin/bash -ev - groupadd frontend - useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering - usermod -a -G frontend aws-kinesis-agent-user - cd /home/dotcom-rendering +groupadd frontend +useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering +usermod -a -G frontend aws-kinesis-agent-user +cd /home/dotcom-rendering - aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${stage}/${app}/${app}.zip ./ - unzip -q ${app}.zip -d ${app} +aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${stage}/${app}/${app}.zip ./ +unzip -q ${app}.zip -d ${app} - chown -R dotcom-rendering:frontend ${app} +chown -R dotcom-rendering:frontend ${app} - cd ${app} +cd ${app} - export TERM=xterm-256color - export NODE_ENV=production - export GU_STAGE=${stage} +export TERM=xterm-256color +export NODE_ENV=production +export GU_STAGE=${stage} - mkdir /var/log/dotcom-rendering - chown -R dotcom-rendering:frontend /var/log/dotcom-rendering +mkdir /var/log/dotcom-rendering +chown -R dotcom-rendering:frontend /var/log/dotcom-rendering - make start-prod +make start-prod - /opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStreamId} /var/log/dotcom-rendering/dotcom-rendering.log - `; +/opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStreamId} /var/log/dotcom-rendering/dotcom-rendering.log +`; From 61391222f93c754003c0d640d370f6fbbb07eb8c Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 15 Aug 2023 13:17:37 +0100 Subject: [PATCH 39/42] change usedata to array join --- .../dotcom-rendering.test.ts.snap | 11 +----- dotcom-rendering/cdk/lib/userData.ts | 39 +++++++++++-------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index dce3d80d025..62a84ab71e8 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -1095,34 +1095,25 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "", [ "#!/bin/bash -ev - groupadd frontend useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering usermod -a -G frontend aws-kinesis-agent-user cd /home/dotcom-rendering - aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/TEST/rendering/rendering.zip ./ unzip -q rendering.zip -d rendering - chown -R dotcom-rendering:frontend rendering - cd rendering - export TERM=xterm-256color export NODE_ENV=production export GU_STAGE=TEST - mkdir /var/log/dotcom-rendering chown -R dotcom-rendering:frontend /var/log/dotcom-rendering - make start-prod - /opt/aws-kinesis-agent/configure-aws-kinesis-agent eu-west-1 ", { "Ref": "ELKStreamId", }, - " /var/log/dotcom-rendering/dotcom-rendering.log -", + " /var/log/dotcom-rendering/dotcom-rendering.log", ], ], }, diff --git a/dotcom-rendering/cdk/lib/userData.ts b/dotcom-rendering/cdk/lib/userData.ts index e6e3d320e4d..c4b97c6bdd8 100644 --- a/dotcom-rendering/cdk/lib/userData.ts +++ b/dotcom-rendering/cdk/lib/userData.ts @@ -13,28 +13,33 @@ export const getUserData = ({ region, stage, elkStreamId, -}: UserDataProps): string => `#!/bin/bash -ev +}: UserDataProps): string => { + const userData = [ + `#!/bin/bash -ev`, -groupadd frontend -useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering -usermod -a -G frontend aws-kinesis-agent-user -cd /home/dotcom-rendering + `groupadd frontend`, + `useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering`, + `usermod -a -G frontend aws-kinesis-agent-user`, + `cd /home/dotcom-rendering`, -aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${stage}/${app}/${app}.zip ./ -unzip -q ${app}.zip -d ${app} + `aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/${stage}/${app}/${app}.zip ./`, + `unzip -q ${app}.zip -d ${app}`, -chown -R dotcom-rendering:frontend ${app} + `chown -R dotcom-rendering:frontend ${app}`, -cd ${app} + `cd ${app}`, -export TERM=xterm-256color -export NODE_ENV=production -export GU_STAGE=${stage} + `export TERM=xterm-256color`, + `export NODE_ENV=production`, + `export GU_STAGE=${stage}`, -mkdir /var/log/dotcom-rendering -chown -R dotcom-rendering:frontend /var/log/dotcom-rendering + `mkdir /var/log/dotcom-rendering`, + `chown -R dotcom-rendering:frontend /var/log/dotcom-rendering`, -make start-prod + `make start-prod`, -/opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStreamId} /var/log/dotcom-rendering/dotcom-rendering.log -`; + `/opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStreamId} /var/log/dotcom-rendering/dotcom-rendering.log`, + ].join('\n'); + + return userData; +}; From 9301a74cd6201a22c2848f99e525b962eb719228 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 15 Aug 2023 13:34:25 +0100 Subject: [PATCH 40/42] remove imdsv2 requirement --- .../cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap | 3 --- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 62a84ab71e8..bd856431111 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -1014,9 +1014,6 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Ref": "AMIRendering", }, "InstanceType": "t4g.micro", - "MetadataOptions": { - "HttpTokens": "required", - }, "SecurityGroupIds": [ { "Fn::GetAtt": [ diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index 48a2babb799..a2d857ac532 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -208,6 +208,7 @@ export class DotcomRendering extends GuStack { role: instanceRole, additionalSecurityGroups: [instanceSecurityGroup], vpcSubnets: { subnets: publicSubnets }, + withoutImdsv2: true, }); this.overrideLogicalId(asg, { logicalId: 'AutoscalingGroup', From 02a906682d993f4d8b8dbecb098dcf88d059ba33 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 15 Aug 2023 14:53:02 +0100 Subject: [PATCH 41/42] base64 encode userdata --- .../dotcom-rendering.test.ts.snap | 28 +------------------ dotcom-rendering/cdk/lib/userData.ts | 2 +- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index bd856431111..5b701d78223 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -1087,33 +1087,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, ], "UserData": { - "Fn::Base64": { - "Fn::Join": [ - "", - [ - "#!/bin/bash -ev -groupadd frontend -useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering -usermod -a -G frontend aws-kinesis-agent-user -cd /home/dotcom-rendering -aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/TEST/rendering/rendering.zip ./ -unzip -q rendering.zip -d rendering -chown -R dotcom-rendering:frontend rendering -cd rendering -export TERM=xterm-256color -export NODE_ENV=production -export GU_STAGE=TEST -mkdir /var/log/dotcom-rendering -chown -R dotcom-rendering:frontend /var/log/dotcom-rendering -make start-prod -/opt/aws-kinesis-agent/configure-aws-kinesis-agent eu-west-1 ", - { - "Ref": "ELKStreamId", - }, - " /var/log/dotcom-rendering/dotcom-rendering.log", - ], - ], - }, + "Fn::Base64": "IyEvYmluL2Jhc2ggLWV2Cmdyb3VwYWRkIGZyb250ZW5kCnVzZXJhZGQgLXIgLW0gLXMgL3Vzci9iaW4vbm9sb2dpbiAtZyBmcm9udGVuZCBkb3Rjb20tcmVuZGVyaW5nCnVzZXJtb2QgLWEgLUcgZnJvbnRlbmQgYXdzLWtpbmVzaXMtYWdlbnQtdXNlcgpjZCAvaG9tZS9kb3Rjb20tcmVuZGVyaW5nCmF3cyAtLXJlZ2lvbiBldS13ZXN0LTEgczMgY3AgczM6Ly9hd3MtZnJvbnRlbmQtYXJ0aWZhY3RzL2Zyb250ZW5kL1RFU1QvcmVuZGVyaW5nL3JlbmRlcmluZy56aXAgLi8KdW56aXAgLXEgcmVuZGVyaW5nLnppcCAtZCByZW5kZXJpbmcKY2hvd24gLVIgZG90Y29tLXJlbmRlcmluZzpmcm9udGVuZCByZW5kZXJpbmcKY2QgcmVuZGVyaW5nCmV4cG9ydCBURVJNPXh0ZXJtLTI1NmNvbG9yCmV4cG9ydCBOT0RFX0VOVj1wcm9kdWN0aW9uCmV4cG9ydCBHVV9TVEFHRT1URVNUCm1rZGlyIC92YXIvbG9nL2RvdGNvbS1yZW5kZXJpbmcKY2hvd24gLVIgZG90Y29tLXJlbmRlcmluZzpmcm9udGVuZCAvdmFyL2xvZy9kb3Rjb20tcmVuZGVyaW5nCm1ha2Ugc3RhcnQtcHJvZAovb3B0L2F3cy1raW5lc2lzLWFnZW50L2NvbmZpZ3VyZS1hd3Mta2luZXNpcy1hZ2VudCBldS13ZXN0LTEgJHtUb2tlbltUT0tFTi43MjNdfSAvdmFyL2xvZy9kb3Rjb20tcmVuZGVyaW5nL2RvdGNvbS1yZW5kZXJpbmcubG9n", }, }, "TagSpecifications": [ diff --git a/dotcom-rendering/cdk/lib/userData.ts b/dotcom-rendering/cdk/lib/userData.ts index c4b97c6bdd8..2b4de8c81c2 100644 --- a/dotcom-rendering/cdk/lib/userData.ts +++ b/dotcom-rendering/cdk/lib/userData.ts @@ -41,5 +41,5 @@ export const getUserData = ({ `/opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStreamId} /var/log/dotcom-rendering/dotcom-rendering.log`, ].join('\n'); - return userData; + return Buffer.from(userData).toString('base64'); }; From 16a7834b7063695466fc00cf21427104bf128716 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 15 Aug 2023 16:40:53 +0100 Subject: [PATCH 42/42] use private vpc and un-encode userdata --- .../dotcom-rendering.test.ts.snap | 36 +++++++++++++++++-- dotcom-rendering/cdk/lib/dotcom-rendering.ts | 5 ++- dotcom-rendering/cdk/lib/userData.ts | 2 +- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap index 5b701d78223..afbaa2d22ef 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/dotcom-rendering.test.ts.snap @@ -36,6 +36,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "gu:cdk:constructs": [ "GuVpcParameter", "GuSubnetListParameter", + "GuSubnetListParameter", "GuSecurityGroup", "GuStringParameter", "GuClassicLoadBalancer", @@ -132,6 +133,11 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` "Description": "(Optional) ARN of action to execute when notification alarms change state", "Type": "CommaDelimitedList", }, + "PrivateSubnets": { + "Default": "/account/vpc/primary/subnets/private", + "Description": "A list of private subnets", + "Type": "AWS::SSM::Parameter::Value>", + }, "PublicSubnets": { "Default": "/account/vpc/primary/subnets/public", "Description": "A list of public subnets", @@ -379,7 +385,7 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, ], "VPCZoneIdentifier": { - "Ref": "PublicSubnets", + "Ref": "PrivateSubnets", }, }, "Type": "AWS::AutoScaling::AutoScalingGroup", @@ -1087,7 +1093,33 @@ exports[`The DotcomRendering stack matches the snapshot 1`] = ` }, ], "UserData": { - "Fn::Base64": "IyEvYmluL2Jhc2ggLWV2Cmdyb3VwYWRkIGZyb250ZW5kCnVzZXJhZGQgLXIgLW0gLXMgL3Vzci9iaW4vbm9sb2dpbiAtZyBmcm9udGVuZCBkb3Rjb20tcmVuZGVyaW5nCnVzZXJtb2QgLWEgLUcgZnJvbnRlbmQgYXdzLWtpbmVzaXMtYWdlbnQtdXNlcgpjZCAvaG9tZS9kb3Rjb20tcmVuZGVyaW5nCmF3cyAtLXJlZ2lvbiBldS13ZXN0LTEgczMgY3AgczM6Ly9hd3MtZnJvbnRlbmQtYXJ0aWZhY3RzL2Zyb250ZW5kL1RFU1QvcmVuZGVyaW5nL3JlbmRlcmluZy56aXAgLi8KdW56aXAgLXEgcmVuZGVyaW5nLnppcCAtZCByZW5kZXJpbmcKY2hvd24gLVIgZG90Y29tLXJlbmRlcmluZzpmcm9udGVuZCByZW5kZXJpbmcKY2QgcmVuZGVyaW5nCmV4cG9ydCBURVJNPXh0ZXJtLTI1NmNvbG9yCmV4cG9ydCBOT0RFX0VOVj1wcm9kdWN0aW9uCmV4cG9ydCBHVV9TVEFHRT1URVNUCm1rZGlyIC92YXIvbG9nL2RvdGNvbS1yZW5kZXJpbmcKY2hvd24gLVIgZG90Y29tLXJlbmRlcmluZzpmcm9udGVuZCAvdmFyL2xvZy9kb3Rjb20tcmVuZGVyaW5nCm1ha2Ugc3RhcnQtcHJvZAovb3B0L2F3cy1raW5lc2lzLWFnZW50L2NvbmZpZ3VyZS1hd3Mta2luZXNpcy1hZ2VudCBldS13ZXN0LTEgJHtUb2tlbltUT0tFTi43MjNdfSAvdmFyL2xvZy9kb3Rjb20tcmVuZGVyaW5nL2RvdGNvbS1yZW5kZXJpbmcubG9n", + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash -ev +groupadd frontend +useradd -r -m -s /usr/bin/nologin -g frontend dotcom-rendering +usermod -a -G frontend aws-kinesis-agent-user +cd /home/dotcom-rendering +aws --region eu-west-1 s3 cp s3://aws-frontend-artifacts/frontend/TEST/rendering/rendering.zip ./ +unzip -q rendering.zip -d rendering +chown -R dotcom-rendering:frontend rendering +cd rendering +export TERM=xterm-256color +export NODE_ENV=production +export GU_STAGE=TEST +mkdir /var/log/dotcom-rendering +chown -R dotcom-rendering:frontend /var/log/dotcom-rendering +make start-prod +/opt/aws-kinesis-agent/configure-aws-kinesis-agent eu-west-1 ", + { + "Ref": "ELKStreamId", + }, + " /var/log/dotcom-rendering/dotcom-rendering.log", + ], + ], + }, }, }, "TagSpecifications": [ diff --git a/dotcom-rendering/cdk/lib/dotcom-rendering.ts b/dotcom-rendering/cdk/lib/dotcom-rendering.ts index a2d857ac532..2b958696e5f 100644 --- a/dotcom-rendering/cdk/lib/dotcom-rendering.ts +++ b/dotcom-rendering/cdk/lib/dotcom-rendering.ts @@ -39,6 +39,9 @@ export class DotcomRendering extends GuStack { const publicSubnets = GuVpc.subnetsFromParameter(this, { type: SubnetType.PUBLIC, }); + const privateSubnets = GuVpc.subnetsFromParameter(this, { + type: SubnetType.PRIVATE, + }); const lbSecurityGroup = new GuSecurityGroup( this, @@ -207,7 +210,7 @@ export class DotcomRendering extends GuStack { imageRecipe: props.amiRecipe, role: instanceRole, additionalSecurityGroups: [instanceSecurityGroup], - vpcSubnets: { subnets: publicSubnets }, + vpcSubnets: { subnets: privateSubnets }, withoutImdsv2: true, }); this.overrideLogicalId(asg, { diff --git a/dotcom-rendering/cdk/lib/userData.ts b/dotcom-rendering/cdk/lib/userData.ts index 2b4de8c81c2..c4b97c6bdd8 100644 --- a/dotcom-rendering/cdk/lib/userData.ts +++ b/dotcom-rendering/cdk/lib/userData.ts @@ -41,5 +41,5 @@ export const getUserData = ({ `/opt/aws-kinesis-agent/configure-aws-kinesis-agent ${region} ${elkStreamId} /var/log/dotcom-rendering/dotcom-rendering.log`, ].join('\n'); - return Buffer.from(userData).toString('base64'); + return userData; };