Identities

An IAM identity refers to some entity that provides access to AWS resources. There are several types of identities provided by IAM for this purpose, including users, user groups, and roles. By default, newly created identities have no permissions. These entities must be explicitly assigned permissions to successfully perform API calls, which will be discussed in the following Access section.

Subsections of Identity

Users

Users

IAM users represent a human user or programmatic workload and provides access to AWS resources or services. Users belong to a single parent account and consists of the following properties and credentials:

  • Access Credentials: Programmatic long-lived credentials consisting of an access key ID and secret key.
  • Path: The IAM namespace which contains the user.
  • Login Profile: AWS web console credentials.
  • MFA Device: MFA devices required for accessing AWS resources, whether through the web console or programmatically.
  • SSL Certificates: May be used to authenticate with certain AWS services.
  • SSH Keys: SSH public keys to be used with AWS Codecommit, a git repository service, during application development.

Command

aws iam get-user --user-name $USERNAME

Output

{
    "User": {
        "Path": "/",
        "UserName": "username",
        "UserId": "ADSAYFF72444NEI6MNIOP",
        "Arn": "arn:aws:iam::012345678910:user/username",
        "CreateDate": "2019-07-10T23:13:11+00:00",
        "Tags": [
            {
                "Key": "TagName",
                "Value": "TagValue"
            }
        ]
    }
}

Upon creating a new AWS account, a root user is automatically created, which has complete access to all cloud resources with limited exceptions. For this reason, it is recommended that you create an administrator user for everyday use and restrict access to root user credentials.

Console Login Process

There are two possible login flows depending on the type of user attempting to sign in.1

Root User

To sign in as a root user, simply navigate to the sign-in page, select “Root User”, and enter the root user credentials and optional MFA code.

IAM User

To sign in to the AWS console as a non-root user, you can either use standard sign-in webpage and provide the target AWS account number or specify the account alias in the URL as such: https://<account_id_or_alias>.signin.aws.amazon.com/console/.

Subsections of Users

Provision Users

To provision an IAM user, the only required property is the username, which is unique within the AWS account.

pulumi up -y
pulumi destroy -y
// iam/identities/users/create_user/ts/index.ts

import * as aws from "@pulumi/aws";

// Create IAM user with long-lived access credentials
const user = new aws.iam.User("techsquawks-user", {
    name: "techsquawks-user"
});

// Export user information
export const userArn = user.arn;
export const userName = user.name;
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/create_user/js/index.js

"use strict";
const aws = require("@pulumi/aws");

// Create IAM user with long-lived access credentials
const user = new aws.iam.User("techsquawks-user", {
    name: "techsquawks-user"
});

// Export user information
exports.userArn = user.arn;
exports.userName = user.name;
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
# iam/identities/users/create_user/py/__main__.py

import pulumi
from pulumi_aws import iam

# Create IAM user with long-lived access credentials
user = iam.User("techsquawks-user", name="techsquawks-user")

# Export user information
pulumi.export('userArn', user.arn)
pulumi.export('userName', user.name)
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/create_user/go/main.go

package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Create IAM user with long-lived access credentials
		user, err := iam.NewUser(ctx, "techsquawks-user", &iam.UserArgs{
			Name: pulumi.String("techsquawks-user"),
		})
		if err != nil {
			return err
		}

		// Export user information
		ctx.Export("userArn", user.Arn)
		ctx.Export("userName", user.Name)
		return nil
	})
}
Deploy Repository Report Issue

Stack Outputs

Outputs:
    userArn : "arn:aws:iam::012345678910:user/techsquawks-user"
    userName: "techsquawks-user"

To further verify that the user was provisioned correctly, the identity can be fetched via the get-user IAM API call.

Command

aws iam get-user --user-name techsquawks-user

Output

{
    "User": {
        "Path": "/",
        "UserName": "techsquawks-user",
        "UserId": "DBSAYFF32444NEI6MNIOP",
        "Arn": "arn:aws:iam::012345678910:user/techsquawks-user",
        "CreateDate": "2023-07-10T23:13:11+00:00",
    }
}

Paths

Paths are analogous to namespaces and help in organizing IAM users both for reporting and associating permissions.

pulumi up -y
pulumi destroy -y
// iam/identities/users/paths/ts/index.ts

import * as aws from "@pulumi/aws";

// Create IAM user with long-lived access credentials
const user1 = new aws.iam.User("techsquawks-user-1", {
    name: "techsquawks-user",
    path: "/example/path/1/"
});
const user2 = new aws.iam.User("techsquawks-user-2", {
    name: "techsquawks-user-2",
    path: "/example/path/2/"
});
const user2a = new aws.iam.User("techsquawks-user-2a", {
    name: "techsquawks-user-2a",
    path: "/example/path/2/"
});
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/paths/js/index.js

"use strict";
const aws = require("@pulumi/aws");

// Create IAM user with long-lived access credentials
const user1 = new aws.iam.User("techsquawks-user-1", {
    name: "techsquawks-user1",
    path: "/example/path/1/"
});
const user2 = new aws.iam.User("techsquawks-user-2", {
    name: "techsquawks-user-2",
    path: "/example/path/2/"
});
const user2a = new aws.iam.User("techsquawks-user-2a", {
    name: "techsquawks-user-2a",
    path: "/example/path/2/"
});
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
# iam/identities/users/paths/py/__main__.py

import pulumi
from pulumi_aws import iam

# Create IAM user with long-lived access credentials
user1 = iam.User("techsquawks-user-1", name="techsquawks-user1", path="/example/path/1/")
user2 = iam.User("techsquawks-user-2a", name="techsquawks-user2", path="/example/path/2/")
user2a = iam.User("techsquawks-user-2b", name="techsquawks-user2a", path="/example/path/2/")
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/paths/go/main.go

package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Create IAM user with long-lived access credentials
		_, err := iam.NewUser(ctx, "techsquawks-user1", &iam.UserArgs{
			Name: pulumi.String("techsquawks-user1"),
			Path: pulumi.String("/example/path/1/"),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewUser(ctx, "techsquawks-user2", &iam.UserArgs{
			Name: pulumi.String("techsquawks-user2"),
			Path: pulumi.String("/example/path/2/"),
		})
		if err != nil {
			return err
		}
		_, err = iam.NewUser(ctx, "techsquawks-user2a", &iam.UserArgs{
			Name: pulumi.String("techsquawks-user2a"),
			Path: pulumi.String("/example/path/2/"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
Deploy Repository Report Issue

Users who belong to the same path can be targeted in certain IAM operations, such as listing users:

Command

aws iam list-users --path "/example/path/2/"

Output

{
    "Users": [
        {
            "Path": "/example/path/2/",
            "UserName": "techsquawks-user-2",
            "UserId": "AFCAYGH7AQ44FXTSPCX55",
            "Arn": "arn:aws:iam::012345678910:user/example/path/2/techsquawks-user-2",
            "CreateDate": "2023-07-24T22:39:32+00:00"
        },
		{
            "Path": "/example/path/2/",
            "UserName": "techsquawks-user-2a",
            "UserId": "BEAAYHI7AQ45GXTQPCX44",
            "Arn": "arn:aws:iam::012345678910:user/example/path/2/techsquawks-user-2a",
            "CreateDate": "2023-07-24T22:39:32+00:00"
        }
    ]
}

Access Credentials

To access cloud resources through the API, users may be associated with a set of long-lived access credential key pair, consisting of an access key ID and secret key for signing requests.

pulumi up -y
pulumi destroy -y
// iam/identities/users/access_credentials/ts/index.ts

import * as aws from "@pulumi/aws";

// Create IAM user with long-lived access credentials
const user = new aws.iam.User("techsquawks-user", {
    name: "techsquawks-user"
});
const credentials = new aws.iam.AccessKey("techsquawks-user-credentials", {user: user.name});

// Export user information
export const userArn = user.arn;
export const userName = user.name;
export const userAccessKeyId = credentials.id;
export const userAccessSecret = credentials.secret;
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/access_credentials/js/index.js

"use strict";
const aws = require("@pulumi/aws");

// Create IAM user with long-lived access credentials
const user = new aws.iam.User("techsquawks-user", {
    name: "techsquawks-user"
});
const credentials = new aws.iam.AccessKey("techsquawks-user-credentials", {user: user.name});

// Export user information
exports.userArn = user.arn;
exports.userName = user.name;
exports.userAccessKeyId = credentials.id;
exports.userAccessSecret = credentials.secret;
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
# iam/identities/users/access_credentials/py/__main__.py

import pulumi
from pulumi_aws import iam

# Create IAM user with long-lived access credentials
user = iam.User("techsquawks-user", name="techsquawks-user")
credentials = iam.AccessKey("techsquawks-user-credentials", user=user.name)

# Export user information
pulumi.export('userArn', user.arn)
pulumi.export('userName', user.name)
pulumi.export('userAccessKeyId', credentials.id)
pulumi.export('userSecretKey', credentials.secret)
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/access_credentials/go/main.go

package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Create IAM user with long-lived access credentials
		user, err := iam.NewUser(ctx, "techsquawks-user", &iam.UserArgs{
			Name: pulumi.String("techsquawks-user"),
		})
		if err != nil {
			return err
		}
		credentials, err := iam.NewAccessKey(ctx, "techsquawks-user-credentials", &iam.AccessKeyArgs{
			User: user.Name,
		})
		if err != nil {
			return err
		}

		// Export user information
		ctx.Export("userArn", user.Arn)
		ctx.Export("userName", user.Name)
		ctx.Export("userAccessKeyId", credentials.ID())
		ctx.Export("userSecretKey", credentials.Secret)
		return nil
	})
}
Deploy Repository Report Issue

Stack Outputs

Outputs:
    userAccessKeyId: "BKINYAJ78Q44D3CT4NER"
    userArn :        "arn:aws:iam::012345678910:user/techsquawks-user"
    userName:        "techsquawks-user"
    userSecretKey:   [secret]
Info

Sensitive values are hidden by default in the Pulumi stack output. To get the secret key value, execute pulumi stack output userSecretKey --show-secrets --stack dev

To validate that the access credentials work as expected, invoke the caller identity API using the newly provisioned credentials:

Command

AWS_ACCESS_KEY_ID=$(pulumi stack output userAccessKeyId --stack dev)
AWS_SECRET_ACCESS_KEY=$(pulumi stack output userSecretKey --stack dev --show-secrets)
aws sts get-caller-identity

Output

{
    "UserId": "BKXNYAA78Q56W3CT4NTJ",
    "Account": "012345678910,
    "Arn": "arn:aws:iam::012345678910:user/techsquawks-user"
}

Console Passwords

While API access requires user access keys, password credentials are required for accessing the AWS account resources via the web console. IAM provides various API methods and resources for managing user passwords. For instance, login profiles define the password constraints for a given user.

pulumi up -y
pulumi destroy -y
// iam/identities/users/user_login_profile/ts/index.ts

import * as aws from "@pulumi/aws";

// Create IAM user with password/console credentials
const user = new aws.iam.User("techsquawks-user", {
    name: "techsquawks-user",
    forceDestroy: true
});
const loginProfile = new aws.iam.UserLoginProfile("techsquawks-user-login-profile", {
    user: user.name,
    passwordLength: 15,
    passwordResetRequired: true
});
export const password = loginProfile.password;
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/user_login_profile/js/index.js

"use strict";
const aws = require("@pulumi/aws");

// Create IAM user with password/console credentials
const user = new aws.iam.User("techsquawks-user", {
    name: "techsquawks-user",
    forceDestroy: true
});
const loginProfile = new aws.iam.UserLoginProfile("techsquawks-user-login-profile", {
    user: user.name,
    passwordLength: 15,
    passwordResetRequired: true
});
exports.password = loginProfile.password;
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
# iam/identities/users/user_login_profile/py/__main__.py

import pulumi
from pulumi_aws import iam

# Create IAM user with long-lived access credentials
user = iam.User("techsquawks-user", name="techsquawks-user")
login_profile = iam.UserLoginProfile("techsquawks-user-login-profile",
    user=user.name,
    password_length=15,
    password_reset_required=True
);

pulumi.export('password', login_profile.password)
Deploy Repository Report Issue
pulumi up -y
pulumi destroy -y
// iam/identities/users/user_login_profile/go/main.go

package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Create IAM user with password/console credentials
		user, err := iam.NewUser(ctx, "techsquawks-user", &iam.UserArgs{
			Name: pulumi.String("techsquawks-user"),
		})
		if err != nil {
			return err
		}
		loginProfile, err := iam.NewUserLoginProfile(ctx, "loginProfile", &iam.UserLoginProfileArgs{
			User:                  user.Name,
			PasswordLength:        pulumi.Int(15),
			PasswordResetRequired: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		ctx.Export("password", loginProfile.Password)
		return nil
	})
}
Deploy Repository Report Issue

Groups

Groups

IAM groups act as containers for multiple IAM users. Groups can have IAM policies associated with them, which are automatically inherited by child users when evaluating access. This allows administrators to more easily manage access for multiple users simultaneously. User may belong to several groups, but groups cannot contain other groups.

There is no default group for newly created users. Therefore, any groups must be explicitly created and poclicies must be explicitly assigned to the groups.1

Figure 1: Permissions may be assigned to groups which are automatically applied to assigned users.

Figure 1: Permissions may be assigned to groups which are automatically applied to assigned users.

Groups are rather simply entities, with the main component required being the group name. Similar to users, groups also may have a path specified which can be used to further distinguish groups while querying.

Command

aws iam list-groups

Output

{
    "Groups": [
        {
            "Path": "/",
            "GroupName": "Admins",
            "GroupId": "AGPAYGH7AQ44DKI7UGME4",
            "Arn": "arn:aws:iam::012345678910:group/Administrators",
            "CreateDate": "2019-10-01T22:50:30+00:00"
        },
        {
            "Path": "/",
            "GroupName": "Developers",
            "GroupId": "AGPAYGH7AQ44BVG3THNH7",
            "Arn": "arn:aws:iam::012345678910:group/Developers",
            "CreateDate": "2019-07-10T23:10:47+00:00"
        },
        {
            "Path": "/",
            "GroupName": "Devops",
            "GroupId": "ADSAYFF72444NEI6MNIOP",
            "Arn": "arn:aws:iam::012345678910:group/Devops",
            "CreateDate": "2019-07-10T23:10:47+00:00"
        }
    ]
}

Roles

Roles

Similar to users, roles are IAM identities intended to grant access to AWS resources. However, unlike users, roles lack any long-lived credentials such as console passwords and access key pairs. Rather, they are assumed by other authorized identities temporarily to obtain a certain level of access. 1 Once assumed, the user is given temporary short-lived credentials via the AWS STS service, which are associated with the role permissions.

In addition to authorizing other human users to perform certain actions, roles may also be associated with AWS services to perform certain API calls. These are known as service-linked roles. 2

Use Cases 3

  • Provide access across multiple AWS accounts: Enables users to have certain role-defined access for a given account.
  • Provide access for non-AWS workloads: Third-party applications may assume the role to assume access to AWS resources.
  • Provide access to third-party AWS accounts: Allows other organizations with AWS users and services to access your account as permitted by a given role.
  • Provide access through identity federation: Enables organizations to leverage their existing user information and define role mappings for given groups.