constructor()

in packages/constructs/L3/ai/gaia-l3-construct/lib/shared/index.ts [43:268]


  constructor(scope: Construct, id: string, props: GAIASharedL3ConstructProps) {
    super(scope, id);

    const powerToolsLayerVersion = '46';
    this.defaultEnvironmentVariables = {
      POWERTOOLS_DEV: props.config.powertoolsDevLogging === undefined ? 'true' : props.config.powertoolsDevLogging,
      LOG_LEVEL: 'INFO',
      POWERTOOLS_LOGGER_LOG_EVENT: 'true',
      POWERTOOLS_SERVICE_NAME: 'chatbot',
    };

    const vpc: ec2.IVpc = ec2.Vpc.fromVpcAttributes(this, 'VPC', {
      vpcId: props.config.vpc.vpcId,
      availabilityZones: [''],
    });

    this.appSubnets = props.config.vpc.appSubnets.map(appSubnetId => {
      return ec2.Subnet.fromSubnetAttributes(this, `subnet-${appSubnetId}`, {
        subnetId: appSubnetId,
      });
    });

    this.appSecurityGroup = ec2.SecurityGroup.fromSecurityGroupId(
      this,
      'DefaultAppSecurityGroup',
      props.config.vpc.appSecurityGroupId,
    );

    this.dataSubnets = props.config.vpc.dataSubnets.map(dataSubnetId => {
      return ec2.Subnet.fromSubnetAttributes(this, `subnet-${dataSubnetId}`, {
        subnetId: dataSubnetId,
      });
    });

    this.dataSecurityGroup = ec2.SecurityGroup.fromSecurityGroupId(
      this,
      'DefaultDataSecurityGroup',
      props.config.vpc.dataSecurityGroupId,
    );

    const configParameter = new ssm.StringParameter(this, 'Config', {
      stringValue: JSON.stringify(props.config),
    });

    const powerToolsArn =
      lambdaArchitecture === lambda.Architecture.X86_64
        ? `arn:${cdk.Aws.PARTITION}:lambda:${cdk.Aws.REGION}:017000801446:layer:AWSLambdaPowertoolsPythonV2:${powerToolsLayerVersion}`
        : `arn:${cdk.Aws.PARTITION}:lambda:${cdk.Aws.REGION}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:${powerToolsLayerVersion}`;

    const powerToolsLayer = lambda.LayerVersion.fromLayerVersionArn(this, 'PowertoolsLayer', powerToolsArn);

    const commonLayer = new Layer(this, 'CommonLayer', {
      runtime: pythonRuntime,
      architecture: lambdaArchitecture,
      assetOverridePath: props.config.codeOverwrites?.commonLibsLayerCodeZipPath,
    });

    const pythonSDKLayerPath =
      props.config?.codeOverwrites?.genAiCoreLayerCodePath !== undefined
        ? props.config.codeOverwrites.genAiCoreLayerCodePath
        : path.join(__dirname, './layers/python-sdk');

    const pythonSDKLayer = new lambda.LayerVersion(this, 'PythonSDKLayer', {
      code: lambda.Code.fromAsset(pythonSDKLayerPath),
      compatibleRuntimes: [pythonRuntime],
      compatibleArchitectures: [lambdaArchitecture],
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });

    const secretRotationLambdaRole = new MdaaRole(this, 'SecretsRotationLambdaRole', {
      roleName: 'GAIASecretsRotationLambdaRole',
      assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
      naming: props.naming,
      createOutputs: false,
      createParams: false,
    });
    secretRotationLambdaRole.addToPolicy(
      new iam.PolicyStatement({
        effect: Effect.ALLOW,
        actions: ['ec2:CreateNetworkInterface', 'ec2:DescribeNetworkInterfaces', 'ec2:DeleteNetworkInterface'],
        resources: ['*'],
      }),
    );

    const secretRotationLambda = new MdaaLambdaFunction(this, 'SecretRotationLambda', {
      functionName: 'GAIASecretsRotationLambdaHandler',
      naming: props.naming,
      createParams: false,
      createOutputs: false,
      role: secretRotationLambdaRole,
      code: lambda.Code.fromAsset(path.join(__dirname, './secrets-rotation-function')),
      handler: 'index.handler',
      runtime: pythonRuntime,
      architecture: lambdaArchitecture,
      timeout: cdk.Duration.minutes(10),
      memorySize: 512,
      tracing: lambda.Tracing.ACTIVE,
      layers: [commonLayer.layer],
      vpc: vpc,
      securityGroups: [this.appSecurityGroup],
      vpcSubnets: { subnets: this.appSubnets },
    });

    MdaaNagSuppressions.addCodeResourceSuppressions(
      secretRotationLambda,
      [
        {
          id: 'NIST.800.53.R5-LambdaDLQ',
          reason: 'Function is for Secrets Manager rotation and error handling will be handled by Secrets Manager.',
        },
        {
          id: 'NIST.800.53.R5-LambdaConcurrency',
          reason:
            'Function is for Secrets Manager rotation and will only execute once a month. Reserved concurrency not appropriate.',
        },
        {
          id: 'HIPAA.Security-LambdaDLQ',
          reason: 'Function is for Secrets Manager rotation and error handling will be handled by Secrets Manager.',
        },
        {
          id: 'HIPAA.Security-LambdaConcurrency',
          reason:
            'Function is for Secrets Manager rotation and will only execute once a month. Reserved concurrency not appropriate.',
        },
        {
          id: 'PCI.DSS.321-LambdaDLQ',
          reason: 'Function is for Secrets Manager rotation and error handling will be handled by Secrets Manager.',
        },
        {
          id: 'PCI.DSS.321-LambdaConcurrency',
          reason:
            'Function is for Secrets Manager rotation and will only execute once a month. Reserved concurrency not appropriate.',
        },
      ],
      true,
    );

    const xOriginVerifySecret = new secretsmanager.Secret(this, 'X-Origin-Verify-Secret', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      encryptionKey: props.encryptionKey,
      generateSecretString: {
        excludePunctuation: true,
        generateStringKey: 'headerValue',
        secretStringTemplate: '{}',
      },
    });

    xOriginVerifySecret.grantRead(secretRotationLambdaRole);
    xOriginVerifySecret.grantWrite(secretRotationLambdaRole);
    xOriginVerifySecret.addRotationSchedule('XOriginVerifySecretRotationSchedule', {
      rotationLambda: secretRotationLambda,
      automaticallyAfter: cdk.Duration.days(30),
    });

    new ssm.StringParameter(this, 'XOriginVerifySecretArnSSMParam', {
      parameterName: props.naming.ssmPath('origin/verify/secret/arn'),
      stringValue: xOriginVerifySecret.secretArn,
    });

    MdaaNagSuppressions.addCodeResourceSuppressions(
      secretRotationLambdaRole,
      [
        {
          id: 'AwsSolutions-IAM5',
          reason:
            'Secret resource not known ahead of time, role is for a secret rotation lambda managed by secrets manager',
          appliesTo: ['Resource::*'],
        },
        {
          id: 'AwsSolutions-IAM5',
          reason: 'Network interfaces to be created are unknown',
          appliesTo: ['Resource::arn:<AWS::Partition>:ec2:<AWS::Region>:<AWS::AccountId>:network-interface/*'],
        },
        { id: 'NIST.800.53.R5-IAMNoInlinePolicy', reason: 'Inline policy is specific to this role and function.' },
        { id: 'HIPAA.Security-IAMNoInlinePolicy', reason: 'Inline policy is specific to this role and function.' },
        { id: 'PCI.DSS.321-IAMNoInlinePolicy', reason: 'Inline policy is specific to this role and function.' },
      ],
      true,
    );

    // Placeholder secret for 3RD Party LLM API Keys
    const apiKeysSecret = new secretsmanager.Secret(this, '3rdPartyApiKeysSecret', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      encryptionKey: props.encryptionKey,
      secretObjectValue: {},
    });

    MdaaNagSuppressions.addCodeResourceSuppressions(
      apiKeysSecret,
      [
        {
          id: 'AwsSolutions-SMG4',
          reason:
            'Key entry is managed via the console and ties to 3rd party api keys, no support for automatic rotation',
        },
        {
          id: 'NIST.800.53.R5-SecretsManagerRotationEnabled',
          reason:
            'Key entry is managed via the console and ties to 3rd party api keys, no support for automatic rotation',
        },
        {
          id: 'HIPAA.Security-SecretsManagerRotationEnabled',
          reason:
            'Key entry is managed via the console and ties to 3rd party api keys, no support for automatic rotation',
        },
        {
          id: 'PCI.DSS.321-SecretsManagerRotationEnabled',
          reason:
            'Key entry is managed via the console and ties to 3rd party api keys, no support for automatic rotation',
        },
      ],
      true,
    );

    this.vpc = vpc;
    this.configParameter = configParameter;
    this.xOriginVerifySecret = xOriginVerifySecret;
    this.apiKeysSecret = apiKeysSecret;
    this.powerToolsLayer = powerToolsLayer;
    this.commonLayer = commonLayer.layer;
    this.pythonSDKLayer = pythonSDKLayer;

    new cdk.CfnOutput(this, 'ApiKeysSecretName', {
      value: apiKeysSecret.secretName,
    });
  }