npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@interlace/serverless-iam-roles-per-function

v1.0.0

Published

Per-function IAM roles for Serverless Framework — strict validation, role consolidation, CLI introspection, EventBridge / S3 / DLQ auto-permissions, and full v3+v4 support

Downloads

68

Readme

@interlace/serverless-iam-roles-per-function

Per-function IAM roles for Serverless Framework — strict, typed, observable.

A drop-in replacement for serverless-iam-roles-per-function (community v3.2.0, last published 2021-05-21 — 5 years stale) that keeps the same config keys, adds first-class TypeScript types, four CLI commands, EventBridge + S3 auto-permissions, and strict statement validation.

Supports Serverless Framework v3 and v4 out of the box — runtime and TypeScript types. The plugin's default export shape works with both versions' loader, and the exported config types (InterlaceIamConfig, InterlaceFunctionIamConfig, ValidationFinding) are usable from serverless.ts regardless of framework version.

Why switch?

Source-backed comparison (community is [email protected]). Full citations: docs/community-plugin-comparison.md.

| Capability | Community | @interlace/serverless-iam-roles-per-function | | ----------------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------- | | Per-function IAM role generation | ✅ | ✅ | | iamRoleStatements / iamRoleStatementsInherit / …Name | ✅ | ✅ (config keys identical for drop-in) | | iamPermissionsBoundary / iamGlobalPermissionsBoundary | ✅ | ✅ | | defaultInherit | ✅ | ✅ | | Auto-permissions: SQS / DynamoDB / Kinesis / DLQ (onError) | ✅ | ✅ | | Auto-permissions: EventBridge event.eventBridge.eventBus | ❌ | ✅ events:PutEvents | | Auto-permissions: S3 event.s3.bucket | ❌ | ✅ s3:GetObject scoped to arn:aws:s3:::<bucket>/* | | iamManagedPolicies per function | ❌ | ✅ | | iamRoleStatementsTemplate (share base policies) | ❌ | ✅ Define once in custom.…statementTemplates, reference by name | | suppressGlobalRole (drop the broad fallback role) | ❌ | ✅ Removes IamRoleLambdaExecution when every function has its own role | | requirePerFunctionRoles (fail-fast enforcement) | ❌ | ✅ Aborts deploy if any function lacks iamRoleStatements | | Strict statement validation (Effect enum, Action format, Sid) | ⚠️ Presence-only | ✅ Effect must be Allow/Deny; Action service:action; Sid [A-Za-z0-9]+ | | sls iam preview (dry-run) | ❌ | ✅ Lists per-function roles a deploy would create | | sls iam audit (find functions on the global role) | ❌ | ✅ Optional --strict mode for CI gates | | sls iam validate (strict statement-grammar check) | ❌ | ✅ With --strict-wildcard-action / --strict-wildcard-resource | | sls iam status (summary) | ❌ | ✅ | | TypeScript types for serverless.ts config | ❌ Plugin ships .d.ts for the class only — not configs | ✅ InterlaceFunctionIamConfig and InterlaceIamConfig exported | | Zero runtime dependencies | ❌ lodash@^4.17.20 | ✅ | | Active maintenance | ❌ Last release 2021-05-21 (5 years) | ✅ Shipping 2026-05-03 |

Install

npm install @interlace/serverless-iam-roles-per-function --save-dev

Quick Start

# serverless.yml
plugins:
  - '@interlace/serverless-iam-roles-per-function'

custom:
  interlaceIamRolesPerFunction:
    defaultInherit: false # if true, all functions inherit provider.iam.role.statements
    suppressGlobalRole: true # drop the broad IamRoleLambdaExecution role
    requirePerFunctionRoles: true # fail the deploy if any function is missing iamRoleStatements
    statementTemplates:
      data-read:
        - Effect: Allow
          Action: ['dynamodb:GetItem', 'dynamodb:Query']
          Resource: '*'

functions:
  listUsers:
    handler: src/handler.list
    iamRoleStatementsTemplate: data-read # reuse the named template above
    iamRoleStatements: # plus function-specific statements
      - Effect: Allow
        Action: ['s3:GetObject']
        Resource: 'arn:aws:s3:::my-bucket/*'

Drop-in compatibility

If you're switching from the community plugin, keep your existing config. The plugin reads both:

  • custom.interlaceIamRolesPerFunction — the new canonical key
  • custom.serverless-iam-roles-per-function — backwards-compat alias for the community key

So the only change in serverless.yml is the entry under plugins:. See the Migration Guide for details and gotchas.

TypeScript config (serverless.ts)

import type { Serverless } from 'serverless/aws';
import type {
  InterlaceIamConfig,
  InterlaceFunctionIamConfig,
} from '@interlace/serverless-iam-roles-per-function';

const serverlessConfiguration: Serverless = {
  service: 'my-service',
  plugins: ['@interlace/serverless-iam-roles-per-function'],

  custom: {
    interlaceIamRolesPerFunction: {
      defaultInherit: false,
      suppressGlobalRole: true,
      statementTemplates: {
        'data-read': [
          { Effect: 'Allow', Action: ['dynamodb:GetItem'], Resource: '*' },
        ],
      },
    } satisfies InterlaceIamConfig,
  },

  functions: {
    listUsers: {
      handler: 'src/handler.list',
      iamRoleStatementsTemplate: 'data-read',
      iamRoleStatements: [
        {
          Effect: 'Allow',
          Action: ['s3:GetObject'],
          Resource: 'arn:aws:s3:::my-bucket/*',
        },
      ],
    } as { handler: string } & InterlaceFunctionIamConfig,
  },
};

export = serverlessConfiguration;

Auto-permissions

Permissions are derived from the function's events: block — you don't have to repeat them in iamRoleStatements.

| Event source | Actions granted | | -------------------------- | ------------------------------------------------------------------------------ | | events: [sqs: …] | sqs:ReceiveMessage, sqs:DeleteMessage, sqs:GetQueueAttributes | | events: [stream: …] | dynamodb:GetRecords/GetShardIterator/DescribeStream or kinesis: triple | | onError: arn:…sns… | sns:Publish | | events: [eventBridge: …] | events:PutEvents (community plugin: not granted) | | events: [s3: …] | s3:GetObject for arn:aws:s3:::<bucket>/* (community plugin: not granted) |

The function's CloudWatch log-group statement is always granted (scoped to the function's own log group, not *).

Statement templates

Define a base policy once and reference it from any function:

custom:
  interlaceIamRolesPerFunction:
    statementTemplates:
      data-read:
        - Effect: Allow
          Action: ['dynamodb:GetItem', 'dynamodb:Query']
          Resource:
            Fn::GetAtt: [UsersTable, Arn]
      cache-write:
        - Effect: Allow
          Action: ['elasticache:DescribeCacheClusters']
          Resource: '*'

functions:
  listUsers:
    handler: src/handler.list
    iamRoleStatementsTemplate: data-read # reused from the template

Functions can still add their own iamRoleStatements: array — the resolved statement list is [log, auto-perms, inherited?, template, function-specific].

CLI commands

# Dry-run: list the per-function roles a deploy would create
sls iam preview

# Audit: list functions falling back to the global role
sls iam audit
sls iam audit --strict     # exit non-zero if any function lacks iamRoleStatements

# Validate every iamRoleStatements block against the strict grammar
sls iam validate
sls iam validate --strict-wildcard-action
sls iam validate --strict-wildcard-resource
sls iam validate --warnings-as-errors

# Summary: how many functions have per-function roles
sls iam status

iam preview works without an AWS account — it clones the compiled CloudFormation template, runs the role-builder, then restores the original.

Configuration reference

custom.interlaceIamRolesPerFunction

| Key | Type | Default | Description | | ------------------------------ | -------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | defaultInherit | boolean | false | If true, every function with iamRoleStatements also includes provider.iam.role.statements. Per-function override via iamRoleStatementsInherit. | | iamGlobalPermissionsBoundary | string \| Fn::Sub \| Fn::ImportValue | — | Permissions boundary applied to the global role and any per-function role that doesn't set its own. | | suppressGlobalRole | boolean | false | Remove the broad IamRoleLambdaExecution role from the template — only safe when every function has its own role. | | requirePerFunctionRoles | boolean | false | Fail the deploy if any function is missing iamRoleStatements. Set iamRoleStatements: [] on intentionally-empty functions. | | statementTemplates | Record<string, IamStatement[]> | {} | Named base policies referenced by iamRoleStatementsTemplate. |

Per-function (under functions.<name>)

| Key | Type | Description | | --------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | iamRoleStatements | IamStatement[] | Per-function policy statements. Triggers per-function role creation. | | iamRoleStatementsInherit | boolean | Override defaultInherit for this function. | | iamRoleStatementsName | string | Custom role name (max 64 chars; will be truncated by stripping the Lambda suffix if needed). | | iamRoleStatementsTemplate | string | Name of a template defined under custom.…statementTemplates. | | iamPermissionsBoundary | string \| Fn::Sub \| Fn::ImportValue | Permissions boundary for this function's role only. | | iamManagedPolicies | string[] | List of managed-policy ARNs to attach to the function's role. AWS-managed VPC policy is auto-attached when the function has VPC config. |

How it works

  1. before:package:finalize — runs after Serverless compiles the CloudFormation template.
  2. For each function with iamRoleStatements, clone the global role into a new resource named <NormalizedFunctionName>IamRoleLambdaExecution.
  3. Replace its Policies[0].PolicyDocument.Statement with [log, auto-perms, inherited?, template, function-specific].
  4. Update the AWS::Lambda::Function resource's Role.Fn::GetAtt[0] and DependsOn to point at the new role.
  5. Update any AWS::Lambda::EventSourceMapping resources to depend on the new role.
  6. If suppressGlobalRole: true and every function has its own role, delete the IamRoleLambdaExecution resource.

The plugin runs entirely at synth time. Nothing is invoked at deploy or runtime.

Validation rules

The strict statement validator (sls iam validate) catches what the community plugin's presence-only check misses:

  • Effect must be exactly "Allow" or "Deny" (community plugin only checks for non-empty).
  • Action and NotAction are mutually exclusive (Resource and NotResource likewise).
  • Action strings should match service:action (warning) or * (warning by default; promote with --strict-wildcard-action).
  • Sid must match ^[A-Za-z0-9]+$ per the AWS spec.
  • Wildcard Resource is a warning by default (some services legitimately need it; promote with --strict-wildcard-resource).

Documentation

License

MIT — © Ofri Peretz