Skip to main content

AWS IAM Policy Reference

This document outlines the IAM permissions required for NightOps to manage AWS resources. The policy follows the principle of least privilege, granting only the permissions necessary for each operation.

Quick Start

For a quick setup, use the complete policy below. For more granular control, see the Per-Service Policies section.

Complete NightOps Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EC2ReadAccess",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeInstanceStatus",
"ec2:DescribeTags"
],
"Resource": "*"
},
{
"Sid": "EC2WriteAccess",
"Effect": "Allow",
"Action": [
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/nightops:managed": "true"
}
}
},
{
"Sid": "RDSReadAccess",
"Effect": "Allow",
"Action": [
"rds:DescribeDBInstances",
"rds:ListTagsForResource"
],
"Resource": "*"
},
{
"Sid": "RDSWriteAccess",
"Effect": "Allow",
"Action": [
"rds:StartDBInstance",
"rds:StopDBInstance"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"rds:db-tag/nightops:managed": "true"
}
}
},
{
"Sid": "ECSReadAccess",
"Effect": "Allow",
"Action": [
"ecs:ListClusters",
"ecs:ListServices",
"ecs:DescribeServices",
"ecs:DescribeClusters"
],
"Resource": "*"
},
{
"Sid": "ECSWriteAccess",
"Effect": "Allow",
"Action": [
"ecs:UpdateService"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"ecs:ResourceTag/nightops:managed": "true"
}
}
},
{
"Sid": "EKSReadAccess",
"Effect": "Allow",
"Action": [
"eks:ListClusters",
"eks:ListNodegroups",
"eks:DescribeCluster",
"eks:DescribeNodegroup"
],
"Resource": "*"
},
{
"Sid": "EKSWriteAccess",
"Effect": "Allow",
"Action": [
"eks:UpdateNodegroupConfig"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"eks:ResourceTag/nightops:managed": "true"
}
}
},
{
"Sid": "RedshiftReadAccess",
"Effect": "Allow",
"Action": [
"redshift:DescribeClusters",
"redshift:DescribeTags"
],
"Resource": "*"
},
{
"Sid": "RedshiftWriteAccess",
"Effect": "Allow",
"Action": [
"redshift:PauseCluster",
"redshift:ResumeCluster"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"redshift:ResourceTag/nightops:managed": "true"
}
}
},
{
"Sid": "AutoScalingReadAccess",
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeTags"
],
"Resource": "*"
},
{
"Sid": "AutoScalingWriteAccess",
"Effect": "Allow",
"Action": [
"autoscaling:UpdateAutoScalingGroup"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"autoscaling:ResourceTag/nightops:managed": "true"
}
}
}
]
}

Per-Service Policies

Use these individual policies if you only need NightOps to manage specific services.

EC2 Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EC2ReadAccess",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeInstanceStatus",
"ec2:DescribeTags"
],
"Resource": "*"
},
{
"Sid": "EC2WriteAccess",
"Effect": "Allow",
"Action": [
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/nightops:managed": "true"
}
}
}
]
}

RDS Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RDSReadAccess",
"Effect": "Allow",
"Action": [
"rds:DescribeDBInstances",
"rds:ListTagsForResource"
],
"Resource": "*"
},
{
"Sid": "RDSWriteAccess",
"Effect": "Allow",
"Action": [
"rds:StartDBInstance",
"rds:StopDBInstance"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"rds:db-tag/nightops:managed": "true"
}
}
}
]
}

ECS Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECSReadAccess",
"Effect": "Allow",
"Action": [
"ecs:ListClusters",
"ecs:ListServices",
"ecs:DescribeServices",
"ecs:DescribeClusters"
],
"Resource": "*"
},
{
"Sid": "ECSWriteAccess",
"Effect": "Allow",
"Action": [
"ecs:UpdateService"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"ecs:ResourceTag/nightops:managed": "true"
}
}
}
]
}

EKS Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EKSReadAccess",
"Effect": "Allow",
"Action": [
"eks:ListClusters",
"eks:ListNodegroups",
"eks:DescribeCluster",
"eks:DescribeNodegroup"
],
"Resource": "*"
},
{
"Sid": "EKSWriteAccess",
"Effect": "Allow",
"Action": [
"eks:UpdateNodegroupConfig"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"eks:ResourceTag/nightops:managed": "true"
}
}
}
]
}

Redshift Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RedshiftReadAccess",
"Effect": "Allow",
"Action": [
"redshift:DescribeClusters",
"redshift:DescribeTags"
],
"Resource": "*"
},
{
"Sid": "RedshiftWriteAccess",
"Effect": "Allow",
"Action": [
"redshift:PauseCluster",
"redshift:ResumeCluster"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"redshift:ResourceTag/nightops:managed": "true"
}
}
}
]
}

Auto Scaling Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AutoScalingReadAccess",
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeTags"
],
"Resource": "*"
},
{
"Sid": "AutoScalingWriteAccess",
"Effect": "Allow",
"Action": [
"autoscaling:UpdateAutoScalingGroup"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"autoscaling:ResourceTag/nightops:managed": "true"
}
}
}
]
}

Cross-Account Access

For multi-account setups, create a role in each spoke account that NightOps can assume.

Trust Policy (Spoke Account)

Replace HUB_ACCOUNT_ID with your NightOps hub account ID:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::HUB_ACCOUNT_ID:role/NightOpsExecutionRole"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "YOUR_EXTERNAL_ID"
}
}
}
]
}

Hub Account Policy

The NightOps execution role in the hub account needs permission to assume roles in spoke accounts:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRoleInSpokeAccounts",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/NightOpsRole"
}
]
}

Permission Boundaries

For additional security, you can create a permission boundary that limits what NightOps can do:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOnlyTaggedResources",
"Effect": "Allow",
"Action": [
"ec2:StartInstances",
"ec2:StopInstances",
"rds:StartDBInstance",
"rds:StopDBInstance",
"ecs:UpdateService",
"eks:UpdateNodegroupConfig",
"redshift:PauseCluster",
"redshift:ResumeCluster",
"autoscaling:UpdateAutoScalingGroup"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/nightops:managed": "true"
}
}
},
{
"Sid": "DenyDestructiveActions",
"Effect": "Deny",
"Action": [
"ec2:TerminateInstances",
"rds:DeleteDBInstance",
"ecs:DeleteService",
"eks:DeleteNodegroup",
"redshift:DeleteCluster",
"autoscaling:DeleteAutoScalingGroup"
],
"Resource": "*"
}
]
}

Permissions Summary Table

ServiceRead ActionsWrite Actions
EC2DescribeInstances, DescribeInstanceStatus, DescribeTagsStartInstances, StopInstances
RDSDescribeDBInstances, ListTagsForResourceStartDBInstance, StopDBInstance
ECSListClusters, ListServices, DescribeServices, DescribeClustersUpdateService
EKSListClusters, ListNodegroups, DescribeCluster, DescribeNodegroupUpdateNodegroupConfig
RedshiftDescribeClusters, DescribeTagsPauseCluster, ResumeCluster
ASGDescribeAutoScalingGroups, DescribeTagsUpdateAutoScalingGroup
STS-AssumeRole (hub account only)

Security Best Practices

Tag-Based Access Control

All write actions are conditioned on the nightops:managed=true tag. This ensures NightOps can only modify resources explicitly opted-in for management.

External ID

Always use an External ID when configuring cross-account access to prevent the confused deputy problem.

Regular Audits

Periodically review CloudTrail logs to audit NightOps actions and ensure only expected resources are being managed.

Next Steps