Skip to main content
ANVISoftware Solutions
Lesson 4 of 17Intermediate16 min

Identity and Permissions

By the end of this lesson

Grant least-privilege access to people and services.

Every action in a cloud account is taken by an identity. A person signing in is an identity. So is the employees API when it reads a photo from storage, and so is the deployment pipeline when it pushes a new version.

Permissions are granted to identities, and the mechanism is the same everywhere even though the names differ: a role or policy describes a set of allowed actions, and you attach it to an identity, scoped to a set of resources. Get this right and a mistake is contained. Get it wrong and a single leaked credential reaches everything.

The pieces, with both providers' names:

Identity
Who or what is acting. A user, a group, or a non-human identity for an application. Azure calls the application kind a service principal or a managed identity; AWS calls it an IAM role.
Role or policy
The set of permitted actions. Azure has role definitions assigned at a scope; AWS has policies attached to roles or users. Both answer the same question: which actions, on which resources.
Scope
The set of resources a grant applies to. Narrower is better: one storage container rather than the whole account. This is the part most often left wide open because it is quicker.
Least privilege
Granting exactly the permissions a task needs and nothing else. It is a practice rather than a feature, and it is maintained by removing access as often as you grant it.
Managed identity or IAM role
An identity the provider attaches to your running service, with credentials issued and rotated automatically. Your code asks the platform for a token instead of holding a secret. This is how service-to-service authentication should work.

The two ways a service can authenticate to another service, side by side:

 Long-lived key or secretManaged identity or IAM role
What your code holdsA string that grants access until someone revokes itNothing. It asks the platform for a short-lived token at run time
RotationA manual job someone has to remember, usually during an incidentAutomatic, and invisible to your code
If it leaksUsable by anyone, anywhere, until rotatedThere is no durable value to leak; tokens are short-lived and tied to the workload
Local developmentWorks everywhere, which is exactly why it spreads into laptops and shared documentsNeeds a developer-signin fallback, which is extra setup
Setup effortCopy and pasteEnable the identity, then grant it a role at the right scope
Use whenThe platform genuinely cannot issue an identity, and you have a rotation planAnything running on the provider's own compute
A narrowly scoped policy for the employees API
JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadWriteEmployeePhotosOnly",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::anvi-employees-store/photos/*"
    },
    {
      "Sid": "ListOnlyThePhotosPrefix",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::anvi-employees-store",
      "Condition": {
        "StringLike": { "s3:prefix": "photos/*" }
      }
    }
  ]
}
  • Action lists the operations allowed. This policy can read and write objects, and it cannot delete them — so a bug in the API cannot remove a photo, and neither can anyone who compromises the API.
  • Resource is the scope, and the trailing photos/* is the important character sequence. Without it the grant covers the whole bucket, including anything unrelated stored there later.
  • Listing a bucket is a separate permission from reading objects in it, which catches people out. The condition on the prefix keeps the listing to the photos folder rather than revealing every key in the bucket.
  • Azure expresses the same intent differently — a built-in role such as Storage Blob Data Contributor assigned to the managed identity at the scope of one container. Read the role's actions before assigning it; built-in roles are convenient and some are broader than their name suggests.

Giving the employees API access to its photo storage with no secret anywhere. The commands differ by provider; the four steps do not.

  1. Turn on an identity for the service

    Enable a system-assigned managed identity on the app, or create an IAM role and attach it to the compute. The provider now has an identity it can issue tokens for, and nothing has been granted yet.

  2. Decide the smallest useful permission

    Write down the actions the code performs. Reads a photo, writes a photo. Not delete, not list the whole account, not manage the storage resource itself.

  3. Grant it at the narrowest scope

    Assign the role on the single container or bucket prefix, not on the resource group, subscription or account. Scope is where least privilege is either honoured or quietly abandoned.

  4. Remove the old credential

    Delete the connection string from configuration and rotate it at the provider so the old value stops working. Until you do, the secret is still valid and you have added a mechanism rather than replaced one.

Summary

  • Every action is taken by an identity, and permissions are granted to identities at a scope
  • Least privilege means the actions the code performs, on the narrowest resource scope that covers them
  • Prefer managed identities and IAM roles over long-lived keys, because they remove the secret you can leak
  • A committed credential is compromised; rotate it rather than deleting the line
  • Separate identities per workload keep logs meaningful and revocation safe

Practice

Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.

Try it yourself

Narrow one grant

Take any permission you have granted in a cloud account and write down: which actions does the code actually perform, and what is the smallest resource scope that covers them?

Then compare that with what is currently granted.

Show solution

Most first attempts find a grant that is one or two levels too wide — a subscription or account scope where a single resource would do, or a contributor role where a data-reader role covers the code's actual calls.

The useful habit is deriving the permission from the code rather than from the error message. Working from errors tends to end at the first role that makes the error stop, which is usually broader than needed.

Think about it

Trace the blast radius

An access key for the employees API is accidentally published in a public repository. The key has a contributor role on the whole production resource group.

List what someone who finds it can do, and then say what would have been different if the API had used a managed identity scoped to one storage container.

Show solution

With a contributor role on the resource group: read and change the database, read every object in storage, alter configuration, create resources that cost money, and delete things. The key works from anywhere in the world with no further access to your systems.

With a managed identity scoped to one container, there is no key in the repository to find. The service gets short-lived tokens from the platform, and those tokens are issued to the workload rather than to whoever holds a string.

Note the second half of the answer too: scope. Even if a token were somehow captured, it would permit reading and writing photos in one container for a short window — not touching the database. Least privilege does not prevent the leak; it decides how much the leak is worth.

Knowledge check

Nothing is recorded and there is no score. The explanation appears either way.

Why is a managed identity or IAM role preferred over a long-lived access key for service-to-service calls?
A credential was committed to a repository three weeks ago and the line has since been deleted. What must you do?

Saved in this browser only.