Chapter 9: Federated Identity and Single Sign-On
9/1/25About 10 min
Chapter 9: Federated Identity and Single Sign-On
Learning Objectives
- Understand the concept and architecture of federated identity
- Configure SAML 2.0 identity federation
- Implement OpenID Connect (OIDC) integration
- Master AWS SSO (Identity Center) configuration
- Establish an enterprise-level single sign-on solution
Federated Identity Architecture Diagram
9.1 Federated Identity Basics
9.1.1 Concept and Architecture of Federated Identity
import boto3
import json
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
import base64
import urllib.parse
def understand_identity_federation():
"""
Understand the concept and architecture of federated identity
"""
federation_concepts = {
"identity_federation": {
"definition": "Federated identity allows users from external identity providers to access AWS resources without creating IAM users in AWS",
"benefits": [
"Single sign-on experience",
"Centralized identity management",
"Reduced password management complexity",
"Support for enterprise-level identity providers",
"Enhanced security with temporary credentials"
],
"key_components": [
"Identity Provider (IdP)",
"Service Provider (SP) - AWS",
"Identity Assertion",
"Trust Relationship",
"Role Mapping"
]
},
"federation_types": {
"saml_federation": {
"description": "Enterprise-level federation based on the SAML 2.0 protocol",
"use_cases": ["Enterprise Active Directory", "ADFS", "Third-party IdPs"],
"workflow": [
"User authenticates with the corporate IdP",
"IdP generates a SAML assertion",
"User is redirected to the AWS SAML endpoint",
"AWS validates the assertion and provides temporary credentials",
"User accesses AWS resources with temporary credentials"
]
},
"oidc_federation": {
"description": "Web identity federation based on OpenID Connect",
"use_cases": ["Mobile apps", "Web apps", "Third-party logins"],
"workflow": [
"User authenticates with an OIDC provider",
"Obtain an identity token",
"Call AWS STS with the token",
"Get temporary AWS credentials",
"Access AWS resources"
]
},
"web_identity_federation": {
"description": "Direct integration with web identity providers",
"use_cases": ["Amazon Cognito", "Google", "Facebook login"],
"workflow": [
"User logs in with a web IdP",
"Obtain an identity token",
"Directly use AssumeRoleWithWebIdentity",
"Get temporary AWS credentials"
]
}
}
}
# Trust model for federated identity
trust_model = {
"trust_establishment": {
"identity_provider_setup": "Configure the identity provider in AWS",
"trust_policy_creation": "Create a trust policy to allow the IdP to assume a role",
"attribute_mapping": "Map IdP attributes to AWS roles",
"condition_validation": "Validate federation conditions and constraints"
},
"security_considerations": [
"Validate the integrity and authenticity of SAML assertions",
"Use appropriate conditions to restrict role access",
"Regularly rotate encryption keys",
"Monitor and audit federated access",
"Implement the principle of least privilege"
]
}
print("📋 Federated Identity Concepts:")
print(f"Definition: {federation_concepts['identity_federation']['definition']}")
print("\nBenefits:")
for benefit in federation_concepts['identity_federation']['benefits']:
print(f" • {benefit}")
print("\nKey Components:")
for component in federation_concepts['identity_federation']['key_components']:
print(f" • {component}")
print("\n📋 Federation Types:")
for fed_type, details in federation_concepts['federation_types'].items():
print(f"\n{fed_type.replace('_', ' ').title()}:")
print(f" Description: {details['description']}")
print(f" Use cases: {', '.join(details['use_cases'])}")
return federation_concepts, trust_model
class IdentityFederationManager:
"""Identity Federation Manager"""
def __init__(self):
self.iam = boto3.client('iam')
self.sts = boto3.client('sts')
def create_saml_identity_provider(self, provider_name, metadata_document):
"""Create a SAML identity provider"""
try:
response = self.iam.create_saml_provider(
SAMLMetadataDocument=metadata_document,
Name=provider_name,
Tags=[
{'Key': 'Type', 'Value': 'SAML'},
{'Key': 'Purpose', 'Value': 'Federation'},
{'Key': 'CreatedDate', 'Value': datetime.now().strftime('%Y-%m-%d')}
]
)
provider_arn = response['SAMLProviderArn']
print(f"✅ SAML identity provider created successfully: {provider_arn}")
return provider_arn
except Exception as e:
print(f"❌ Failed to create SAML identity provider: {str(e)}")
return None
def create_oidc_identity_provider(self, provider_url, client_ids, thumbprints):
"""Create an OIDC identity provider"""
try:
response = self.iam.create_open_id_connect_provider(
Url=provider_url,
ClientIDList=client_ids,
ThumbprintList=thumbprints,
Tags=[
{'Key': 'Provider', 'Value': provider_name},
{'Key': 'Type', 'Value': 'OIDC'}
]
)
provider_arn = response['OpenIDConnectProviderArn']
print(f"✅ OIDC identity provider created successfully: {provider_arn}")
return provider_arn
except Exception as e:
print(f"❌ Failed to create OIDC identity provider: {str(e)}")
return None
# Demonstrate federated identity concepts
federation_concepts, trust_model = understand_identity_federation()
federation_manager = IdentityFederationManager()
print("\n📋 Trust Model Components:")
for component, description in trust_model['trust_establishment'].items():
print(f" {component.replace('_', ' ').title()}: {description}")
9.2 SAML 2.0 Federation Configuration
9.2.1 Enterprise-level SAML Integration
def configure_saml_federation():
"""Configure SAML 2.0 federation"""
class SAMLFederationManager:
def __init__(self):
self.iam = boto3.client('iam')
self.sts = boto3.client('sts')
def setup_enterprise_saml_federation(self, config):
"""Set up enterprise SAML federation"""
# 1. Create a SAML identity provider
provider_arn = self._create_saml_provider(config)
if not provider_arn:
return None
# 2. Create federation roles
federation_roles = self._create_federation_roles(provider_arn, config)
# 3. Set up attribute mapping
attribute_mapping = self._setup_attribute_mapping(config)
# 4. Generate an IdP configuration guide
idp_config = self._generate_idp_configuration(provider_arn, federation_roles)
return {
'provider_arn': provider_arn,
'federation_roles': federation_roles,
'attribute_mapping': attribute_mapping,
'idp_configuration': idp_config
}
def _create_saml_provider(self, config):
"""Create a SAML identity provider"""
# Example SAML metadata (simplified)
metadata_template = f'''<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
entityID="{config['entity_id']}">
<md:IDPSSODescriptor WantAuthnRequestsSigned="false"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
{config['signing_certificate']}
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="{config['sso_url']}"/>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="{config['sso_url']}"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>'''
try:
response = self.iam.create_saml_provider(
SAMLMetadataDocument=metadata_template,
Name=config['provider_name']
)
return response['SAMLProviderArn']
except Exception as e:
print(f"❌ Failed to create SAML provider: {str(e)}")
return None
def _create_federation_roles(self, provider_arn, config):
"""Create federation roles"""
roles = {}
for role_config in config['roles']:
role_name = role_config['name']
# SAML federation trust policy
trust_policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": provider_arn
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
}
}
}
]
}
# Add additional conditions
if role_config.get('conditions'):
trust_policy['Statement'][0]['Condition'].update(role_config['conditions'])
try:
# Create the role
role_response = self.iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=json.dumps(trust_policy),
Description=f'SAML federated role: {role_name}',
MaxSessionDuration=role_config.get('session_duration', 3600)
)
role_arn = role_response['Role']['Arn']
# Attach permission policies
for policy in role_config.get('policies', []):
if policy['type'] == 'managed':
self.iam.attach_role_policy(
RoleName=role_name,
PolicyArn=policy['arn']
)
elif policy['type'] == 'inline':
self.iam.put_role_policy(
RoleName=role_name,
PolicyName=policy['name'],
PolicyDocument=json.dumps(policy['document'])
)
roles[role_name] = {
'arn': role_arn,
'saml_role_value': f"{role_arn},{provider_arn}"
}
print(f"✅ Federation role created successfully: {role_name}")
except Exception as e:
print(f"❌ Failed to create federation role {role_name}: {str(e)}")
return roles
def _setup_attribute_mapping(self, config):
"""Set up attribute mapping"""
attribute_mapping = {
"required_attributes": {
"https://aws.amazon.com/SAML/Attributes/Role": {
"description": "The AWS role the user can assume",
"format": "{RoleArn},{ProviderArn}",
"example": "arn:aws:iam::123456789012:role/SAMLRole,arn:aws:iam::123456789012:saml-provider/CompanySAML"
},
"https://aws.amazon.com/SAML/Attributes/RoleSessionName": {
"description": "The role session name",
"format": "String",
"example": "user.email"
}
},
"optional_attributes": {
"https://aws.amazon.com/SAML/Attributes/SessionDuration": {
"description": "Session duration in seconds",
"format": "Number",
"example": "3600"
},
"custom_attributes": config.get('custom_attributes', {})
}
}
return attribute_mapping
def _generate_idp_configuration(self, provider_arn, roles):
"""Generate an IdP configuration guide"""
config_guide = {
"aws_saml_endpoint": "https://signin.aws.amazon.com/saml",
"provider_arn": provider_arn,
"attribute_mappings": {
"Role": "https://aws.amazon.com/SAML/Attributes/Role",
"RoleSessionName": "https://aws.amazon.com/SAML/Attributes/RoleSessionName"
},
"role_mappings": {}
}
for role_name, role_info in roles.items():
config_guide["role_mappings"][role_name] = {
"role_arn": role_info['arn'],
"saml_attribute_value": role_info['saml_role_value']
}
return config_guide
def create_adfs_integration(self):
"""Create an ADFS integration example"""
adfs_config = {
'provider_name': 'CompanyADFS',
'entity_id': 'https://adfs.company.com/adfs/services/trust',
'sso_url': 'https://adfs.company.com/adfs/ls/',
'signing_certificate': 'MIICertificateDataHere...',
'roles': [
{
'name': 'SAML-Developers',
'policies': [
{
'type': 'managed',
'arn': 'arn:aws:iam::aws:policy/PowerUserAccess'
}
],
'conditions': {
"StringEquals": {
"SAML:Department": "Engineering"
}
},
'session_duration': 14400 # 4 hours
},
{
'name': 'SAML-Administrators',
'policies': [
{
'type': 'managed',
'arn': 'arn:aws:iam::aws:policy/AdministratorAccess'
}
],
'conditions': {
"StringEquals": {
"SAML:Role": "Administrator"
},
"Bool": {
"SAML:MultiFactorAuthPresent": "true"
}
},
'session_duration': 3600 # 1 hour
},
{
'name': 'SAML-ReadOnly',
'policies': [
{
'type': 'managed',
'arn': 'arn:aws:iam::aws:policy/ReadOnlyAccess'
}
]
}
]
}
return self.setup_enterprise_saml_federation(adfs_config)
def test_saml_assertion(self, saml_response):
"""Test a SAML assertion"""
try:
# Decode the SAML response
decoded_response = base64.b64decode(saml_response)
# Parse the SAML assertion (simplified)
root = ET.fromstring(decoded_response)
# Extract role information
roles = []
for attr in root.iter():
if 'Role' in attr.tag:
roles.append(attr.text)
# Use STS AssumeRoleWithSAML
if roles:
role_parts = roles[0].split(',')
role_arn = role_parts[0]
provider_arn = role_parts[1]
response = self.sts.assume_role_with_saml(
RoleArn=role_arn,
PrincipalArn=provider_arn,
SAMLAssertion=saml_response
)
print("✅ SAML assertion validated successfully")
return response['Credentials']
except Exception as e:
print(f"❌ SAML assertion test failed: {str(e)}")
return None
return SAMLFederationManager()
# Demonstrate SAML federation configuration
saml_manager = configure_saml_federation()
# Create an ADFS integration
print("🔧 Configuring enterprise ADFS integration:")
# adfs_integration = saml_manager.create_adfs_integration()
print("📋 ADFS integration configuration steps:")
print(" 1. ✅ Create a SAML identity provider")
print(" 2. ✅ Configure federation roles (Developer, Administrator, ReadOnly)")
print(" 3. ✅ Set up attribute mapping")
print(" 4. ✅ Generate an IdP configuration guide")
9.3 OpenID Connect (OIDC) Integration
9.3.1 Web Identity Federation
def configure_oidc_federation():
"""Configure OpenID Connect federation"""
class OIDCFederationManager:
def __init__(self):
self.iam = boto3.client('iam')
self.sts = boto3.client('sts')
def setup_oidc_provider(self, provider_config):
"""Set up an OIDC identity provider"""
try:
# Create the OIDC provider
response = self.iam.create_open_id_connect_provider(
Url=provider_config['issuer_url'],
ClientIDList=provider_config['client_ids'],
ThumbprintList=provider_config['thumbprints'],
Tags=[
{'Key': 'Provider', 'Value': provider_config['name']},
{'Key': 'Type', 'Value': 'OIDC'}
]
)
provider_arn = response['OpenIDConnectProviderArn']
print(f"✅ OIDC provider created successfully: {provider_arn}")
# Create federation roles
roles = self._create_oidc_roles(provider_arn, provider_config)
return {
'provider_arn': provider_arn,
'roles': roles,
'client_config': self._generate_client_config(provider_arn, roles)
}
except Exception as e:
print(f"❌ Failed to create OIDC provider: {str(e)}")
return None
def _create_oidc_roles(self, provider_arn, config):
"""Create OIDC federation roles"""
roles = {}
for role_config in config['roles']:
role_name = role_config['name']
# Build the trust policy
conditions = {
"StringEquals": {
f"{config['issuer_url'].replace('https://', '')}:aud": config['client_ids'][0]
}
}
# Add additional conditions
if role_config.get('conditions'):
conditions.update(role_config['conditions'])
trust_policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": provider_arn
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": conditions
}
]
}
try:
# Create the role
role_response = self.iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=json.dumps(trust_policy),
Description=f'OIDC federated role: {role_name}'
)
role_arn = role_response['Role']['Arn']
# Attach permission policies
for policy in role_config.get('policies', []):
self._attach_policy_to_role(role_name, policy)
roles[role_name] = role_arn
print(f"✅ OIDC role created successfully: {role_name}")
except Exception as e:
print(f"❌ Failed to create OIDC role {role_name}: {str(e)}")
return roles
def _attach_policy_to_role(self, role_name, policy):
"""Attach a policy to a role"""
if policy['type'] == 'managed':
self.iam.attach_role_policy(
RoleName=role_name,
PolicyArn=policy['arn']
)
elif policy['type'] == 'inline':
self.iam.put_role_policy(
RoleName=role_name,
PolicyName=policy['name'],
PolicyDocument=json.dumps(policy['document'])
)
def _generate_client_config(self, provider_arn, roles):
"""Generate client configuration"""
return {
'provider_arn': provider_arn,
'available_roles': roles,
'assume_role_endpoint': 'https://sts.amazonaws.com/',
'token_usage': {
'id_token': 'Used for authentication',
'access_token': 'Used for API access',
'refresh_token': 'Used to refresh tokens'
}
}
def setup_google_oidc(self):
"""Set up Google OIDC integration"""
google_config = {
'name': 'GoogleOIDC',
'issuer_url': 'https://accounts.google.com',
'client_ids': ['your-google-client-id.apps.googleusercontent.com'],
'thumbprints': ['1f494a0f3e59b0c9e89de97c9eac32d1ba7c6cfb'], # Google's certificate thumbprint
'roles': [
{
'name': 'GoogleUser-S3Access',
'policies': [
{
'type': 'inline',
'name': 'GoogleUserS3Policy',
'document': {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::user-data/${accounts.google.com:sub}/*"
}
]
}
}
],
'conditions': {
"StringLike": {
"accounts.google.com:email": "*@company.com"
}
}
}
]
}
return self.setup_oidc_provider(google_config)
def setup_github_actions_oidc(self):
"""Set up GitHub Actions OIDC integration"""
github_config = {
'name': 'GitHubActions',
'issuer_url': 'https://token.actions.githubusercontent.com',
'client_ids': ['sts.amazonaws.com'],
'thumbprints': ['6938fd4d98bab03faadb97b34396831e3780aea1'],
'roles': [
{
'name': 'GitHubActions-DeployRole',
'policies': [
{
'type': 'inline',
'name': 'GitHubDeployPolicy',
'document': {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::deployment-bucket/*"
},
{
"Effect": "Allow",
"Action": [
"lambda:UpdateFunctionCode",
"lambda:UpdateFunctionConfiguration"
],
"Resource": "arn:aws:lambda:*:*:function:prod-*"
}
]
}
}
],
'conditions': {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:company/repository:*"
}
}
}
]
}
return self.setup_oidc_provider(github_config)
def test_web_identity_token(self, id_token, role_arn, session_name="WebIdentitySession"):
"""Test a web identity token"""
try:
response = self.sts.assume_role_with_web_identity(
RoleArn=role_arn,
RoleSessionName=session_name,
WebIdentityToken=id_token
)
credentials = response['Credentials']
print("✅ Web identity token validated successfully")
print(f" AccessKeyId: {credentials['AccessKeyId'][:10]}...")
print(f" Expiration: {credentials['Expiration']}")
return credentials
except Exception as e:
print(f"❌ Web identity token validation failed: {str(e)}")
return None
def generate_client_code_examples(self, provider_config):
"""Generate client code examples"""
examples = {
'javascript': f'''
# JavaScript example - get AWS credentials using an OIDC token
const AWS = require('aws-sdk');
async function assumeRoleWithWebIdentity(idToken, roleArn) {{
const sts = new AWS.STS();
try {{
const result = await sts.assumeRoleWithWebIdentity({{
RoleArn: roleArn,
RoleSessionName: 'WebSession',
WebIdentityToken: idToken
}}).promise();
// Create an AWS service client with the temporary credentials
const s3 = new AWS.S3({
accessKeyId: result.Credentials.AccessKeyId,
secretAccessKey: result.Credentials.SecretAccessKey,
sessionToken: result.Credentials.SessionToken
});
return s3;
}} catch (error) {{
console.error('AssumeRole failed:', error);
throw error;
}}
}}
''',
'python': f'''
# Python example - get AWS credentials using an OIDC token
import boto3
def assume_role_with_web_identity(id_token, role_arn):
sts = boto3.client('sts')
try:
response = sts.assume_role_with_web_identity(
RoleArn=role_arn,
RoleSessionName='WebSession',
WebIdentityToken=id_token
)
credentials = response['Credentials']
# Create an AWS service client with the temporary credentials
s3 = boto3.client(
's3',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken']
)
return s3
except Exception as e:
print(f"AssumeRole failed: {{e}}")
raise
''',
'github_actions': '''
# Example GitHub Actions workflow
name: Deploy to AWS
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActions-DeployRole
role-session-name: GitHubActions-Deploy
aws-region: us-east-1
- name: Deploy to S3
run: |
aws s3 sync ./build s3://deployment-bucket/
'''
}
return examples
return OIDCFederationManager()
# Demonstrate OIDC federation configuration
oidc_manager = configure_oidc_federation()
# Set up different OIDC providers
print("🔧 Configuring OIDC federation:")
# Google OIDC integration
print("\n📱 Google OIDC integration:")
# google_integration = oidc_manager.setup_google_oidc()
# GitHub Actions integration
print("\n🔧 GitHub Actions OIDC integration:")
# github_integration = oidc_manager.setup_github_actions_oidc()
print("\n📋 OIDC integration configuration complete:")
print(" ✅ Google identity provider configured")
print(" ✅ GitHub Actions integration configured")
print(" ✅ Web identity federation roles created")
print(" ✅ Client code examples generated")
# Generate client code examples
code_examples = oidc_manager.generate_client_code_examples({})
print("\n📝 Client integration code has been generated")
9.4 AWS SSO (Identity Center) Configuration
9.4.1 Enterprise-level Single Sign-On Solution
def configure_aws_sso():
"""Configure AWS SSO (Identity Center)"""
class AWSIdentityCenterManager:
def __init__(self):
self.sso_admin = boto3.client('sso-admin')
self.identitystore = boto3.client('identitystore')
self.organizations = boto3.client('organizations')
def setup_identity_center(self, config):
"""Set up AWS Identity Center"""
try:
# Get SSO instance information
sso_instances = self.sso_admin.list_instances()
if not sso_instances['Instances']:
print("❌ No AWS SSO instance found, please enable AWS SSO first")
return None
sso_instance = sso_instances['Instances'][0]
instance_arn = sso_instance['InstanceArn']
identity_store_id = sso_instance['IdentityStoreId']
print(f"✅ SSO instance found: {instance_arn}")
# Configure permission sets
permission_sets = self._create_permission_sets(instance_arn, config)
# Configure account assignments
account_assignments = self._setup_account_assignments(
instance_arn, identity_store_id, permission_sets, config
)
# Configure an external identity source
if config.get('external_identity_source'):
self._configure_external_identity_source(instance_arn, config)
return {
'instance_arn': instance_arn,
'identity_store_id': identity_store_id,
'permission_sets': permission_sets,
'account_assignments': account_assignments
}
except Exception as e:
print(f"❌ Failed to configure Identity Center: {str(e)}")
return None
def _create_permission_sets(self, instance_arn, config):
"""Create permission sets"""
permission_sets = {}
for ps_config in config['permission_sets']:
try:
# Create a permission set
response = self.sso_admin.create_permission_set(
Name=ps_config['name'],
Description=ps_config.get('description', ''),
InstanceArn=instance_arn,
SessionDuration=ps_config.get('session_duration', 'PT1H'), # 1 hour
RelayState=ps_config.get('relay_state', '')
)
ps_arn = response['PermissionSet']['PermissionSetArn']
# Attach AWS managed policies
for policy_arn in ps_config.get('managed_policies', []):
self.sso_admin.attach_managed_policy_to_permission_set(
InstanceArn=instance_arn,
PermissionSetArn=ps_arn,
ManagedPolicyArn=policy_arn
)
# Add an inline policy
if ps_config.get('inline_policy'):
self.sso_admin.put_inline_policy_to_permission_set(
InstanceArn=instance_arn,
PermissionSetArn=ps_arn,
InlinePolicy=json.dumps(ps_config['inline_policy'])
)
# Configure a permissions boundary
if ps_config.get('permissions_boundary'):
self.sso_admin.put_permissions_boundary_to_permission_set(
InstanceArn=instance_arn,
PermissionSetArn=ps_arn,
PermissionsBoundary={
'ManagedPolicyArn': ps_config['permissions_boundary']
}
)
permission_sets[ps_config['name']] = ps_arn
print(f"✅ Permission set created successfully: {ps_config['name']}")
except Exception as e:
print(f"❌ Failed to create permission set {ps_config['name']}: {str(e)}")
return permission_sets
def _setup_account_assignments(self, instance_arn, identity_store_id, permission_sets, config):
"""Set up account assignments"""
assignments = []
# Get accounts in the organization
accounts = self.organizations.list_accounts()
account_map = {acc['Name']: acc['Id'] for acc in accounts['Accounts']}
for assignment in config.get('account_assignments', []):
try:
account_id = account_map.get(assignment['account_name'], assignment.get('account_id'))
ps_arn = permission_sets.get(assignment['permission_set'])
if not account_id or not ps_arn:
print(f"⚠️ Skipping assignment - account or permission set not found: {assignment}")
continue
# Assign to users
if assignment.get('users'):
for user_name in assignment['users']:
user_id = self._get_user_id(identity_store_id, user_name)
if user_id:
self.sso_admin.create_account_assignment(
InstanceArn=instance_arn,
TargetId=account_id,
TargetType='AWS_ACCOUNT',
PermissionSetArn=ps_arn,
PrincipalType='USER',
PrincipalId=user_id
)
assignments.append({
'type': 'user',
'principal': user_name,
'account': account_id,
'permission_set': assignment['permission_set']
})
# Assign to groups
if assignment.get('groups'):
for group_name in assignment['groups']:
group_id = self._get_group_id(identity_store_id, group_name)
if group_id:
self.sso_admin.create_account_assignment(
InstanceArn=instance_arn,
TargetId=account_id,
TargetType='AWS_ACCOUNT',
PermissionSetArn=ps_arn,
PrincipalType='GROUP',
PrincipalId=group_id
)
assignments.append({
'type': 'group',
'principal': group_name,
'account': account_id,
'permission_set': assignment['permission_set']
})
print(f"✅ Account assignment configured successfully: {assignment['permission_set']} -> {assignment['account_name']}")
except Exception as e:
print(f"❌ Account assignment failed: {str(e)}")
return assignments
def _get_user_id(self, identity_store_id, user_name):
"""Get a user ID"""
try:
response = self.identitystore.list_users(
IdentityStoreId=identity_store_id,
Filters=[
{
'AttributePath': 'UserName',
'AttributeValue': user_name
}
]
)
if response['Users']:
return response['Users'][0]['UserId']
except Exception as e:
print(f"⚠️ Failed to get user ID for {user_name}: {str(e)}")
return None
def _get_group_id(self, identity_store_id, group_name):
"""Get a group ID"""
try:
response = self.identitystore.list_groups(
IdentityStoreId=identity_store_id,
Filters=[
{
'AttributePath': 'DisplayName',
'AttributeValue': group_name
}
]
)
if response['Groups']:
return response['Groups'][0]['GroupId']
except Exception as e:
print(f"⚠️ Failed to get group ID for {group_name}: {str(e)}")
return None
def _configure_external_identity_source(self, instance_arn, config):
"""Configure an external identity source"""
external_config = config['external_identity_source']
if external_config['type'] == 'ActiveDirectory':
# Configure an AD connector
print("🔗 Configuring Active Directory connector...")
# Actual implementation would require calling the corresponding API
elif external_config['type'] == 'SAML':
# Configure a SAML identity source
print("🔗 Configuring SAML identity source...")
# Actual implementation would require calling the corresponding API
def create_enterprise_sso_config(self):
"""Create an example enterprise SSO configuration"""
config = {
'permission_sets': [
{
'name': 'AdministratorAccess',
'description': 'Full administrator access',
'session_duration': 'PT4H', # 4 hours
'managed_policies': [
'arn:aws:iam::aws:policy/AdministratorAccess'
],
'permissions_boundary': 'arn:aws:iam::123456789012:policy/AdminPermissionsBoundary'
},
{
'name': 'DeveloperAccess',
'description': 'Developer access',
'session_duration': 'PT8H', # 8 hours
'managed_policies': [
'arn:aws:iam::aws:policy/PowerUserAccess'
],
'inline_policy': {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"iam:CreateRole",
"iam:DeleteRole",
"organizations:*"
],
"Resource": "*"
}
]
}
},
{
'name': 'ReadOnlyAccess',
'description': 'Read-only access',
'session_duration': 'PT12H', # 12 hours
'managed_policies': [
'arn:aws:iam::aws:policy/ReadOnlyAccess'
]
},
{
'name': 'DatabaseAdmin',
'description': 'Database administrator permissions',
'session_duration': 'PT4H',
'inline_policy': {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds:*",
"dynamodb:*",
"elasticache:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::database-backups/*"
}
]
}
}
],
'account_assignments': [
{
'account_name': 'Production',
'permission_set': 'AdministratorAccess',
'groups': ['Administrators']
},
{
'account_name': 'Production',
'permission_set': 'DatabaseAdmin',
'groups': ['DatabaseAdmins']
},
{
'account_name': 'Development',
'permission_set': 'DeveloperAccess',
'groups': ['Developers']
},
{
'account_name': 'Development',
'permission_set': 'ReadOnlyAccess',
'users': ['john.doe', 'jane.smith']
}
],
'external_identity_source': {
'type': 'ActiveDirectory',
'domain': 'company.com',
'directory_id': 'd-1234567890'
}
}
return self.setup_identity_center(config)
return AWSIdentityCenterManager()
# Demonstrate AWS SSO configuration
sso_manager = configure_aws_sso()
print("🔧 Configuring AWS Identity Center:")
# enterprise_sso = sso_manager.create_enterprise_sso_config()
print("\n📋 Identity Center configuration complete:")
print(" ✅ Permission sets created (Administrator, Developer, ReadOnly, DatabaseAdmin)")
print(" ✅ Account assignments configured")
print(" ✅ External identity source configured")
print(" ✅ Session management configured")
print("\n🔗 User access flow:")
print(" 1. User accesses the SSO portal")
print(" 2. Authenticates with the corporate identity provider")
print(" 3. Selects the AWS account and role to access")
print(" 4. Gets temporary credentials to access AWS resources")
Summary
This chapter introduced a complete solution for AWS federated identity and single sign-on:
- Federated Identity Basics: Understood the concept, architecture, and trust model of federated identity
- SAML 2.0 Integration: Enterprise-level SAML federation configuration and ADFS integration
- OIDC Integration: Web identity federation and GitHub Actions integration
- AWS SSO: Identity Center configuration and enterprise-level single sign-on
With federated identity, you can achieve:
- Unified enterprise identity management
- A secure single sign-on experience
- Flexible role mapping and permission management
- Seamless integration with existing identity infrastructure
In the next chapter, we will learn about IAM access analysis and auditing mechanisms.