in packages/@aws-cdk/aws-lambda/lib/function.ts [608:809]
constructor(scope: Construct, id: string, props: FunctionProps) {
super(scope, id, {
physicalName: props.functionName,
});
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 (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_ARN: Stack.of(scope).formatArn({
service: 'codeguru-profiler',
resource: 'profilingGroup',
resourceName: props.profilingGroup.profilingGroupName,
}),
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_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);
}
this.deadLetterQueue = this.buildDeadLetterQueue(props);
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 Error('Either architecture or architectures must be specified but not both.');
}
if (props.architectures && props.architectures.length > 1) {
throw new Error('Only one architecture must be specified.');
}
this._architecture = props.architecture ?? (props.architectures && props.architectures[0]);
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,
},
layers: Lazy.list({ produce: () => this.layers.map(layer => layer.layerVersionArn) }, { omitEmpty: true }), // 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,
vpcConfig: this.configureVpc(props),
deadLetterConfig: this.buildDeadLetterConfig(this.deadLetterQueue),
tracingConfig: this.buildTracingConfig(props),
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,
});
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 Error('Layers are not supported for container image functions');
}
this.addLayers(...props.layers);
}
for (const event of props.events || []) {
this.addEventSource(event);
}
// Log retention
if (props.logRetention) {
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);
}
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 Error('Cannot configure \'filesystem\' without configuring a VPC.');
}
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);
}