C# and AWS Lambdas, Part 4 – Storing the Zip in S3, Setup with Pulumi IaC

Full source code available here.

In the previous post I showed how to use Pulumi to create a lambda, API gateway and upload a zip of Web API application directly to the lambda.

In the post I’m going to use S3 to store the zip of the of a simple .NET application (not a Web API app) and point the lambda at it, brining all the resources up using Pulumi. One drawback of using S3 to store the zip for a lambda is that when you update the zip in S3, AWS doesn’t deploy the new zip to the lambda, but I will show a way of handling that in the next blog post.

Every time I write these posts it feels like a lot of work to set up the infrastructure, learn about the AWS components, policies, etc. But then when I run pulumi destroy -y and pulumi up -y as I make changes I appreciate the speed and predictability.

What’s needed

A lambda role.
A policy to execute the lambda.
An S3 bucket.
Block all public access to the bucket – optional.
Put zip in the bucket.
The lambda function pointing to the zip in the bucket.

A few of these are the same as in the previous post, but the S3 resources are new.

Note that I included BucketPublicAccessBlock section, this makes the S3 bucket more secure than it would be under the default settings.

The code
Here is the code that sets it all up. I’ve included a helloworld.zip file in the attached source code zip. This is NOT how you would normally upload the zip to S3, but it’s easy for demonstration purposes. In a more realistic scenario, you would a CI/CD pipeline to compile the code and drop it in S3.

using Pulumi;
using S3 = Pulumi.Aws.S3;
using Aws = Pulumi.Aws;
class MyStack : Stack
    public MyStack()

        var lambdaRole = new Aws.Iam.Role("PulumiHelloWorld_LambdaRole", new Aws.Iam.RoleArgs
            AssumeRolePolicy =
    ""Version"": ""2012-10-17"",
    ""Statement"": [
        ""Action"": ""sts:AssumeRole"",
        ""Principal"": {
            ""Service"": ""lambda.amazonaws.com""
        ""Effect"": ""Allow"",
        ""Sid"": """"

        var lambdaPolicyAttachment = new Aws.Iam.PolicyAttachment("PulumiHelloWorld_LambdaPolicyAttachment", new Aws.Iam.PolicyAttachmentArgs
            Roles =
            PolicyArn = Aws.Iam.ManagedPolicy.AWSLambdaBasicExecutionRole.ToString(),

        var bucket = new S3.Bucket("PulumiHelloWorld_S3Bucket", new S3.BucketArgs
            BucketName = "pulumi-hello-world-s3-bucket",
            Acl = "private"

        var bucketPublicAccessBlock = new S3.BucketPublicAccessBlock("PulumiHelloWorld_PublicAccessBlock", new S3.BucketPublicAccessBlockArgs
            Bucket = bucket.Id,
            BlockPublicAcls = true,
            BlockPublicPolicy = true,
            RestrictPublicBuckets = true,
            IgnorePublicAcls = true

        var bucketObject = new S3.BucketObject("PulumiHelloWorld_ZipFile", new S3.BucketObjectArgs
            Bucket = bucket.BucketName.Apply(name => name),
            Acl = "private",
            Source = new FileArchive("helloworld.zip")

        var lambdaFunction = new Aws.Lambda.Function("PulumiHelloWorld_LambdaFunction", new Aws.Lambda.FunctionArgs
            Handler = "HelloWorldLambda::HelloWorldLambda.Function::FunctionHandler",
            MemorySize = 128,
            Publish = false,
            ReservedConcurrentExecutions = -1,
            Role = lambdaRole.Arn,
            Runtime = Aws.Lambda.Runtime.DotnetCore3d1,
            Timeout = 4,
            S3Bucket = bucket.BucketName,
            S3Key = bucketObject.Key

        this.LambdaFunctionName = lambdaFunction.Name;

    public Output<string> LambdaFunctionName { get; set; }

To test out the lambda, open it in the AWS console, navigate to the lambda and add a test as described in part 1 of this series of posts.

Full source code available here.

Leave a Reply

Your email address will not be published. Required fields are marked *