IAM

Identity and Access Management (IAM) service enables cloud administrators to define, manage, and audit an AWS user’s level of access to AWS APIs and resources within an account.

Subsections of IAM

Introduction

IAM, which stands for Identity Access Management, is an AWS service used to manage API and web console access to cloud resources.

Overview

When a user attempts to access an AWS resource through the API or web console, an HTTP request is sent to the endpoint for the target AWS service. After verifying the request signature, it is evaluated against all relevant IAM access policies, which dictate the allowed actions for the caller. If there exists at least one rule granting access and no explicit deny rule in any policies or permission boundaries, then the request is granted. Otherwise, it is rejected.

IAM Access Flow IAM Access Flow

Features

IAM primarily handles all aspects of authentication and authorization when accessing cloud services.1 Authentication determines the validity of the identity of a user while authorization determines whether a given identity is permitted to perform a given action.

Identity

Identity is an entity that enables access to an AWS account’s cloud resources.1 IAM provides the following entities for managing identity information:

  • Users: Represents an entity, typically a human user, that accesses AWS resources or services
  • Groups: Manages access for a collection of AWS users.
  • Roles: Temporarily assumed by an identity to perform a given set of actions.

Access

Once the identity of the caller has been established, the next step is to determine whether they are authorized to perform the requested action. To accomplish this, IAM evaluates the identity against all relevant policies, which specifies the allowed and restricted actions for a given user. These IAM policies can be directly assigned to users or groups or through Attribute Access Control (ABAC), which associates a policy with an identity based on the identity’s tags.2

Management

In addition to providing services to define identity information and access, IAM also provides services to ensure best practices are enforced regarding access. Some of this tooling includes analyzing user’s access patterns to refine permissions, identifying unused permissions, and other auditing capabilities.

Non-Features

IAM handles authentication and authorization when accessing resources via the AWS APIs. It is not intended for the following use-cases:

  • Network Security: IAM does not evaluate non-AWS API traffic to AWS resources, such as network connections to public servers or databases.
  • Application Identity Management: IAM is not intended to provide identity functionality for third-party applications and services, such as storing user credentials, and handling of login/account-reset flows.

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.