🔒 AWS Secure Configuration Guidance

FedRAMP Rev5 Recommended Secure Configuration (RSC) Requirements

Guidance Home Downloads

FRR-RSC-02: Top-Level Administrative Accounts Security Settings Guidance

Applies to: Low, Moderate, High
Last Updated: 2025-11-25
Version: 1.0.0

Overview

This guidance explains security-related settings that can ONLY be operated by the 
AWS root account and their security implications for your organization.

Root Only Settings

Account Name and Email Management

Description:

Change AWS account name and root user email address

How It Works:

Root user can modify account-level settings including the email address used for root login and AWS communications.

Configuration Items:

  • modifiable_settings: ['Account name - Display name for the AWS account', 'Root email address - Primary contact and login credential', 'Alternate contacts - Security, Billing, Operations', 'Account phone number - For account recovery']
  • security_implications: ['CRITICAL: Email change redirects all AWS notifications', 'HIGH: Unauthorized change could lock out legitimate users', 'MEDIUM: Account name changes affect billing and reporting']

Example Cli:

# Get account alias
aws iam list-account-aliases

# Get account summary
aws iam get-account-summary

# Get contact information (requires root credentials)
aws account get-contact-information

Example Boto3:

import boto3

iam = boto3.client('iam')
account = boto3.client('account')

# Get account alias
aliases = iam.list_account_aliases()
print(f"Account Alias: {aliases['AccountAliases']}")

# Get account summary
summary = iam.get_account_summary()
print(f"Users: {summary['SummaryMap'].get('Users', 0)}")
print(f"Roles: {summary['SummaryMap'].get('Roles', 0)}")

# Get contact info (requires root or delegated admin)
try:
    contacts = account.get_contact_information()
    print(f"Primary Contact: {contacts['ContactInformation']}")
except:
    print("Contact info requires root credentials")

Close AWS Account

Description:

Permanently close the AWS account

How It Works:

Account closure is a root-only operation. Account enters 90-day suspension period before permanent deletion.

Configuration Items:

  • closure_process: ['Verify all resources are deleted or backed up', 'Settle all outstanding charges', 'Download final billing reports', '90-day grace period for recovery', 'Permanent deletion after grace period']
  • prerequisites: ['No active resources in any region', 'No pending charges or credits', 'Root account access required', 'MFA verification required']

Example Cli:

# Monitor for account closure attempts
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=CloseAccount \
  --max-results 10

Example Boto3:

import boto3
from datetime import datetime, timedelta

cloudtrail = boto3.client('cloudtrail')

# Check for account closure attempts
events = cloudtrail.lookup_events(
    LookupAttributes=[
        {'AttributeKey': 'EventName', 'AttributeValue': 'CloseAccount'}
    ],
    StartTime=datetime.now() - timedelta(days=365)
)

if events['Events']:
    print(f"âš  Found {len(events['Events'])} account closure attempts")
    for event in events['Events']:
        print(f"  {event['EventTime']}: {event.get('Username', 'Unknown')}")
else:
    print("✓ No account closure attempts found")

Change AWS Support Plan

Description:

Upgrade or downgrade AWS Support subscription

How It Works:

Support plan changes require root account. Higher tiers provide faster security incident response.

Configuration Items:

  • support_tiers: ['Basic - Free, community support only', 'Developer - $29/month, business hours email support', 'Business - $100/month, 24/7 phone/chat, < 1 hour response', 'Enterprise - $15,000/month, TAM, < 15 minute response']
  • fedramp_requirements: ['Moderate: Business Support minimum recommended', 'High: Enterprise Support strongly recommended', '24/7 security incident response required']

Example Cli:

# Check support plan via Trusted Advisor (requires Business/Enterprise)
aws support describe-trusted-advisor-checks --language en --region us-east-1

Example Boto3:

import boto3

support = boto3.client('support', region_name='us-east-1')

try:
    # This call succeeds only with Business/Enterprise support
    checks = support.describe_trusted_advisor_checks(language='en')
    print("✓ Business or Enterprise Support plan active")
    print(f"  Available checks: {len(checks['checks'])}")
except Exception as e:
    if 'SubscriptionRequiredException' in str(e):
        print("âš  Basic or Developer Support plan")
        print("  Recommendation: Upgrade to Business/Enterprise for FedRAMP")
    else:
        print(f"Error checking support plan: {e}")

Billing and Cost Management Settings

Description:

Configure billing preferences and payment methods

How It Works:

Root account has exclusive access to billing settings, payment methods, and tax information.

Configuration Items:

  • billing_operations: ['Add/remove payment methods', 'Update tax registration information', 'Enable/disable billing alerts', 'Configure Cost Explorer', 'Set up budgets and alerts', 'Access billing reports']

Example Cli:

# Check billing alerts (available to all users with permissions)
aws cloudwatch describe-alarms --alarm-name-prefix Billing

# Get cost and usage (requires Cost Explorer enabled)
aws ce get-cost-and-usage \
  --time-period Start=2025-11-01,End=2025-11-30 \
  --granularity MONTHLY \
  --metrics BlendedCost

Example Boto3:

import boto3
from datetime import datetime, timedelta

cloudwatch = boto3.client('cloudwatch')
ce = boto3.client('ce')

# Check for billing alarms
alarms = cloudwatch.describe_alarms(AlarmNamePrefix='Billing')
print(f"Billing alarms configured: {len(alarms['MetricAlarms'])}")

# Get current month costs
start = datetime.now().replace(day=1).strftime('%Y-%m-%d')
end = datetime.now().strftime('%Y-%m-%d')

try:
    costs = ce.get_cost_and_usage(
        TimePeriod={'Start': start, 'End': end},
        Granularity='MONTHLY',
        Metrics=['BlendedCost']
    )
    for result in costs['ResultsByTime']:
        amount = result['Total']['BlendedCost']['Amount']
        print(f"Current month cost: ${float(amount):.2f}")
except:
    print("Cost Explorer not enabled or insufficient permissions")

Monitoring And Alerts

Monitor Root-Only Setting Changes

Description:

Track all changes to root-only settings via CloudTrail

How It Works:

CloudTrail logs all API calls including root-only operations. Set up alerts for unauthorized changes.

Configuration Items:

  • monitored_events: ['UpdateAccountName - Account name changes', 'UpdateAccountPasswordPolicy - Password policy changes', 'CloseAccount - Account closure attempts', 'UpdateAccountSettings - General account settings', 'PutAccountAlias - Account alias changes']

Example Cli:

# Monitor account setting changes
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateAccountName \
  --max-results 10

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateAccountPasswordPolicy \
  --max-results 10

Example Boto3:

import boto3
from datetime import datetime, timedelta

cloudtrail = boto3.client('cloudtrail')

# Events to monitor
root_only_events = [
    'UpdateAccountName',
    'UpdateAccountPasswordPolicy',
    'CloseAccount',
    'UpdateAccountSettings',
    'PutAccountAlias',
    'DeleteAccountAlias'
]

start_time = datetime.now() - timedelta(days=90)

print("Root-Only Setting Changes (Last 90 Days)")
print("=" * 60)

for event_name in root_only_events:
    events = cloudtrail.lookup_events(
        LookupAttributes=[
            {'AttributeKey': 'EventName', 'AttributeValue': event_name}
        ],
        StartTime=start_time
    )
    
    if events['Events']:
        print(f"\n{event_name}:")
        for event in events['Events']:
            user = event.get('Username', 'Unknown')
            time = event['EventTime']
            print(f"  {time} by {user}")

Automated Root Settings Audit

Description:

Complete audit script for root-only settings

How It Works:

Scans account for root-only setting changes, validates support plan, generates audit report.

Example Boto3:

#!/usr/bin/env python3
import boto3
from datetime import datetime, timedelta
import json

def audit_root_settings():
    iam = boto3.client('iam')
    cloudtrail = boto3.client('cloudtrail')
    support = boto3.client('support', region_name='us-east-1')
    
    audit_results = {
        'timestamp': datetime.now().isoformat(),
        'account_info': {},
        'recent_changes': [],
        'support_plan': 'Unknown',
        'findings': []
    }
    
    # Get account info
    try:
        aliases = iam.list_account_aliases()
        audit_results['account_info']['aliases'] = aliases['AccountAliases']
        
        summary = iam.get_account_summary()
        audit_results['account_info']['users'] = summary['SummaryMap'].get('Users', 0)
        audit_results['account_info']['roles'] = summary['SummaryMap'].get('Roles', 0)
    except Exception as e:
        audit_results['findings'].append(f"Error getting account info: {e}")
    
    # Check for recent root-only changes
    root_events = [
        'UpdateAccountName', 'CloseAccount', 'UpdateAccountSettings'
    ]
    
    start_time = datetime.now() - timedelta(days=30)
    
    for event_name in root_events:
        try:
            events = cloudtrail.lookup_events(
                LookupAttributes=[
                    {'AttributeKey': 'EventName', 'AttributeValue': event_name}
                ],
                StartTime=start_time,
                MaxResults=5
            )
            
            for event in events['Events']:
                audit_results['recent_changes'].append({
                    'event': event_name,
                    'time': event['EventTime'].isoformat(),
                    'user': event.get('Username', 'Unknown')
                })
        except:
            pass
    
    # Check support plan
    try:
        checks = support.describe_trusted_advisor_checks(language='en')
        audit_results['support_plan'] = 'Business or Enterprise'
    except:
        audit_results['support_plan'] = 'Basic or Developer'
        audit_results['findings'].append(
            'RECOMMENDATION: Upgrade to Business/Enterprise Support for FedRAMP'
        )
    
    # Generate findings
    if len(audit_results['recent_changes']) > 0:
        audit_results['findings'].append(
            f"Found {len(audit_results['recent_changes'])} root-only setting changes in last 30 days"
        )
    
    return audit_results

if __name__ == '__main__':
    results = audit_root_settings()
    print(json.dumps(results, indent=2, default=str))

References