I set up AWS DevOps Agent a while back across our AWS Organizations environment — a central account running the Agent Space, with development and production connected to it — and did the IAM side in Terraform rather than clicking through the console.
This is the walkthrough I wanted when I started: what the moving parts actually are, the order to do them in, and the four or five places where the documented happy path doesn't match what happens.
If you just want the official steps, the AWS user guide is good and I'll link the relevant pages as I go. What follows is that path plus the friction.
What AWS DevOps Agent is, briefly
It went generally available at the end of March 2026. Two halves: release management (still preview — code review, release readiness, autonomous testing) and production operations, which is the part I actually use.
Production operations does four things: investigates incidents when an alert arrives, produces mitigation plans, surfaces preventative recommendations from patterns across your incident history, and answers on-demand SRE questions conversationally.
The useful mental model isn't "AI in the AWS console." It's a read-only correlation layer over infrastructure you already have instrumented. During an incident I normally open CloudWatch, RDS metrics, EC2, the EKS console, load balancer health, Logs Insights, and deployment history — and only then start thinking. The agent collapses the collecting part. It doesn't generate telemetry, and if your observability is thin it just gets you to an incomplete picture faster.
Three concepts you need before any of the setup makes sense:
Agent Space — the container and the access boundary. It defines which AWS accounts, which integrations, and which users the agent can reach. Investigation history and chat history are isolated per Agent Space. Agent Spaces are regional.
Topology — what the agent builds after you connect an account. It's a graph of your resources and their relationships, and it's what lets the agent reason about blast radius instead of just reading metrics.
IAM roles — how it reaches anything at all. The agent never uses your credentials. It assumes roles you create, using the service principal aidevops.amazonaws.com.
One structural thing that confused me for the first ten minutes: there are two consoles. Administrators configure Agent Spaces in the AWS Management Console. Operators run investigations in a separate web app, with its own IAM role and its own auth flow. So you're creating two roles before you've connected a second account.
Prerequisites
Before you start clicking:
A supported Region. Six of them: N. Virginia, Oregon, Frankfurt, Ireland, Sydney, Tokyo. The Agent Space stores its data in the Region where you create it, so pick with data residency in mind — this isn't easily changed later.
IAM permissions to create roles, in every account you plan to connect. The console's auto-create option needs this in the primary account; secondary accounts need it wherever you create the cross-account role.
iam:PassRole in the primary account — only needed if you're connecting secondary accounts, but get it in place early. More on why below, because this one produces a genuinely confusing error.
A decision about console vs. infrastructure-as-code. The console's auto-create path is the fastest way to a working Agent Space and it's what I'd use to evaluate the service. It's not what I'd want owning cross-account trust into production long-term. You can start with auto-create and move to IaC later; you can't easily reconstruct why a hand-clicked trust policy says what it says.
Awareness that this costs money. Pay-per-second, no commitment, billing started April 10, 2026. AWS Support customers get monthly credits scaled to their support tier. Worth checking what your tier includes before you turn it loose on a large estate.
Step 1 — Create the Agent Space
In the AWS DevOps Agent console, choose Create Agent Space. You give it a name, an optional description, and an optional response language (it defaults to matching the language of your input).
Then two sections that both create IAM roles, and this is the real decision point of the whole setup.
"Give this Agent Space AWS resource access" — the role the agent uses to investigate resources in the primary account. Three options: auto-create a role, assign an existing role, or build one from a policy template. Auto-create is the recommended default.
"Enable web app" — the operator role, for the separate web app where investigations actually happen. Same three options.
Then Create.
The moment the space exists, it starts scanning the account for resources and relationships. That takes a few minutes. Once it's done, Operator access appears on the Agent Space details page and opens the web app in a new tab.
What I'd do differently on a second run: use auto-create for the web app role and a pre-created role for resource access. The web app role is boring and account-local. The resource access role is the one that matters, the one you'll want to reason about later, and the one whose ARN you'll need again for anything you connect later.
Step 2 — Understand the IAM before you accept it
Auto-create is convenient and I'd still recommend it for a first look, but you should know what it's building, because the same three components appear in every account you ever connect.
The trust policy
The role trusts the AWS DevOps Agent service principal directly:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "aidevops.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "<PRIMARY_ACCOUNT_ID>"
},
"ArnLike": {
"aws:SourceArn": "arn:aws:aidevops:<REGION>:<PRIMARY_ACCOUNT_ID>:agentspace/*"
}
}
}
]
}
Those two conditions are the entire security story. aws:SourceAccount and aws:SourceArn are confused deputy prevention — without them, the service principal is a wide-open door, because aidevops.amazonaws.com is the same principal in every AWS account on earth. The conditions are what say "only an Agent Space in my account may use this role."
The operator role's trust policy is nearly identical but needs sts:TagSession alongside sts:AssumeRole, because the web app scopes access using a session tag (aws:PrincipalTag/AgentSpaceId). If you're hand-building that one and it fails, missing TagSession is the reason.
The permissions
Attach the AWS-managed AIDevOpsAgentAccessPolicy for resource access — read-only permissions for discovery, configuration and metric reads, and log analysis. The operator role gets AIDevOpsOperatorAppAccessPolicy instead.
The bit everyone forgets
A small inline policy allowing iam:CreateServiceLinkedRole, scoped to the Resource Explorer service-linked role:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCreateServiceLinkedRoles",
"Effect": "Allow",
"Action": ["iam:CreateServiceLinkedRole"],
"Resource": [
"arn:aws:iam::<ACCOUNT_ID>:role/aws-service-role/resource-explorer-2.amazonaws.com/AWSServiceRoleForResourceExplorer"
]
}
]
}
This is what lets the agent bootstrap Resource Explorer in the account. Skip it and the setup still succeeds — you just get a much thinner topology and no obvious error telling you why. It goes on every role, in every account, primary and secondary.
Why it matters: the agent discovers resources two ways — by walking CloudFormation stacks, and via Resource Explorer for everything else. If you're a Terraform shop, that second path is doing nearly all the work, because none of your infrastructure is in CloudFormation. Resource Explorer largely sets itself up once this policy is in place, and it indexes off tags, so inconsistent tagging gives you an inconsistent topology. Give it time before you panic: tagged resources show up within minutes, untagged ones can take up to two hours.
Step 3 — Connect the primary AWS account
If you used the console flow, this already happened — creating the Agent Space with a resource access role associates the primary account for you.
If you're doing it via CLI or IaC, it's an explicit association, and the thing to notice is accountType:
aws devops-agent associate-service \
--agent-space-id <AGENT_SPACE_ID> \
--service-id aws \
--configuration '{
"aws": {
"assumableRoleArn": "arn:aws:iam::<PRIMARY_ACCOUNT_ID>:role/<AGENT_SPACE_ROLE>",
"accountId": "<PRIMARY_ACCOUNT_ID>",
"accountType": "monitor"
}
}' \
--region <REGION>
monitor is the primary account that hosts the Agent Space. source is every additional account. You'll use source in step 5.
Step 4 — Verify the primary account works
Three checks, in increasing order of how much I trust them.
One — the association exists:
aws devops-agent list-associations \
--agent-space-id <AGENT_SPACE_ID> \
--region <REGION>
Two — the topology has something in it. Open the web app via Operator access, go to the Topology page, and switch to the System view, which shows account and Region boundaries. Then All Resources to see whether discovery actually found your estate. Empty here means Resource Explorer, tags, or patience — in that order.
Three — ask it something only a working connection can answer. Not "what can you do," but something specific to a real resource: "list the RDS instances in this account and their instance classes." If it answers with your actual inventory, the role works. This is the only check I fully trust, because the first two can pass while the agent still can't read anything useful.
Step 5 — Connect additional AWS accounts
This is where most real deployments end up, because applications rarely live in one account.
The trust model
The important thing to understand — and it's not obvious from the console flow — is that the agent does not assume a role in the primary account and then chain into the second account. The AWS DevOps Agent service principal assumes the role in the secondary account directly:
AWS DevOps Agent (aidevops.amazonaws.com)
│
│ sts:AssumeRole
├──────────────► DevOpsAgentRole (Secondary Account A)
│ ├── AIDevOpsAgentAccessPolicy
│ └── inline: Resource Explorer SLR
│
└──────────────► DevOpsAgentRole (Secondary Account B)
├── AIDevOpsAgentAccessPolicy
└── inline: Resource Explorer SLR
There's no role in the middle. What restricts it is the trust policy conditions — the same aws:SourceAccount and aws:SourceArn pair, pointing at the primary account and its Agent Space.
The console path
Agent Space → Capabilities tab → Cloud section → Secondary sources → Add. You name the role, and the console hands you a trust policy and an inline policy. You take both to the IAM console in the target account, create a custom-trust-policy role, paste the trust policy, attach AIDevOpsAgentAccessPolicy, name the role exactly what you told the console it would be called, create it, then add the inline policy. Back in the primary account, choose Next and confirm the status shows Active.
It works. It's also a lot of clipboard for something that grants cross-account read access to production.
The gotcha that will cost you twenty minutes
The principal adding a secondary account needs iam:PassRole in the primary account.
This fails with an HTTP 403 AccessDeniedException naming iam:PassRole — even if that principal already has full `aidevops:`*. And the fix has a trap in it:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::<PRIMARY_ACCOUNT_ID>:role/*",
"Condition": {
"StringEquals": { "iam:PassedToService": "aidevops.amazonaws.com" }
}
}
]
}
The Resource must be the role wildcard. A policy scoped to specific role ARNs does not satisfy the check — the check itself is performed against the wildcard. This is counterintuitive if you've been trained to scope everything, and it's the kind of thing you'll spend a while assuming you got the ARN wrong.
Also: the check re-runs every time you update a secondary account, not just when you add one. Already-connected accounts keep working until the next update, so this can surface weeks later.
My setup: one Agent Space, Dev and Prod
Here's how it looks in my environment:
Organization / Management Account
│
AWS DevOps Agent Agent Space
│
┌────────┴────────┐
│ │
Development Production
Account Account
One Agent Space in a central account, with dev and prod connected as secondary sources. Each target account has its own role — not a shared one — so permissions can diverge later, the two show up as distinct principals in CloudTrail, and revoking one environment means deleting one role.
Why one Agent Space instead of one per environment
I could have built two. AWS's documentation actually nudges you that way — it lists "environment isolation: separate production from non-production" as a reason to create multiple Agent Spaces.
I went with one because a lot of my real investigations don't respect the account boundary. "This started after last night's deploy" begins in dev and lands in prod. "Why is prod behaving differently under the same query pattern?" is inherently a comparison. Two Agent Spaces would put me back to being the integration layer between two tools that can't see each other — which is exactly the problem I was trying to reduce.
Secondary benefits: one place to look, one topology, integrations configured once instead of per environment, and adding the next account is three Terraform resources rather than a new setup project. Account separation is completely untouched — dev and prod are still separate AWS accounts with separate roles.
The trade you're making, stated plainly: operator access and investigation data are both scoped per Agent Space, not per connected account. Anyone who can open the web app can ask about every account in that space. There's no "dev-only operator" tier inside a space. Concurrency is shared too — the defaults are 3 concurrent investigations and 10 concurrent chat invocations per space, both adjustable.
For me that's fine, because web app access is restricted to the DevOps team and that team already holds production authority. Developers don't get an operator login. When the set of people allowed to investigate dev is identical to the set allowed to investigate prod, per-environment isolation is guarding a boundary that doesn't exist in the org chart.
So the decision rule isn't "always separate prod." It's "do the same humans investigate both?" If developers self-serve investigations in dev while a separate group owns prod, split the spaces — the isolation is doing real work there.
Why I did the IAM in Terraform
Cross-account trust into production, created by hand from a clipboard, with no diff and no review, is the kind of thing that's correct on day one and mysterious on day ninety.
The shape is one aliased provider per target account and three resources in each:
provider "aws" {
alias = "dev"
region = "eu-central-1"
assume_role { role_arn = "arn:aws:iam::<DEV_ACCOUNT_ID>:role/<DEPLOY_ROLE>" }
}
provider "aws" {
alias = "prod"
region = "eu-central-1"
assume_role { role_arn = "arn:aws:iam::<PROD_ACCOUNT_ID>:role/<DEPLOY_ROLE>" }
}
locals {
agent_space_account = "<MANAGEMENT_ACCOUNT_ID>"
agent_space_arn = "arn:aws:aidevops:eu-central-1:<MANAGEMENT_ACCOUNT_ID>:agentspace/<AGENT_SPACE_ID>"
}
Alias every provider and leave no default. An unaliased default provider in a config that touches production IAM means any resource where someone forgets the provider argument silently lands wherever the default points.
Then, per account:
data "aws_caller_identity" "dev" { provider = aws.dev }
data "aws_iam_policy" "devops_agent_access_dev" {
provider = aws.dev
name = "AIDevOpsAgentAccessPolicy"
}
resource "aws_iam_role" "devops_agent_dev" {
provider = aws.dev
name = "DevOpsAgentRole-AgentSpace-dev"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "aidevops.amazonaws.com" }
Action = "sts:AssumeRole"
Condition = {
StringEquals = {
"aws:SourceAccount" = local.agent_space_account
"aws:SourceArn" = local.agent_space_arn
}
}
}]
})
}
resource "aws_iam_role_policy_attachment" "devops_agent_access_dev" {
provider = aws.dev
role = aws_iam_role.devops_agent_dev.name
policy_arn = data.aws_iam_policy.devops_agent_access_dev.arn
}
resource "aws_iam_role_policy" "devops_agent_dev_slr" {
provider = aws.dev
name = "AllowCreateResourceExplorerServiceLinkedRole"
role = aws_iam_role.devops_agent_dev.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowCreateServiceLinkedRoles"
Effect = "Allow"
Action = ["iam:CreateServiceLinkedRole"]
Resource = [
"arn:aws:iam::${data.aws_caller_identity.dev.account_id}:role/aws-service-role/resource-explorer-2.amazonaws.com/AWSServiceRoleForResourceExplorer"
]
}]
})
}
Two details worth copying:
StringEquals on aws:SourceArn, not ArnLike with agentspace/*. The docs use the wildcard because on the primary role you have a chicken-and-egg problem — the space doesn't exist yet. For secondary accounts it already does, so pin the exact ARN. It costs nothing, and it means a second Agent Space created in that account tomorrow, by someone else, cannot reach production.
data.aws_caller_identity instead of a hardcoded account ID in the service-linked-role ARN. It has to resolve to the account the role lives in. Paste the number by hand and a future provider change gives you a policy pointing at the wrong account and an SLR failure with an unhelpful error.
The prod block is the same three resources against aws.prod. Right now that's literal duplication; the module refactor happens when the third account arrives. Two copies didn't justify the indirection.
What this buys: the trust relationship is reviewable in a pull request, drift is visible, and there's a git history explaining why production trusts one specific Agent Space ARN. The associations can be code too — there are awscc provider resources for the agent space and its associations.
Running a first real investigation
Once both accounts were connected, the test I used was a question I've genuinely asked during incidents: "Why did database connections drop around 14:30 yesterday?"
The manual version of that investigation:
CloudWatch alarm history
↓
RDS connection + CPU metrics for the window
↓
application logs in CloudWatch Logs Insights
↓
deployment history — did anything ship?
↓
security groups / subnets / NAT — did networking change?
↓
CloudTrail — did anyone change a config?
Six places, each requiring me to hold the previous answer in my head while navigating to the next. Roughly twenty minutes before I have a hypothesis.
The agent-assisted version is the same investigation with the fetching collapsed. I describe the symptom and the window; it pulls the metrics, checks the topology for what's connected to that database, looks at deployment data if a pipeline is connected, and returns a correlated picture with its reasoning trail.
What you get is a starting hypothesis and the evidence behind it — not a verdict. Sometimes it lands on the cause. Sometimes it surfaces a correlation that turns out to be coincidental, and I only catch that because I know the system. It has never removed the need for me to understand what a connection pool is.
Every reasoning step lands in an immutable agent journal, and API calls land in CloudTrail. That matters practically, not just for compliance: an answer you can't audit is an answer you can't act on at 3am.
Where it's earned the most in my day-to-day:
RDS — CPU spikes, connection swings, memory pressure, whether utilisation matches the instance class. The win isn't that it knows something I don't; it's that "pull these five metrics over this window and tell me what's unusual" is one sentence instead of five console navigations.
EKS — because Kubernetes incidents so often aren't Kubernetes incidents. A pod that can't reach a database has a problem living in RDS, a security group, a route table, or an IAM policy. Asking about the pod and the AWS-side dependency in one conversation removes a real context switch. One caveat if you're following along: EKS needs an extra step beyond the IAM role above — an access entry on the cluster for the agent's role — which I'll cover in a separate post.
Resource sizing, carefully. AWS documents preventative recommendations spanning observability, infrastructure optimisation, pipelines, and resilience. I use the agent as one input when asking whether something looks oversized — it assembles the utilisation picture fast. It is not a rightsizing engine, and I still cross-check against Compute Optimizer, Cost Explorer, and actual knowledge of the workload's traffic shape.
Things I learned, and things to watch
Nothing in my stack got turned off. Prometheus and Grafana still hold my dashboards. CloudWatch still holds the AWS-native signal. Alerting still fires, logs and traces are where they were, Terraform still owns the infrastructure, kubectl is still open in a terminal. The agent reads what already exists — it's a correlation layer over your observability, not a replacement for it. Getting that ordering backwards is the most reliable way to be disappointed.
Onboard dev first and live with it. Same reason you don't test a restore for the first time during an outage.
Decide who can open the web app before you connect production, not after. In a shared Agent Space this is the control, because operator access is per-space. I wired the operator app to our corporate identity provider through the external IdP flow — Microsoft Entra ID — so access follows the same directory groups and MFA policy as everything else, and offboarding someone removes their agent access with their email. IAM Identity Center is the other first-class option; raw IAM authentication links with their 10-minute sessions are the fallback. That setup has enough moving parts that it deserves its own post, which I'll write separately.
PII in logs is your problem. AWS states PII is not automatically filtered and recommends redaction. If your application logs contain customer data, the agent can read it during log analysis. For anyone in a regulated industry, that's a conversation to have before onboarding, not after.
Think about where the Agent Space lives. Mine sits in the central org account, which is convenient and which I'd revisit. AWS's own guidance leans toward a dedicated account as primary with application accounts attached as secondaries, and general Organizations practice keeps workloads out of the management account. Designing from scratch, I'd put it in a dedicated tooling account.
Removing a secondary account does not delete the IAM role. Cleanup is on you — or on Terraform, which is one more argument for managing it as code.
Check the quotas against your reality. 3 concurrent investigations and 10 concurrent chat invocations per Agent Space by default, both adjustable. 100 Agent Spaces per account per Region. Fine for most teams, worth a look if you're planning something large.
Worth it?
Setup is genuinely about an hour for a single account, and maybe a couple of hours for a multi-account setup done properly in Terraform with the IAM reviewed rather than clicked.
What I'd tell someone evaluating it: the value shows up in the first ten minutes of an investigation, and drops off sharply after that. The mechanical phase — open the consoles, pull the metrics, line up timestamps, check the deploys — compresses. The reasoning phase doesn't, and shouldn't.
I don't think AI agents reduce how much AWS, Kubernetes, networking, or database internals you need to know. If anything they raise it, because the new skill is judging whether the agent's reasoning holds up. A confident, wrong correlation is only dangerous to someone who can't tell.
But the evidence-gathering phase was always the least valuable part of the job, and it's the first part that's turned out to be automatable. That's a smaller claim than the marketing makes, and it's the one I'd actually stand behind.
Top comments (0)