Chapter 02: IAM User and Group Management
9/1/25About 5 min
Chapter 02: IAM User and Group Management
Learning Objectives
- Create and manage IAM users
- Set up user access credentials
- Create and manage user groups
- Implement hierarchical management of user permissions
- Configure Multi-Factor Authentication (MFA)
Knowledge Points
IAM User Types and Purposes
User Group Management Architecture
Example Code
Complete IAM User Management Example
import boto3
import json
import secrets
import string
from datetime import datetime
from botocore.exceptions import ClientError
class IAMUserManager:
"""
IAM User Manager - provides complete user lifecycle management
"""
def __init__(self):
self.iam = boto3.client('iam')
def create_user_with_credentials(self, username, enable_console=True,
enable_programmatic=True, groups=None):
"""
Create a user and configure access credentials
Args:
username: The username
enable_console: Whether to enable console access
enable_programmatic: Whether to enable programmatic access
groups: A list of user groups
"""
print(f"Creating user: {username}")
try:
# 1. Create the user
user_response = self.iam.create_user(
UserName=username,
Tags=[
{'Key': 'CreatedBy', 'Value': 'IAMManager'},
{'Key': 'CreatedDate', 'Value': datetime.now().isoformat()},
{'Key': 'Environment', 'Value': 'Development'}
]
)
user_arn = user_response['User']['Arn']
print(f"✓ User created successfully: {user_arn}")
credentials = {}
# 2. Configure console access
if enable_console:
password = self._generate_password()
self.iam.create_login_profile(
UserName=username,
Password=password,
PasswordResetRequired=True
)
credentials['console_password'] = password
print("✓ Console access enabled")
# 3. Configure programmatic access
if enable_programmatic:
access_key_response = self.iam.create_access_key(UserName=username)
access_key = access_key_response['AccessKey']
credentials['access_key_id'] = access_key['AccessKeyId']
credentials['secret_access_key'] = access_key['SecretAccessKey']
print("✓ Programmatic access enabled")
# 4. Add to user groups
if groups:
for group_name in groups:
try:
self.iam.add_user_to_group(
GroupName=group_name,
UserName=username
)
print(f"✓ User added to group: {group_name}")
except ClientError as e:
if e.response['Error']['Code'] == 'NoSuchEntity':
print(f"⚠️ Group does not exist, creating group: {group_name}")
self.create_group(group_name)
self.iam.add_user_to_group(
GroupName=group_name,
UserName=username
)
print(f"✓ User added to newly created group: {group_name}")
else:
print(f"❌ Failed to add to group: {e}")
return credentials
except ClientError as e:
if e.response['Error']['Code'] == 'EntityAlreadyExists':
print(f"❌ User {username} already exists")
else:
print(f"❌ Failed to create user: {e}")
return None
def create_group(self, group_name, description="", policies=None):
"""
Create a user group and attach policies
Args:
group_name: The group name
description: The group description
policies: A list of policy ARNs to attach
"""
print(f"Creating user group: {group_name}")
try:
# Create the group
response = self.iam.create_group(
GroupName=group_name,
Path='/departments/'
)
group_arn = response['Group']['Arn']
print(f"✓ Group created successfully: {group_arn}")
# Attach policies
if policies:
for policy_arn in policies:
try:
self.iam.attach_group_policy(
GroupName=group_name,
PolicyArn=policy_arn
)
print(f"✓ Policy attached: {policy_arn}")
except ClientError as e:
print(f"❌ Failed to attach policy {policy_arn}: {e}")
return group_arn
except ClientError as e:
if e.response['Error']['Code'] == 'EntityAlreadyExists':
print(f"❌ Group {group_name} already exists")
else:
print(f"❌ Failed to create group: {e}")
return None
def setup_mfa_for_user(self, username, device_name="default"):
"""
Set up an MFA device for a user
Args:
username: The username
device_name: The MFA device name
"""
print(f"Setting up MFA for user {username}")
try:
# Create a virtual MFA device
response = self.iam.create_virtual_mfa_device(
VirtualMFADeviceName=f"{username}-{device_name}",
Tags=[
{'Key': 'Owner', 'Value': username},
{'Key': 'CreatedDate', 'Value': datetime.now().isoformat()}
]
)
mfa_device = response['VirtualMFADevice']
serial_number = mfa_device['SerialNumber']
qr_code_png = mfa_device['QRCodePNG']
print(f"✓ MFA device created successfully")
print(f" Device serial number: {serial_number}")
print(f" QR code data length: {len(qr_code_png)} bytes")
# Note: In a real application, you would need to display the QR code for the user to scan
# and then get two consecutive TOTP codes to activate the device
print("📱 Please scan the QR code with your MFA app, then call the enable_mfa_device method")
return {
'serial_number': serial_number,
'qr_code_png': qr_code_png
}
except ClientError as e:
print(f"❌ Failed to create MFA device: {e}")
return None
def enable_mfa_device(self, username, serial_number, auth_code1, auth_code2):
"""
Enable an MFA device
Args:
username: The username
serial_number: The MFA device serial number
auth_code1: The first authentication code
auth_code2: The second authentication code
"""
try:
self.iam.enable_mfa_device(
UserName=username,
SerialNumber=serial_number,
AuthenticationCode1=auth_code1,
AuthenticationCode2=auth_code2
)
print(f"✓ MFA device enabled: {serial_number}")
return True
except ClientError as e:
print(f"❌ Failed to enable MFA device: {e}")
return False
def rotate_access_keys(self, username):
"""
Rotate a user's access keys
Args:
username: The username
"""
print(f"Rotating access keys for user {username}")
try:
# 1. Get existing access keys
current_keys = self.iam.list_access_keys(UserName=username)
if len(current_keys['AccessKeyMetadata']) >= 2:
print("❌ User already has 2 access keys, cannot create more")
return None
# 2. Create a new access key
new_key_response = self.iam.create_access_key(UserName=username)
new_key = new_key_response['AccessKey']
print(f"✓ New access key created successfully: {new_key['AccessKeyId']}")
print(f"📋 New key information:")
print(f" Access Key ID: {new_key['AccessKeyId']}")
print(f" Secret Access Key: {new_key['SecretAccessKey']}")
# 3. Prompt the user to update their application configuration
print("⚠️ Please update your application configuration to use the new access key")
print("⚠️ After confirming that the new key works correctly, please call the delete_old_access_key method to delete the old key")
return new_key
except ClientError as e:
print(f"❌ Failed to rotate access keys: {e}")
return None
def delete_access_key(self, username, access_key_id):
"""
Delete a specified access key
Args:
username: The username
access_key_id: The ID of the access key to delete
"""
try:
self.iam.delete_access_key(
UserName=username,
AccessKeyId=access_key_id
)
print(f"✓ Access key deleted: {access_key_id}")
return True
except ClientError as e:
print(f"❌ Failed to delete access key: {e}")
return False
def get_user_summary(self, username):
"""
Get a summary of a user's details
Args:
username: The username
"""
print(f"Getting details for user {username}")
try:
summary = {}
# Basic user information
user = self.iam.get_user(UserName=username)
summary['user'] = {
'username': user['User']['UserName'],
'arn': user['User']['Arn'],
'user_id': user['User']['UserId'],
'create_date': user['User']['CreateDate'].isoformat(),
'tags': user['User'].get('Tags', [])
}
# Login profile
try:
login_profile = self.iam.get_login_profile(UserName=username)
summary['console_access'] = {
'enabled': True,
'password_reset_required': login_profile['LoginProfile']['PasswordResetRequired'],
'create_date': login_profile['LoginProfile']['CreateDate'].isoformat()
}
except ClientError as e:
if e.response['Error']['Code'] == 'NoSuchEntity':
summary['console_access'] = {'enabled': False}
# Access keys
access_keys = self.iam.list_access_keys(UserName=username)
summary['access_keys'] = []
for key in access_keys['AccessKeyMetadata']:
summary['access_keys'].append({
'access_key_id': key['AccessKeyId'],
'status': key['Status'],
'create_date': key['CreateDate'].isoformat()
})
# User groups
groups = self.iam.get_groups_for_user(UserName=username)
summary['groups'] = [group['GroupName'] for group in groups['Groups']]
# Directly attached policies
attached_policies = self.iam.list_attached_user_policies(UserName=username)
summary['attached_policies'] = [
policy['PolicyName'] for policy in attached_policies['AttachedPolicies']
]
# Inline policies
inline_policies = self.iam.list_user_policies(UserName=username)
summary['inline_policies'] = inline_policies['PolicyNames']
# MFA devices
mfa_devices = self.iam.list_mfa_devices(UserName=username)
summary['mfa_devices'] = [
device['SerialNumber'] for device in mfa_devices['MFADevices']
]
return summary
except ClientError as e:
print(f"❌ Failed to get user information: {e}")
return None
def _generate_password(self, length=12):
"""
Generate a secure password
Args:
length: The password length
"""
# Ensure the password contains various character types
password_chars = (
string.ascii_lowercase +
string.ascii_uppercase +
string.digits +
"!@#$%^&*()"
)
password = ''.join(secrets.choice(password_chars) for _ in range(length))
# Ensure it contains at least one number, uppercase letter, lowercase letter, and symbol
if (not any(c.islower() for c in password) or
not any(c.isupper() for c in password) or
not any(c.isdigit() for c in password) or
not any(c in "!@#$%^&*()" for c in password)):
return self._generate_password(length)
return password
# Example usage
def demo_user_management():
"""
User management demonstration
"""
manager = IAMUserManager()
# 1. Create a development group
dev_policies = [
'arn:aws:iam::aws:policy/PowerUserAccess', # Development environment permissions
]
manager.create_group('developers', description="Development team", policies=dev_policies)
# 2. Create a new user
new_user_credentials = manager.create_user_with_credentials(
"new_developer",
groups=['developers']
)
if new_user_credentials:
print("\nNew user credentials:")
print(json.dumps(new_user_credentials, indent=2))
# 3. Get user summary
summary = manager.get_user_summary("new_developer")
if summary:
print("\nUser summary:")
print(json.dumps(summary, indent=2))
# 4. Set up MFA (requires manual intervention)
# mfa_info = manager.setup_mfa_for_user("new_developer")
# if mfa_info:
# # In a real app, you would display the QR code and get the codes from the user
# auth_code1 = input("Enter the first authentication code: ")
# auth_code2 = input("Enter the second authentication code: ")
# manager.enable_mfa_device(
# "new_developer",
# mfa_info['serial_number'],
# auth_code1,
# auth_code2
# )
if __name__ == "__main__":
demo_user_management()