HowTo give Kiro safe access to your AWS environment

Kiro is AWS’s agentic coding tool, available both as an IDE and a CLI. I’ve been using the CLI a lot lately, including for AWS work where I let it run aws commands on my behalf. It works well. But the moment you point it at a real AWS account from a laptop that already has prod credentials configured locally, there’s a gap nobody talks about.

You probably have a dozen profiles in ~/.aws/config. Setting up a dedicated IAM role for Kiro and pointing AWS_PROFILE at it doesn’t actually stop Kiro from running aws --profile prod-admin ec2 terminate-instances. The CLI reads the whole config file and that profile is right there. AWS_PROFILE only sets a default. It restricts nothing.

This post is CLI-first. The IDE works the same way for steps 1 and 2, and I’ll note the differences at the end. Production accounts only — if you’re playing in a sandbox, none of this matters much.

Step 1: A read-only IAM role for Kiro

Start from the AWS managed policy ReadOnlyAccess and strip what you don’t want the agent reading. At minimum, deny reads on Secrets Manager, SSM SecureString parameters, and anything storing customer data.

Use IAM Identity Center to access the role via SSO. No long-lived access keys. Credentials are short-lived, they expire on their own, and CloudTrail records the actual user who assumed the role.

A permission boundary on top of this role is a good idea, but out of scope for this post. The role’s permissions policy is your ceiling for now.

Step 2: Isolate Kiro’s AWS config from your shell’s

This is the part that matters.

The AWS CLI and SDK read ~/.aws/config and ~/.aws/credentials by default. Both file locations can be overridden with environment variables:

export AWS_CONFIG_FILE=~/.aws/kiro/config
export AWS_SHARED_CREDENTIALS_FILE=~/.aws/kiro/credentials

When these are set, the SDK ignores the default files entirely. Profiles that aren’t in the file you specified simply do not exist as far as aws is concerned. This is the only mechanism that actually prevents Kiro from seeing your other profiles. Filtering file reads at the agent level (which we’ll add in step 3) is not enough on its own, because the AWS SDK reads ~/.aws/config directly without going through the agent’s fs_read tool.

Create the isolated config. Put every Kiro profile you’ll need in here. Start with read-only and add a write role for the dev account. Add a prod read-only role too if you want Kiro to be able to look at prod without being able to change it:

# ~/.aws/kiro/config
[profile kiro-readonly]
sso_session = my-sso
sso_account_id = 123456789012
sso_role_name = KiroReadOnly
region = us-east-1

[profile kiro-dev-write]
sso_session = my-sso
sso_account_id = 123456789012
sso_role_name = KiroDevWrite
region = us-east-1

[profile kiro-prod-readonly]
sso_session = my-sso
sso_account_id = 999999999999
sso_role_name = KiroReadOnly
region = us-east-1

[sso-session my-sso]
sso_start_url = https://my-org.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access

Touch an empty credentials file alongside it so the SDK doesn’t fall back to anything else:

touch ~/.aws/kiro/credentials
chmod 600 ~/.aws/kiro/credentials

Now you need to make sure Kiro is launched with these env vars set, plus a default profile. Kiro doesn’t have a way to set env vars for its shell tool yet (kirodotdev/Kiro#40), so the vars have to be set in the parent process. The cleanest way I’ve found is a shell alias in ~/.zshrc:

alias kiro-aws='AWS_CONFIG_FILE=~/.aws/kiro/config \
  AWS_SHARED_CREDENTIALS_FILE=~/.aws/kiro/credentials \
  AWS_PROFILE=kiro-readonly \
  kiro-cli chat --agent aws-readonly'

Always launch Kiro for AWS work with kiro-aws instead of kiro-cli directly. From inside the session, ask it to run aws configure list-profiles. You should see only the kiro-* profiles. If prod-admin shows up, the isolation didn’t take.

This step alone takes Kiro from “could touch anything you have credentials for” to “physically cannot see your other accounts”.

Step 3: A custom agent and a steering document

Kiro CLI lets you define custom agents in ~/.kiro/agents/<n>.json with their own tool settings. The CLI has a dedicated use_aws tool that calls AWS APIs directly, separate from the general shell tool. Define a custom agent for AWS work:

{
  "name": "aws-readonly",
  "description": "AWS infrastructure assistant. Default read-only access via kiro-* profiles. Write operations require explicit user confirmation and a profile switch. Never attempt to use credentials outside the isolated AWS config.",
  "tools": [
    "read",
    "shell",
    "use_aws",
    "thinking",
    "grep",
    "glob",
    "delegate"
  ],
  "toolsSettings": {
    "shell": {
      "autoAllowReadonly": true
    }
  },
  "resources": [],
  "hooks": {},
  "includeMcpJson": true,
  "model": null
}

A few things going on here. The description field doubles as a system-level hint, so I put the key constraints right in there. I dropped write from the tools list — this agent is for AWS investigation, not editing your codebase. If you need file edits in the same session, add it back. delegate is included so Kiro can spin up sub-tasks for longer investigations (e.g., “check all Lambda functions in this account for public URLs”). The shell tool with autoAllowReadonly lets read-only shell commands run without prompting every time.

The isolated config from step 2 is doing the heavy lifting regardless. Since it only contains kiro-* profiles, any aws --profile something-else would just fail with “profile not found.” The whole point of isolating the config is so we don’t have to filter every shell command for safety.

The steering document lives at ~/.kiro/steering/AGENTS.md. This is where you give the agent enough context to make good decisions on its own, not just a list of don’ts:

# AWS Access Rules for Kiro

## Context

This environment uses isolated AWS credentials. The only profiles available
are in `~/.aws/kiro/config`, set via `AWS_CONFIG_FILE`. The standard
`~/.aws/config` is not visible. Do not assume other profiles exist.

## Available profiles

| Profile                | Account     | Access level | Use case                          |
|------------------------|-------------|--------------|-----------------------------------|
| `kiro-readonly`        | dev         | read-only    | Default. Inspecting resources.    |
| `kiro-dev-write`       | dev         | read-write   | Making changes in dev.            |
| `kiro-prod-readonly`   | production  | read-only    | Investigating prod issues.        |

`kiro-readonly` is the default (set via `AWS_PROFILE`). You do not need to
pass `--profile` for it.

## Rules

### Read operations
- `describe`, `get`, `list`, and similar read calls can run without asking.
- If you need to look at prod, use `--profile kiro-prod-readonly` and tell
  the user you're switching to the prod account before running the command.

### Write operations
- Any mutating call (`create`, `update`, `delete`, `put`, `modify`,
  `terminate`, `stop`, `start`, `reboot`, `invoke`) requires explicit
  user confirmation before execution.
- Before asking for confirmation, state: what you intend to do, which
  resources will be affected (ARN or name), and which profile you'll use.
- Only use profiles ending in `-write` for mutations. Never attempt a write
  call with a read-only profile.

### Credential boundaries
- If a call returns `AccessDenied` or `UnauthorizedAccess`, stop and ask
  the user. Do not:
  - Try a different profile hoping it has more permissions
  - Look for credentials in the filesystem
  - Suggest the user re-authenticate or assume a different role
  - Attempt to modify IAM policies to grant yourself access
- The permissions you have are intentional. Treat access errors as a signal
  to check with the user, not a problem to solve.

### General behavior
- When running multiple AWS commands in sequence, prefer combining related
  reads into a single investigation before presenting findings.
- If a task requires both read and write operations, do all the reads first
  with the read-only profile, present your plan, then switch to the write
  profile only after the user confirms.
- Always include `--output json` for programmatic parsing. Use `--output
  table` only when the user asks for a summary view.

The “credential boundaries” section is what prevents the drift behavior. Without it, when Kiro hits an AccessDenied it will try to find another way around it. The agent is helpful to a fault.

Step 4: Verify activity in CloudTrail

Every API call the agent makes shows up in CloudTrail, tied to the SSO user who assumed the role. The role session name looks something like KiroReadOnly/your_user or KiroDevWrite/your_user, so you can tell at a glance which role was used and by whom.

You can filter for this in the CloudTrail console under Event history by searching for the username KiroReadOnly (or whatever your role is named). No extra setup required — standard CloudTrail covers this.

If you have CloudTrail Lake set up, a saved query makes this even faster:

SELECT eventTime, eventName, requestParameters
FROM <your_event_data_store>
WHERE userIdentity.sessionContext.sessionIssuer.userName LIKE 'Kiro%'
  AND eventTime > current_timestamp - interval '24' hour
ORDER BY eventTime DESC

Either way, when something looks off in the account, you want CloudTrail to answer “what did the agent do” without having to ask the agent itself.

Working with write access

When you actually need Kiro to make changes, just tell it to use the write profile:

Use --profile kiro-dev-write to update the IAM policy attached to the lambda role.

The steering doc kicks in. Kiro asks for confirmation, explains what it’s about to do, and only then runs the command. The IAM role enforces what kiro-dev-write can actually touch, so even if the agent misreads your prompt the blast radius is bounded by the role’s permissions.

For prod writes, the same pattern. Make a KiroProdWrite IAM role with very narrow permissions, add a kiro-prod-write profile to the isolated config, and ask Kiro to use it explicitly when you need it. Roles are cheap. Blast radius is not.

A note on the IDE

The Kiro IDE doesn’t have a use_aws tool. All AWS commands go through the shell tool, which means the config isolation from step 2 and the steering document are your primary guardrails. Steps 1 and 2 apply identically: create the role, isolate the config, then launch the IDE from a terminal where the env vars are already exported so the IDE’s agent inherits them. Step 3’s custom agent JSON is CLI-only, but the steering doc at ~/.kiro/steering/AGENTS.md is read by both.

That’s it

The IAM role gets all the airtime, but the config isolation in step 2 is what actually keeps Kiro from drifting into the wrong account when you’re moving fast.

comments powered by Disqus