Beetroot LogoBeetroot

Ingest Lambda IAM Role

Create a role for the ingestion Lambda.

Goal

Create an IAM role for the Ingestion Lambda that can:

  • read raw photos from the beetroot-raw S3 bucket
  • write face thumbnails to the beetroot-thumbs S3 bucket
  • call Rekognition (DetectFaces, SearchFacesByImage, IndexFaces)
  • write records to DynamoDB (Photos, Persons, Occurrences)
  • write logs to CloudWatch

Least privilege

Do not use broad policies like AdministratorAccess. We'll grant only the exact permissions this Lambda needs.

Create the IAM role

  1. Go to IAM → Roles → Create role
  2. Trusted entity: AWS service
  3. Use case: Lambda
  4. Click on Next
  5. Skip to Step 3 by clicking on Name, review and create on the step indicator
  6. Role name: beetroot-ingest-role
  7. Click Create role

Add logging permissions

  1. Open the role: IAM → Roles → beetroot-ingest-role
  2. Go to Permissions → Add permissions → Attach policies
  3. Select: AWSLambdaBasicExecutionRole
  4. Click Add Permissions

Create the permissions policy

In this step, you will build the policy yourself using the visual editor, then we’ll also provide the final JSON so you can verify your work.

Open the visual policy editor

  1. Open the role: IAM → Roles → beetroot-ingest-role
  2. Click Add permissions → Create inline policy
  3. Choose the Visual editor (not JSON)
  1. Select service: S3

  2. Under Actions:

    • Expand Read
    • Select: GetObject
  3. Under Resources:

    • Choose Specific
    • In the Object section, click Add ARN
  4. In the Add ARN popup, fill:

    • Resource bucket name: beetroot-raw
    • Resource object name: photos-raw/*

    The ARN should resolve to:

    • Resource ARN: arn:aws:s3:::beetroot-raw/photos-raw/*
  5. Click Add to save the ARN.

  6. Click Add additional permissions (we will add S3 Thumbs Bucket next)

Why two S3 resource entries?

We read only from photos-raw/_ and write only to faces-thumbs/_ . This keeps access tightly scoped.

  1. Select service: S3

  2. Under Actions:

    • Expand Write
    • Select: PutObject
  3. Under Resources:

    • Choose Specific
    • In the Object section, click Add ARN
  4. In the Add ARN popup, fill:

    • Resource bucket name: beetroot-thumbs
    • Resource object name: faces-thumbs/*

    The ARN should resolve to:

    • Resource ARN: arn:aws:s3:::beetroot-thumbs/faces-thumbs/*
  5. Click Add to save the ARN.

  6. Click Add additional permissions (we will add DynamoDB next)

  1. Select service: S3

  2. Under Actions, allow:

    • GetObject
    • PutObject
  3. Under Resources, click Add ARN and add two resource entries:

    • Raw photos (read):

      Bucket: beetroot-raw

      Object: photos-raw/*

    • Thumbnails (write):

      Bucket: beetroot-thumbs

      Object: faces-thumbs/*

  4. Click Add additional permissions (we will add DynamoDB next)

  1. Select service: DynamoDB

  2. Under Actions:

    • Expand Read and select:
      • GetItem
      • Query
    • Expand Write and select:
      • PutItem
      • UpdateItem
  3. Under Resources:

    • Choose Specific
    • In the Table section, click Add ARN
  4. In the Add ARN popup, add these tables one by one (same region: us-east-1):

    • Table name: Persons
      • Resource ARN: arn:aws:dynamodb:us-east-1:*:table/Persons
    • Table name: Photos
      • Resource ARN: arn:aws:dynamodb:us-east-1:*:table/Photos
    • Table name: Occurrences
      • Resource ARN: arn:aws:dynamodb:us-east-1:*:table/Occurrences
  5. Click Add to save the ARNs, then click Next.

Why Query is needed

The ingestion Lambda uses Query when reading occurrence mappings by personId, and may use lookups during idempotency checks.

  1. Select service: Rekognition

  2. Under Actions:

    • Expand Read and select:
      • DetectFaces
      • SearchFacesByImage
    • Expand Write and select:
      • IndexFaces
  3. Under Resources:

    • Choose All resources
  4. Click Next to continue.

Why All resources for Rekognition?

In IAM, many Rekognition actions don't support scoping to a single collection ARN in the visual editor. The least-privilege approach here is restricting the actions to only what we need.

Save the inline policy

  1. Click Next
  2. Policy name: BeetrootIngestPolicy
  3. Click Create policy

Full Policy JSON

You can verify your policy configuration by switching to the JSON editor and comparing it against the policy document below. Alternatively, paste the following JSON directly to attach all required permissions at once.

Replace bucket names if yours differ.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadRawPhotos",
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::beetroot-raw/photos-raw/*"]
    },
    {
      "Sid": "WriteFaceThumbnails",
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": ["arn:aws:s3:::beetroot-thumbs/faces-thumbs/*"]
    },
    {
      "Sid": "DynamoDBAccess",
      "Effect": "Allow",
      "Action": [
        "dynamodb:PutItem",
        "dynamodb:UpdateItem",
        "dynamodb:GetItem",
        "dynamodb:Query"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:*:table/Persons",
        "arn:aws:dynamodb:us-east-1:*:table/Photos",
        "arn:aws:dynamodb:us-east-1:*:table/Occurrences"
      ]
    },
    {
      "Sid": "RekognitionFaceOps",
      "Effect": "Allow",
      "Action": [
        "rekognition:DetectFaces",
        "rekognition:SearchFacesByImage",
        "rekognition:IndexFaces"
      ],
      "Resource": "*"
    }
  ]
}

Checkpoint

In the role's Permissions tab, you should see:

  • AWSLambdaBasicExecutionRole
  • BeetrootIngestPolicy

On this page