The Cloud Custodian Plugin runs Cloud Custodian policies in dry-run mode, builds a full inventory baseline for each configured resource type, converts each resource/policy pair into a standardized per-resource payload, and then executes CCF OPA bundles against those payloads to generate evidence.
- Load Cloud Custodian policy YAML from config.
- Parse top-level
policiesand iterate one policy entry per check. - Register runtime-derived subject templates for each configured resource type during runner-v2
Init. - Run one unfiltered inventory collection for each unique resource type.
- Run each configured check with:
custodian run --dryrun -s <output-dir> <single-policy-file>- Compare each check's matched resources with the inventory baseline.
- Build one standardized payload per resource per check. Matched resources are marked
non_compliant; baseline resources not matched by that check are markedcompliant. - Evaluate each OPA policy bundle path from agent
EvalRequest.policyPaths. - Send evidence via the plugin gRPC helper (
CreateEvidence).
This plugin always enforces read-only Cloud Custodian execution:
--dryrunis always used.- Mutating actions are not applied.
- For AWS checks, the plugin runs with
--region allto evaluate across all AWS regions by default unlessaws_regionsis configured. - On execution failures or timeouts, the plugin includes the tail of any generated
custodian-run.logartifacts in the execution error.
All plugin config fields are strings (agent gRPC map<string,string> contract).
| Key | Required | Description |
|---|---|---|
policies_yaml |
Conditionally | Inline Cloud Custodian policy YAML. Preferred over policies_path when both are set. |
policies_path |
Conditionally | Local path, file://, http://, or https:// location for policy YAML. Used when policies_yaml is empty. |
custodian_binary |
No | Path/name of Cloud Custodian executable. Default: custodian. |
custodian_debug |
No | Boolean (true/false) toggle to pass --debug to Cloud Custodian. This increases Cloud Custodian diagnostic output on stderr. Default: false. |
custodian_verbose |
No | Boolean (true/false) toggle to pass -v to Cloud Custodian. This increases Cloud Custodian diagnostic output on stderr. Default: false. |
custodian_aws_api_trace |
No | Boolean (true/false) toggle to inject a temporary Python sitecustomize.py into the Custodian child process. Logs botocore API start/end/error events to stderr and custodian-aws-api-trace.jsonl in the check output directory. Default: false. |
custodian_network_diagnostics |
No | Boolean (true/false) toggle to run Go DNS/TLS preflight probes for relevant AWS service endpoints before Custodian starts and log child process TCP socket snapshots while Custodian is running. Preflight failures stop the Custodian check before execution. If aws_regions is unset or only all, service-derived probes are skipped unless custodian_network_diagnostic_endpoints is configured. For AWS resource types not mapped to diagnostic services, diagnostics require explicit custodian_network_diagnostic_endpoints; otherwise the preflight fails. Default: false. |
custodian_network_diagnostic_endpoints |
No | Comma or whitespace separated list of additional endpoint hostnames, host:port values, or HTTPS URLs to DNS/TLS probe when custodian_network_diagnostics is enabled. Non-HTTPS URL schemes are rejected. Use this for AWS VPC endpoint DNS names such as vpce-123.backup.eu-west-1.vpce.amazonaws.com. Default: unset. |
custodian_log_tail_during_run |
No | Boolean (true/false) toggle to tail discovered custodian-run.log artifacts during the monitor loop, not only after process exit. Default: false. |
aws_regions |
No | Comma or whitespace separated AWS regions passed as repeated --region flags. Duplicate entries are removed while preserving order. Default: unset, which falls back to --region all for AWS checks. |
check_timeout_seconds |
No | Per-check timeout in seconds. Default: 300. |
policy_labels |
No | JSON map of labels merged into generated evidence labels. |
resource_identity_fields |
No | JSON object mapping Cloud Custodian resource types to ordered identity field paths. Built-in defaults are used after configured fields. Example: {"aws.ec2":["InstanceId","Arn"]}. |
debug_dump_payloads |
No | Boolean (true/false) toggle to write standardized resource payload JSON files for troubleshooting. Default: false. |
debug_payload_output_dir |
No | Directory where debug payload JSON files are written. If set, debug dumping is auto-enabled. Default when enabled without explicit path: debug-standardized-payloads. |
preserve_execution_artifacts |
No | Boolean (true/false) toggle to keep the temporary Cloud Custodian execution root after a check execution failure for postmortem review. Default: false. |
Validation rules:
- At least one of
policies_yamlorpolicies_pathmust be provided. custodian_binarymust resolve on PATH (or as explicit executable path).check_timeout_secondsmust be a positive integer.resource_identity_fields, when set, must be valid JSON and each resource type must include at least one field path.- Policy YAML must include top-level
policiesarray.
plugins:
cloud_custodian:
source: ./dist/plugin
policies:
- ./policy-bundle
config:
policies_yaml: |
policies:
- name: ec2-public-ip-check
resource: aws.ec2
filters:
- type: value
key: PublicIpAddress
op: not-null
custodian_binary: custodian
check_timeout_seconds: "300"
policy_labels: '{"team":"cloud-security","environment":"prod"}'plugins:
cloud_custodian:
source: ./dist/plugin
policies:
- ./policy-bundle
config:
policies_path: file:///etc/ccf/cloud-custodian.yaml
custodian_binary: /usr/local/bin/custodianEach resource/check iteration produces one payload with this shape:
{
"schema_version": "v2",
"source": "cloud-custodian",
"check": {
"name": "ec2-public-ip-check",
"resource": "aws.ec2",
"provider": "aws",
"index": 0,
"metadata": {}
},
"resource": {
"id": "i-1234567890abcdef0",
"type": "aws.ec2",
"provider": "aws",
"account_id": "123456789012",
"region": "us-east-1",
"identity_fields": {
"InstanceId": "i-1234567890abcdef0"
},
"data": {"...": "..."}
},
"assessment": {
"status": "non_compliant",
"matched": true,
"inventory_status": "baseline",
"matched_resource_count": 3,
"artifact_path": "/tmp/ccf-cloud-custodian-123/001-ec2-public-ip-check",
"resources_path": "/tmp/ccf-cloud-custodian-123/001-ec2-public-ip-check/ec2-public-ip-check/resources.json"
},
"execution": {
"status": "success",
"dry_run": true,
"exit_code": 0,
"started_at": "2026-03-06T12:00:00Z",
"ended_at": "2026-03-06T12:00:01Z",
"duration_ms": 1000,
"stdout": "...",
"stderr": "",
"error": "",
"errors": []
},
"raw_policy": {
"name": "ec2-public-ip-check",
"resource": "aws.ec2",
"filters": []
}
}Generated evidence labels include resource_id, resource_type, provider,
and any available account_id/region. The resource subject includes the
resource identifier as a link and a resource_id property. When the AWS
resource identifier is an ARN, generated evidence includes an AWS Resource
Explorer link using an exact id:<arn> search. AWS S3 bucket names are parsed
as arn:aws:s3:::<bucket-name> for Resource Explorer links. AWS Route53 hosted
zone identifiers such as /hostedzone/Z123 are normalized to full ARNs such as
arn:aws:route53:::hostedzone/Z123.
assessment.inventory_status is baseline for resources found in the unfiltered
inventory run. If a policy returns a resource that is not present in the baseline,
the plugin still evaluates it as non_compliant and sets
inventory_status to missing_from_baseline.
provider extraction rule:
aws.s3->aws- non
<provider>.<resource>formats ->unknown
- Cloud Custodian CLI must be installed and executable.
- Cloud/provider credentials must be available in the plugin process environment (ambient credentials/profile/env vars).
Run:
go test ./...