constructor()

in v2/base/cdk/lib/BaseImplementationStack.ts [17:257]


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

    const stackName = cdk.Stack.of(this).stackName
    if (stackName.length > 20) {
      console.error("Stack name must be less than 20 characters in length")
      process.exitCode = 1
    }
    // suffix to use for all stack resources to make unique
    // In this stack all resources will use the format STACKNAME-RESOURCENAME-RANDOMSUFFIX

    const stackRandom: string = makeid(8, stackName)
    // Layered constructs - each constructs derived values can be used for subsequent constructs

    // create S3 bucket for artifacts - greengrass-component-artifacts-stackRandom-AccountId-Region
    // NOTE: This bucket and all contents will be deleted upon stack deletion
    const componentBucketName = `${cdk.Stack.of(this).stackName}-ggcomponents-${cdk.Fn.ref("AWS::AccountId")}-${cdk.Fn.ref("AWS::Region")}`
    const componentBucket = new s3.Bucket(this, "ComponentBucket", {
      bucketName: componentBucketName,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true
    })

    // Create IoT role alias for use by Greengrass core
    const greengrassRoleMinimalPolicy = new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: [
            "iot:DescribeCertificate",
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents",
            "logs:DescribeLogStreams",
            "iot:Connect",
            "iot:Publish",
            "iot:Subscribe",
            "iot:Receive",
            "s3:GetBucketLocation"
          ],
          resources: ["*"]
        }),
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ["s3:GetObject"],
          resources: [`arn:aws:s3:::${componentBucketName}/*`]
        })
      ]
    })

    // Define stack-specific names for IoT role alias
    const roleAliasName = fullResourceName({
      stackName: stackName,
      baseName: "GreengrassV2TokenExchangeRole",
      suffix: stackRandom,
      resourceRegex: "\\w=,@-",
      maxLength: 128
    })
    const iamRoleName = fullResourceName({
      stackName: stackName,
      baseName: "RoleForIoTRoleAlias",
      suffix: stackRandom,
      resourceRegex: "\\w+=,.@-",
      maxLength: 64
    })
    // Then create IoT role alias
    const greengrassRoleAlias = new IotRoleAlias(this, "IoTRoleAlias", {
      iotRoleAliasName: roleAliasName,
      iamRoleName: iamRoleName,
      iamPolicy: greengrassRoleMinimalPolicy
    })

    // Define stack-specific names for the IoT thing name and policy
    const greengrassCoreThingName = fullResourceName({
      stackName: stackName,
      baseName: "greengrass-core",
      suffix: stackRandom,
      resourceRegex: "a-zA-Z0-9:_-",
      maxLength: 128
    })
    const greengrassCoreIotPolicyName = fullResourceName({
      stackName: stackName,
      baseName: "greengrass-minimal-policy",
      suffix: stackRandom,
      resourceRegex: "\\w+=,.@-",
      maxLength: 128
    })
    // Then create IoT thing, certificate/private key, and IoT Policy
    const iotThingCertPol = new IotThingCertPolicy(this, "GreengrassCore", {
      thingName: greengrassCoreThingName,
      iotPolicyName: greengrassCoreIotPolicyName,
      iotPolicy: myConst.greengrassCoreMinimalIoTPolicy,
      encryptionAlgorithm: "RSA",
      policyParameterMapping: {
        region: cdk.Fn.ref("AWS::Region"),
        account: cdk.Fn.ref("AWS::AccountId"),
        rolealiasname: greengrassRoleAlias.roleAliasName
      }
    })

    // Define stack-specific name of the IoT thing group
    const groupName = fullResourceName({
      stackName: stackName,
      baseName: "greengrass-deployment-group",
      suffix: stackRandom,
      resourceRegex: "a-zA-Z0-9:_-",
      maxLength: 128
    })
    // Then create thing group and add thing
    const deploymentGroup = new IotThingGroup(this, "DeploymentGroup", {
      thingGroupName: groupName
    })
    deploymentGroup.addThing(iotThingCertPol.thingArn)

    // Greengrass component process

    // Create Hello World component
    // uses same component file name and path as AWS published components,
    // see the source recipe file for more details
    const componentName = "ggAccel.example.HelloWorld"
    const componentVersion = "1.0.0"
    const helloWorldComponent = new GreengrassV2Component(this, "HelloWorldComponent", {
      componentName: componentName,
      componentVersion: componentVersion,
      bucket: componentBucket,
      artifactZipPrefix: `${componentName}/${componentVersion}/`,
      targetArtifactKeyName: `${componentName}.zip`,
      sourceArtifactPath: path.join(__dirname, "..", "components", componentName, "artifacts", componentName, componentVersion),
      sourceRecipeFile: path.join(__dirname, "..", "components", componentName, `${componentName}-${componentVersion}.yaml`)
    })

    // Create the deployment with AWS public and stack components, target the thing group
    // and add the components/version/updates
    const greengrassDeployment = new GreengrassV2Deployment(this, "GreengrassDeployment", {
      targetArn: deploymentGroup.thingGroupArn,
      deploymentName: `${this.stackName} - Example deployment`,
      component: {
        // accelerator component(s)
        [helloWorldComponent.componentName]: {
          componentVersion: helloWorldComponent.componentVersion,
          configurationUpdate: {
            merge: JSON.stringify({
              Message: "Welcome from the Greengrass accelerator stack"
            })
          }
        }
      }
    })
    // Add core public components
    greengrassDeployment.addComponent({
      "aws.greengrass.Nucleus": {
        componentVersion: "2.4.0"
      },
      "aws.greengrass.Cli": {
        componentVersion: "2.4.0"
      }
    })
    greengrassDeployment.addComponent({
      "aws.greengrass.LocalDebugConsole": {
        componentVersion: "2.2.2",
        configurationUpdate: {
          merge: JSON.stringify({
            httpsEnabled: "false"
          })
        }
      }
    })

    // Set stack outputs to be consumed by local processes
    new cdk.CfnOutput(this, "ComponentBucketArn", {
      exportName: `${stackName}-ComponentBucketArn`,
      value: componentBucket.bucketArn
    })
    new cdk.CfnOutput(this, "IotRoleAliasName", {
      value: greengrassRoleAlias.roleAliasName
    })
    new cdk.CfnOutput(this, "ThingArn", {
      exportName: `${stackName}-ThingArn`,
      value: iotThingCertPol.thingArn
    })
    new cdk.CfnOutput(this, "ThingName", {
      exportName: `${stackName}-ThingName`,
      value: greengrassCoreThingName
    })
    new cdk.CfnOutput(this, "IotPolicyArn", {
      value: iotThingCertPol.iotPolicyArn
    })
    new cdk.CfnOutput(this, "RoleAliasArn", {
      value: greengrassRoleAlias.roleAliasArn
    })
    new cdk.CfnOutput(this, "IamRoleArn", {
      exportName: `${stackName}-IamRoleArn`,
      value: greengrassRoleAlias.iamRoleArn
    })
    new cdk.CfnOutput(this, "CertificateArn", {
      exportName: `${stackName}-CertificateArn`,
      value: iotThingCertPol.certificateArn
    })
    new cdk.CfnOutput(this, "CertificatePemParameter", {
      value: iotThingCertPol.certificatePemParameter
    })
    new cdk.CfnOutput(this, "PrivateKeySecretParameter", {
      value: iotThingCertPol.privateKeySecretParameter
    })
    new cdk.CfnOutput(this, "DataAtsEndpointAddress", {
      value: iotThingCertPol.dataAtsEndpointAddress
    })
    new cdk.CfnOutput(this, "CredentialProviderEndpointAddress", {
      value: iotThingCertPol.credentialProviderEndpointAddress
    })

    // ************ End of CDK Constructs / stack - Supporting functions below ************

    function makeid(length: number, seed: string) {
      // Generate a n-length random value for each resource
      var result = ""
      var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
      var charactersLength = characters.length
      seedrandom(seed, { global: true })
      for (var i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength))
      }
      return result
    }

    interface ResourceName {
      stackName: string
      baseName: string
      suffix: string
      resourceRegex: string
      maxLength: number
    }

    function fullResourceName({ stackName, baseName, suffix, resourceRegex, maxLength }: ResourceName) {
      let re = new RegExp(`[^\\[${resourceRegex}]`, "g")
      let resourceName = `${stackName}-${baseName}`.replace(re, "")
      resourceName = resourceName.substring(0, maxLength - suffix.length - 1)
      resourceName = `${resourceName}-${suffix}`
      return resourceName
    }
  }