constructor()

in packages/aws-cdk-lib/aws-lambda/lib/function.ts [914:1174]


  constructor(scope: Construct, id: string, props: FunctionProps) {
    super(scope, id, {
      physicalName: props.functionName,
    });
    // Enhanced CDK Analytics Telemetry
    addConstructMetadata(this, props);

    if (props.functionName && !Token.isUnresolved(props.functionName)) {
      if (props.functionName.length > 64) {
        throw new ValidationError(`Function name can not be longer than 64 characters but has ${props.functionName.length} characters.`, this);
      }
      if (!/^[a-zA-Z0-9-_]+$/.test(props.functionName)) {
        throw new ValidationError(`Function name ${props.functionName} can contain only letters, numbers, hyphens, or underscores with no spaces.`, this);
      }
    }

    if (props.description && !Token.isUnresolved(props.description)) {
      if (props.description.length > 256) {
        throw new ValidationError(`Function description can not be longer than 256 characters but has ${props.description.length} characters.`, this);
      }
    }

    const managedPolicies = new Array<iam.IManagedPolicy>();

    // the arn is in the form of - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
    managedPolicies.push(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'));

    if (props.vpc) {
      // Policy that will have ENI creation permissions
      managedPolicies.push(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'));
    }

    this.role = props.role || new iam.Role(this, 'ServiceRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies,
    });
    this.grantPrincipal = this.role;

    // add additional managed policies when necessary
    if (props.filesystem) {
      const config = props.filesystem.config;
      if (!Token.isUnresolved(config.localMountPath)) {
        if (!/^\/mnt\/[a-zA-Z0-9-_.]+$/.test(config.localMountPath)) {
          throw new ValidationError(`Local mount path should match with ^/mnt/[a-zA-Z0-9-_.]+$ but given ${config.localMountPath}.`, this);
        }
        if (config.localMountPath.length > 160) {
          throw new ValidationError(`Local mount path can not be longer than 160 characters but has ${config.localMountPath.length} characters.`, this);
        }
      }
      if (config.policies) {
        config.policies.forEach(p => {
          this.role?.addToPrincipalPolicy(p);
        });
      }
    }

    for (const statement of (props.initialPolicy || [])) {
      this.role.addToPrincipalPolicy(statement);
    }

    const code = props.code.bind(this);
    verifyCodeConfig(code, props);

    let profilingGroupEnvironmentVariables: { [key: string]: string } = {};
    if (props.profilingGroup && props.profiling !== false) {
      this.validateProfiling(props);
      props.profilingGroup.grantPublish(this.role);
      profilingGroupEnvironmentVariables = {
        AWS_CODEGURU_PROFILER_GROUP_NAME: props.profilingGroup.profilingGroupName,
        AWS_CODEGURU_PROFILER_TARGET_REGION: props.profilingGroup.env.region,
        AWS_CODEGURU_PROFILER_GROUP_ARN: props.profilingGroup.profilingGroupArn,
        AWS_CODEGURU_PROFILER_ENABLED: 'TRUE',
      };
    } else if (props.profiling) {
      this.validateProfiling(props);
      const profilingGroup = new ProfilingGroup(this, 'ProfilingGroup', {
        computePlatform: ComputePlatform.AWS_LAMBDA,
      });
      profilingGroup.grantPublish(this.role);
      profilingGroupEnvironmentVariables = {
        AWS_CODEGURU_PROFILER_GROUP_NAME: profilingGroup.profilingGroupName,
        AWS_CODEGURU_PROFILER_TARGET_REGION: profilingGroup.env.region,
        AWS_CODEGURU_PROFILER_GROUP_ARN: profilingGroup.profilingGroupArn,
        AWS_CODEGURU_PROFILER_ENABLED: 'TRUE',
      };
    }

    const env = { ...profilingGroupEnvironmentVariables, ...props.environment };
    for (const [key, value] of Object.entries(env)) {
      this.addEnvironment(key, value);
    }

    // DLQ can be either sns.ITopic or sqs.IQueue
    const dlqTopicOrQueue = this.buildDeadLetterQueue(props);
    if (dlqTopicOrQueue !== undefined) {
      if (this.isQueue(dlqTopicOrQueue)) {
        this.deadLetterQueue = dlqTopicOrQueue;
      } else {
        this.deadLetterTopic = dlqTopicOrQueue;
      }
    }

    let fileSystemConfigs: CfnFunction.FileSystemConfigProperty[] | undefined = undefined;
    if (props.filesystem) {
      fileSystemConfigs = [{
        arn: props.filesystem.config.arn,
        localMountPath: props.filesystem.config.localMountPath,
      }];
    }

    if (props.architecture && props.architectures !== undefined) {
      throw new ValidationError('Either architecture or architectures must be specified but not both.', this);
    }
    if (props.architectures && props.architectures.length > 1) {
      throw new ValidationError('Only one architecture must be specified.', this);
    }
    this._architecture = props.architecture ?? (props.architectures && props.architectures[0]);

    if (props.ephemeralStorageSize && !props.ephemeralStorageSize.isUnresolved()
      && (props.ephemeralStorageSize.toMebibytes() < 512 || props.ephemeralStorageSize.toMebibytes() > 10240)) {
      throw new ValidationError(`Ephemeral storage size must be between 512 and 10240 MB, received ${props.ephemeralStorageSize}.`, this);
    }

    const resource: CfnFunction = new CfnFunction(this, 'Resource', {
      functionName: this.physicalName,
      description: props.description,
      code: {
        s3Bucket: code.s3Location && code.s3Location.bucketName,
        s3Key: code.s3Location && code.s3Location.objectKey,
        s3ObjectVersion: code.s3Location && code.s3Location.objectVersion,
        zipFile: code.inlineCode,
        imageUri: code.image?.imageUri,
        sourceKmsKeyArn: code.sourceKMSKeyArn,
      },
      layers: Lazy.list({ produce: () => this.renderLayers() }), // Evaluated on synthesis
      handler: props.handler === Handler.FROM_IMAGE ? undefined : props.handler,
      timeout: props.timeout && props.timeout.toSeconds(),
      packageType: props.runtime === Runtime.FROM_IMAGE ? 'Image' : undefined,
      runtime: props.runtime === Runtime.FROM_IMAGE ? undefined : props.runtime.name,
      role: this.role.roleArn,
      // Uncached because calling '_checkEdgeCompatibility', which gets called in the resolve of another
      // Token, actually *modifies* the 'environment' map.
      environment: Lazy.uncachedAny({ produce: () => this.renderEnvironment() }),
      memorySize: props.memorySize,
      ephemeralStorage: props.ephemeralStorageSize ? {
        size: props.ephemeralStorageSize.toMebibytes(),
      } : undefined,
      vpcConfig: this.configureVpc(props),
      deadLetterConfig: this.buildDeadLetterConfig(dlqTopicOrQueue),
      reservedConcurrentExecutions: props.reservedConcurrentExecutions,
      imageConfig: undefinedIfNoKeys({
        command: code.image?.cmd,
        entryPoint: code.image?.entrypoint,
        workingDirectory: code.image?.workingDirectory,
      }),
      kmsKeyArn: props.environmentEncryption?.keyArn,
      fileSystemConfigs,
      codeSigningConfigArn: props.codeSigningConfig?.codeSigningConfigArn,
      architectures: this._architecture ? [this._architecture.name] : undefined,
      runtimeManagementConfig: props.runtimeManagementMode?.runtimeManagementConfig,
      snapStart: this.configureSnapStart(props),
      loggingConfig: this.getLoggingConfig(props),
      recursiveLoop: props.recursiveLoop,
    });

    if ((props.tracing !== undefined) || (props.adotInstrumentation !== undefined)) {
      resource.tracingConfig = this.buildTracingConfig(props.tracing ?? Tracing.ACTIVE);
    }

    this._logGroup = props.logGroup;

    resource.node.addDependency(this.role);

    this.functionName = this.getResourceNameAttribute(resource.ref);
    this.functionArn = this.getResourceArnAttribute(resource.attrArn, {
      service: 'lambda',
      resource: 'function',
      resourceName: this.physicalName,
      arnFormat: ArnFormat.COLON_RESOURCE_NAME,
    });

    this.runtime = props.runtime;
    this.timeout = props.timeout;

    this.architecture = props.architecture ?? Architecture.X86_64;

    if (props.layers) {
      if (props.runtime === Runtime.FROM_IMAGE) {
        throw new ValidationError('Layers are not supported for container image functions', this);
      }

      this.addLayers(...props.layers);
    }

    for (const event of props.events || []) {
      this.addEventSource(event);
    }

    // Log retention
    if (props.logRetention) {
      if (props.logGroup) {
        throw new ValidationError('CDK does not support setting logRetention and logGroup', this);
      }
      const logRetention = new logs.LogRetention(this, 'LogRetention', {
        logGroupName: `/aws/lambda/${this.functionName}`,
        retention: props.logRetention,
        role: props.logRetentionRole,
        logRetentionRetryOptions: props.logRetentionRetryOptions as logs.LogRetentionRetryOptions,
      });
      this._logGroup = logs.LogGroup.fromLogGroupArn(this, 'LogGroup', logRetention.logGroupArn);
      this._logRetention = logRetention;
    }

    props.code.bindToResource(resource);

    // Event Invoke Config
    if (props.onFailure || props.onSuccess || props.maxEventAge || props.retryAttempts !== undefined) {
      this.configureAsyncInvoke({
        onFailure: props.onFailure,
        onSuccess: props.onSuccess,
        maxEventAge: props.maxEventAge,
        retryAttempts: props.retryAttempts,
      });
    }

    this.currentVersionOptions = props.currentVersionOptions;

    if (props.filesystem) {
      if (!props.vpc) {
        throw new ValidationError('Cannot configure \'filesystem\' without configuring a VPC.', this);
      }
      const config = props.filesystem.config;
      if (config.dependency) {
        this.node.addDependency(...config.dependency);
      }
      // There could be a race if the Lambda is used in a CustomResource. It is possible for the Lambda to
      // fail to attach to a given FileSystem if we do not have a dependency on the SecurityGroup ingress/egress
      // rules that were created between this Lambda's SG & the Filesystem SG.
      this.connections.securityGroups.forEach(sg => {
        sg.node.findAll().forEach(child => {
          if (child instanceof CfnResource && child.cfnResourceType === 'AWS::EC2::SecurityGroupEgress') {
            resource.node.addDependency(child);
          }
        });
      });
      config.connections?.securityGroups.forEach(sg => {
        sg.node.findAll().forEach(child => {
          if (child instanceof CfnResource && child.cfnResourceType === 'AWS::EC2::SecurityGroupIngress') {
            resource.node.addDependency(child);
          }
        });
      });
    }

    // Configure Lambda insights
    this.configureLambdaInsights(props);

    this.configureAdotInstrumentation(props);

    this.configureParamsAndSecretsExtension(props);
  }