constructor()

in deployment/custom-deployment/lib/smart-product-event.ts [34:248]


  constructor(parent: cdk.Construct, name: string, props: SmartProductEventProps) {
    super(parent, name);

    //=============================================================================================
    // Resources
    //=============================================================================================
    const notificationSnsTopic = new sns.Topic(this, 'Topic', { displayName: 'SmartProductNotificationSNS' });

    const notificationServiceRole = new iam.Role(this, 'NotificationRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com')
    })
    const notificationService = new lambda.CfnFunction(this, 'NotificationService', {
      functionName: generateName(`${name}-NotificationService`, 64),
      description: "Smart Product Solution SNS and MQTT notification microservice",
      code: {
        s3Bucket: process.env.BUILD_OUTPUT_BUCKET,
        s3Key: `smart-product-solution/${props.solutionVersion}/smart-product-notification-service.zip`
      },
      handler: 'index.handler',
      runtime: 'nodejs12.x',
      role: notificationServiceRole.roleArn,
      timeout: 300,
      memorySize: 256,
      environment: {
        variables: {
          SETTINGS_TBL: props.settingsTable.tableName,
          REGISTRATION_TBL: props.registrationTable.tableName,
          LOGGING_LEVEL: '2',
          IDP: props.userPool.userPoolId,
          SNS_TOPIC: notificationSnsTopic.topicName
        }
      }
    })

    const eventProxyRole = new iam.Role(this, 'EventProxyRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com')
    })
    const eventProxyService = new lambda.CfnFunction(this, 'EventProxy', {
      functionName: generateName(`${name}-EventProxyService`, 64),
      description: "Smart Product Solution event proxy microservice",
      code: {
        s3Bucket: process.env.BUILD_OUTPUT_BUCKET,
        s3Key: `smart-product-solution/${props.solutionVersion}/smart-product-event-proxy.zip`
      },
      handler: 'index.handler',
      runtime: 'nodejs12.x',
      role: eventProxyRole.roleArn,
      timeout: 60,
      memorySize: 256,
      environment: {
        variables: {
          EVENTS_TBL: props.eventsTable.tableName,
          REGISTRATION_TBL: props.registrationTable.tableName,
          LOGGING_LEVEL: '2',
          IDP: props.userPool.userPoolId,
          NOTIFICATION_LAMBDA: `${notificationService.functionName}`
        }
      }
    })

    const eventRule = new iot.CfnTopicRule(this, 'EventRule', {
      ruleName: "SmartProductEventRule",
      topicRulePayload: {
        actions: [{ lambda: { functionArn: eventProxyService.attrArn } }],
        description: 'Processing of event messages from smart products.',
        ruleDisabled: false,
        sql: `select * from '${props.eventTopic}/#'`
      }
    })

    //=============================================================================================
    // Permissions and Policies
    //=============================================================================================
    // Notification Log Policy
    const notificationLogPolicy = new iam.Policy(this, 'NotificationLogPolicy', {
      statements: [new iam.PolicyStatement({
        actions: [
          'logs:CreateLogGroup',
          'logs:CreateLogStream',
          'logs:PutLogEvents'
        ],
        resources: [`arn:aws:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/${notificationService.functionName}:*`]
      })]
    })
    const notificationLogPolicyResource = notificationLogPolicy.node.findChild('Resource') as iam.CfnPolicy;
    notificationLogPolicyResource.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [{
          id: 'W12',
          reason: `The * resource allows ${notificationServiceRole.roleName} to access its own logs.`
        }]
      }
    }
    notificationLogPolicy.attachToRole(notificationServiceRole);

    // Notification Dynamo Policy
    const notificationDynamoPolicy = new iam.Policy(this, 'NotificationDynamoPolicy', {
      statements: [
        new iam.PolicyStatement({
          actions: [
            'dynamodb:GetItem'
          ],
          resources: [
            `${props.settingsTable.tableArn}`
          ]
        }),
        new iam.PolicyStatement({
          actions: [
            'dynamodb:Query'
          ],
          resources: [
            `${props.registrationTable.tableArn}`,
            `${props.registrationTable.tableArn}/index/deviceId-index`
          ]
        })
      ]
    })
    notificationDynamoPolicy.attachToRole(notificationServiceRole);

    // Notification Cognito Policy
    const notificationCognitoPolicy = new iam.Policy(this, 'NotificationCognitoPolicy', {
      statements: [new iam.PolicyStatement({
        actions: [
          'cognito-idp:ListUsers'
        ],
        resources: [`${props.userPool.userPoolArn}`]
      })]
    })
    notificationCognitoPolicy.attachToRole(notificationServiceRole);

    // Notification SNS Policy
    const notificationSnsPolicy = new iam.Policy(this, 'NotificationSnsPolicy', {
      statements: [new iam.PolicyStatement({
        actions: [
          'sns:Publish'
        ],
        resources: ['*'] // for phone numbers
      })]
    })
    const notificationSnsPolicyResource = notificationSnsPolicy.node.findChild('Resource') as iam.CfnPolicy;
    notificationSnsPolicyResource.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [{
          id: 'W12',
          reason: `The * resource allows ${notificationServiceRole.roleName} to publish notifications to users.`
        }]
      }
    }
    notificationSnsPolicy.attachToRole(notificationServiceRole);

    // Event Log Policy
    const eventLogPolicy = new iam.Policy(this, 'EventProxyLogPolicy', {
      statements: [new iam.PolicyStatement({
        actions: [
          'logs:CreateLogGroup',
          'logs:CreateLogStream',
          'logs:PutLogEvents'
        ],
        resources: [`arn:aws:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/${eventProxyService.functionName}:*`]
      })]
    })
    const eventLogPolicyResource = eventLogPolicy.node.findChild('Resource') as iam.CfnPolicy;
    eventLogPolicyResource.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [{
          id: 'W12',
          reason: `The * resource allows ${eventProxyRole.roleName} to access its own logs.`
        }]
      }
    }
    eventLogPolicy.attachToRole(eventProxyRole);

    // Event DynamoDB Policy
    const eventDynamoPolicy = new iam.Policy(this, 'EventProxyDynamoPolicy', {
      statements: [
        new iam.PolicyStatement({
          actions: [
            'dynamodb:Query'
          ],
          resources: [
            `${props.registrationTable.tableArn}`,
            `${props.registrationTable.tableArn}/index/deviceId-index`
          ]
        }),
        new iam.PolicyStatement({
          actions: [
            'dynamodb:PutItem',
          ],
          resources: [
            `${props.eventsTable.tableArn}`,
          ]
        })
      ]
    })
    eventDynamoPolicy.attachToRole(eventProxyRole);

    // Event Lambda Policy
    const eventLambdaPolicy = new iam.Policy(this, 'EventProxyLambdaPolicy', {
      statements: [new iam.PolicyStatement({
        actions: [
          'lambda:InvokeFunction'
        ],
        resources: [notificationService.attrArn]
      })]
    })
    eventLambdaPolicy.attachToRole(eventProxyRole);

    new lambda.CfnPermission(this, 'LambdaInvokeEventProxyPermission', {
      functionName: `${eventProxyService.attrArn}`,
      action: 'lambda:InvokeFunction',
      principal: 'iot.amazonaws.com',
      sourceArn: `arn:aws:iot:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:rule/${eventRule.ruleName}`,
      sourceAccount: cdk.Aws.ACCOUNT_ID
    })
  }