GitHub Action
Add cloud cost reports to your pull requests
Post actual cloud costs (not estimates) and carbon footprint to your PRs automatically.
Quick Start
1. Create an API Key
Go to Settings > API Keys and create a new key.
2. Add Secret to Repository
- Go to your repo’s Settings > Secrets and variables > Actions
- Click New repository secret
- Name:
CLOUDEXPAT_API_KEY - Paste your API key
3. Create Workflow File
Create .github/workflows/cloudexpat-cost-report.yml:
name: CloudExpat Cost Report
on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
permissions:
pull-requests: write
contents: read
jobs:
cost-report:
name: Generate Cost Report
runs-on: ubuntu-latest
steps:
- name: Fetch Cost Report from CloudExpat
id: fetch-report
env:
CLOUDEXPAT_API_KEY: ${{ secrets.CLOUDEXPAT_API_KEY }}
run: |
if [ -z "$CLOUDEXPAT_API_KEY" ]; then
echo "::error::CLOUDEXPAT_API_KEY secret is not set"
exit 1
fi
RESPONSE=$(curl -s -w "\n%{http_code}" \
"https://data.cloudexpat.com/v1/report?format=markdown" \
-H "Authorization: Bearer $CLOUDEXPAT_API_KEY")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" != "200" ]; then
echo "::error::Failed to fetch report. HTTP $HTTP_CODE"
exit 1
fi
echo "$BODY" > report.md
echo "success=true" >> $GITHUB_OUTPUT
- name: Post Report as PR Comment
if: github.event_name == 'pull_request' && steps.fetch-report.outputs.success == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('report.md', 'utf8');
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('CloudExpat Cost Report')
);
const body = report + '\n\n---\n<sub>Generated by [CloudExpat](https://app.cloudexpat.com)</sub>';
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
- name: Add Report to Job Summary
if: steps.fetch-report.outputs.success == 'true'
run: cat report.md >> $GITHUB_STEP_SUMMARY
Features
- Actual costs — Real data from AWS Cost Explorer, Azure Cost Management
- Carbon emissions — Track your cloud’s environmental impact
- Multi-account — Aggregates all connected cloud accounts
- Smart updates — Updates existing PR comments instead of creating duplicates
- Job summary — Report also appears in GitHub Actions summary
Configuration Options
Report Period
# Last 30 days (default)
"https://data.cloudexpat.com/v1/report?format=markdown&period=month"
# Last 7 days
"https://data.cloudexpat.com/v1/report?format=markdown&period=week"
# Last 24 hours
"https://data.cloudexpat.com/v1/report?format=markdown&period=day"
Filter by Account
# Specific account only
"https://data.cloudexpat.com/v1/report?format=markdown&accountId=YOUR_ACCOUNT_ID"
Exclude Carbon Data
"https://data.cloudexpat.com/v1/report?format=markdown&include_carbon=false"
Scheduled Reports
Add to your workflow triggers:
on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 9am UTC
Example Output
When posted to a PR, you’ll see:
## CloudExpat Cost Report
**Report Period**: 2025-11-16 to 2025-12-16
### Cost Summary
| Account | Provider | Current | Previous | Change |
|---------|----------|--------:|---------:|-------:|
| Production | AWS | $1,234.56 | $1,100.00 | +12.2% |
| Staging | Azure | $456.78 | $480.00 | -4.8% |
| **Total** | | **$1,691.34** | **$1,580.00** | **+7.0%** |
### Carbon Emissions
| Account | Provider | Emissions |
|---------|----------|----------:|
| Production | AWS | 45.67 kgCO2e |
| Staging | Azure | 12.34 kgCO2e |
### Resource Inventory
| Resource Type | Count |
|--------------|------:|
| EC2 Instances | 12 |
| S3 Buckets | 8 |
| RDS Databases | 3 |
Data Quality
The report includes data quality indicators:
- Full Cost Data — Complete data from Cost Explorer/Cost Management
- Estimated — Using pricing estimates when detailed data unavailable
- Resource Inventory — Fallback showing detected resources when cost data is pending
New accounts may take 24-48 hours for cost data to appear.
Troubleshooting
“CLOUDEXPAT_API_KEY secret is not set”
- Add the secret in Settings > Secrets and variables > Actions
“Invalid API key format”
- Keys must start with
ce_live_followed by 32 characters - Create a new key at Settings > API Keys
“No cost data available”
- Cost data takes 24-48h after connecting a new account
- AWS: Enable Cost Explorer in your AWS account
- Azure: Ensure Cost Management API access is granted
Comment not appearing
- Check the workflow has
pull-requests: writepermission - Verify the workflow ran successfully in the Actions tab
Security Best Practices
- One key per repository — Easier to revoke if compromised
- Never commit keys — Always use GitHub Secrets
- Rotate periodically — Create new keys and revoke old ones
- Review access — Revoke unused keys from the dashboard
API keys provide read-only access to your cost and carbon data.