constructor()

in packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts [463:581]


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

    if (!props.onCreate && !props.onUpdate && !props.onDelete) {
      throw new Error('At least `onCreate`, `onUpdate` or `onDelete` must be specified.');
    }

    if (!props.role && !props.policy) {
      throw new Error('At least one of `policy` or `role` (or both) must be specified.');
    }

    if (props.onCreate && !props.onCreate.physicalResourceId) {
      throw new Error("'physicalResourceId' must be specified for 'onCreate' call.");
    }

    if (!props.onCreate && props.onUpdate && !props.onUpdate.physicalResourceId) {
      throw new Error("'physicalResourceId' must be specified for 'onUpdate' call when 'onCreate' is omitted.");
    }

    for (const call of [props.onCreate, props.onUpdate, props.onDelete]) {
      if (call?.physicalResourceId?.responsePath) {
        AwsCustomResource.breakIgnoreErrorsCircuit([call], 'PhysicalResourceId.fromResponse');
      }
    }

    if (includesPhysicalResourceIdRef(props.onCreate?.parameters)) {
      throw new Error('`PhysicalResourceIdReference` must not be specified in `onCreate` parameters.');
    }

    this.props = props;

    let memorySize = props.memorySize;

    if (props.installLatestAwsSdk) {
      memorySize ??= 512;
    }

    const provider = new AwsCustomResourceSingletonFunction(this, 'Provider', {
      uuid: AwsCustomResource.PROVIDER_FUNCTION_UUID,
      lambdaPurpose: 'AWS',
      memorySize: memorySize,
      timeout: props.timeout || cdk.Duration.minutes(2),
      role: props.role,
      // props.logRetention is deprecated, make sure we only set it if it is actually provided
      // otherwise jsii will print warnings even for users that don't use this directly
      ...(props.logRetention ? { logRetention: props.logRetention } : {}),
      logGroup: props.logGroup,
      functionName: props.functionName,
      vpc: props.vpc,
      vpcSubnets: props.vpcSubnets,
    });
    this.grantPrincipal = provider.grantPrincipal;

    const installLatestAwsSdk = (props.installLatestAwsSdk
      ?? this.node.tryGetContext(cxapi.AWS_CUSTOM_RESOURCE_LATEST_SDK_DEFAULT)
      ?? true);

    if (installLatestAwsSdk && props.installLatestAwsSdk === undefined) {
      // This is dangerous. Add a warning.
      Annotations.of(this).addWarningV2('@aws-cdk/custom-resources:installLatestAwsSdkNotSpecified', [
        'installLatestAwsSdk was not specified, and defaults to true. You probably do not want this.',
        `Set the global context flag \'${cxapi.AWS_CUSTOM_RESOURCE_LATEST_SDK_DEFAULT}\' to false to switch this behavior off project-wide,`,
        'or set the property explicitly to true if you know you need to call APIs that are not in Lambda\'s built-in SDK version.',
      ].join(' '));
    }

    const create = props.onCreate || props.onUpdate;
    this.customResource = new cdk.CustomResource(this, 'Resource', {
      resourceType: props.resourceType || 'Custom::AWS',
      serviceToken: provider.functionArn,
      serviceTimeout: props.serviceTimeout,
      pascalCaseProperties: true,
      removalPolicy: props.removalPolicy,
      properties: {
        create: create && this.formatSdkCall(create),
        update: props.onUpdate && this.formatSdkCall(props.onUpdate),
        delete: props.onDelete && this.formatSdkCall(props.onDelete),
        installLatestAwsSdk,
      },
    });

    // Create the policy statements for the custom resource function role, or use the user-provided ones
    if (props.policy) {
      const statements = [];
      if (props.policy.statements.length !== 0) {
        // Use custom statements provided by the user
        for (const statement of props.policy.statements) {
          statements.push(statement);
        }
      } else {
        // Derive statements from AWS SDK calls
        for (const call of [props.onCreate, props.onUpdate, props.onDelete]) {
          if (call && call.assumedRoleArn == null) {
            const statement = new iam.PolicyStatement({
              actions: [awsSdkToIamAction(call.service, call.action)],
              resources: props.policy.resources,
            });
            statements.push(statement);
          } else if (call && call.assumedRoleArn != null) {
            const statement = new iam.PolicyStatement({
              actions: ['sts:AssumeRole'],
              resources: [call.assumedRoleArn],
            });
            statements.push(statement);
          }
        }
      }
      const policy = new iam.Policy(this, 'CustomResourcePolicy', {
        statements: statements,
      });
      if (provider.role !== undefined) {
        policy.attachToRole(provider.role);
      }

      // If the policy was deleted first, then the function might lose permissions to delete the custom resource
      // This is here so that the policy doesn't get removed before onDelete is called
      this.customResource.node.addDependency(policy);
    }
  }