constructor()

in lib/call-forwarding-with-sma-stack.ts [12:246]


  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const outgoingWav = new s3.Bucket(this, "outgoingWav", {
      publicReadAccess: false,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true, // NOT recommended for production code
    });

    const outboundWavBucketPolicy = new iam.PolicyStatement({
      effect: iam.Effect.ALLOW,
      actions: ["s3:GetObject", "s3:PutObject", "s3:PutObjectAcl"],
      principals: [
        new iam.ServicePrincipal("voiceconnector.chime.amazonaws.com"),
      ],
      resources: [outgoingWav.bucketArn, `${outgoingWav.bucketArn}/*`],
      sid: "SIPMediaApplicationRead",
    });

    outgoingWav.addToResourcePolicy(outboundWavBucketPolicy);

    new s3deploy.BucketDeployment(this, "WavDeploy", {
      sources: [s3deploy.Source.asset("./wav_files")],
      destinationBucket: outgoingWav,
      contentType: "audio/wav",
    });

    const calledNumber = new dynamodb.Table(this, "calledNumber", {
      partitionKey: {
        name: "dialed_number",
        type: dynamodb.AttributeType.STRING,
      },
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    });

    const smaLambdaRole = new iam.Role(this, "smaLambdaRole", {
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          "service-role/AWSLambdaBasicExecutionRole"
        ),
      ],
    });

    const handlerLambdaRole = new iam.Role(this, "handlerLambdaRole", {
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
      inlinePolicies: {
        ["chimePolicy"]: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              resources: ["*"],
              actions: [
                "chime:ListVoiceConnectors",
                "chime:ListPhoneNumbers",
                "chime:GetPhoneNumber",
                "chime:UpdateSipRule",
                "chime:DeleteSipRule",
                "chime:UpdatePhoneNumber",
                "chime:AssociatePhoneNumbersWithVoiceConnector",
                "chime:DisassociatePhoneNumbersFromVoiceConnector",
                "chime:GetSipMediaApplication",
                "chime:CreateSipRule",
              ],
            }),
          ],
        }),
      },
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          "service-role/AWSLambdaBasicExecutionRole"
        ),
      ],
    });

    const chimeCreateRole = new iam.Role(this, "createChimeLambdaRole", {
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
      inlinePolicies: {
        ["chimePolicy"]: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              resources: ["*"],
              actions: [
                "chime:CreateVoiceConnector",
                "chime:GetVoiceConnector",
                "chime:UpdateVoiceConnector",
                "chime:CreateSipMediaApplication",
                "chime:CreateSipRule",
                "chime:GetSipMediaApplication",
                "chime:GetSipRule",
                "chime:UpdateSipMediaApplication",
                "chime:UpdateSipRule",
                "chime:AssociatePhoneNumbersWithVoiceConnector",
                "chime:CreatePhoneNumberOrder",
                "chime:GetPhoneNumber",
                "chime:GetPhoneNumberOrder",
                "chime:SearchAvailablePhoneNumbers",
                "chime:UpdatePhoneNumber",
                "lambda:GetPolicy",
                "lambda:AddPermission",
              ],
            }),
          ],
        }),
      },
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          "service-role/AWSLambdaBasicExecutionRole"
        ),
      ],
    });

    const smaLambda = new lambda.Function(this, "smaLambda", {
      code: lambda.Code.fromAsset("src/smaLambda"),
      handler: "callForwardingSMA.lambda_handler",
      runtime: lambda.Runtime.PYTHON_3_8,
      environment: {
        CallForwardingTableName: calledNumber.tableName,
        WavBucketName: outgoingWav.bucketName,
        LoopGreetingWhileRinging: "True",
      },
      role: smaLambdaRole,
      timeout: Duration.seconds(6),
    });
    calledNumber.grantReadWriteData(smaLambda);

    const createSMALambda = new lambda.Function(this, "createSMALambda", {
      code: lambda.Code.fromAsset("src/createChimeSMAResources"),
      handler: "createChimeSMAResources.on_event",
      runtime: lambda.Runtime.PYTHON_3_8,
      role: chimeCreateRole,
      timeout: Duration.seconds(60),
    });

    const chimeSMAProvider = new custom.Provider(this, "chimeProvider", {
      onEventHandler: createSMALambda,
    });

    const smaResources = new CustomResource(this, "smaResources", {
      serviceToken: chimeSMAProvider.serviceToken,
      properties: {
        lambdaArn: smaLambda.functionArn,
        region: this.region,
        smaName: this.stackName + "-callForward",
        phoneNumberRequired: true,
      },
    });

    smaResources.node.addDependency(smaLambda);
    const smaID = smaResources.getAttString("smaID");
    const smaPhoneNumber = smaResources.getAttString("phoneNumber");
    new cdk.CfnOutput(this, "smaPhoneNumber", { value: smaPhoneNumber });

    const handlerLambda = new lambda.Function(this, "handlerLambda", {
      code: lambda.Code.fromAsset("src/handlerLambda"),
      handler: "callForwardHandler.lambda_handler",
      runtime: lambda.Runtime.PYTHON_3_8,
      environment: {
        CallForwardingTableName: calledNumber.tableName,
        SMA_ID: smaID,
      },
      role: handlerLambdaRole,
      timeout: Duration.seconds(10),
    });
    calledNumber.grantReadWriteData(handlerLambda);

    const createVCLambda = new lambda.Function(this, "createVCLambda", {
      code: lambda.Code.fromAsset("src/createChimeVCResources"),
      handler: "createChimeVCResources.on_event",
      runtime: lambda.Runtime.PYTHON_3_8,
      role: chimeCreateRole,
      timeout: Duration.seconds(60),
    });

    const chimeVCProvider = new custom.Provider(this, "chimeVCProvider", {
      onEventHandler: createVCLambda,
    });

    const vcResources = new CustomResource(this, "outboundSMA", {
      serviceToken: chimeVCProvider.serviceToken,
      properties: {
        region: this.region,
      },
    });

    vcResources.node.addDependency(smaLambda);
    const voiceConnectorId = vcResources.getAttString("voiceConnectorId");
    const vcPhoneNumber = vcResources.getAttString("phoneNumber");
    new cdk.CfnOutput(this, "voiceConnectorId", { value: voiceConnectorId });
    new cdk.CfnOutput(this, "vcPhoneNumber", { value: vcPhoneNumber });

    const api = new apigateway.RestApi(this, "workWithChime", {
      endpointConfiguration: {
        types: [apigateway.EndpointType.REGIONAL],
      },
    });

    const updateNumber = api.root.addResource("updateNumber");
    const updateNumberIntegration = new apigateway.LambdaIntegration(
      handlerLambda
    );
    updateNumber.addMethod("POST", updateNumberIntegration, {
      methodResponses: [{ statusCode: "200" }],
    });
    updateNumber.addCorsPreflight({
      allowOrigins: ["*"],
      allowMethods: ["POST", "OPTIONS"],
    });

    const queryNumber = api.root.addResource("queryNumber");
    const queryNumberIntegration = new apigateway.LambdaIntegration(
      handlerLambda
    );
    queryNumber.addMethod("POST", queryNumberIntegration, {
      methodResponses: [{ statusCode: "200" }],
    });
    queryNumber.addCorsPreflight({
      allowOrigins: ["*"],
      allowMethods: ["POST", "OPTIONS"],
    });

    const listVoiceConnectors = api.root.addResource("listVoiceConnectors");
    const listVoiceConnectorsIntegration = new apigateway.LambdaIntegration(
      handlerLambda
    );
    listVoiceConnectors.addMethod("POST", listVoiceConnectorsIntegration, {
      methodResponses: [{ statusCode: "200" }],
    });
    listVoiceConnectors.addCorsPreflight({
      allowOrigins: ["*"],
      allowMethods: ["POST", "OPTIONS"],
    });

    new cdk.CfnOutput(this, "chimeAPI", { value: api.url });
  }