Skip to main content
Your progress
0 of 4 lessons complete0%
T1 / M1.1 / L2 OF 4 / Operator TIER / 10 min

AWS: the IAM policy and why it's read-mostly

Outcome

By the end of this lesson, you will be able to configure the AWS IAM role for ZopNight and explain which permissions enable read-only discovery versus scoped-write execution.


TierOperator
JTBD”Get an AWS account into ZopNight in under 10 minutes with the right policy.”
PersonasPlatform Engineer · Security/Compliance
PrerequisitesL1
Time10 minutes
Bloom verbConfigure (Apply) and Explain (Understand)

1. Concept

You do not give ZopNight a password. You create an IAM role in your AWS account, which is a named set of permissions, and you allow ZopNight’s account to borrow it. AWS calls that assuming a role, and it is the standard way one AWS account lets another do work on its behalf.

You keep the role. You decide what is in it, and you can change or delete it at any time without involving anyone.

The read-only part

Finding resources, reading their usage, pulling the bill and producing recommendations all need permission to look and nothing more. That part of the role looks like this:

Terminal window
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DiscoveryReadOnly",
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"rds:Describe*",
"eks:Describe*",
"eks:List*",
"ecs:Describe*",
"ecs:List*",
"lambda:Get*",
"lambda:List*",
"s3:Get*",
"s3:List*",
"elasticloadbalancing:Describe*",
"autoscaling:Describe*",
"cloudfront:Get*",
"cloudfront:List*",
"dynamodb:Describe*",
"dynamodb:List*",
"route53:Get*",
"route53:List*"
],
"Resource": "*"
},
{
"Sid": "ResourceExplorer",
"Effect": "Allow",
"Action": [
"resource-explorer-2:Search",
"resource-explorer-2:List*",
"resource-explorer-2:Get*"
],
"Resource": "*"
},
{
"Sid": "Metrics",
"Effect": "Allow",
"Action": [
"cloudwatch:GetMetricStatistics",
"cloudwatch:GetMetricData",
"cloudwatch:ListMetrics"
],
"Resource": "*"
},
{
"Sid": "Pricing",
"Effect": "Allow",
"Action": [
"pricing:DescribeServices",
"pricing:GetProducts"
],
"Resource": "*"
},
{
"Sid": "BillingReadOnly",
"Effect": "Allow",
"Action": [
"ce:Get*",
"ce:List*",
"ce:Describe*"
],
"Resource": "*"
},
{
"Sid": "ActivityLogs",
"Effect": "Allow",
"Action": [
"cloudtrail:LookupEvents"
],
"Resource": "*"
}
]
}

Read the verbs and you can check this yourself. Every one is Describe, Get, List or Lookup. None of them changes anything.

Deploy only this and you get the full picture: every resource found, every report, every recommendation. What you do not get is anything happening. No schedules, no automatic fixes. This is what “monitor mode” means, and it is a perfectly reasonable place to stop.

The parts that can change something

If you want the schedules and the automatic fixes, you add specific extra permissions. Each one is separate, and each one is your decision:

For scheduling EC2 start/stop:

Terminal window
{
"Sid": "EC2StartStop",
"Effect": "Allow",
"Action": ["ec2:StartInstances", "ec2:StopInstances"],
"Resource": "*",
"Condition": {
"StringEqualsIfExists": {"aws:ResourceTag/environment": ["dev", "test", "stage"]}
}
}

Read the Condition block. It says this permission applies only to machines tagged dev, test or stage.

That is worth dwelling on. If somebody sets up a ZopNight schedule badly and it tries to stop a production machine, AWS refuses the request. The protection lives in your account, not in ours, which is the only place it is worth having.

For EBS termination (auto-remediation of orphaned volumes):

Terminal window
{
"Sid": "EBSOrphanCleanup",
"Effect": "Allow",
"Action": ["ec2:DeleteVolume", "ec2:CreateSnapshot"],
"Resource": "*"
}

For autoscaling policy management:

Terminal window
{
"Sid": "AutoscalerPolicyMgmt",
"Effect": "Allow",
"Action": [
"autoscaling:UpdateAutoScalingGroup",
"autoscaling:PutScalingPolicy",
"autoscaling:DeletePolicy",
"autoscaling:PutScheduledUpdateGroupAction",
"autoscaling:DeleteScheduledAction",
"application-autoscaling:Put*",
"application-autoscaling:Delete*",
"application-autoscaling:Register*",
"application-autoscaling:Deregister*"
],
"Resource": "*"
}

The shape is the same every time: a named list of actions, optionally narrowed by tag, and your policy is what decides. Anything ZopNight asks for passes through AWS’s own permission check before it reaches a resource.

Saying who may borrow the role

The role also carries a short document naming who is allowed to assume it:

Terminal window
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<zopnight-account>:role/discovery-role"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {"sts:ExternalId": "<unique-external-id>"}
}
}]
}

The external ID is not optional. ZopNight generates a different one for every organisation, and the role will not be assumed without it.

It exists to stop a specific problem. Without it, knowing ZopNight’s account number would be enough for one customer to talk ZopNight into reaching into another customer’s account. The shared name for that problem is the confused deputy: the deputy has the authority, and somebody else decides how it gets used. The external ID means every request has to prove which account it is for.

Why it is not simply read-only

A purely read-only role means no schedules, no automatic fixes, no scaling changes. That is a reporting tool, and reporting is the thing you already have.

So the honest description is read-mostly. Reading is the floor. Every ability to change something sits above it and is added one at a time, by you.

Most customers start in monitor mode and turn on the write permissions gradually, as they get comfortable with what the product does. That order is deliberate, and it is the same pattern the Engineer track calls progressive autonomy.

When you have many AWS accounts

Large companies run an AWS Organization with dozens or hundreds of accounts under it. You do not set each one up by hand.

  • The role is rolled out to every member account using a CloudFormation StackSet, which is AWS’s way of applying one template across many accounts at once
  • Every copy carries the same role name and the same external ID for your ZopNight organisation
  • ZopNight lists each member account separately, all under one organisation

This works at 100 accounts as easily as at 3, and ZopNight gives you the template.


2. Demo

A typical AWS connection walkthrough:

Terminal window
T+0 Customer opens ZopNight → Cloud Accounts → Add AWS Account
T+1 min ZopNight displays the IAM trust policy + permissions template
T+1 min Customer copies the CloudFormation template URL
T+2 min Customer launches in AWS CloudFormation Console
T+3 min Stack creates the IAM role
T+4 min Customer pastes role ARN + external ID back to ZopNight
T+4 min ZopNight validates via STS AssumeRole
T+5 min Account marked Active; discovery preview begins

Five minutes from start to active. The CloudFormation template eliminates manual IAM policy editing.


3. Hands-on (6 min)

Connect an AWS account (or audit one already connected):

Terminal window
1. In ZopNight, navigate to Cloud Accounts → Add AWS Account.
2. Note the offered CloudFormation template URL.
3. In AWS Console (a sandbox account, not production), launch the stack.
4. Inspect the IAM role created. Verify:
- Every Action ends with Describe, Get, List, or Lookup
(unless you elected to enable scoped writes)
- The trust policy includes the external ID
- The role name matches the ZopNight-published convention
5. Paste the role ARN back to ZopNight; verify connection in <60 seconds.
6. After connection, open Permission Visibility (covered in L4) and
confirm Granted across the expected service categories.

4. Knowledge check

Q1

The AWS IAM role for ZopNight discovery is described as “read-mostly” because:

A. The baseline is read-only across all services needed for discovery, metrics, billing, and recommendations
B. It uses an old API
C. It only has around 50% of the permissions that it actually needs in order to function correctly
D. It can only read every other request

Show answer

Correct: A. Scoped writes (StartInstances, StopInstances, DeleteVolume, etc.) are opt-in extensions added by the customer to unlock execution. Read-mostly = read-only baseline + opt-in scoped writes. The customer controls which extensions to grant.

Q2

The sts:ExternalId condition in the trust policy primarily defends against:

A. SQL injection through the role name, which the condition escapes before the whole policy statement is evaluated
B. Phishing
C. The “confused deputy” attack: where one customer’s account could trick ZopNight into accessing another customer’s role
D. DoS

Show answer

Correct: C. The unique external ID per organization makes this structurally impossible. External ID is the canonical defense against the confused deputy pattern in cross-account assume-role. AWS documentation calls this out explicitly.

Q3

A team wants ZopNight to schedule EC2 instances tagged environment=dev but never anything tagged environment=prod. The defensible IAM configuration:

A. Grant ec2:StartInstances and ec2:StopInstances with a Condition requiring aws:ResourceTag/environment to be in [“dev”, “test”, “stage”]
B. Don’t grant EC2 start/stop permissions
C. Rely on ZopNight never requesting anything in production, since the product does not target production resources by default
D. Use a separate AWS account for dev

Show answer

Correct: A. The IAM denies any prod-tagged request before ZopNight even attempts the call. Tag-based IAM conditions are the right tool for this. The defense is at the cloud provider’s policy layer, not at the application layer. Even a buggy ZopNight call cannot stop a prod instance if the IAM forbids it.


5. Apply

ZopNight publishes the current policy and the CloudFormation template at:

For organizations adopting StackSet deployment across many accounts:

  • The CloudFormation template is StackSet-compatible
  • The external ID is the same across all member accounts in one ZopNight organization
  • Best practice: deploy via StackSet to a defined OU rather than per-account manual setup

Glossary terms touched

IAM role · Assume-role · External ID · Confused deputy · CloudFormation StackSet


Start with the bill.

Foundations takes about five hours. The first lesson is nine minutes.

Open curriculum. No login. No paywall. 290 lessons across 7 courses, three publicly verifiable credentials. Read it on the train, take the exam on a Saturday, list the credential on your résumé Monday.

5h median time to finish Foundations
0 logins, paywalls, or marketing forms
open curriculum, public credential verifier
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·