Check Amazon Macie Is Enabled in Every Region

A long data center corridor lined with server racks and blue ceiling lights

Photo by David Slaager on Pexels

To confirm Amazon Macie is enabled in all Regions, call GetMacieSession in every Region your account has enabled. A Region where Macie is on returns a status of ENABLED or PAUSED; a Region where it was never turned on answers with AccessDeniedException. Then check automated sensitive data discovery with GetAutomatedDiscoveryConfiguration.

Macie finds sensitive data in S3 and watches bucket security, but only in the Regions where you’ve turned it on. It’s common to enable it in the Region you work in and forget the rest, so a bucket created in eu-west-1 for a quick test is never inventoried or evaluated.

This example is for engineers auditing their detective controls. You’ll get a script that loops over every enabled Region and reports whether Amazon Macie is enabled there, how often it publishes findings, whether automated discovery runs, and a bucket summary from GetBucketStatistics. With --apply it enables Macie where it’s missing. It follows the same pattern as the checks that confirm GuardDuty is enabled in every AWS Region and Security Hub is enabled in every Region.

Why must Amazon Macie be enabled in each Region?

Enabling Macie creates a Macie session: a resource that represents Macie for one account in one Region. An account has at most one session per Region, and Macie only builds its inventory from the S3 general purpose buckets in that Region. There’s no global switch, so a Region without a session is a Region Macie can’t see.

  • ENABLED: Macie maintains the bucket inventory, evaluates bucket security and access control, and runs any discovery you’ve configured.
  • PAUSED: the account is still enabled, but all Macie activities are suspended and classification jobs are cancelled. Settings and data are kept. A paused Region looks enabled at a glance, which is why the script reports it separately.
  • No session: the API reference notes that when Macie isn’t enabled, GetMacieSession returns an HTTP 403 response, which the SDK raises as AccessDeniedException. A missing IAM permission raises the same exception, so run the script with a policy you know is complete.

Automated sensitive data discovery is a separate setting, also per Region. When it’s on, Macie samples objects across your buckets and builds sensitivity statistics, typically within 48 hours. When it’s off, most sensitivity statistics are 0, and the Sensitive column in the report means nothing. Only a standalone account or the Macie administrator of an organization can read or change it; member accounts get an error, which the script shows as n/a.

What does Macie cost once it’s on?

The first time you enable Macie, the account starts a 30-day free trial. During the trial there’s no charge for the bucket inventory and monitoring or for automated sensitive data discovery in each Region; sensitive data discovery jobs that analyze more than 1 GB of data are charged. After the trial, these us-east-1 rates apply, taken from the AWS Price List API (published 11 September 2026). Other Regions differ; check the Amazon Macie pricing page before you roll out.

Usage (us-east-1, as of September 2026) Price
Bucket inventory and monitoring $0.003288 per bucket per day (about $0.10 per bucket per month)
Automated discovery: objects monitored $0.0000000033 per object per day (about $0.01 per 100,000 objects per month)
Sensitive data discovery: data inspected First 1 GB per month free, then $1.00 per GB up to 50,000 GB, $0.50 per GB for the next 450,000 GB, $0.25 per GB beyond

Worked example. An account with 214 buckets and 10 million objects in us-east-1, with automated discovery on, pays for monitoring: 214 × $0.003288 × 30 = $21.11 per month. Object monitoring adds 10,000,000 × $0.0000000033 × 30 = $0.99. On top of that comes the data Macie actually inspects, which depends on how much it samples. Macie shows its own estimate in the console and API, and a monthly AWS Budget alert created with SDK v3 catches a surprise before the invoice does.

What does the script do?

  1. Lists RegionsDescribeRegions returns the Regions enabled for your account, or you pass --regions.
  2. Reads the sessionGetMacieSession per Region gives the status and findingPublishingFrequency: how often policy finding updates go to Security Hub CSPM and EventBridge (FIFTEEN_MINUTES, ONE_HOUR or SIX_HOURS).
  3. Checks automated discoveryGetAutomatedDiscoveryConfiguration returns ENABLED or DISABLED.
  4. Summarizes bucketsGetBucketStatistics gives the bucket count, publicly accessible buckets, buckets with no default encryption setting, and buckets scored sensitive. For a Macie administrator, this includes member accounts’ buckets.
  5. Enables, if asked--apply calls EnableMacie with status ENABLED in each Region reported NOT ENABLED, then reads the session again. Paused Regions are left for you to decide.

Prerequisites

  • Node.js 18 or later with tsx, plus @aws-sdk/client-macie2 and @aws-sdk/client-ec2.
  • A profile for the account. The guide to AWS SDK v3 credential providers for profiles and SSO covers the options.
  • If you use AWS Organizations, know whether the account is standalone, a member, or the Macie administrator; that decides which calls answer.

Which IAM permissions does it need?

Macie’s read actions don’t take resource ARNs, so they use "*". The last statement is only for --apply: EnableMacie creates the AWSServiceRoleForAmazonMacie service-linked role the first time, so the caller needs permission to create it.

macie-check-policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadMacieStatus",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeRegions",
        "macie2:GetMacieSession",
        "macie2:GetAutomatedDiscoveryConfiguration",
        "macie2:GetBucketStatistics"
      ],
      "Resource": "*"
    },
    {
      "Sid": "EnableOnlyWithApply",
      "Effect": "Allow",
      "Action": "macie2:EnableMacie",
      "Resource": "*"
    },
    {
      "Sid": "MacieServiceLinkedRole",
      "Effect": "Allow",
      "Action": "iam:CreateServiceLinkedRole",
      "Resource": "arn:aws:iam::*:role/aws-service-role/macie.amazonaws.com/AWSServiceRoleForAmazonMacie",
      "Condition": { "StringLike": { "iam:AWSServiceName": "macie.amazonaws.com" } }
    }
  ]
}

Drop the last two statements for a report-only role. The guide to reviewing an IAM policy for least privilege explains why write actions belong in a separate role.

The script to check Amazon Macie in every Region

check-macie-all-regions.ts

// check-macie-all-regions.ts
// Checks Amazon Macie in every enabled Region: session status, finding publishing frequency, automated
// sensitive data discovery and a bucket summary. --apply enables Macie where it isn't enabled yet.
// Usage:
//   npx tsx check-macie-all-regions.ts [--regions us-east-1,eu-west-1]
//   npx tsx check-macie-all-regions.ts --regions eu-west-1 --apply [--frequency ONE_HOUR]
import { randomUUID } from "node:crypto";
import { EC2Client, DescribeRegionsCommand } from "@aws-sdk/client-ec2";
import {
  Macie2Client,
  EnableMacieCommand,
  GetAutomatedDiscoveryConfigurationCommand,
  GetBucketStatisticsCommand,
  GetMacieSessionCommand,
  type FindingPublishingFrequency,
} from "@aws-sdk/client-macie2";

const args = process.argv.slice(2);
const flag = (name: string): string | undefined => {
  const i = args.indexOf(name);
  return i >= 0 ? args[i + 1] : undefined;
};
const apply = args.includes("--apply");
const FREQUENCIES = ["FIFTEEN_MINUTES", "ONE_HOUR", "SIX_HOURS"];
const frequencyArg = flag("--frequency");
if (frequencyArg && !FREQUENCIES.includes(frequencyArg)) {
  console.error(`--frequency must be one of ${FREQUENCIES.join(", ")}`);
  process.exit(1);
}
const frequency = frequencyArg as FindingPublishingFrequency | undefined;

interface Row {
  Region: string;
  Macie: string;
  Publishing: string;
  AutoDiscovery: string;
  Buckets: number | string;
  Public: number | string;
  NoDefaultEncryption: number | string;
  Sensitive: number | string;
}

const errName = (err: unknown): string => (err instanceof Error ? err.name : "Error");
const errText = (err: unknown): string => (err instanceof Error ? `${err.name}: ${err.message}` : String(err));

async function enabledRegions(): Promise<string[]> {
  const listed = flag("--regions");
  if (listed) return listed.split(",").map((r) => r.trim()).filter(Boolean);
  const ec2 = new EC2Client({ region: process.env.AWS_REGION ?? "us-east-1" });
  const out = await ec2.send(new DescribeRegionsCommand({ AllRegions: false }));
  return (out.Regions ?? []).map((r) => r.RegionName ?? "").filter(Boolean).sort();
}

async function checkRegion(region: string): Promise<{ row: Row; enabled: boolean }> {
  const macie = new Macie2Client({ region });
  const row: Row = {
    Region: region,
    Macie: "?",
    Publishing: "-",
    AutoDiscovery: "-",
    Buckets: "-",
    Public: "-",
    NoDefaultEncryption: "-",
    Sensitive: "-",
  };
  try {
    const session = await macie.send(new GetMacieSessionCommand({}));
    row.Macie = session.status ?? "UNKNOWN";
    row.Publishing = session.findingPublishingFrequency ?? "-";
  } catch (err) {
    // Without a Macie session the call fails with AccessDeniedException; so does a missing IAM permission.
    row.Macie = errName(err) === "AccessDeniedException" ? "NOT ENABLED" : `ERROR ${errName(err)}`;
    return { row, enabled: false };
  }
  try {
    const auto = await macie.send(new GetAutomatedDiscoveryConfigurationCommand({}));
    row.AutoDiscovery = auto.status ?? "UNKNOWN";
  } catch (err) {
    row.AutoDiscovery = `n/a (${errName(err)})`; // member accounts can't read the organization setting
  }
  try {
    const stats = await macie.send(new GetBucketStatisticsCommand({}));
    row.Buckets = stats.bucketCount ?? 0;
    row.Public = stats.bucketCountByEffectivePermission?.publiclyAccessible ?? 0;
    row.NoDefaultEncryption = stats.bucketCountByEncryptionType?.unencrypted ?? 0;
    row.Sensitive = stats.bucketStatisticsBySensitivity?.sensitive?.totalCount ?? 0;
  } catch (err) {
    row.Buckets = `n/a (${errName(err)})`;
  }
  return { row, enabled: true };
}

async function main(): Promise<void> {
  const regions = await enabledRegions();
  const rows: Row[] = [];
  const missing: string[] = [];
  for (const region of regions) {
    const { row, enabled } = await checkRegion(region);
    rows.push(row);
    if (!enabled && row.Macie === "NOT ENABLED") missing.push(region);
  }
  console.table(rows);
  const paused = rows.filter((r) => r.Macie === "PAUSED").map((r) => r.Region);
  const noAuto = rows.filter((r) => r.AutoDiscovery === "DISABLED").map((r) => r.Region);
  console.log(`${regions.length} Regions checked: ${missing.length} without Macie, ${paused.length} paused, ${noAuto.length} with automated discovery off.`);
  if (paused.length) console.log(`Paused (not re-enabled by this script): ${paused.join(", ")}`);

  if (!apply) {
    console.log("Report only: nothing was changed. Add --apply to enable Macie in the Regions marked NOT ENABLED.");
    return;
  }
  for (const region of missing) {
    const macie = new Macie2Client({ region });
    try {
      await macie.send(
        new EnableMacieCommand({ status: "ENABLED", clientToken: randomUUID(), findingPublishingFrequency: frequency }),
      );
      const check = await macie.send(new GetMacieSessionCommand({}));
      console.log(`Enabled Macie in ${region}: status ${check.status}, publishing ${check.findingPublishingFrequency}`);
    } catch (err) {
      console.error(`Could not enable Macie in ${region}: ${errText(err)}`);
      process.exitCode = 1;
    }
  }
}

main().catch((err) => {
  console.error(errText(err));
  process.exit(1);
});

Enabling starts billing: each Region you enable begins its own 30-day trial and then bills per bucket. Run the report first, and limit --apply with --regions to the Regions that hold S3 data you care about.

How do you run it?

Terminal

npm install @aws-sdk/client-macie2 @aws-sdk/client-ec2
npm install --save-dev tsx typescript @types/node

# Report on every enabled Region
AWS_PROFILE=security-audit npx tsx check-macie-all-regions.ts

# Enable Macie in one Region, publishing finding updates hourly
AWS_PROFILE=security-admin npx tsx check-macie-all-regions.ts --regions eu-west-1 --apply --frequency ONE_HOUR

Sample output

Output (with –apply –frequency ONE_HOUR)

┌─────────┬──────────────┬───────────────┬───────────────────┬───────────────┬─────────┬────────┬─────────────────────┬───────────┐
│ (index) │ Region       │ Macie         │ Publishing        │ AutoDiscovery │ Buckets │ Public │ NoDefaultEncryption │ Sensitive │
├─────────┼──────────────┼───────────────┼───────────────────┼───────────────┼─────────┼────────┼─────────────────────┼───────────┤
│ 0       │ 'ap-south-1' │ 'PAUSED'      │ 'SIX_HOURS'       │ 'DISABLED'    │ 6       │ 0      │ 0                   │ 0         │
│ 1       │ 'eu-west-1'  │ 'NOT ENABLED' │ '-'               │ '-'           │ '-'     │ '-'    │ '-'                 │ '-'       │
│ 2       │ 'us-east-1'  │ 'ENABLED'     │ 'FIFTEEN_MINUTES' │ 'ENABLED'     │ 214     │ 2      │ 0                   │ 17        │
│ 3       │ 'us-west-2'  │ 'ENABLED'     │ 'SIX_HOURS'       │ 'DISABLED'    │ 38      │ 0      │ 0                   │ 0         │
└─────────┴──────────────┴───────────────┴───────────────────┴───────────────┴─────────┴────────┴─────────────────────┴───────────┘
4 Regions checked: 1 without Macie, 1 paused, 2 with automated discovery off.
Paused (not re-enabled by this script): ap-south-1
Enabled Macie in eu-west-1: status ENABLED, publishing ONE_HOUR

Regions and numbers are illustrative. eu-west-1 had no session and was enabled. ap-south-1 is paused, so its six buckets aren’t being evaluated; resume it with UpdateMacieSession or disable it on purpose. In us-east-1, two buckets are publicly accessible and 17 are scored sensitive, which is the overlap to look at first. The script to find public and private S3 buckets with the AWS SDK names them, and us-west-2 has automated discovery off, so zero sensitive buckets there means “not analyzed”, not “clean”.

Troubleshooting

  • Every Region shows NOT ENABLED. Probably a permission problem, not a Macie one: AccessDeniedException covers both. Test GetMacieSession in a Region where you know Macie is on; the guide to troubleshooting IAM access denied errors helps find the missing statement.
  • AutoDiscovery shows n/a. The account is a Macie member; the setting belongs to the administrator account. Run the script there.
  • A Region shows ERROR with a network error name. Macie isn’t offered in every Region. Check the Macie endpoints list for that Region and leave it out with --regions.
  • ConflictException on EnableMacie. The state changed since the report, for example another admin enabled it. Run the report again.

Ask ChatWithCloud instead

For a quick answer without a script, ask ChatWithCloud “Is Amazon Macie enabled in all my Regions, and is automated discovery on?” It writes AWS SDK for JavaScript v2 code, runs it on your machine with your profile and summarizes the result. Changes run without a confirmation step, so use a read-only profile, as ChatWithCloud’s security model recommends. The walkthrough on how to analyze your AWS security posture with an AI CLI has more questions like this one.

Frequently asked questions

Is Amazon Macie a global or regional service?

Regional. Each account has one Macie session per Region, and Macie only inventories and analyzes the S3 buckets in the Region where it’s enabled. You enable it Region by Region.

How do I check if Macie is enabled with the AWS CLI or SDK?

Call GetMacieSession (aws macie2 get-macie-session) in each Region. ENABLED or PAUSED means a session exists; AccessDeniedException means it isn’t enabled or you lack permission.

Does enabling Macie turn on automated sensitive data discovery?

It depends on your account settings, so check it with GetAutomatedDiscoveryConfiguration after enabling. It’s a separate per-Region setting that you can enable or disable independently.

What happens to findings if I pause Macie?

Pausing stops all Macie activity and cancels classification jobs, but keeps your settings and data. Macie stores findings for 90 days.

Related guides

Ask your AWS account in plain English

Your first 15 runs are free, with no OpenAI key needed.

npx chatwithcloud