Onboarding Guide

Creating the PGFlare IAM Role

PGFlare connects to your AWS RDS PostgreSQL instance using a read-only IAM role. No software agents are installed on your infrastructure. This guide walks you through the exact steps required — for Windows, Bash/Linux, and macOS.

Time required: ~20 minutes Permissions needed: IAM Admin + RDS Admin Version: v1.2, March 2026

How PGFlare connects to your database

PGFlare uses the native AWS IAM authentication feature built into RDS PostgreSQL. This means:

What PGFlare reads

View / Source Purpose
pg_stat_statements Slow query detection, query fingerprinting, execution statistics
pg_stat_bgwriter Checkpoint frequency, background writer efficiency
pg_stat_user_tables Table bloat, autovacuum health, dead tuple ratios
pg_locks Lock contention, blocking query detection
pg_stat_activity Active connection count, idle-in-transaction sessions, wait events
pg_stat_replication Replication lag monitoring (if applicable)
pg_indexes, pg_class Index analysis, unused/duplicate index detection
AWS CloudWatch (RDS metrics) CPU, memory, IOPS, FreeStorageSpace, DatabaseConnections

Prerequisites

Before starting, ensure you have:

AWS CLI not installed? Install it from aws.amazon.com/cli. Windows: download the MSI installer. macOS: brew install awscli. Linux: pip install awscli or use your package manager.

Step-by-Step Instructions

STEP 1

Enable IAM Authentication on your RDS Instance

IAM database authentication must be enabled on your RDS instance. If it is already enabled, skip to Step 2.

No downtime required. Enabling IAM authentication on a running RDS instance does not cause a reboot or any interruption to existing connections.

1. Open the RDS Console

2. Click Databases → select your instance → click Modify

3. Scroll to Database authentication

4. Select Password and IAM database authentication

5. Click Continue → choose Apply immediately → click Modify DB instance

PowerShell — replace REGION and DB-IDENTIFIER with your values
# Replace these values
$Region         = "eu-west-2"
$DBIdentifier   = "your-rds-instance-id"

# Enable IAM authentication
aws rds modify-db-instance `
  --db-instance-identifier $DBIdentifier `
  --enable-iam-database-authentication `
  --apply-immediately `
  --region $Region

# Verify: check EnabledIAMDatabaseAuthentication is true
aws rds describe-db-instances `
  --db-instance-identifier $DBIdentifier `
  --region $Region `
  --query "DBInstances[0].IAMDatabaseAuthenticationEnabled"
Bash — replace REGION and DB-IDENTIFIER with your values
# Replace these values
REGION="eu-west-2"
DB_IDENTIFIER="your-rds-instance-id"

# Enable IAM authentication
aws rds modify-db-instance \
  --db-instance-identifier "${DB_IDENTIFIER}" \
  --enable-iam-database-authentication \
  --apply-immediately \
  --region "${REGION}"

# Verify (should return "true")
aws rds describe-db-instances \
  --db-instance-identifier "${DB_IDENTIFIER}" \
  --region "${REGION}" \
  --query 'DBInstances[0].IAMDatabaseAuthenticationEnabled'
macOS Terminal — install AWS CLI first if needed
# Install AWS CLI (if not already installed)
brew install awscli

# Replace these values
REGION="eu-west-2"
DB_IDENTIFIER="your-rds-instance-id"

# Enable IAM authentication
aws rds modify-db-instance \
  --db-instance-identifier "${DB_IDENTIFIER}" \
  --enable-iam-database-authentication \
  --apply-immediately \
  --region "${REGION}"

# Verify (should return "true")
aws rds describe-db-instances \
  --db-instance-identifier "${DB_IDENTIFIER}" \
  --region "${REGION}" \
  --query 'DBInstances[0].IAMDatabaseAuthenticationEnabled'
STEP 2

Get your RDS Resource ID

The RDS Resource ID is needed to scope the IAM policy to your specific instance (not all instances in your account). It looks like db-ABCDEFGHIJKLMNOPQRSTUVWXYZ.

1. RDS Console → Databases → click your instance

2. On the Configuration tab, find Resource ID

3. Copy the value — it starts with db-

# Get the Resource ID (DbiResourceId)
aws rds describe-db-instances `
  --db-instance-identifier $DBIdentifier `
  --region $Region `
  --query "DBInstances[0].DbiResourceId" `
  --output text

# Save the output (e.g. db-ABCDE12345FGHIJ678KLM)
$DBResourceId = "db-REPLACE-WITH-ACTUAL-RESOURCE-ID"
# Get the Resource ID (DbiResourceId)
DB_RESOURCE_ID=$(aws rds describe-db-instances \
  --db-instance-identifier "${DB_IDENTIFIER}" \
  --region "${REGION}" \
  --query 'DBInstances[0].DbiResourceId' \
  --output text)

echo "Resource ID: ${DB_RESOURCE_ID}"
# Output: db-ABCDE12345FGHIJ678KLM
# Get the Resource ID (DbiResourceId)
DB_RESOURCE_ID=$(aws rds describe-db-instances \
  --db-instance-identifier "${DB_IDENTIFIER}" \
  --region "${REGION}" \
  --query 'DBInstances[0].DbiResourceId' \
  --output text)

echo "Resource ID: ${DB_RESOURCE_ID}"
# Output: db-ABCDE12345FGHIJ678KLM
STEP 3

Create the PGFlare IAM Policy

This policy grants PGFlare the minimum required permissions: connect to your RDS instance via IAM auth, describe instances, and read CloudWatch metrics for your RDS resources.

Replace ACCOUNT_ID with your 12-digit AWS Account ID, REGION with your region (e.g. eu-west-2), and DB_RESOURCE_ID with the value from Step 2.

1. Open IAM ConsolePoliciesCreate policy

2. Click the JSON tab and paste the policy below

3. Replace the placeholder values in the Resource ARN

4. Click Next → name it PGFlareMonitorPolicyCreate policy

IAM Policy JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PGFlareIAMConnect",
      "Effect": "Allow",
      "Action": "rds-db:connect",
      "Resource": "arn:aws:rds-db:REGION:ACCOUNT_ID:dbuser:DB_RESOURCE_ID/pgflare_monitor"
    },
    {
      "Sid": "PGFlareDescribeRDS",
      "Effect": "Allow",
      "Action": [
        "rds:DescribeDBInstances",
        "rds:DescribeDBParameters",
        "rds:ListTagsForResource"
      ],
      "Resource": "*"
    },
    {
      "Sid": "PGFlareCloudWatch",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:GetMetricData",
        "cloudwatch:GetMetricStatistics",
        "cloudwatch:ListMetrics"
      ],
      "Resource": "*"
    }
  ]
}
# Replace these values
$AccountId    = "123456789012"
$Region       = "eu-west-2"
$DBResourceId = "db-REPLACE-WITH-ACTUAL-RESOURCE-ID"

# Build the policy JSON
$PolicyJson = @"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PGFlareIAMConnect",
      "Effect": "Allow",
      "Action": "rds-db:connect",
      "Resource": "arn:aws:rds-db:${Region}:${AccountId}:dbuser:${DBResourceId}/pgflare_monitor"
    },
    {
      "Sid": "PGFlareDescribeRDS",
      "Effect": "Allow",
      "Action": [
        "rds:DescribeDBInstances",
        "rds:DescribeDBParameters",
        "rds:ListTagsForResource"
      ],
      "Resource": "*"
    },
    {
      "Sid": "PGFlareCloudWatch",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:GetMetricData",
        "cloudwatch:GetMetricStatistics",
        "cloudwatch:ListMetrics"
      ],
      "Resource": "*"
    }
  ]
}
"@

# Save to temp file (Windows requires a file path for --policy-document)
$PolicyJson | Out-File -FilePath "pgflare-policy.json" -Encoding UTF8

# Create the policy
aws iam create-policy `
  --policy-name "PGFlareMonitorPolicy" `
  --policy-document "file://pgflare-policy.json"

# Note the PolicyArn in the output — you need it in Step 4
# Replace these values
ACCOUNT_ID="123456789012"
REGION="eu-west-2"
DB_RESOURCE_ID="db-REPLACE-WITH-ACTUAL-RESOURCE-ID"

# Create the policy JSON file
cat > pgflare-policy.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PGFlareIAMConnect",
      "Effect": "Allow",
      "Action": "rds-db:connect",
      "Resource": "arn:aws:rds-db:${REGION}:${ACCOUNT_ID}:dbuser:${DB_RESOURCE_ID}/pgflare_monitor"
    },
    {
      "Sid": "PGFlareDescribeRDS",
      "Effect": "Allow",
      "Action": ["rds:DescribeDBInstances", "rds:DescribeDBParameters", "rds:ListTagsForResource"],
      "Resource": "*"
    },
    {
      "Sid": "PGFlareCloudWatch",
      "Effect": "Allow",
      "Action": ["cloudwatch:GetMetricData", "cloudwatch:GetMetricStatistics", "cloudwatch:ListMetrics"],
      "Resource": "*"
    }
  ]
}
EOF

# Create the policy
aws iam create-policy \
  --policy-name "PGFlareMonitorPolicy" \
  --policy-document "file://pgflare-policy.json"

# Note the PolicyArn in the output — you need it in Step 4
# Replace these values
ACCOUNT_ID="123456789012"
REGION="eu-west-2"
DB_RESOURCE_ID="db-REPLACE-WITH-ACTUAL-RESOURCE-ID"

# Create the policy JSON file
cat > pgflare-policy.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PGFlareIAMConnect",
      "Effect": "Allow",
      "Action": "rds-db:connect",
      "Resource": "arn:aws:rds-db:${REGION}:${ACCOUNT_ID}:dbuser:${DB_RESOURCE_ID}/pgflare_monitor"
    },
    {
      "Sid": "PGFlareDescribeRDS",
      "Effect": "Allow",
      "Action": ["rds:DescribeDBInstances", "rds:DescribeDBParameters", "rds:ListTagsForResource"],
      "Resource": "*"
    },
    {
      "Sid": "PGFlareCloudWatch",
      "Effect": "Allow",
      "Action": ["cloudwatch:GetMetricData", "cloudwatch:GetMetricStatistics", "cloudwatch:ListMetrics"],
      "Resource": "*"
    }
  ]
}
EOF

# Create the policy
aws iam create-policy \
  --policy-name "PGFlareMonitorPolicy" \
  --policy-document "file://pgflare-policy.json"
STEP 4

Create the PGFlare IAM Role & attach the policy

Create a role that trusts PGFlare's AWS account, and attach the policy from Step 3. PGFlare will assume this role to connect — you grant access, we cannot exceed the permissions you define here.

Replace PGFLARE_ACCOUNT_ID with the value provided in your PGFlare onboarding email. This is PGFlare's AWS account that will assume the role.

1. IAM Console → RolesCreate role

2. Select AWS account → choose Another AWS account

3. Enter the Account ID provided in your PGFlare onboarding email

4. Click Next → in the search box, find and select PGFlareMonitorPolicy

5. Click Next → name the role pgflare-monitor-role

6. Click Create role

7. Click into the new role and copy the Role ARN — you'll share this with PGFlare

# Replace with PGFlare's account ID (provided in your onboarding email)
$PGFlareAccountId = "PGFLARE_ACCOUNT_ID"
$PolicyArn        = "arn:aws:iam::${AccountId}:policy/PGFlareMonitorPolicy"

# Trust policy — allows PGFlare's AWS account to assume this role
$TrustPolicy = @"
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::${PGFlareAccountId}:root"
    },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": {
        "sts:ExternalId": "PGFLARE_EXTERNAL_ID"
      }
    }
  }]
}
"@

$TrustPolicy | Out-File -FilePath "pgflare-trust.json" -Encoding UTF8

# Create the role
aws iam create-role `
  --role-name "pgflare-monitor-role" `
  --assume-role-policy-document "file://pgflare-trust.json"

# Attach the policy
aws iam attach-role-policy `
  --role-name "pgflare-monitor-role" `
  --policy-arn $PolicyArn

# Get the Role ARN — copy this and share it with PGFlare
aws iam get-role `
  --role-name "pgflare-monitor-role" `
  --query "Role.Arn" `
  --output text
# Replace with PGFlare's account ID (provided in your onboarding email)
PGFLARE_ACCOUNT_ID="PGFLARE_ACCOUNT_ID"
POLICY_ARN="arn:aws:iam::${ACCOUNT_ID}:policy/PGFlareMonitorPolicy"

# Create trust policy
cat > pgflare-trust.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::${PGFLARE_ACCOUNT_ID}:root" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": { "sts:ExternalId": "PGFLARE_EXTERNAL_ID" }
    }
  }]
}
EOF

# Create the role
aws iam create-role \
  --role-name "pgflare-monitor-role" \
  --assume-role-policy-document "file://pgflare-trust.json"

# Attach the policy
aws iam attach-role-policy \
  --role-name "pgflare-monitor-role" \
  --policy-arn "${POLICY_ARN}"

# Get and display the Role ARN — copy this and share with PGFlare
aws iam get-role \
  --role-name "pgflare-monitor-role" \
  --query 'Role.Arn' \
  --output text
# Same as Bash — macOS Terminal supports these commands natively
PGFLARE_ACCOUNT_ID="PGFLARE_ACCOUNT_ID"
POLICY_ARN="arn:aws:iam::${ACCOUNT_ID}:policy/PGFlareMonitorPolicy"

cat > pgflare-trust.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::${PGFLARE_ACCOUNT_ID}:root" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": { "sts:ExternalId": "PGFLARE_EXTERNAL_ID" }
    }
  }]
}
EOF

aws iam create-role \
  --role-name "pgflare-monitor-role" \
  --assume-role-policy-document "file://pgflare-trust.json"

aws iam attach-role-policy \
  --role-name "pgflare-monitor-role" \
  --policy-arn "${POLICY_ARN}"

# Copy the output — share it with PGFlare
aws iam get-role \
  --role-name "pgflare-monitor-role" \
  --query 'Role.Arn' \
  --output text
STEP 5

Create the PostgreSQL monitoring user

Connect to your RDS instance as the master user (e.g. postgres) and run the following SQL. This creates a passwordless user that authenticates via IAM token, then grants it read-only access to the performance views PGFlare requires.

Run as master user only. You need superuser or rds_superuser to grant rds_iam. The pgflare_monitor user will have no ability to read application tables.
PostgreSQL SQL — run in psql, pgAdmin, or the RDS Query Editor
-- Step 5a: Enable pg_stat_statements extension (if not already enabled)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Step 5b: Create the monitoring user (no password — uses IAM auth)
CREATE USER pgflare_monitor;

-- Step 5c: Grant IAM authentication to the user (RDS-specific)
GRANT rds_iam TO pgflare_monitor;

-- Step 5d: Grant the built-in pg_monitor role (read-only system stats)
--         Grants access to pg_stat_activity, pg_stat_bgwriter, pg_locks
--         and all pg_stat_* views without accessing application data
GRANT pg_monitor TO pgflare_monitor;

-- Step 5e: Grant access to pg_stat_statements specifically
GRANT SELECT ON pg_stat_statements TO pgflare_monitor;
GRANT SELECT ON pg_stat_statements_info TO pgflare_monitor;

-- Step 5f: Verify the user was created correctly
SELECT
  usename,
  usesuper,
  usecreatedb,
  usecreaterole,
  usebypassrls
FROM pg_user
WHERE usename = 'pgflare_monitor';

-- Expected output:
--  usename         | usesuper | usecreatedb | usecreaterole | usebypassrls
-- -----------------+----------+-------------+---------------+--------------
--  pgflare_monitor | f        | f           | f             | f
-- (all false — no elevated privileges)
Security verification: All five flags should be f (false). PGFlare's user has no superuser, no DB creation, no role creation, and no bypass RLS capability. It is read-only.
STEP 6

Share your connection details with PGFlare

Email the following details to hello@pgflare.com with the subject line "RDS Onboarding — [Your Company Name]". We'll have monitoring active within 4 hours of receipt.

What PGFlare will never ask for: your database password, application credentials, or access to any database other than the one specified. If anyone contacts you claiming to be PGFlare and requests credentials, do not provide them and contact hello@pgflare.com immediately.
STEP 7

Verify the setup is working

Once PGFlare confirms the connection is active, you can verify the monitoring user is connecting correctly by checking pg_stat_activity:

PostgreSQL — verify monitoring user is connecting
-- Check for active PGFlare connections
SELECT
  usename,
  application_name,
  client_addr,
  state,
  backend_start
FROM pg_stat_activity
WHERE usename = 'pgflare_monitor'
ORDER BY backend_start DESC;

-- If PGFlare is connected, you will see one row.
-- application_name will be 'pgflare'
-- state will be 'idle' (we connect, collect, and disconnect)

Revoking PGFlare access

To instantly revoke PGFlare's access, you can do either of the following — no PGFlare involvement required:

You remain in complete control of access at all times.