How to Add an SES IAM Role to Your EC2 Instance

This guide walks you through creating an IAM role with SES permissions and attaching it to your EC2 instance.

Overview

Using an IAM role is the most secure way to give your EC2 instance access to AWS SES: - ✅ No credentials in your code or .env file - ✅ Automatic credential rotation (every hour) - ✅ No manual key management - ✅ Can’t accidentally leak credentials

Step 1: Create the IAM Role

1.1 Open IAM Console

  1. Log into AWS Console

  2. Go to IAM service (search for “IAM” in the top search bar)

  3. Click Roles in the left sidebar

  4. Click Create role button

1.2 Select Trusted Entity

  1. Select AWS service (should be selected by default)

  2. Under “Use case”, select EC2

  3. Click Next

1.3 Add SES Permissions

Option A: Use AWS Managed Policy (Quick) 1. In the search box, type: AmazonSESFullAccess 2. Check the box next to AmazonSESFullAccess 3. Click Next

Option B: Create Custom Policy (Recommended - Least Privilege) 1. Click Create policy (opens in new tab) 2. Click the JSON tab 3. Paste this policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ses:SendEmail",
        "ses:SendRawEmail"
      ],
      "Resource": "*"
    }
  ]
}
  1. Click Next

  2. Enter policy name: SESMinimalSendPolicy

  3. Click Create policy

  4. Go back to the role creation tab

  5. Click the refresh button

  6. Search for SESMinimalSendPolicy

  7. Check the box next to it

  8. Click Next

1.4 Name and Create the Role

  1. Enter role name: aclarknet-ses-role (or any name you prefer)

  2. Enter description: Allows EC2 instance to send emails via AWS SES

  3. Review the settings

  4. Click Create role

Role created! Now let’s attach it to your EC2 instance.

Step 2: Attach the Role to Your EC2 Instance

2.1 Open EC2 Console

  1. Go to EC2 service (search for “EC2” in the top search bar)

  2. Click Instances in the left sidebar

  3. Find your instance (e.g., the one running aclarknet)

  4. Select it by clicking the checkbox

2.2 Attach the IAM Role

  1. Click Actions dropdown (top right)

  2. Select SecurityModify IAM role

  3. In the “IAM role” dropdown, select aclarknet-ses-role (the role you just created)

  4. Click Update IAM role

Role attached! Your EC2 instance now has permission to send emails via SES.

Note: You do NOT need to restart your EC2 instance. The role takes effect immediately.

Step 3: Update Your Application Configuration

3.1 Edit Your .env File

SSH into your EC2 instance and edit the .env file:

ssh your-ec2-instance
sudo nano /srv/aclarknet/.env

3.2 Update Email Settings

Make sure your .env file has these settings:

# Enable AWS SES
USE_SES=True

# AWS SES Region (required)
AWS_SES_REGION_NAME=us-east-1

# NO credentials needed! Remove or comment out these lines:
# AWS_ACCESS_KEY_ID=...
# AWS_SECRET_ACCESS_KEY=...

# Optional settings
USE_SES_V2=True
AWS_SES_CONFIGURATION_SET=

Important: Remove or comment out AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. The IAM role will provide credentials automatically.

3.3 Restart Your Application

sudo systemctl restart aclarknet.service

Step 4: Verify the IAM Role is Working

4.1 Check IAM Role Attachment

SSH into your EC2 instance and run:

# Check if instance has an IAM role attached
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Should output: aclarknet-ses-role (or whatever you named it)

If you see the role name, it’s attached correctly!

4.2 Get Temporary Credentials (Optional)

To see the actual credentials that boto3 will use:

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/aclarknet-ses-role

You should see JSON output with: - AccessKeyId - Temporary access key - SecretAccessKey - Temporary secret key - Token - Session token - Expiration - When credentials expire (auto-renewed)

Note: These credentials rotate automatically every hour. You never need to manage them!

4.3 Test Email Sending

Test from Django shell:

cd /srv/aclarknet
sudo -u nginx /srv/aclarknet/.venv/bin/python manage.py shell --settings=aclarknet.settings.production
from django.core.mail import send_mail

send_mail(
    'Test Email from AWS SES with IAM Role',
    'This email was sent using IAM role credentials!',
    'aclark@aclark.net',
    ['aclark@aclark.net'],
    fail_silently=False,
)

If successful, you should see 1 (one email sent) and receive the email!

Troubleshooting

Issue: “Unable to locate credentials”

Check 1: Verify role is attached

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

Should output your role name. If empty, the role isn’t attached.

Check 2: Verify .env doesn’t have credentials Make sure AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are removed or commented out.

Check 3: Restart application

sudo systemctl restart aclarknet.service

Issue: “Access Denied” when sending email

Check 1: Verify role has SES permissions 1. Go to IAM Console → Roles 2. Click on aclarknet-ses-role 3. Check “Permissions” tab 4. Should see AmazonSESFullAccess or SESMinimalSendPolicy

Check 2: Verify email address is verified in SES 1. Go to SES Console → Verified identities 2. Make sure aclark@aclark.net is verified 3. Or request production access to send to any address

Issue: Email not sending but no errors

Check application logs:

sudo tail -f /srv/aclarknet/logs/django.log

Look for email-related errors.

Next Steps

  1. ✅ Verify email addresses in AWS SES Console

  2. ✅ Request production access (to send to any email address)

  3. ✅ Set up SPF/DKIM/DMARC records for better deliverability

  4. ✅ Monitor sending in SES Console dashboard

See docs/aws_ses_setup.md for complete AWS SES configuration guide.

Summary

You’ve successfully: - ✅ Created an IAM role with SES permissions - ✅ Attached the role to your EC2 instance - ✅ Configured your application to use the role - ✅ Verified the role is working

Your application can now send emails via AWS SES without any credentials in your code! 🎉