Skill 103 · AWS Fault Injection Service
Subchapter 103.4
references/fis-security.mdMarkdown9 KBView on GitHub
FIS runs real, potentially destructive actions on real resources. Secure both who can run experiments and what an experiment is allowed to touch, and always bound the blast radius.
roleArn)FIS assumes the experiment role to perform actions on your behalf. It is a service role with:
fis.amazonaws.com to assume it, with confused-deputy conditions.Condition on aws:SourceAccount and aws:SourceArn so only your account’s FIS experiments can
assume the role — never leave the trust policy unconditioned.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "fis.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "123456789012" },
"ArnLike": {
"aws:SourceArn": "arn:aws:fis:us-east-1:123456789012:experiment/*"
}
}
}]
}Grant only what the chosen actions need, scoped to the specific target resources — never "*"
across a service. Example for an EC2 reboot experiment on tagged prod instances:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ec2:RebootInstances"],
"Resource": "arn:aws:ec2:us-east-1:123456789012:instance/*",
"Condition": { "StringEquals": { "aws:ResourceTag/env": "prod" } }
},
{
"Effect": "Allow",
"Action": ["ec2:DescribeInstances"],
"Resource": "*"
}
]
}Describe* calls FIS uses for target resolution/filtering generally require Resource: "*"
(they don’t support resource-level scoping), but the fault actions should be scoped by ARN
and/or aws:ResourceTag. Match permissions to the action’s underlying API.
The exact IAM actions each FIS action needs evolve; treat this as a starting point and confirm the
authoritative list before finalizing a policy (see “authoritative source” below). The pattern is
always the same: fault permission(s) scoped by ARN/tag + the Describe* resolution
permission on Resource: "*".
| FIS action | Fault permission(s) | Typical resolution permission |
|---|---|---|
aws:ec2:reboot-instances / stop-instances / terminate-instances | ec2:RebootInstances / ec2:StopInstances(+StartInstances if auto-restart) / ec2:TerminateInstances | ec2:DescribeInstances |
aws:rds:failover-db-cluster / reboot-db-instances | rds:FailoverDBCluster / rds:RebootDBInstance | rds:DescribeDBClusters / rds:DescribeDBInstances |
aws:elasticache:replicationgroup-interrupt-az-power | elasticache:InterruptClusterAzPower | elasticache:DescribeReplicationGroups |
aws:lambda:invocation-* | lambda:<verify with get-action> on the function — never lambda:* + the FIS Lambda extension layer | lambda:GetFunction |
aws:ssm:send-command / start-automation-execution | ssm:SendCommand / ssm:StartAutomationExecution (+ the doc/automation ARNs) | resource-type Describe* |
aws:eks:pod-* | IAM is not sufficient — needs eks:DescribeCluster for resolution plus Kubernetes RBAC mapping the FIS role to a k8s group bound to a Role/ClusterRole, plus the cluster “prepared for FIS” | eks:DescribeCluster |
aws:network:disrupt-connectivity / route-table-* / transit-gateway-* | NACL/route-table EC2 actions (e.g. ec2:CreateNetworkAcl, ec2:CreateNetworkAclEntry, ec2:ReplaceNetworkAclAssociation, ec2:DeleteNetworkAcl) — verify the exact set with get-action | ec2:DescribeSubnets / ec2:DescribeRouteTables / ec2:DescribeVpcs |
aws:ebs:pause-volume-io | ec2:<verify with get-action> volume-IO fault permission — do not grant ec2:* (Nitro only) | ec2:DescribeVolumes |
EKS pod actions are the common trap: IAM alone will not run them. You also need Kubernetes RBAC (map the experiment role into the cluster and bind it to a role that permits the pod operation) and the cluster must be prepared for FIS. Grant
eks:*sparingly and lean on k8s RBAC for the actual pod permissions.
Authoritative source (use when an action is not in the table above, or to confirm one that is):
run aws fis get-action --id <action-id> and consult
How AWS FIS works with IAM (opens in a new tab)
and any AWS-managed FIS policies. Do not invent IAM action names for an action whose underlying
API you cannot confirm — scaffold the correctly-scoped policy and mark the fault permissions as
<verify with get-action> rather than guessing.
cloudwatch:GetDashboard, cloudwatch:GetMetricWidgetImage,
s3:PutObject, s3:GetObject (scoped to the report bucket/prefix); plus
kms:GenerateDataKey, kms:Decrypt if the report bucket uses a customer-managed KMS key.Starting an experiment is a privileged action. Scope the human/CI principals allowed to call
fis:StartExperiment (and template CRUD), ideally with aws:ResourceTag conditions and by
template ARN. FIS supports identity-based policies, ABAC (tags in policies), service-specific
condition keys, and PassRole controls — use iam:PassRole conditions so only approved experiment
roles can be passed to FIS.
When emitting an experiment role, ALSO emit the caller’s PassRole policy. Without it, any
caller who can create/update templates can reference any FIS-trusting role in the account.
Rules: Resource = explicit experiment-role ARN(s), never *. Condition
iam:PassedToService = fis.amazonaws.com.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": ["arn:aws:iam::123456789012:role/FISExperimentRole"],
"Condition": {
"StringEquals": { "iam:PassedToService": "fis.amazonaws.com" }
}
}]
}{"source":"none"} is acceptable only in a throwaway sandbox.COUNT(1) or a small PERCENT(n), single AZ, pre-production first. Widen
only after the experiment passes.actionsMode: skip-all) before a real run to confirm the exact resources.emptyTargetResolutionMode at fail so an over- or
under-matching target surfaces immediately instead of silently doing nothing.aws:SecureTransport, restrict to a dedicated bucket/prefix, and consider S3
Object Lock on report buckets so evidence can’t be altered or deleted.FIS API calls are recorded in AWS CloudTrail (including the actions FIS takes in each target account for multi-account experiments). Review CloudTrail and experiment logs after each run.