This module provisions a production-ready AWS DevOps Agent Space with the IAM roles, account associations, and security controls needed to run autonomous SRE investigations across a multi-account AWS estate. It includes:
- Agent Space Provisioning: Full Agent Space lifecycle managed via the
awsccprovider - IAM Role Management: AgentSpace and Operator App roles with least-privilege AWS-managed policies
- Primary Account Hardening: Explicit deny policy preventing the agent from indexing resources in the hosting account — defence in depth enforced unconditionally via IAM, not tags
- Multi-Account Associations: Hub-and-spoke model with one primary (monitor) association and N secondary (source) workload account associations
- Region Validation: Input validation enforcing deployment only to regions where the
aidevopsservice is available - IAM Propagation Safety: 30-second wait injected before Agent Space creation to avoid race conditions
Appvia's recommended pattern is one Agent Space per team or per product. An Agent Space is the primary security and operational boundary — it controls which accounts the agent can query, who can view investigations, and which integrations are in scope.
| Team | Agent Space | Account Visibility |
|---|---|---|
| Software Dev Team 1 | team1-devops-agent |
Only the accounts and resources that team owns |
| Software Dev Team 2 | team2-devops-agent |
Only the accounts and resources that team owns |
| Platform Team | platform-devops-agent |
All platform accounts — full horizontal view |
This model avoids cross-team data exposure, keeps investigation noise scoped per team, and means each team has independent service quota headroom (concurrent investigations, evaluations). A single AWS account can host multiple Agent Spaces.
The module is designed around a dedicated hosting account (hub) that owns all Agent Spaces, and one or more secondary/workload accounts (spokes) that the agent monitors. This mirrors the recommended AWS deployment model and keeps the hosting infrastructure cleanly separated from the workloads being investigated.
flowchart LR
subgraph Hub["Hosting Account — Hub"]
AS1["AgentSpace: team1-devops-agent"]
AS2["AgentSpace: platform-devops-agent"]
end
AS1 -->|"cross-account role"| WA["Workload Account A"]
AS1 -->|"cross-account role"| WB["Workload Account B"]
AS2 -->|"cross-account role"| WN["Network Account"]
AS2 -->|"cross-account role"| WS["Security Account"]
IAM layout per Agent Space:
| Account | Role | Policy | Purpose |
|---|---|---|---|
| Hub | DevOpsAgentRole-AgentSpace-<prefix> |
AIDevOpsAgentAccessPolicy + DenyPrimaryAccountDiscovery inline deny |
Agent investigation role; explicit deny blocks resource indexing of the hosting account itself |
| Hub | DevOpsAgentRole-WebappAdmin-<prefix> |
AIDevOpsOperatorAppAccessPolicy |
Operator web app authentication |
| Each spoke | DevOpsAgentCrossAccountRole |
AIDevOpsAgentAccessPolicy |
Agent read access to workload resources; trust scoped to the specific AgentSpace ARN |
The cross-account trust policy in each spoke references the exact agent_space_arn output from Phase 1 — a wildcard trust across all Agent Spaces in the hosting account is not used. Use the modules/spoke submodule (included in this module) to create this role in each workload account.
Cross-region monitoring: The AgentSpace region does not constrain which AWS regions the agent can query. Workload accounts in eu-west-2 are fully visible from an AgentSpace hosted in eu-west-1.
Note:
eu-west-2(London) is not a supported AgentSpace region. For UK gov, deploy the AgentSpace itself intoeu-west-1(Ireland) oreu-central-1(Frankfurt) — workloads ineu-west-2remain fully visible.
AWS supports restricting the agent via IAM tag conditions. This module deliberately does not use that approach:
- Tags are mutable. Anyone with
ec2:CreateTagscan remove or alter a tag and bring a resource into scope. IAMDenystatements cannot be overridden by any other attached policy — they are enforced unconditionally. - The hosting account is hard-excluded. An explicit
DenyPrimaryAccountDiscoveryinline policy denies allresource-explorer-2:*andtag:Get*actions on the agentspace role, regardless of whatAIDevOpsAgentAccessPolicypermits. Resource indexing is only intended for secondary workload accounts. - Defence in depth.
AIDevOpsAgentAccessPolicyis read-only by design (Describe*,Get*,List*across ~150 services — the only write action issupport:CreateCase). The inline deny is a second, independent enforcement layer.
At GA (March 2026), AWS confirmed EU-hosted Agent Spaces process inference within the EU — requests do not route through US infrastructure. For UK gov and EU workloads, deploy the AgentSpace into eu-west-1 (Ireland) or eu-central-1 (Frankfurt). Workload accounts in eu-west-2 (London) are fully visible from either region — cross-region monitoring is supported regardless of where the AgentSpace is hosted.
The agent is a read-and-report system. It cannot start/stop instances, execute shell commands, read S3 object content, read Secrets Manager values, invoke Lambda, or modify any IAM resource. The only outbound writes are support:CreateCase and posting findings to connected third-party tools (PagerDuty, ServiceNow, Slack).
On first apply, leave secondary_accounts empty. This creates the Agent Space and emits agent_space_arn as an output, which you need before configuring cross-account trust policies in secondary workload accounts.
module "devops_agent" {
source = "appvia/devops-agent/aws"
version = "0.1.0"
agent_space_name = "Platform Team"
agent_space_description = "DevOps Agent Space for the Platform Engineering team"
agentspace_region = "eu-west-1"
name_prefix = "platform"
tags = {
Team = "platform"
Environment = "prod"
ManagedBy = "terraform"
}
}
output "agent_space_arn" {
value = module.devops_agent.agent_space_arn
}After Phase 1, use the modules/spoke submodule to create the cross-account IAM role in each workload account. Each spoke is called with a provider alias pointing at that account. The agent_space_arn from Phase 1 is passed in directly — the spoke module extracts the hosting account ID from it automatically.
# Configure provider aliases — one per account
provider "aws" {
alias = "hosting"
# assume_role or profile for the hosting account
}
provider "aws" {
alias = "dev"
# assume_role or profile for the dev workload account
}
provider "aws" {
alias = "payments"
# assume_role or profile for the payments workload account
}
# Phase 2a — deploy spoke roles into each workload account
module "spoke_dev" {
source = "appvia/devops-agent/aws//modules/spoke"
version = "0.1.0"
providers = { aws = aws.dev }
agent_space_arn = "arn:aws:aidevops:eu-west-1:HOSTING_ACCOUNT_ID:agentspace/AGENT_SPACE_ID"
}
module "spoke_payments" {
source = "appvia/devops-agent/aws//modules/spoke"
version = "0.1.0"
providers = { aws = aws.payments }
agent_space_arn = "arn:aws:aidevops:eu-west-1:HOSTING_ACCOUNT_ID:agentspace/AGENT_SPACE_ID"
}
# Phase 2b — re-apply the hub with secondary_accounts populated from spoke outputs
module "devops_agent" {
source = "appvia/devops-agent/aws"
version = "0.1.0"
providers = {
aws = aws.hosting
awscc = awscc.hosting
}
agent_space_name = "Platform Team"
agent_space_description = "DevOps Agent Space for the Platform Engineering team"
agentspace_region = "eu-west-1"
name_prefix = "platform"
secondary_accounts = {
dev = {
account_id = "111122223333"
cross_account_role_arn = module.spoke_dev.role_arn
}
payments = {
account_id = "444455556666"
cross_account_role_arn = module.spoke_payments.role_arn
}
}
tags = {
Team = "platform"
Environment = "prod"
ManagedBy = "terraform"
}
}Two-phase apply is required. The spoke trust policy must reference the real
agent_space_arn, which only exists after Phase 1 completes. Hardcode the ARN from the Phase 1 output into each spoke module call, then apply Phase 2 — Terraform will create the spoke roles first (they have no dependency on the hub in Phase 2) and then update the hub associations.
module "devops_agent_team1" {
source = "appvia/devops-agent/aws"
version = "0.1.0"
agent_space_name = "Software Dev Team 1"
agentspace_region = "eu-west-1"
name_prefix = "team1"
secondary_accounts = {
team1-dev = { account_id = "111122223333", cross_account_role_arn = "arn:aws:iam::111122223333:role/DevOpsAgentCrossAccountRole" }
team1-prod = { account_id = "444455556666", cross_account_role_arn = "arn:aws:iam::444455556666:role/DevOpsAgentCrossAccountRole" }
}
tags = { Team = "team1", ManagedBy = "terraform" }
}
module "devops_agent_platform" {
source = "appvia/devops-agent/aws"
version = "0.1.0"
agent_space_name = "Platform Team"
agentspace_region = "eu-west-1"
name_prefix = "platform"
secondary_accounts = {
network = { account_id = "777788889999", cross_account_role_arn = "arn:aws:iam::777788889999:role/DevOpsAgentCrossAccountRole" }
security = { account_id = "000011112222", cross_account_role_arn = "arn:aws:iam::000011112222:role/DevOpsAgentCrossAccountRole" }
dev = { account_id = "111122223333", cross_account_role_arn = "arn:aws:iam::111122223333:role/DevOpsAgentCrossAccountRole" }
prod = { account_id = "444455556666", cross_account_role_arn = "arn:aws:iam::444455556666:role/DevOpsAgentCrossAccountRole" }
}
tags = { Team = "platform", ManagedBy = "terraform" }
}The agentspace_region variable is validated against the regions where the aidevops service is available:
| Region | Location |
|---|---|
us-east-1 |
US East (N. Virginia) |
us-west-2 |
US West (Oregon) |
eu-west-1 |
Europe (Ireland) |
eu-central-1 |
Europe (Frankfurt) |
ap-southeast-2 |
Asia Pacific (Sydney) |
ap-northeast-1 |
Asia Pacific (Tokyo) |
Note:
eu-west-2(London) is not a supported AgentSpace region. For UK gov data residency, deploy intoeu-west-1(Ireland) oreu-central-1(Frankfurt).
See the examples directory for complete usage examples:
- Hosting Account — AgentSpace Deployment — Phase 1: create AgentSpace
- Spoke — Basic Cross-Account Role — minimal workload account setup
- Spoke — ECS/Fargate with Permissions Boundary
- Spoke — EKS with Permissions Boundary
| Name | Version |
|---|---|
| terraform | >= 1.14.0 |
| aws | >= 6.0.0 |
| awscc | ~> 1.0 |
| time | ~> 0.9 |
| Name | Version |
|---|---|
| aws | >= 6.0.0 |
| awscc | ~> 1.0 |
| time | ~> 0.9 |
The terraform-docs utility is used to generate this README. Follow the below steps to update:
- Make changes to the
.terraform-docs.ymlfile - Fetch the
terraform-docsbinary (https://terraform-docs.io/user-guide/installation/) - Run
terraform-docs markdown table --output-file ${PWD}/README.md --output-mode inject .
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| agent_space_name | Display name of the DevOps Agent Space (shown in the console — spaces allowed) | string |
n/a | yes |
| agentspace_region | AWS region where the Agent Space is deployed. Supported regions: us-east-1, us-west-2, eu-west-1, eu-central-1, ap-southeast-2, ap-northeast-1. eu-west-2 (London) is not supported. | string |
n/a | yes |
| agent_space_description | Description for the DevOps Agent Space | string |
"AWS DevOps Agent Space" |
no |
| name_prefix | Short slug used in IAM role names — no spaces or special chars. Defaults to agent_space_name with spaces replaced by hyphens if not set. | string |
"" |
no |
| secondary_accounts | Map of workload accounts to associate as secondary sources. Key is a short label used in resource names. Leave empty on first apply; populate after capturing agent_space_arn. | map(object({ |
{} |
no |
| tags | Tags to apply to all resources | map(string) |
{} |
no |
| Name | Description |
|---|---|
| agent_space_id | ID of the DevOps Agent Space |
| agent_space_arn | ARN of the DevOps Agent Space — use this to scope the workload cross-account role trust policy |
| agent_space_name | Name of the DevOps Agent Space |
| agentspace_role_arn | ARN of the DevOps AgentSpace IAM role (assumed by the agent for resource indexing) |
| operator_role_arn | ARN of the DevOps Operator App IAM role (assumed by the agent to drive the web app) |
