← EP12: Zero Trust Access in the Cloud · EP13: New-Service IAM Checklist · All Cloud IAM Episodes →
TL;DR
- New cloud service IAM permissions ship on GA day — often before your Terraform provider, internal IaC modules, or team wiki catch up
- The fast path is
service:*onResource: *— the tempting unblock, and also how wildcard debt starts (see EP09’s least-privilege audit) - Five-step checklist: find the exact actions, scope the resource, dry-run before granting, attach a guardrail, and put a 30-day review on the calendar
- AWS has no single CLI call that lists “every action for a service” — use the Service Authorization Reference plus IAM Access Analyzer’s policy generation from real CloudTrail activity
- GCP’s
gcloud iam list-testable-permissionsreturns the exact permissions grantable on a specific resource — scoped to what that resource type actually supports - Azure’s
az provider operation show --namespace Microsoft.<Service>lists every operation a resource provider exposes, before you write a single role assignment
The Big Picture
NEW CLOUD SERVICE SHIPS — THE FIRST GRANT DECIDES THE NEXT YEAR
Provider ships GA
│
▼
Team requests access ──────► Tempting shortcut: "service:*" on "*"
│ (unblocks today, becomes next year's
│ wildcard-debt line item in EP09's audit)
▼
STEP 1 — Find the exact actions the task needs
│ (Service Authorization Reference · list-testable-permissions ·
│ provider operation show)
▼
STEP 2 — Scope the resource, not the account
│ (ARN pattern / resource URI / resource group — never "*")
▼
STEP 3 — Dry-run before granting
│ (simulate-principal-policy · policy-troubleshoot iam · what-if)
▼
STEP 4 — Attach a guardrail, not just a grant
│ (permission boundary / SCP · Org Policy · Azure Policy)
▼
STEP 5 — Put a 30-day review on the calendar
│ (provisional access, not permanent — EP09's audit is the
│ backstop for whatever step 5 misses)
▼
Access granted: scoped, guarded, and time-boxed
Introduction
New cloud service IAM permissions land the same day a provider ships something new — usually before your Terraform provider, your internal enablement docs, or anyone’s muscle memory has caught up. A team wants to use the new service today, and the fastest way to unblock them is a wildcard: service:* on Resource: *. It works immediately. It also never gets revisited.
I’ve seen this pattern enough times across AWS, GCP, and Azure environments to stop treating it as a one-off mistake and start treating it as a predictable failure mode. Every cloud provider ships new services and new API actions on existing services continuously — thousands of changes a year across the big three. IAM has to keep up with all of it, and nobody’s tooling updates same-day. The gap between “the service exists” and “the least-privilege policy for it exists” is where every wildcard grant in your account was born.
This episode is the checklist I use to close that gap before it becomes EP09’s least-privilege audit problem six months later.
Why This Keeps Happening
Cloud providers version their IAM action sets independently of their service launches. A service can go GA with its full action list, then add new actions for a feature shipped three months later — with no changelog most teams are subscribed to. Preview and beta services are worse: action names occasionally change between preview and GA, which means a policy scoped correctly during the beta can silently stop matching after the rename.
None of this is a documentation failure you can fix by reading more carefully. It’s a structural lag between provider release velocity and your policy review cycle. The fix isn’t reading faster — it’s having a checklist that runs the same way every time a new service shows up in a support ticket.
Step 1: Find the Exact Actions the Task Needs
AWS
AWS doesn’t expose a single CLI call that lists “every action for this service.” The two real sources:
- The Service Authorization Reference — the canonical, per-service action/resource/condition-key list. Not a CLI, but the ground truth.
- IAM Access Analyzer’s policy generation — build a least-privilege policy from what a role actually called, not from the full service action list:
# Let a trial role use the new service for a short period first, then generate
# a policy scoped to only the actions that were actually invoked
aws accessanalyzer start-policy-generation \
--policy-generation-details principalArn=arn:aws:iam::123456789012:role/new-service-trial-role \
--cloud-trail-details '{
"trails": [{"cloudTrailArn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/management-trail", "allRegions": true}],
"accessRole": "arn:aws:iam::123456789012:role/AccessAnalyzerMonitorRole"
}'
# Poll for the generated policy once the job completes
aws accessanalyzer get-generated-policy --job-id <JOB_ID>
For operators: this generates a policy from observed API calls, not theoretical need. Run the trial role for long enough to exercise every code path the team actually uses — a policy generated from five minutes of testing will be too narrow for production.
GCP
# Returns the exact permissions that CAN be granted on this specific resource —
# scoped to what that resource type supports, not the whole service
gcloud iam list-testable-permissions \
//aiplatform.googleapis.com/projects/my-project/locations/us-central1
Reading the output: each returned permission is one your team might plausibly need — GCP won’t list permissions that don’t apply to this resource type. Cross-reference against the task at hand and grant only the subset actually required.
Azure
# Lists every operation (permission) a resource provider namespace exposes
az provider operation show \
--namespace Microsoft.CognitiveServices \
--query "[].{Operation:name, Description:display.description}" \
-o table
This is the full menu for the namespace — most tasks need a handful of these operations, not all of them. Use it to find the exact operation string for a custom role definition rather than reaching for a built-in Contributor-level role.
Step 2: Scope the Resource, Not the Account
Finding the right action is half the job. The other half is refusing "Resource": "*".
// Bad — every foundation model, in every region, forever
{
"Effect": "Allow",
"Action": "bedrock:*",
"Resource": "*"
}
// Better — scoped to the specific model family the team asked for
{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel"],
"Resource": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude*"
}
The same discipline applies in GCP (bind the role to the specific project or resource, not the organization) and Azure (scope the role assignment to the resource group, not the subscription). A new service is the easiest moment to get this right — there’s no existing wildcard grant to “just extend.”
Step 3: Dry-Run Before You Grant
Test the policy against the real action before it’s live.
# AWS: simulate whether a principal's policy allows a specific action on a specific resource
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/new-service-role \
--action-names bedrock:InvokeModel \
--resource-arns arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2
# GCP: Policy Troubleshooter — does this principal have this permission on this resource, and why (or why not)?
gcloud policy-troubleshoot iam \
//aiplatform.googleapis.com/projects/my-project/locations/us-central1 \
--principal-email=svc-new-service@my-project.iam.gserviceaccount.com \
--permission=aiplatform.endpoints.predict
# Azure: preview what an IaC deployment (including role assignments) will change before applying it
az deployment group what-if \
--resource-group rg-new-service \
--template-file role-assignment.bicep
None of these grant access. All three tell you, before the grant is live, whether the policy you wrote actually does what you think it does.
Step 4: Attach a Guardrail, Not Just a Grant
A grant without a guardrail is one typo away from being an account-wide wildcard. Pair every new-service grant with a boundary that survives the next person copy-pasting the policy:
- AWS — a permission boundary on the role, or an SCP restricting the new service to specific OUs until it’s been reviewed
- GCP — an Org Policy constraint limiting resource locations or restricting which services can be enabled in the first place
- Azure — an Azure Policy assignment enforcing an allowed-services list at the subscription or management group level
The guardrail is what keeps “we scoped it correctly on day one” true after the policy gets copied into three other roles by someone who wasn’t in this conversation.
Step 5: Put a 30-Day Review on the Calendar
Treat every new-service grant as provisional, not permanent. A calendar reminder — not a ticket that can sit in a backlog — to check actual usage against granted permissions 30 days out.
This is the same discipline EP09’s least-privilege audit runs at the account level, applied at the moment of grant instead of six months later. Step 5 is what catches the case where the team’s actual usage turned out narrower than the trial period suggested — or wider, because the trial period didn’t exercise every path.
Production Gotchas
| Mistake | Impact | Fix |
|---|---|---|
| Granting console-wide access “temporarily” while waiting for Terraform provider support | Temporary access outlives the wait — nobody revokes it once the provider resource ships | Time-box the console grant explicitly; automate its removal, don’t rely on memory |
| Scoping a policy to a preview/beta action name | Silent breakage (or worse, silent continued access via an old wildcard) when the action renames at GA | Re-verify the action name against the Service Authorization Reference at GA, not just at preview |
| Assuming a new service reuses an existing condition key | Policy conditions that “should” restrict access silently don’t apply, because the new service doesn’t support that key | Check the service’s supported condition keys before reusing an existing policy pattern |
| Trial period too short for Access Analyzer’s policy generation | Generated policy is too narrow; production breaks on day one under real load | Run the trial long enough to exercise every code path, including error and retry paths |
Quick Reference
| Task | AWS | GCP | Azure |
|---|---|---|---|
| Discover exact actions | Service Authorization Reference + accessanalyzer start-policy-generation |
gcloud iam list-testable-permissions <resource> |
az provider operation show --namespace <Provider> |
| Dry-run a grant | aws iam simulate-principal-policy |
gcloud policy-troubleshoot iam |
az deployment group what-if |
| Guardrail | Permission boundary / SCP | Org Policy constraint | Azure Policy assignment |
| Recurring check | aws accessanalyzer unused-access findings |
IAM Recommender | Access Reviews |
Framework Alignment
| Framework | Control / ID | Mapping |
|---|---|---|
| CISSP | Domain 5 — IAM | Least privilege enforced at initial provisioning, not discovered later through audit |
| CISSP | Domain 1 — Security & Risk Management | Provisional access as a risk-acceptance decision with an explicit review date |
| ISO 27001:2022 | 5.15 Access control | Access rights defined and scoped to business need at the point of grant |
| ISO 27001:2022 | 5.18 Access rights | Review of access rights — extended here to newly granted permissions, not just standing ones |
| SOC 2 | CC6.1 | Logical access controls restrict access to authorized users and processes from first grant |
| SOC 2 | CC6.3 | Access is modified or revoked based on a defined review cadence |
Key Takeaways
- New cloud service IAM permissions ship on the provider’s schedule, not yours — the checklist has to run the same way every time, not only when someone remembers
- The fast path (
service:*on*) is also the path to next year’s wildcard-debt finding — scope it once, at the point of grant, instead of unwinding it later - AWS, GCP, and Azure each expose a different tool for discovering exact actions — none of them is “read the whole service’s docs and guess”
- A grant without a guardrail (permission boundary, SCP, Org Policy, Azure Policy) is one copy-paste away from becoming account-wide
- Provisional access needs an expiration built in from day one — a 30-day calendar review, not a hope that someone runs the audit eventually
What’s Next
This series doesn’t have a fixed episode count anymore — new cloud service IAM permissions are a continuous stream across AWS, GCP, and Azure, and this series continues covering them as they matter operationally, not on a fixed syllabus.
Get the next Cloud IAM episode in your inbox → linuxcent.com/subscribe