← SCP Explorer

Restrict console actions

Prevents manual creation of IAM users and policy attachments through the console, ensuring these actions are only performed through approved automation.

Restrict console actions

Effective strategy: combined SCP and IAM approach

The recommended solution using multiple policy layers

To effectively restrict console administrative actions while allowing automation, we implement a two-layer approach:

1. SCP to set broad boundaries

This SCP restricts sensitive IAM operations to specific automation roles by focusing on the principal's ARN rather than trying to detect the console source:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RestrictSensitiveIAMOperations",
      "Effect": "Deny",
      "Action": [
        "iam:CreateUser",
        "iam:AttachUserPolicy",
        "iam:CreatePolicy",
        "iam:PutUserPolicy"
      ],
      "Resource": "*",
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/AutomationRole*",
            "arn:aws:iam::*:role/ServiceRole*"
          ]
        }
      }
    }
  ]
}

2. IAM policy to control console access

A complementary IAM permission boundary or policy that specifically controls console access:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyConsoleAccessForIAMAdmin",
      "Effect": "Deny",
      "Action": [
        "iam:CreateUser",
        "iam:AttachUserPolicy",
        "iam:CreatePolicy",
        "iam:PutUserPolicy"
      ],
      "Resource": "*",
      "Condition": {
        "Bool": {
          "aws:ViaAWSService": "false"
        },
        "StringNotLike": {
          "aws:UserAgent": "*AWS-CloudFormation*"
        }
      }
    }
  ]
}

Validation strategy

How to test this combined approach works

To validate this solution, test both automated and manual operations:

  • Automation tests (should succeed):
    • Create users through automation roles
    • Attach policies through CloudFormation
    • Perform IAM operations through service roles
  • Manual operations (should be denied):
    • Create users through console
    • Attach policies through console
    • Direct API calls without proper role

The combination of SCP and IAM policies ensures that sensitive IAM operations can only be performed through approved automation channels, while effectively blocking console and direct API access.

SCP policy & validation scenarios

{ "Version": "2012-10-17", "Statement": [ { "Sid": "RestrictSensitiveIAMOperations", "Effect": "Deny", "Action": [ "iam:CreateUser", "iam:AttachUserPolicy", "iam:CreatePolicy", "iam:PutUserPolicy" ], "Resource": "*", "Condition": { "ArnNotLike": { "aws:PrincipalARN": [ "arn:aws:iam::*:role/AutomationRole*", "arn:aws:iam::*:role/ServiceRole*" ] } } } ] }
# Assume an automation role (this should work) aws sts assume-role \ --role-arn arn:aws:iam::123456789012:role/AutomationRole-Test \ --role-session-name test-session # Using automation role credentials: # Create user through automation (should be allowed) aws iam create-user --user-name test-automated-user # Attach policy through automation (should be allowed) aws iam attach-user-policy \ --user-name test-automated-user \ --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess # Using regular user credentials: # Try to create user (should be denied) aws iam create-user --user-name test-manual-user # Try to attach policy (should be denied) aws iam attach-user-policy \ --user-name existing-user \ --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess # Clean up (using automation role) aws iam detach-user-policy \ --user-name test-automated-user \ --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess aws iam delete-user --user-name test-automated-user
import boto3 import json # Initialize clients iam = boto3.client('iam') sts = boto3.client('sts') def test_with_automation_role(): # Assume the automation role try: response = sts.assume_role( RoleArn='arn:aws:iam::123456789012:role/AutomationRole-Test', RoleSessionName='test-session' ) # Create session with automation role credentials automation_credentials = response['Credentials'] automation_iam = boto3.client( 'iam', aws_access_key_id=automation_credentials['AccessKeyId'], aws_secret_access_key=automation_credentials['SecretAccessKey'], aws_session_token=automation_credentials['SessionToken'] ) # Test operations with automation role (should succeed) try: automation_iam.create_user(UserName='test-automated-user') print("✅ Successfully created user with automation role") automation_iam.attach_user_policy( UserName='test-automated-user', PolicyArn='arn:aws:iam::aws:policy/ReadOnlyAccess' ) print("✅ Successfully attached policy with automation role") # Clean up automation_iam.detach_user_policy( UserName='test-automated-user', PolicyArn='arn:aws:iam::aws:policy/ReadOnlyAccess' ) automation_iam.delete_user(UserName='test-automated-user') print("✅ Cleaned up resources created with automation role") except Exception as e: print(f"❌ Error with automation role operations: {e}") except Exception as e: print(f"❌ Error assuming automation role: {e}") def test_with_regular_user(): # Test operations with regular credentials (should fail) try: iam.create_user(UserName='test-manual-user') print("❌ WARNING: Created user without automation role - policy not working") except Exception as e: print("✅ Create user denied as expected:", e) try: iam.attach_user_policy( UserName='existing-user', PolicyArn='arn:aws:iam::aws:policy/ReadOnlyAccess' ) print("❌ WARNING: Attached policy without automation role - policy not working") except Exception as e: print("✅ Attach policy denied as expected:", e) # Run tests print("Testing with automation role:") test_with_automation_role() print("\nTesting with regular user:") test_with_regular_user() print("\nNote: Console access restrictions must be tested manually in the AWS Console")

Counter-example: problematic approach

An example of what not to do

The following approach demonstrates common mistakes when trying to restrict console access:

  • Using aws:RequestedVia which is not a valid global condition key
  • Relying on aws:UserAgent which can be spoofed and doesn't effectively block console access

❌ Do Not Use This Approach:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "iam:CreateUser",
        "iam:AttachUserPolicy"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:ViaAWSService": "false",
          "aws:RequestedVia": "console"    // Not a valid condition key
        }
      }
    }
  ]
}