constructor()

in cdk/lib/admin-console.ts [257:419]


  constructor(scope: App, id: string, props: AdminConsoleProps) {
    super(scope, id, props);

    const { domainName } = props;

    const app = 'admin-console';

    const channelTestsDynamoTable = this.buildTestsTable();
    const archivedTestsDynamoTable = this.buildArchivedTestsTable();
    const channelTestsAuditDynamoTable = this.buildTestsAuditTable();
    const campaignsDynamoTable = this.buildCampaignsTable();
    const bannerDesignsDynamoTable = this.buildBannerDesignsTable();
    const archivedBannerDesignsDynamoTable = this.buildArchivedBannerDesignsTable();
    const permissionsTable = this.buildPermissionsTable();

    const channelTestsDynamoPolicies =
      this.buildChannelTestsDynamoPolicies(channelTestsDynamoTable);
    const campaignsDynamoPolicies = this.buildDynamoPolicies(campaignsDynamoTable);
    const archivedTestsDynamoPolicies = this.buildDynamoPolicies(archivedTestsDynamoTable);
    const channelTestsAuditDynamoPolicies = this.buildDynamoPolicies(channelTestsAuditDynamoTable);
    const bannerDesignsDynamoPolicies = this.buildDynamoPolicies(bannerDesignsDynamoTable);
    const archivedBannerDesignsDynamoPolicies = this.buildDynamoPolicies(
      archivedBannerDesignsDynamoTable,
    );
    const permissionsDynamoPolicies = this.buildDynamoPolicies(permissionsTable);

    const userData = UserData.forLinux();
    userData.addCommands(
      `aws --region ${this.region} s3 cp s3://membership-dist/${this.stack}/${this.stage}/${app}/support-admin-console_1.0-SNAPSHOT_all.deb /tmp
      dpkg -i /tmp/support-admin-console_1.0-SNAPSHOT_all.deb
      /opt/cloudwatch-logs/configure-logs application ${this.stack} ${this.stage} ${app} /var/log/support-admin-console/application.log`
    )


    const policies: Policy[] = [
      new GuAllowPolicy(this, 'Cloudwatch', {
        actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
        resources: ['arn:aws:logs:*:*:*'],
      }),
      new GuAllowPolicy(this, 'SSMGet', {
        actions: ['ssm:GetParametersByPath'],
        resources: [`arn:aws:ssm:${this.region}:${this.account}:parameter/${app}/${this.stage}`],
      }),
      new GuGetS3ObjectsPolicy(this, 'SettingsBucketGet', {
        bucketName: 'support-admin-console',
        paths: [`${this.stage}/*`, 'google-auth-service-account-certificate.json'],
      }),
      new GuPutS3ObjectsPolicy(this, 'SettingsBucketPut', {
        bucketName: 'support-admin-console',
        paths: [`${this.stage}/*`],
      }),
      new GuAllowPolicy(this, 'PublicSettingsBucketPut', {
        actions: ['s3:PutObject', 's3:PutObjectAcl'],
        resources: [
          `arn:aws:s3:::gu-contributions-public/epic/${this.stage}/*`,
          `arn:aws:s3:::gu-contributions-public/banner/${this.stage}/*`,
          `arn:aws:s3:::gu-contributions-public/header/${this.stage}/*`,
          `arn:aws:s3:::gu-contributions-public/gutter/${this.stage}/*`,
          `arn:aws:s3:::gu-contributions-public/supportLandingPage/${this.stage}/*`,
        ],
      }),
      ...channelTestsDynamoPolicies,
      ...campaignsDynamoPolicies,
      ...archivedTestsDynamoPolicies,
      ...channelTestsAuditDynamoPolicies,
      ...bannerDesignsDynamoPolicies,
      ...archivedBannerDesignsDynamoPolicies,
      ...permissionsDynamoPolicies,
      new GuDynamoDBReadPolicy(this, `DynamoRead-super-mode-calculator`, {
        tableName: 'super-mode-calculator-PROD', // always PROD for super mode
      }),
      new GuDynamoDBReadPolicy(this, `DynamoRead-super-mode-calculator/index/end`, {
        tableName: `super-mode-calculator-PROD/index/end`,
      }),
      new GuDynamoDBReadPolicy(this, `DynamoRead-bandit-data`, {
        tableName: `support-bandit-${this.stage}`,
      }),
    ];

    const ec2App = new GuEc2App(this, {
      applicationPort: 9000,
      app,
      access: { scope: AccessScope.PUBLIC },
      certificateProps: {
        domainName,
      },
      monitoringConfiguration:
        this.stage === 'PROD'
          ? {
              http5xxAlarm: {
                tolerated5xxPercentage: 0,
                numberOfMinutesAboveThresholdBeforeAlarm: 1,
                alarmName: `5XX error returned by ${app} ${this.stage}`,
              },
              unhealthyInstancesAlarm: true,
              snsTopicName: 'alarms-handler-topic-PROD',
            }
          : { noMonitoring: true },
      userData,
      roleConfiguration: {
        additionalPolicies: policies,
      },
      scaling: { minimumInstances: 1, maximumInstances: 2 },
      instanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.MICRO),
    });

    // Rule to only allow known http methods
    new ApplicationListenerRule(this, 'AllowKnownMethods', {
      listener: ec2App.listener,
      priority: 1,
      conditions: [ListenerCondition.httpRequestMethods(['GET', 'POST', 'PUT', 'DELETE', 'HEAD'])],
      targetGroups: [ec2App.targetGroup],
    });
    // Default rule to block requests which don't match the above rule
    new ApplicationListenerRule(this, 'BlockUnknownMethods', {
      listener: ec2App.listener,
      priority: 2,
      conditions: [ListenerCondition.pathPatterns(['*'])], // anything
      action: ListenerAction.fixedResponse(400, {
        contentType: 'application/json',
        messageBody: 'Unsupported http method',
      }),
    });

    new GuCname(this, 'cname', {
      app,
      domainName,
      ttl: Duration.minutes(60),
      resourceRecord: ec2App.loadBalancer.loadBalancerDnsName,
    });

    // Cross-account role for CAPI access to Dynamodb
    const capiAccountId = new GuStringParameter(this, 'CapiAccountId', {
      description: 'ID of the CAPI aws account',
    });

    const dynamoPolicyForCapi = new GuDynamoDBReadPolicy(this, `DynamoRead-for-capi`, {
      tableName: channelTestsDynamoTable.tableName,
    });
    const s3ReadPolicyForCapi = new GuGetS3ObjectsPolicy(this, 's3Get-for-capi', {
      bucketName: 'support-admin-console',
      paths: [`${this.stage}/*`, 'channel-switches.json'],
    });

    new Role(this, 'capi-role', {
      roleName: `support-admin-console-channel-tests-capi-role-${this.stage}`,
      assumedBy: new AccountPrincipal(capiAccountId.valueAsString),
      inlinePolicies: {
        dynamoPolicyForCapi: dynamoPolicyForCapi.document,
        s3ReadPolicyForCapi: s3ReadPolicyForCapi.document,
      },
    });

    // This parameter is used by our WAF configuration
    new StringParameter(this, 'AlbSsmParam', {
      parameterName: `/infosec/waf/services/${this.stage}/support-admin-console-alb-arn`,
      description: `The arn of the ALB for amiable-${this.stage}. N.B. this parameter is created via cdk`,
      simpleName: false,
      stringValue: ec2App.loadBalancer.loadBalancerArn,
      tier: ParameterTier.STANDARD,
      dataType: ParameterDataType.TEXT,
    });
  }