FRR-RSC-02: Top-Level Administrative Accounts Security Settings Guidance
Applies to: Low, Moderate, High
Last Updated: 2025-11-25
Version: 1.0.0
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
- AWS Tasks Requiring Root: https://docs.aws.amazon.com/IAM/latest/UserGuide/root-user-tasks.html
- AWS Account Management: https://docs.aws.amazon.com/accounts/latest/reference/
- FedRAMP Rev5 RSC Requirements