Giving Cursor Cloud Agent AWS Access with IAM Roles Anywhere
by Andrew Melbourne • 3/8/2026
Blog HomeRecently, the 8090 Software Factory team has been investing in harness engineering to automate more of our development process. Cursor's Cloud Agent is a core part of that: isolated VMs we can spin up to continuously build features, fix bugs, resolve CI failures, and pay down technical debt.
We're building Software Factory, a multi-tenant SaaS application. Its backend relies on Cognito for auth, S3 for uploads, and CodeArtifact for private Python packages. For the Cloud Agent to be useful beyond linting and unit tests — to actually run Software Factory, reproduce bugs, and query production logs — it needs AWS access.
Developers authenticate locally with aws sso login. CI uses GitHub Actions OIDC, where GitHub mints a token that AWS trusts directly. Neither maps cleanly to a Cloud Agent VM: we do not want agents performing developer SSO on a developer's behalf, and Cursor doesn't expose an OIDC identity for its VMs the way GitHub does for Actions runners.
Static access keys would work, but we explicitly avoid them because long-lived credentials are a security risk and depend on manual key rotation. We wanted short-lived credentials that expire automatically, without adding rotation overhead.
The solution we found was IAM Roles Anywhere. It lets workloads outside AWS get temporary credentials using X.509 certificates. You register a certificate authority with AWS, issue a client cert, and the workload exchanges that cert for short-lived STS credentials — conceptually similar to OIDC federation, but the trust is rooted in your CA instead of an identity provider.
We could not find clear, end-to-end guidance online for this exact Cursor Cloud setup. I spent a full day working with the Cursor Cloud onboarding agent at cursor.com/onboarding, and we tag-teamed the implementation by digging through AWS and Cursor docs together until we got it working. This article itself was written by the onboarding agent after successfully completing the process.
Note: For security, the AWS account number and exact permission scopes in the examples below have been modified for this article.
Setup
1. Generate certificates
A self-signed CA works fine. No need for AWS Private CA.
# CA certificate (the important bit: CA:TRUE)
openssl req -x509 -newkey rsa:4096 \
-keyout ca-key.pem -out ca-cert.pem \
-days 1825 -nodes \
-subj "/CN=MyAgentCA/O=MyOrg" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
# Client certificate signed by that CA
openssl req -newkey rsa:2048 \
-keyout client-key.pem -out client.csr \
-nodes -subj "/CN=cursor-agent/O=MyOrg"
openssl x509 -req -in client.csr \
-CA ca-cert.pem -CAkey ca-key.pem \
-CAcreateserial -out client-cert.pem -days 365
2. Create AWS resources
# Register CA as a trust anchor
aws rolesanywhere create-trust-anchor \
--name "cursor-agent-ca" \
--source "sourceType=CERTIFICATE_BUNDLE,sourceData={x509CertificateData=$(cat ca-cert.pem)}" \
--enabled --region us-west-2
# IAM role — trust only this specific trust anchor
aws iam create-role --role-name CursorAgentRole \
--assume-role-policy-document '{
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "rolesanywhere.amazonaws.com"},
"Action": ["sts:AssumeRole", "sts:TagSession", "sts:SetSourceIdentity"],
"Condition": {
"ArnEquals": {"aws:SourceArn": "<trust-anchor-arn>"}
}
}]
}'
# Scope permissions to what the app actually needs
aws iam put-role-policy --role-name CursorAgentRole \
--policy-name AgentPolicy --policy-document '{
"Statement": [
{
"Effect": "Allow",
"Action": ["cognito-idp:ListUsers", "cognito-idp:AdminGetUser"],
"Resource": "arn:aws:cognito-idp:us-west-2:123456789012:userpool/*"
},
{
"Effect": "Allow",
"Action": ["logs:GetLogEvents", "logs:FilterLogEvents"],
"Resource": "arn:aws:logs:us-west-2:123456789012:log-group:/ecs/my-app:*"
}
]
}'
# Link trust anchor to role
aws rolesanywhere create-profile \
--name "cursor-agent" \
--role-arns "arn:aws:iam::123456789012:role/CursorAgentRole" \
--enabled --region us-west-2
3. Configure the Cloud Agent VM
Add four Cursor secrets: the CA cert, client cert, private key, and a JSON blob with the three ARNs.
On VM startup, the update script installs AWS's aws_signing_helper and writes a credential_process config:
curl -sL "https://rolesanywhere.amazonaws.com/releases/1.7.3/X86_64/Linux/Amzn2023/aws_signing_helper" \
-o /usr/local/bin/aws_signing_helper && chmod +x /usr/local/bin/aws_signing_helper
mkdir -p ~/.aws
cat > ~/.aws/config <<EOF
[default]
region = us-west-2
credential_process = /usr/local/bin/aws_signing_helper credential-process \
--certificate /tmp/cert.pem \
--private-key /tmp/key.pem \
--intermediates /tmp/ca-cert.pem \
--trust-anchor-arn $TRUST_ANCHOR_ARN \
--profile-arn $PROFILE_ARN \
--role-arn $ROLE_ARN
EOF
That's it. Every boto3 call and aws CLI command now gets temporary credentials automatically. No application code changes.
Gotchas
We hit several issues that aren't obvious from the docs.
CA:TRUE is mandatory. The default openssl req -x509 doesn't add Basic Constraints. Roles Anywhere rejects the CA cert with Incorrect basic constraints for CA certificate. Add -addext "basicConstraints=critical,CA:TRUE".
You must send the certificate chain. Even though the trust anchor has your CA cert, the signing helper doesn't include it in requests by default. Without --intermediates ca-cert.pem, you get Untrusted certificate. Insufficient certificate. This error message gives no hint that the missing chain is the problem.
PEM newlines collapse in Cursor secrets. Pasting a PEM cert into the secrets panel turns newlines into spaces. The signing helper can't parse it. We reconstruct proper PEM before writing to disk:
import re, textwrap
m = re.search(r'(-----BEGIN [^-]+-----)\s*(.*?)\s*(-----END [^-]+-----)', raw, re.DOTALL)
header, body, footer = m.group(1), m.group(2), m.group(3)
body = body.replace(" ", "").replace("\n", "")
pem = header + "\n" + "\n".join(textwrap.wrap(body, 64)) + "\n" + footer + "\n"
Certs and trust anchor must match exactly. If you regenerate certs but reuse an old trust anchor, the CA fingerprints won't match. Same "Untrusted certificate" error, different root cause. Always provision everything in one pass.
How we structured it
We version-control the setup alongside other Cursor config:
.cursor/
├── cloud-agent/
│ ├── setup_vm.sh # Entry point
│ ├── setup_aws_credentials.sh # Roles Anywhere config
│ ├── setup_backend.sh # Python deps
│ ├── setup_frontend.sh # Node deps
│ └── provision_roles_anywhere.sh # One-time AWS setup
├── skills/
└── rules/
The Cursor VM snapshot stores one line: bash .cursor/cloud-agent/setup_vm.sh. Everything reviewable in PRs.
The scripts also support graceful fallback. On startup, setup_backend.sh checks if aws sts get-caller-identity succeeds. If it does, it fetches a CodeArtifact token and installs real private packages. If not, it installs lightweight stubs that satisfy the import graph so the app still starts — enough for unit tests and frontend work. When you add AWS credentials later, the next VM boot picks them up and installs the real packages automatically.