diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..e3a4d64
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,38 @@
+## Summary
+
+Describe what this PR changes and why.
+
+## Type Of Change
+
+- [ ] New AWS check in an existing bundle
+- [ ] New codebundle
+- [ ] Bug fix
+- [ ] Documentation update
+- [ ] Test or validation update
+
+## What Changed
+
+- [ ] Cloud Custodian policy files (`*.yaml` or `*.j2`)
+- [ ] SLI logic (`sli.robot`)
+- [ ] Runbook logic (`runbook.robot`)
+- [ ] RunWhen templates (`.runwhen/*`)
+- [ ] Bundle documentation (`README.md`)
+
+## Validation
+
+Include commands run and short outcomes.
+
+```text
+Example:
+- Ran custodian policy against test account/region
+- Verified ResourceCount and resources.json output
+- Verified runbook issue fields (title, severity, next steps)
+```
+
+## Contributor Checklist
+
+- [ ] I did not commit secrets or credentials.
+- [ ] The change is scoped and backwards-safe for existing checks.
+- [ ] New defaults and config variables are documented.
+- [ ] Issue text in runbook output is actionable.
+- [ ] I updated docs for any user-visible behavior changes.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..a186454
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,114 @@
+# Contributing
+
+Thanks for contributing to this AWS CodeCollection.
+
+This repository packages Cloud Custodian checks as RunWhen codebundles. A high-quality contribution should improve one of these areas:
+
+- Detection coverage (new AWS health/security check)
+- Signal quality (better thresholds, less noise)
+- Triage quality (clear runbook issues and next steps)
+- Reliability (test improvements, parsing robustness, docs)
+
+## Prerequisites
+
+- Python 3.10+
+- Cloud Custodian (`c7n`), Robot Framework, and dependencies from `requirements.txt`
+- AWS CLI configured with credentials for the target account/region
+- Optional but recommended for end-to-end tests:
+ - Docker
+ - Terraform
+ - Task (`task` command)
+ - jq
+
+## Development Environment
+
+### Option A (recommended): Dev Container
+
+Open this repository in VS Code Dev Containers using `.devcontainer.json`.
+
+### Option B: Local environment
+
+```bash
+python -m venv .venv
+source .venv/bin/activate
+pip install -r requirements.txt
+```
+
+## Recommended First Contribution
+
+Start by adding one new check to an existing bundle (for example in `codebundles/aws-c7n-s3-health`), instead of creating a new bundle from scratch.
+
+### Add A New Check To An Existing Bundle
+
+1. Add a policy file
+
+Create a new Cloud Custodian policy file (`*.yaml`) or template (`*.j2`) in the bundle directory.
+
+2. Update SLI logic
+
+Edit the bundle `sli.robot` to:
+
+- run the new policy
+- read its `ResourceCount`
+- incorporate it into the score or metric output
+
+3. Update runbook logic
+
+Edit `runbook.robot` to:
+
+- parse and summarize the new check results
+- raise issues with clear title, expected/actual, severity, and next steps
+
+4. Update bundle documentation
+
+Update the bundle `README.md` with:
+
+- what the new check detects
+- any new config variables and defaults
+- expected operational impact
+
+5. Validate locally
+
+At minimum, run the policy directly and ensure it returns expected output.
+
+For full validation, use the bundle `.test` workflow (if present).
+
+## Creating A New Bundle (Second Contribution)
+
+When you are ready, copy a similar bundle and update all four areas consistently:
+
+- policy files (`*.yaml` / `*.j2`)
+- `sli.robot`
+- `runbook.robot`
+- `.runwhen/generation-rules` and `.runwhen/templates`
+
+Ensure `baseName`, template names, and `pathToRobot` values align.
+
+## Branch And PR Workflow
+
+1. Fork this repository and create a feature branch.
+2. Keep PRs focused on one bundle or one logical change.
+3. Add tests or validation notes in the PR description.
+4. Open a PR using the included template.
+
+## Quality Checklist
+
+Before opening a PR, confirm:
+
+- No secrets or credentials are committed.
+- New policy names are unique and descriptive.
+- Runbook issues include actionable remediation steps.
+- Bundle README reflects new behavior and config.
+- Generation templates still resolve correctly.
+- Existing checks in the touched bundle continue to work.
+
+## Security And Credentials
+
+- Never commit AWS keys, tokens, or workspace secrets.
+- Use local secret files only under ignored paths.
+- Use least-privilege AWS IAM permissions for testing.
+
+## Need Help?
+
+- Open a draft PR early and describe your intended check.
+- Include sample policy output and expected issue text for faster feedback.
diff --git a/README.md b/README.md
index 3397a4d..ff36494 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,90 @@
-
+
-
+# aws-c7ncodecollection
-# codecollection-template
-A hello-world-style template for codecollection authors to get started writing codebundles. This template contains the minimum file structure expected by the RunWhen platform.
+AWS health and governance CodeCollection for RunWhen, powered by Cloud Custodian (c7n) and Robot Framework.
-[](https://github.com/runwhen-contrib/codecollection-template/actions/workflows/build.yaml)
+Each codebundle in this repository does two things:
-## Getting Started
-Looking to be a contributor for CodeCollections or start your own? We'd love to collaborate! Head on over to our [public docs](https://docs.runwhen.com/public/v/runwhen-authors/codecollection-development/getting-started/running-your-first-codebundle) to get started.
+- Produces an SLI score/metric for a class of AWS risks.
+- Produces runbook output with issue-level triage details.
-File Structure overview of devcontainer:
+## What Is Included
+
+The repository currently includes the following codebundles under `codebundles/`:
+
+- `aws-c7n-acm-health`: expired, pending validation, failed, soon-to-expire, and unused certificates.
+- `aws-c7n-ebs-health`: unattached volumes, unencrypted volumes, and unused snapshots.
+- `aws-c7n-ec2-health`: stale instances, long-stopped instances, and invalid Auto Scaling Groups.
+- `aws-c7n-monitoring-health`: CloudTrail and CloudWatch logging hygiene checks.
+- `aws-c7n-network-health`: insecure security-group ingress, unused EIP/ELB, missing VPC flow logs.
+- `aws-c7n-rds-health`: backup-disabled, public, and unencrypted RDS instances.
+- `aws-c7n-s3-health`: public S3 bucket exposure checks.
+
+## Repository Layout
+
+- `codebundles/`: runnable checks, runbooks, and RunWhen metadata templates.
+- `libraries/CloudCustodian/Core/`: shared Python helper keywords used by Robot suites.
+- `.github/workflows/score.yaml`: automated scoring/suggestion workflow for codebundles.
+- `.github/workflows/release.yaml`: scheduled/manual release workflow.
+
+## How A Bundle Works
+
+Inside each bundle, you will usually see:
+
+- One or more Cloud Custodian policies (`*.yaml`) and/or templates (`*.j2`).
+- `sli.robot`: computes and pushes a metric.
+- `runbook.robot`: parses findings and raises actionable issues.
+- `.runwhen/generation-rules/` and `.runwhen/templates/`: SLX/SLI/Runbook generation for the RunWhen platform.
+- `.test/`: optional Terraform and Taskfile-based end-to-end test harness.
+
+## Quickstart (Contributor)
+
+### 1) Clone and open the repository
+
+```bash
+git clone https://github.com/Saurabhtbj1201/aws-c7ncodecollection.git
+cd aws-c7ncodecollection
```
--/app/
- |- auth/ #store secrets here, it should already be properly gitignored for you
- |- codecollection/
- | |- codebundles/ # stores codebundles that can be run
- | |- libraries/ # stores python keyword libraries used by codebundles
- |- dev_facade/ # provides interfaces equivalent to those used on the platform, but just dry runs the keywords to assist with development
- ...
+
+### 2) Choose your environment
+
+- Recommended: use the dev container configuration in `.devcontainer.json`.
+- Alternative: set up a local Python environment and install dependencies:
+
+```bash
+python -m venv .venv
+source .venv/bin/activate
+pip install -r requirements.txt
+```
+
+### 3) Provide AWS credentials for read-only checks
+
+Most bundles require at least:
+
+```bash
+export AWS_ACCESS_KEY_ID="..."
+export AWS_SECRET_ACCESS_KEY="..."
+export AWS_DEFAULT_REGION="us-west-2"
+export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
```
-The included script `ro` wraps the `robot` RobotFramework binary, and includes some extra functionality to write logs to a consistent location for viewing in a HTTP server at http://localhost:3000/ that is always running as part of the devcontainer.
+### 4) Start with one existing check
+
+The easiest first path is to add a new policy/check to an existing bundle (commonly `aws-c7n-s3-health` or `aws-c7n-ec2-health`) before creating a brand-new bundle.
+
+## Contributing
+
+See `CONTRIBUTING.md` for a step-by-step guide, checklist, and PR workflow.
+
+## Notes For Windows Contributors
-### Quickstart
+The `.test/Taskfile.yaml` flows use shell utilities (`source`, `awk`, `jq`, `column`) and are easiest to run in a Linux devcontainer or WSL shell.
-Navigate to the codebundle directory
-`cd codecollection/codebundles/hello_world/`
+## Related Documentation
-Run the codebundle
-`ro sli.robot`
+- RunWhen author docs: https://docs.runwhen.com/public/v/runwhen-authors/codecollection-development/getting-started/running-your-first-codebundle
diff --git a/codebundles/aws-c7n-s3-health/README.md b/codebundles/aws-c7n-s3-health/README.md
index a1ff1cd..f107028 100644
--- a/codebundles/aws-c7n-s3-health/README.md
+++ b/codebundles/aws-c7n-s3-health/README.md
@@ -5,8 +5,12 @@ This codebundle starts out as an example of integrating the custodian (c7n) cli
## SLI
A simple SLI that counts S3 buckets that are public. Uses the custodian cli.
+This bundle now evaluates:
+- S3 buckets with public access enabled
+- S3 buckets missing default encryption
+
## TaskSet
-Similar to the SLI, but produces a report on the specific resources and raises issues for each public bucket.
+Similar to the SLI, but produces a report on the specific resources and raises issues for each bucket that is public or unencrypted.
## Required Configuration
diff --git a/codebundles/aws-c7n-s3-health/runbook.robot b/codebundles/aws-c7n-s3-health/runbook.robot
index 081474e..de8a2dc 100644
--- a/codebundles/aws-c7n-s3-health/runbook.robot
+++ b/codebundles/aws-c7n-s3-health/runbook.robot
@@ -23,7 +23,7 @@ List S3 Buckets With Public Access in AWS Account `${AWS_ACCOUNT_NAME}`
RW.Core.Add Pre To Report ${c7n_output.stdout} # Note: This actual data needs to be parsed to be usable in the report. Json data in a report like this isn't super useful.
${parsed_results}= CloudCustodian.Core.Parse Custodian Results # Note: This just an example of simple parsing with a custom keyword.
- ... input_dir=${OUTPUT_DIR}/aws-c7n-s3-health
+ ... input_dir=${OUTPUT_DIR}/aws-c7n-s3-health/s3-public-buckets
RW.Core.Add Pre To Report ${parsed_results}
# Convert custodian json output to a list.
@@ -48,6 +48,39 @@ List S3 Buckets With Public Access in AWS Account `${AWS_ACCOUNT_NAME}`
END
END
+List S3 Buckets Without Default Encryption in AWS Account `${AWS_ACCOUNT_NAME}`
+ [Documentation] Fetch total number of S3 buckets without default encryption and raise an issue for each bucket.
+ [Tags] s3 storage aws security encryption data:config
+ ${c7n_output}= RW.CLI.Run Cli
+ ... cmd=custodian run -r ${AWS_REGION} --output-dir ${OUTPUT_DIR}/aws-c7n-s3-health ${CURDIR}/s3-unencrypted-buckets.yaml
+ ... env=${env}
+ ${report_data}= RW.CLI.Run Cli
+ ... cmd=cat ${OUTPUT_DIR}/aws-c7n-s3-health/s3-unencrypted-buckets/resources.json
+
+ ${parsed_results}= CloudCustodian.Core.Parse Custodian Results
+ ... input_dir=${OUTPUT_DIR}/aws-c7n-s3-health/s3-unencrypted-buckets
+ RW.Core.Add Pre To Report ${parsed_results}
+
+ TRY
+ ${bucket_list}= Evaluate json.loads(r'''${report_data.stdout}''') json
+ EXCEPT
+ Log Failed to load JSON payload, defaulting to empty list. WARN
+ ${bucket_list}= Create List
+ END
+
+ IF len(@{bucket_list}) > 0
+ FOR ${item} IN @{bucket_list}
+ RW.Core.Add Issue
+ ... severity=2
+ ... expected=AWS S3 Buckets in AWS Account `${AWS_ACCOUNT_NAME}` should have default encryption enabled
+ ... actual=AWS S3 Bucket `${item["Name"]}` in AWS Account `${AWS_ACCOUNT_NAME}` does not have default encryption enabled
+ ... title=AWS S3 Bucket `${item["Name"]}` in AWS Account `${AWS_ACCOUNT_NAME}` is missing default encryption
+ ... reproduce_hint=${c7n_output.cmd}
+ ... details=${item}
+ ... next_steps=Enable default encryption on AWS S3 bucket `${item["Name"]}`.
+ END
+ END
+
** Keywords ***
diff --git a/codebundles/aws-c7n-s3-health/s3-unencrypted-buckets.yaml b/codebundles/aws-c7n-s3-health/s3-unencrypted-buckets.yaml
new file mode 100644
index 0000000..5eab538
--- /dev/null
+++ b/codebundles/aws-c7n-s3-health/s3-unencrypted-buckets.yaml
@@ -0,0 +1,6 @@
+policies:
+ - name: s3-unencrypted-buckets
+ resource: aws.s3
+ filters:
+ - type: bucket-encryption
+ state: false
diff --git a/codebundles/aws-c7n-s3-health/sli.robot b/codebundles/aws-c7n-s3-health/sli.robot
index b9e616f..57eb7e0 100644
--- a/codebundles/aws-c7n-s3-health/sli.robot
+++ b/codebundles/aws-c7n-s3-health/sli.robot
@@ -19,7 +19,24 @@ Count S3 Buckets With Public Access in AWS Account `${AWS_ACCOUNT_NAME}`
... env=${env}
${count}= RW.CLI.Run Cli
... cmd=cat ${OUTPUT_DIR}/aws-c7n-s3-health/s3-public-buckets/metadata.json | jq '.metrics[] | select(.MetricName == "ResourceCount") | .Value'
- RW.Core.Push Metric ${count.stdout}
+ ${public_bucket_count}= Evaluate int(${count.stdout})
+ Set Global Variable ${public_bucket_count}
+
+Count S3 Buckets Without Default Encryption in AWS Account `${AWS_ACCOUNT_NAME}`
+ [Documentation] Fetch total number of S3 buckets without default encryption enabled.
+ [Tags] s3 storage aws security encryption data:config
+ ${c7n_output}= RW.CLI.Run Cli
+ ... cmd=custodian run -r ${AWS_REGION} --output-dir ${OUTPUT_DIR}/aws-c7n-s3-health ${CURDIR}/s3-unencrypted-buckets.yaml
+ ... env=${env}
+ ${count}= RW.CLI.Run Cli
+ ... cmd=cat ${OUTPUT_DIR}/aws-c7n-s3-health/s3-unencrypted-buckets/metadata.json | jq '.metrics[] | select(.MetricName == "ResourceCount") | .Value'
+ ${unencrypted_bucket_count}= Evaluate int(${count.stdout})
+ Set Global Variable ${unencrypted_bucket_count}
+
+Generate S3 Health Metric
+ [Documentation] Combine insecure bucket findings into a single metric.
+ ${s3_unhealthy_bucket_count}= Evaluate int(${public_bucket_count}) + int(${unencrypted_bucket_count})
+ RW.Core.Push Metric ${s3_unhealthy_bucket_count}