Raxx · internal docs

internal · gated ↑ index

IAM policy — claude-infisical-bootstrap

IAM user: claude-infisical-bootstrap Account: 521228113048 Policy name: claude-infisical-bootstrap-read-write (inline) Last updated: 2026-04-28 UTC Updated by: #569 (sre-agent investigation → feature-developer-agent patch)


Purpose

claude-infisical-bootstrap is the sole IAM user available to agent tooling in this account. It is used by:


Policy statements

LightsailInstanceManagement (Resource: *)

Actions: CreateInstances, GetInstances, GetInstance, DeleteInstance, AllocateStaticIp, AttachStaticIp, DetachStaticIp, GetStaticIp, GetStaticIps, PutInstancePublicPorts, GetInstancePortStates, ImportKeyPair, GetKeyPair, GetKeyPairs, DeleteKeyPair, DownloadDefaultKeyPair, GetOperation, GetOperations, GetRegions, GetBlueprints, GetBundles

Why: Terraform's AWS provider requires these to manage the raxx-tickets Lightsail instance, static IP, and firewall rules defined in terraform/freescout/main.tf. GetRegions, GetBlueprints, and GetBundles are required by the Lightsail provider to resolve blueprint and bundle IDs. Resource scoping to specific instance ARNs is not supported by Lightsail IAM (the API enforces name-level checks internally, not ARN-level).

LightsailSnapshotPreApply (Resource: *)

Actions: CreateInstanceSnapshot, GetInstanceSnapshots, GetInstanceMetricData

Why: The FreeScout runbook requires a verified snapshot before any destructive terraform apply. CreateInstanceSnapshot takes the pre-apply backup. GetInstanceSnapshots confirms it is in available state before proceeding. GetInstanceMetricData allows the sre-agent to inspect CPU/memory metrics during post-apply verification. Added in #569.

TerraformStateLock (Resource: arn:aws:dynamodb:us-east-1:521228113048:table/raxx-iac-state-locks)

Actions: dynamodb:GetItem, dynamodb:PutItem, dynamodb:DeleteItem

Why: Terraform uses a DynamoDB table as a distributed lock when reading or writing state. Without PutItem, terraform plan/apply fails immediately with Error acquiring the state lock. GetItem reads lock state; DeleteItem releases the lock on success or timeout. Scoped to the exact lock table ARN — no other DynamoDB tables are accessible. Added in #569.

TerraformStateBucket (Resource: arn:aws:s3:::raxx-iac-state-prod/*)

Actions: s3:GetObject, s3:PutObject, s3:DeleteObject

Why: The freescout terraform backend stores state at s3://raxx-iac-state-prod/freescout/terraform.tfstate (see terraform/freescout/versions.tf). GetObject is required to read current state at plan time. PutObject writes updated state after apply. DeleteObject is required by the AWS provider for state workspace operations. Scoped to raxx-iac-state-prod/* only — no other S3 buckets are accessible. Added in #569.

TerraformStateBucketList (Resource: arn:aws:s3:::raxx-iac-state-prod)

Actions: s3:ListBucket

Why: s3:ListBucket must be applied to the bucket ARN (not the /* suffix). Terraform requires it to enumerate state files and detect whether a key exists before attempting a GetObject. Without it, missing-state scenarios return 403 instead of the expected 404, breaking workspace initialization. Scoped to raxx-iac-state-prod only. Added in #569.


What this policy does NOT grant


Change history

Date (UTC) Change Issue
2026-04-28 Original policy created — Lightsail instance management only (initial bootstrap)
2026-04-28 Added LightsailSnapshotPreApply, TerraformStateLock, TerraformStateBucket, TerraformStateBucketList #569

Verification

After any policy update, confirm all three permission groups via IAM policy simulation:

# DynamoDB state lock
aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::521228113048:user/claude-infisical-bootstrap" \
  --action-names "dynamodb:GetItem" "dynamodb:PutItem" "dynamodb:DeleteItem" \
  --resource-arns "arn:aws:dynamodb:us-east-1:521228113048:table/raxx-iac-state-locks" \
  --query 'EvaluationResults[*].{Action:EvalActionName,Decision:EvalDecision}'

# S3 object operations
aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::521228113048:user/claude-infisical-bootstrap" \
  --action-names "s3:GetObject" "s3:PutObject" "s3:DeleteObject" \
  --resource-arns "arn:aws:s3:::raxx-iac-state-prod/freescout/terraform.tfstate" \
  --query 'EvaluationResults[*].{Action:EvalActionName,Decision:EvalDecision}'

# S3 bucket list
aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::521228113048:user/claude-infisical-bootstrap" \
  --action-names "s3:ListBucket" \
  --resource-arns "arn:aws:s3:::raxx-iac-state-prod" \
  --query 'EvaluationResults[*].{Action:EvalActionName,Decision:EvalDecision}'

# Lightsail snapshot
aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::521228113048:user/claude-infisical-bootstrap" \
  --action-names "lightsail:CreateInstanceSnapshot" "lightsail:GetInstanceSnapshots" "lightsail:GetInstanceMetricData" \
  --resource-arns "*" \
  --query 'EvaluationResults[*].{Action:EvalActionName,Decision:EvalDecision}'

All decisions should be allowed.

To read the live policy JSON:

aws iam get-user-policy \
  --user-name claude-infisical-bootstrap \
  --policy-name claude-infisical-bootstrap-read-write \
  --query 'PolicyDocument'