Security AWS

Configure CrowdStrike Falcon Cloud Security CSPM and ECR Registry Assessment for AWS

A payments company runs 40-odd AWS accounts under one Organization, and the cloud security team has exactly the problem every multi-account shop hits eventually: they have agent coverage on EC2 from the CrowdStrike Falcon sensor, but no continuous picture of the configuration of the accounts themselves — the public S3 bucket someone spun up for a one-day test, the security group that quietly opened 0.0.0.0/0 to RDS, the IAM role with *:* that a contractor created and never removed. They also ship roughly 200 container images a week to Amazon ECR, and “is this image safe to deploy” is currently answered by a vibe. The mandate from the CISO is concrete: onboard every account into CrowdStrike Falcon Cloud Security for agentless posture management (CSPM), turn on behavioural Indicator-of-Attack (IOA) detection so a live attacker action — not just a static misconfig — pages the SOC, and make ECR registry scanning a gate in the build pipeline. This guide is the step-by-step to do exactly that, repeatably, with Terraform.

The thing to internalise before you start: Falcon Cloud Security gives you two complementary lenses. Agentless CSPM reads your AWS control plane through a cross-account IAM role and tells you what is misconfigured and what an attack path looks like. IOA detection consumes a CloudTrail-derived event stream and tells you what an attacker is doing right now — a root login from a new country, a disabled GuardDuty detector, a sudden PutBucketPolicy that makes data public. ECR assessment is a third lens on your supply chain. You will wire all three.

Prerequisites

Target topology

Configure CrowdStrike Falcon Cloud Security CSPM and ECR Registry Assessment for AWS — topology

The picture has three flows landing in one Falcon tenant. CSPM is a cross-account IAM role per AWS account that Falcon assumes (read-only) from its own AWS account to enumerate your config. IOA is a per-account EventBridge rule that forwards CloudTrail management events to a CrowdStrike-owned EventBridge bus, where the behavioural engine scores them. ECR assessment is the Falcon image-assessment service pulling (or being pushed) image digests, with results surfaced in the console and queryable from CI. Around the edge: Okta/Entra gate console access; HashiCorp Vault holds the API credentials and the scan token; detections fan out to ServiceNow (incidents) and the SOC; Wiz runs in parallel as an independent posture cross-check; and Datadog/Dynatrace ingest the detection stream for dashboards. GitHub Actions / Jenkins / Argo CD call the ECR scan as a deploy gate.

1. Create the Falcon API client and stage credentials in Vault

Everything that follows is driven by the CrowdStrike Falcon API, so the first concrete step is a scoped API client and getting its secret out of human hands and into HashiCorp Vault.

In the Falcon console: Support and resources → API clients and keys → Create API client. Name it aws-cspm-onboarding, grant scopes CSPM registration: Read & Write and Falcon Container Image: Read & Write, and copy the client ID and secret once (the secret is shown exactly once).

Stash them in Vault and read them back as environment variables the Terraform CrowdStrike provider expects:

# Write once (an operator does this; the value never touches disk in CI)
vault kv put secret/falcon/cspm \
  client_id="$FALCON_CLIENT_ID" \
  client_secret="$FALCON_CLIENT_SECRET" \
  cloud="us-1"

# In the pipeline / shell, hydrate the provider's expected env vars
export FALCON_CLIENT_ID=$(vault kv get -field=client_id secret/falcon/cspm)
export FALCON_CLIENT_SECRET=$(vault kv get -field=client_secret secret/falcon/cspm)
export FALCON_CLOUD=$(vault kv get -field=cloud secret/falcon/cspm)

Confirm the credentials work and you can see the tenant before touching AWS:

# Quick OAuth2 smoke test against the Falcon API
curl -s -X POST "https://api.${FALCON_CLOUD}.crowdstrike.com/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=${FALCON_CLIENT_ID}&client_secret=${FALCON_CLIENT_SECRET}" \
  | python3 -c "import sys,json;print('token-ok' if json.load(sys.stdin).get('access_token') else 'FAILED')"

A token-ok here means the rest of the guide will work. A failure now is a wrong cloud region (us-1, us-2, eu-1, us-gov-1) or a mistyped secret — fix it before proceeding.

2. Register the AWS Organization with Falcon CSPM (Terraform)

CrowdStrike publishes the crowdstrike/crowdstrike Terraform provider, which talks to the Falcon API to register the account and hand you back the exact IAM trust policy AWS must satisfy. The clean pattern is: register at the Org level, then deploy the IAM role into every member via a StackSet.

Define the provider and register the Organization:

terraform {
  required_providers {
    crowdstrike = { source = "crowdstrike/crowdstrike", version = "~> 0.4" }
    aws         = { source = "hashicorp/aws",         version = "~> 5.40" }
  }
}

# Reads FALCON_CLIENT_ID / FALCON_CLIENT_SECRET / FALCON_CLOUD from env
provider "crowdstrike" {}

# Register the whole AWS Org for agentless CSPM + IOA behavioural detection
resource "crowdstrike_cloud_aws_account" "org" {
  account_id           = "123456789012"          # the Org MANAGEMENT account
  organization_id      = "o-abcd1234ef"          # your AWS Organizations Org ID
  is_organization_mgmt = true

  asset_inventory   = { enabled = true }         # agentless CSPM config read
  realtime_visibility = {                        # IOA / behavioural detection
    enabled              = true
    cloudtrail_region    = "us-east-1"
  }
  sensor_management = { enabled = false }        # we already manage sensors separately
}

# What AWS must trust — Falcon hands these back after registration
output "iam_role_name"        { value = crowdstrike_cloud_aws_account.org.iam_role_name }
output "intermediate_role"    { value = crowdstrike_cloud_aws_account.org.intermediate_role_arn }
output "external_id"          { value = crowdstrike_cloud_aws_account.org.external_id }
output "eventbridge_role_arn" { value = crowdstrike_cloud_aws_account.org.eventbus_arn }
terraform init
terraform apply -target=crowdstrike_cloud_aws_account.org

After apply, terraform output external_id and iam_role_name give you the values the IAM role in the next step must use. The external ID is the confused-deputy protection: it is unique to your registration, and the cross-account role only trusts CrowdStrike when that exact external ID is presented.

3. Deploy the read-only CSPM IAM role to every account (StackSet)

CrowdStrike provides a CloudFormation template for the role, but doing it in Terraform via a CloudFormation StackSet keeps it in the same plan and rolls it to all accounts in the Org. The role is strictly read-only for posture and grants CrowdStrike’s account permission to assume it.

data "crowdstrike_cloud_aws_account" "reg" {
  account_id = "123456789012"
}

resource "aws_cloudformation_stack_set" "falcon_cspm_role" {
  name             = "CrowdStrike-CSPM-ReadOnly"
  permission_model = "SERVICE_MANAGED"           # uses Org trusted access, auto-deploys to members
  capabilities     = ["CAPABILITY_NAMED_IAM"]

  auto_deployment {
    enabled                          = true       # new accounts get the role automatically
    retain_stacks_on_account_removal = false
  }

  parameters = {
    RoleName     = data.crowdstrike_cloud_aws_account.reg.iam_role_name
    ExternalID   = data.crowdstrike_cloud_aws_account.reg.external_id
    CSRoleArn    = data.crowdstrike_cloud_aws_account.reg.intermediate_role_arn
  }

  # Pull the official CrowdStrike CSPM role template (pinned, reviewed in your repo)
  template_body = file("${path.module}/templates/cs-cspm-readonly-role.yaml")
}

resource "aws_cloudformation_stack_set_instance" "falcon_role_all" {
  stack_set_name = aws_cloudformation_stack_set.falcon_cspm_role.name
  deployment_targets {
    organizational_unit_ids = ["ou-abcd-11111111"]   # the OU(s) holding your member accounts
  }
  region = "us-east-1"                                  # IAM is global; one region pins the stack
}
terraform apply
# Watch the StackSet fan out
aws cloudformation list-stack-instances \
  --stack-set-name CrowdStrike-CSPM-ReadOnly \
  --query 'Summaries[].[Account,Status]' --output table

The role this creates attaches AWS-managed SecurityAudit plus a small CrowdStrike-specific read policy (for services SecurityAudit does not cover, like a few config-history and ECR describe calls). Nothing in it can mutate your environment — verify that claim yourself in step 7.

4. Turn on IOA behavioural detection via EventBridge

CSPM scanning (step 2) reads config on a schedule. IOA detection is the real-time half: it needs a copy of CloudTrail management events delivered to a CrowdStrike-owned EventBridge event bus, where the behavioural engine evaluates them against attack indicators. Because you set realtime_visibility.enabled = true in step 2, Falcon already provisioned the target bus and gave you eventbus_arn. Now create the per-account EventBridge rule that forwards events to it.

Add this to the StackSet template (so it lands in every account alongside the role), or deploy as its own StackSet:

resource "aws_cloudwatch_event_rule" "falcon_ioa" {
  name          = "cs-falcon-ioa-forward"
  description   = "Forward CloudTrail management events to CrowdStrike for IOA detection"
  event_pattern = jsonencode({
    detail-type = ["AWS API Call via CloudTrail"]
  })
}

resource "aws_cloudwatch_event_target" "falcon_ioa" {
  rule      = aws_cloudwatch_event_rule.falcon_ioa.name
  arn       = data.crowdstrike_cloud_aws_account.reg.eventbus_arn   # CrowdStrike's bus
  role_arn  = aws_iam_role.eventbridge_forward.arn                  # role allowing events:PutEvents
}

IOA detection assumes CloudTrail is actually capturing management events. If you do not already have an Org-wide trail, that is the prerequisite that makes IOA meaningful:

# Confirm an Org trail exists and is logging management events
aws cloudtrail describe-trails --query 'trailList[].[Name,IsOrganizationTrail]' --output table
aws cloudtrail get-trail-status --name org-management-trail --query 'IsLogging'

If IsLogging is false or no Org trail exists, create one before relying on IOA — otherwise the EventBridge rule forwards nothing and the SOC sees silence, not safety.

5. Enable ECR registry assessment

The third lens is your container supply chain. Falcon’s image assessment can scan Amazon ECR registries for vulnerabilities and embedded secrets. There are two integration styles; pick one per pipeline.

Style A — registry connection (Falcon pulls). Connect the ECR registry to Falcon so it inventories and assesses images on a schedule. Falcon assumes a read role into ECR (the CSPM role from step 3 already carries ecr:Describe*, ecr:GetDownloadUrlForLayer, ecr:BatchGetImage). Register the registry via the API:

ACCESS_TOKEN=$(curl -s -X POST "https://api.${FALCON_CLOUD}.crowdstrike.com/oauth2/token" \
  -d "client_id=${FALCON_CLIENT_ID}&client_secret=${FALCON_CLIENT_SECRET}" \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")

curl -s -X POST "https://api.${FALCON_CLOUD}.crowdstrike.com/container-security/entities/registries/v1" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
        "type": "ecr",
        "url": "123456789012.dkr.ecr.us-east-1.amazonaws.com",
        "details": { "aws_iam_role": "arn:aws:iam::123456789012:role/CrowdStrike-ECR-Read",
                     "aws_external_id": "'"$(terraform output -raw external_id)"'" }
      }'

Style B — inline scan in CI (you push the image to the scanner). Better as a deploy gate: scan the image you just built, before it is promoted, and fail the build on policy. CrowdStrike ships a self-contained scanner image. In GitHub Actions:

- name: CrowdStrike image assessment (gate)
  env:
    FALCON_CLIENT_ID:     ${{ secrets.FALCON_CLIENT_ID }}
    FALCON_CLIENT_SECRET: ${{ secrets.FALCON_CLIENT_SECRET }}
  run: |
    docker run --rm \
      -v /var/run/docker.sock:/var/run/docker.sock \
      registry.crowdstrike.com/falcon-imageanalyzer/us-1/release/falcon-image-analyzer:latest \
      --client-id "$FALCON_CLIENT_ID" --client-secret "$FALCON_CLIENT_SECRET" \
      --cloud "$FALCON_CLOUD" \
      --image "123456789012.dkr.ecr.us-east-1.amazonaws.com/payments-api:${GIT_SHA}" \
      --fail-on "high"        # non-zero exit if a High/Critical CVE or secret is found

For Jenkins the same docker run goes in a sh step; for Argo CD, run the scan as a pre-sync hook job so a non-compliant image never syncs to the cluster. On self-hosted runners that lack the scanner image, an Ansible play (community.docker.docker_image) pre-pulls it so the gate step is fast and offline-safe.

6. Route detections to ServiceNow, the SOC, and your observability stack

A finding nobody sees is worthless. Falcon Cloud Security pushes CSPM findings and IOA detections out through its integrations and the streaming API.

A minimal Fusion-to-ServiceNow trigger condition, expressed as the rule you configure in the console:

WHEN  cloud detection severity >= High
 AND  cloud provider = AWS
THEN  create ServiceNow incident  (assignment group = Cloud-SOC, priority = mapped from severity)

7. Validation

Prove each of the three lenses is live before you call it done.

CSPM is reading config. In the console, Cloud security → Account registration: every account shows status Active / Provisioned. From the API:

curl -s "https://api.${FALCON_CLOUD}.crowdstrike.com/cloud-connect-cspm-aws/entities/account/v1?status=provisioned" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  | python3 -c "import sys,json;d=json.load(sys.stdin);print('accounts provisioned:',len(d.get('resources',[])))"

Then deliberately create a finding and watch it surface (do this in a sandbox account):

# Create an intentionally public bucket policy, then expect a CSPM finding within a scan cycle
aws s3api put-bucket-policy --bucket cs-test-public-bucket \
  --policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"s3:GetObject","Resource":"arn:aws:s3:::cs-test-public-bucket/*"}]}'
# In console: Cloud security → Assessment → filter "S3 bucket is publicly accessible" → expect the bucket listed

IOA is detecting behaviour. Trigger a classic indicator and confirm a detection appears under Cloud security → Detections (give it a few minutes for the EventBridge → bus → engine path):

# A commonly-flagged IOA: stopping CloudTrail logging (do this only in a sandbox!)
aws cloudtrail stop-logging --name org-management-trail
# Expect an IOA detection like "CloudTrail logging disabled" — then immediately re-enable:
aws cloudtrail start-logging --name org-management-trail

Verify the role is genuinely read-only (the claim from step 3). It should fail to mutate:

aws sts assume-role --role-arn "arn:aws:iam::<member>:role/CrowdStrike-CSPM-ReadOnly" \
  --role-session-name verify --external-id "$(terraform output -raw external_id)" >/tmp/cs.json
# Using those creds, a write must be denied:
AWS_ACCESS_KEY_ID=... aws s3api put-object --bucket any --key x 2>&1 | grep -q 'AccessDenied' \
  && echo "read-only confirmed"

ECR assessment ran. Confirm the CI gate fails closed by scanning a knowingly-vulnerable image and asserting a non-zero exit, then check the console Cloud security → Images lists the digest with its CVE count.

8. Rollback and teardown

Because every mutation went through Terraform and StackSets, teardown is clean and ordered — remove forwarders first so nothing dangles, then deregister.

# 1. Stop forwarding events (do this first so no orphaned rule points at a dead bus)
terraform destroy -target=aws_cloudwatch_event_target.falcon_ioa \
                  -target=aws_cloudwatch_event_rule.falcon_ioa

# 2. Remove the IAM role from all member accounts
terraform destroy -target=aws_cloudformation_stack_set_instance.falcon_role_all
terraform destroy -target=aws_cloudformation_stack_set.falcon_cspm_role

# 3. Deregister the account/Org from Falcon (also via API if Terraform state is gone)
terraform destroy -target=crowdstrike_cloud_aws_account.org

# API fallback to deregister if state is lost:
curl -s -X DELETE \
  "https://api.${FALCON_CLOUD}.crowdstrike.com/cloud-connect-cspm-aws/entities/account/v1?ids=123456789012" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

For the ECR registry connection (Style A), delete it via the registries API DELETE …/registries/v1?ids=<registry_id>. Finally, rotate the Falcon API client in the console and remove secret/falcon/cspm from Vault if you are decommissioning entirely.

Common pitfalls

Security notes

The CSPM role is read-only by design — verify that in step 7 rather than trusting it. Keep the Falcon API client scoped to exactly CSPM registration and Falcon Container Image; do not reuse a broad admin client. The OAuth2 secret and the ECR scan credentials live only in HashiCorp Vault and are injected at runtime — they must never appear in a .tfvars, a CI log, or git history. Gate human access to the Falcon console behind Okta / Entra ID SAML with MFA and map console roles to IdP groups so analyst, manager, and admin separation is enforced at the identity layer. Route every High/Critical detection to ServiceNow so there is an audited incident trail, and let Wiz independently confirm that the posture controls CrowdStrike reports are actually holding.

Cost notes

Agentless CSPM and IOA are licensed per cloud account/asset in your Falcon Cloud Security subscription, so the dominant cost lever is scoping — onboard the OUs that hold real workloads, and exclude pure sandbox OUs if your licensing is per-account. The AWS-side infrastructure (the IAM role, the EventBridge rule) is effectively free; the only AWS charge that scales is CloudTrail management-event delivery and any S3 storage for the trail, which you are almost certainly already paying. ECR inline scanning consumes CI minutes — cache the scanner image (the Ansible pre-pull above) so you pay for the scan, not a repeated multi-hundred-MB pull on every build. Finally, sending the detection stream to Datadog/Dynatrace incurs log-ingestion cost there; filter to High/Critical at the source if volume becomes a line item.

AWSCrowdStrikeCSPMCloud SecurityECRTerraform
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading