For the AWS IAM service, a Principal element is used to specify an entity that is allowed (or denied) access to the resource. This is only applicable to IAM roles, where the principal is an entity who can assume that role, or in a resource-based policy where the entity is being allowed access to the resource, such as an S3 bucket1.

An example of an Elasticsearch resource-based policy (often referred to as a domain access policy in the Elasticsearch context) is provided below:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "111111111111",
          "arn:aws:iam::222222222222:user/test-user",
          "arn:aws:iam::333333333333:role/role-name",
          "arn:aws:sts::333333333333:assumed-role/role-name/role-session-name"
        ]
      },
      "Action": [
        "es:*"
      ],
      "Resource": "arn:aws:es:ap-southeast-2:111111111111:domain/test-domain/*"
    }
  ]
}

Example domain access policy for an Elasticsearch cluster, showing AWS Account Id, assumed-role and AWS IAM role principal types

Recently whilst deploying an Elasticsearch domain access policy, I was wrestling with an error that AWS kept replying with. I initially thought I’d fat-fingered the ARN for the role and that an extra colon or an extraneous segment had slipped in somewhere. After some double-checking I noted that when I was deploying the POC, the role had already existed when I updated the domain access policy.

So I decided to test whether order was important - does a principal need to exist before you add it to an trust or resource policy? The test would be simple enough:

  1. Create a dummy IAM role (i.e. don’t attach any permissions to it)
  2. Assign a principal to it that clearly doesn’t exist (e.g. use an AWS ELB account id combined with a random string for the role name to test)
  3. Assign a principal to it that does exist (try a role from a different account you control)

The behaviours observed from this test were:

  • Applying/updating a policy with a principal that does exist will work
  • Applying/updating a policy with a principal that does not exist will not work


I was a bit surprised by this behaviour when I observed it. On the one hand, it’s useful for preventing errors/typos in principals - IAM is complex enough without typos being introduced! But on the other hand it does provide a way to scout other AWS accounts by enumeration. For example, by enumerating updates to the principals in a trust statement I could:

  • Determine whether an AWS Account Id is valid
  • Determine whether a role or user exists in another AWS account

Actively enumerating and exploring other AWS accounts in this manner is likely a violation of service agreements and not covered by the Customer Service Policy for Penetration Testing so running this as a script or automation at scale is likely going to get your account on an abuse report, so I definitely do not recommend performing these actions.

As I was a bit concerned by this behaviour, I fired a short email explaining what I observed to aws-security@amazon.com, as per the Vulnerability Reporting practice. I was interested to determine whether this was a known & accepted behaviour, I expected that it was, but it’s good to be sure. The reply was quite rapid returning within a few hours:

Hi Brent,

Thank you for bringing your security concern to our attention. We greatly appreciate and encourage reports from the security community worldwide.

We do not believe the behavior you describe in this report presents a security concern, rather, it is expected behavior.

If you discover or become aware of other security concerns specific to AWS products and services, please do not hesitate to contact us again at aws-security@amazon.com

This at least confirmed that it was expected behaviour, and they likely have mitigating controls that offset the risk of abuse (I presume).

I did later find one benefit to this behaviour other than user experience. As per the documentation, if you specify an IAM role as a principal in a role trust policy, AWS transforms the ARN to the role’s unique principal ID when saved. This happed in the background and the console will translate this to the ‘friendly’ role ARN when you view the trust policy for the role. What this prevents is escalation by deleting the trusted role (wherever it exists) and recreating it as the new role will have a different unique principal ID to that which was saved when creating the trust relationship2.

This is a reasonable trade-off to the potential for abuse described above, but it also informs us that we can’t destroy & recreate roles that are trusted without consequence.


I hope you found the above blog post of interest and informative, I definitely learned a bit more about IAM principals going through the process!

tl;dr AWS will validate an IAM Principal across unrelated accounts, allowing for the validation of an AWS Account ID & whether a role exists in that account. This is an accepted behaviour.


The image header for this blogpost was created by Kai Brame their work can be found as linked and amongst others on Unsplash.


  1. As explained in the official AWS IAM documentation as of date of this post. ↩︎

  2. Detailed explanation of IAM roles as principals in Trust Policy behaviour found here, and relied upon as of date of this post. ↩︎