Kensio Software Blog »

@kensio/yulin v0.34.2 adds simulated IAM

Hugh Grigg | Kensio Software | Friday 24 Jul 2026

Version v0.34.2 of the @kensio/yulin package adds a simulated IAM service for local development and isolated testing.

The simulator supports Roles with CreateRoleCommand and Users with CreateUserCommand, plus inline policies with PutRolePolicyCommand and PutUserPolicyCommand.

Managed policies are created with CreatePolicyCommand and attached with AttachRolePolicyCommand.

Sim IAM evaluates those policies into allow/deny decisions. Simulated STS issues temporary Role sessions with AssumeRoleCommand, after checking the Role’s trust policy.

Creating a Role with an inline policy and authorizing an action against it looks much the same as it does against the real AWS SDK:

import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
import { SimAws } from "@kensio/yulin";

const simAws = new SimAws();
const simIam = simAws.account("123456789012").iam();

const createRoleOutput = await simIam.createRole(
  new CreateRoleCommand({
    RoleName: "ExampleReaderRole",
    AssumeRolePolicyDocument: JSON.stringify({
      Version: "2012-10-17",
      Statement: {
        Effect: "Allow",
        Principal: { AWS: "arn:aws:iam::123456789012:root" },
        Action: "sts:AssumeRole",
      },
    }),
  }),
);

await simIam.putRolePolicy(
  new PutRolePolicyCommand({
    RoleName: "ExampleReaderRole",
    PolicyName: "ReadExampleObjects",
    PolicyDocument: JSON.stringify({
      Version: "2012-10-17",
      Statement: [
        {
          Effect: "Allow",
          Action: "s3:GetObject",
          Resource: "arn:aws:s3:::example-bucket/*",
        },
        {
          Effect: "Deny",
          Action: "s3:GetObject",
          Resource: "arn:aws:s3:::example-bucket/protected/config.json",
        },
      ],
    }),
  }),
);

const decision = simIam.authorize({
  action: "s3:GetObject",
  resource: "arn:aws:s3:::example-bucket/public/config.json",
  caller: { kind: "arn", arn: createRoleOutput.Role.Arn },
});

console.log(decision.isAllowed);

The authorize() method returns a decision object rather than throwing, so a test can assert on why a request was allowed or denied.

The decision exposes value as "Allow", "ExplicitDeny" or "ImplicitDeny", along with the statements that matched and the resolved caller.

An explicit Deny in any evaluated policy wins. Otherwise a matching Allow in an identity or resource policy allows the request, and anything else is an implicit deny.

The part that matters most for system tests is that the other simulated services authorize their own actions through sim IAM.

That covers S3, Route53, DynamoDB, ACM, CloudFront and Lambda actions, including s3:GetObject, route53:CreateHostedZone, dynamodb:PutItem and lambda:InvokeFunction.

Sim IAM and sim CloudFormation authorize their own control plane commands as well, so iam:CreateRole and cloudformation:CreateStack can be denied for a caller that is not allowed to make them. Those commands take an optional caller:

import { GetObjectCommand } from "@aws-sdk/client-s3";

const simS3 = simAws.account("123456789012").region("eu-west-2").s3();

const output = await simS3.getObject(
  new GetObjectCommand({
    Bucket: "example-bucket",
    Key: "public/config.json",
  }),
  {
    caller: { kind: "arn", arn: createRoleOutput.Role.Arn },
  },
);

Reading protected/config.json with that same caller throws an AWS-like access denied error with a 403 status code, before the simulated S3 service looks the Object up at all.

Omitting the caller defaults to the Account root, which is allowed within its own Account, so existing tests that never mention IAM keep working.

S3 Bucket policies are supplied to sim IAM as resource policies for the request, so a Bucket policy can allow or deny a read that identity policies say nothing about.

Policy conditions are supported for the StringEquals, StringLike and NumericLessThanEquals operators, including the ForAllValues: and ForAnyValue: set variants of the string operators. Condition values such as S3 Object tags are supplied by the service handling the request.

Access keys created with CreateAccessKeyCommand are registered with the simulated Account, so a caller can be given as credentials rather than an ARN.

Those credentials are authenticated before any policies are evaluated, as are the temporary session credentials returned by simulated STS.

Sim CloudFormation can create IAM resources too, so Roles and policies can come from the same template as the rest of the infrastructure:

{
  "Resources": {
    "ExampleRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "RoleName": "ExampleReaderRole",
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": {
            "Effect": "Allow",
            "Principal": {
              "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
          }
        },
        "Policies": [
          {
            "PolicyName": "ReadExampleObjects",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": {
                "Effect": "Allow",
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::example-bucket/*"
              }
            }
          }
        ]
      }
    }
  }
}

AWS::IAM::Role, AWS::IAM::ManagedPolicy and AWS::IAM::Policy are supported.

An AWS::IAM::Policy puts its document onto each Role named in Roles as an inline policy, which is the shape CDK grants such as bucket.grantRead(fn) synthesise into.

IAM Users and Groups are not simulated as policy principals, so a template naming them fails deployment rather than dropping the grant.

Sim IAM implements the policy behaviour that multi-service tests most commonly need rather than full IAM parity.

Permissions boundaries, session policies and service control policies are not evaluated. Managed policies have a single version, and deleting or detaching Roles, Users, policies and access keys is not supported yet.

IAM is usually the last part of an AWS system to get any testing. Tests mock the SDK call away, so the policies themselves are only exercised once everything is deployed.

That is a slow way to find out that a Role is missing a permission, or that a wildcard was broader than it looked.

This is why I think IAM is one of the more useful things to simulate. A test can deploy the real template, act as a specific Role, and assert that the read it should not be allowed to make is actually denied, in the same run as the rest of the system tests.

The IAM simulator integrates with the rest of Yulin, so S3, Route53, DynamoDB, CloudFront, Lambda, CloudFormation and CDK can all participate in the same simulated environment for local development and system tests.