AWS
CloudPlans never receives AWS keys. It assumes an IAM role of your account that you create once:
- The role must trust
arn:aws:iam::605134468875:role/cloudplansbatch(AWS account605134468875) with the external IDcloudplans. - The role needs the permissions of the services you want to manage (below).
- You paste the role ARN in the credential (Create).
The console generates this for you: in Credentials → Add Credential → AWS, click Helper: AWS CloudShell setup, tick only the services you use and run the script it shows in AWS CloudShell. The script and the Terraform below are the ones for all the services, so you can trim them to what you use.
Permissions per service
Always required (they validate the credential and let Find Resources list your regions): ec2:DescribeInstancesec2:DescribeRegions
| Service | IAM actions needed |
|---|---|
| AWS EC2 (Stoppable) | ec2:DescribeInstancesec2:StartInstancesec2:StopInstances |
| AWS EC2 (Vertical Resize) | ec2:DescribeInstancesec2:StartInstancesec2:StopInstancesec2:ModifyInstanceAttribute |
| AWS EKS (Scalable) | eks:ListClusterseks:ListNodegroupseks:DescribeNodegroupeks:UpdateNodegroupConfig |
| AWS ECS (Scalable) | ecs:ListClustersecs:ListServicesecs:DescribeServicesecs:UpdateService |
| AWS Auto Scaling (Scalable) | autoscaling:DescribeAutoScalingGroupsautoscaling:UpdateAutoScalingGroupautoscaling:DescribeLaunchConfigurationsec2:DescribeLaunchTemplateVersions |
| AWS RDS (Stoppable) | rds:DescribeDBInstancesrds:StartDBInstancerds:StopDBInstance |
| AWS RDS / Aurora (Vertical Resize) | rds:DescribeDBInstancesrds:StartDBInstancerds:StopDBInstancerds:ModifyDBInstance |
| AWS DocumentDB (Stoppable) | rds:DescribeDBClustersrds:StartDBClusterrds:StopDBClusterrds:DescribeDBInstances |
| AWS Elastic Beanstalk (Scalable) | elasticbeanstalk:DescribeEnvironmentselasticbeanstalk:DescribeEnvironmentResourcesautoscaling:DescribeAutoScalingGroupsautoscaling:UpdateAutoScalingGroup |
| AWS Fargate (Scalable) | ecs:ListClustersecs:ListServicesecs:DescribeServicesecs:UpdateService |
| AWS Amazon MQ (Vertical Resize) | mq:ListBrokersmq:DescribeBrokermq:UpdateBrokermq:RebootBroker |
| AWS SageMaker Endpoint (Scalable) | sagemaker:ListEndpointssagemaker:DescribeEndpointsagemaker:UpdateEndpointWeightsAndCapacities |
| AWS ElastiCache (Vertical Resize) | elasticache:DescribeReplicationGroupselasticache:ModifyReplicationGroup |
| AWS Redshift Provisioned (Stoppable) | redshift:DescribeClustersredshift:PauseClusterredshift:ResumeCluster |
| AWS DynamoDB Provisioned (Vertical Resize) | dynamodb:ListTablesdynamodb:DescribeTabledynamodb:UpdateTable |
Amazon DocumentDB is authorised through the rds: IAM namespace (there is no docdb: IAM prefix). The Auto Scaling Group read actions on launch configurations/templates are only used to estimate the savings of the group.
CloudShell script
# Run this in AWS CloudShell, in the AWS account you want CloudPlans to manage.
ROLE_NAME="CloudPlansSchedulerRole"
EXTERNAL_ID="cloudplans" # optional, recommended
# 1. Trust policy: who can assume the role
cat > /tmp/trust.json <<'JSON'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::605134468875:role/cloudplansbatch" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "sts:ExternalId": "cloudplans" } }
}]
}
JSON
# 2. Permissions policy — minimum actions for the resource types you selected:
# AWS EC2 (Stoppable), AWS EC2 (Vertical Resize), AWS EKS (Scalable), AWS ECS (Scalable), AWS Auto Scaling (Scalable), AWS RDS (Stoppable), AWS RDS / Aurora (Vertical Resize), AWS DocumentDB (Stoppable), AWS Elastic Beanstalk (Scalable), AWS Fargate (Scalable), AWS Amazon MQ (Vertical Resize), AWS SageMaker Endpoint (Scalable), AWS ElastiCache (Vertical Resize), AWS Redshift Provisioned (Stoppable), AWS DynamoDB Provisioned (Vertical Resize)
cat > /tmp/perm.json <<'JSON'
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeRegions",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:ModifyInstanceAttribute",
"eks:ListClusters",
"eks:ListNodegroups",
"eks:DescribeNodegroup",
"eks:UpdateNodegroupConfig",
"ecs:ListClusters",
"ecs:ListServices",
"ecs:DescribeServices",
"ecs:UpdateService",
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:UpdateAutoScalingGroup",
"autoscaling:DescribeLaunchConfigurations",
"ec2:DescribeLaunchTemplateVersions",
"rds:DescribeDBInstances",
"rds:StartDBInstance",
"rds:StopDBInstance",
"rds:ModifyDBInstance",
"rds:DescribeDBClusters",
"rds:StartDBCluster",
"rds:StopDBCluster",
"elasticbeanstalk:DescribeEnvironments",
"elasticbeanstalk:DescribeEnvironmentResources",
"mq:ListBrokers",
"mq:DescribeBroker",
"mq:UpdateBroker",
"mq:RebootBroker",
"sagemaker:ListEndpoints",
"sagemaker:DescribeEndpoint",
"sagemaker:UpdateEndpointWeightsAndCapacities",
"elasticache:DescribeReplicationGroups",
"elasticache:ModifyReplicationGroup",
"redshift:DescribeClusters",
"redshift:PauseCluster",
"redshift:ResumeCluster",
"dynamodb:ListTables",
"dynamodb:DescribeTable",
"dynamodb:UpdateTable"
],
"Resource": "*"
}
]
}
JSON
# 3. Create role + attach inline policy
aws iam create-role \
--role-name "$ROLE_NAME" \
--assume-role-policy-document file:///tmp/trust.json
aws iam put-role-policy \
--role-name "$ROLE_NAME" \
--policy-name CloudPlansSchedulerPolicy \
--policy-document file:///tmp/perm.json
# 4. Print the ARN you must paste in the CloudPlans console
aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text
Terraform
data "aws_iam_policy_document" "trust" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::605134468875:role/cloudplansbatch"]
}
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = ["cloudplans"]
}
}
}
data "aws_iam_policy_document" "permissions" {
statement {
effect = "Allow"
actions = [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeLaunchConfigurations",
"autoscaling:UpdateAutoScalingGroup",
"dynamodb:DescribeTable",
"dynamodb:ListTables",
"dynamodb:UpdateTable",
"ec2:DescribeInstances",
"ec2:DescribeLaunchTemplateVersions",
"ec2:DescribeRegions",
"ec2:ModifyInstanceAttribute",
"ec2:StartInstances",
"ec2:StopInstances",
"ecs:DescribeServices",
"ecs:ListClusters",
"ecs:ListServices",
"ecs:UpdateService",
"eks:DescribeNodegroup",
"eks:ListClusters",
"eks:ListNodegroups",
"eks:UpdateNodegroupConfig",
"elasticache:DescribeReplicationGroups",
"elasticache:ModifyReplicationGroup",
"elasticbeanstalk:DescribeEnvironmentResources",
"elasticbeanstalk:DescribeEnvironments",
"mq:DescribeBroker",
"mq:ListBrokers",
"mq:RebootBroker",
"mq:UpdateBroker",
"rds:DescribeDBClusters",
"rds:DescribeDBInstances",
"rds:ModifyDBInstance",
"rds:StartDBCluster",
"rds:StartDBInstance",
"rds:StopDBCluster",
"rds:StopDBInstance",
"redshift:DescribeClusters",
"redshift:PauseCluster",
"redshift:ResumeCluster",
"sagemaker:DescribeEndpoint",
"sagemaker:ListEndpoints",
"sagemaker:UpdateEndpointWeightsAndCapacities"
]
resources = ["*"]
}
}
resource "aws_iam_role" "cloudplans" {
name = "CloudPlansSchedulerRole"
assume_role_policy = data.aws_iam_policy_document.trust.json
}
resource "aws_iam_role_policy" "cloudplans" {
name = "CloudPlansSchedulerPolicy"
role = aws_iam_role.cloudplans.id
policy = data.aws_iam_policy_document.permissions.json
}
output "role_arn" {
description = "Paste this ARN in the CloudPlans console"
value = aws_iam_role.cloudplans.arn
}