Infracost estimate: monthly cost will increase by $0.00 - When base branch is empty
kaykhan opened this issue · 5 comments
We have started a new infrastructure project where we are deploying aws-msk. We have two branches.
In our main
branch it is empty.
In dev/modules
it contains our new terraform config.
We have created a PR from dev/modules
to main
. During the CI run we get the following message when we are expecting more.
Infracost estimate: monthly cost will increase by $0.00
If we run infracost breakdown --path . --show-skipped
locally in our dev/module
s branch we get what we are expecting to see in our CI.
Project: <redacted>/infra-kafka/applications/prod/msk
Name Monthly Qty Unit Monthly Cost
aws_s3_bucket.custom_plugins
└─ Standard
├─ Storage Monthly cost depends on usage: $0.023 per GB
├─ PUT, COPY, POST, LIST requests Monthly cost depends on usage: $0.005 per 1k requests
├─ GET, SELECT, and all other requests Monthly cost depends on usage: $0.0004 per 1k requests
├─ Select data scanned Monthly cost depends on usage: $0.002 per GB
└─ Select data returned Monthly cost depends on usage: $0.0007 per GB
module.prod_msk_default.aws_cloudwatch_log_group.this
├─ Data ingested Monthly cost depends on usage: $0.50 per GB
├─ Archival Storage Monthly cost depends on usage: $0.03 per GB
└─ Insights queries data scanned Monthly cost depends on usage: $0.005 per GB
module.prod_msk_default.aws_msk_cluster.this
├─ Instance (kafka.m5.large) 2,190 hours $459.90
└─ Storage (autoscaling) 3 GB $0.30
module.prod_msk_mongodb_default_connector.aws_cloudwatch_log_group.this
├─ Data ingested Monthly cost depends on usage: $0.50 per GB
├─ Archival Storage Monthly cost depends on usage: $0.03 per GB
└─ Insights queries data scanned Monthly cost depends on usage: $0.005 per GB
module.prod_msk_mysql_default_connector.aws_cloudwatch_log_group.this
├─ Data ingested Monthly cost depends on usage: $0.50 per GB
├─ Archival Storage Monthly cost depends on usage: $0.03 per GB
└─ Insights queries data scanned Monthly cost depends on usage: $0.005 per GB
OVERALL TOTAL $460.20
──────────────────────────────────
30 cloud resources were detected:
∙ 6 were estimated, 4 of which include usage-based costs, see https://infracost.io/usage-file
∙ 14 were free:
∙ 4 x aws_iam_role
∙ 4 x aws_iam_role_policy_attachment
∙ 2 x aws_iam_policy
∙ 1 x aws_appautoscaling_policy
∙ 1 x aws_iam_instance_profile
∙ 1 x aws_msk_configuration
∙ 1 x aws_security_group
∙ 10 are not supported yet, see https://infracost.io/requested-resources:
∙ 3 x aws_mskconnect_custom_plugin
∙ 3 x aws_s3_object
∙ 2 x aws_mskconnect_connector
∙ 2 x aws_mskconnect_worker_configuration
terraform-ci.yaml
name: terraform-ci
on:
workflow_call:
inputs:
TF_ROOT:
required: true
type: string
TF_APP_NAME:
required: true
type: string
jobs:
cost:
needs: plan
name: cost
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout/@v3
- name: Setup Infracost
uses: infracost/actions/setup@v2
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Checkout base branch
uses: actions/checkout@v3
with:
ref: '${{ github.event.pull_request.base.ref }}'
- name: Generate Infracost cost estimate baseline
run: |
infracost breakdown --path=${{ inputs.TF_ROOT }} \
--format=json \
--out-file=/tmp/infracost-base.json
cat /tmp/infracost-base.json
- name: Checkout PR branch
uses: actions/checkout@v3
- name: Generate Infracost diff
run: |
infracost diff --path=${{ inputs.TF_ROOT }} \
--format=json \
--compare-to=/tmp/infracost-base.json \
--out-file=/tmp/infracost.json
cat /tmp/infracost-base.json
echo "here"
cat /tmp/infracost.json
- name: Post Infracost comment
run: |
infracost comment github --path=/tmp/infracost.json \
--repo=$GITHUB_REPOSITORY \
--github-token=${{github.token}} \
--pull-request=${{github.event.pull_request.number}} \
--behavior=new
/tmp/infracost-base.json
{"version":"0.2","metadata":{"infracostCommand":"breakdown","vcsBranch":"main","vcsCommitSha":"c9137bb438353a9a2d6630c7491c16bc28bd584e","vcsCommitAuthorName":"Kay Khan","vcsCommitAuthorEmail":"redacted","vcsCommitTimestamp":"2023-08-16T12:43:47Z","vcsCommitMessage":"first commit","vcsRepositoryUrl":"https://github.com/redacted/infra-kafka","vcsProvider":"github","vcsBaseBranch":"main","vcsPullRequestTitle":"feat: kafka + kafkaui","vcsPullRequestUrl":"https://github.com/redacted/infra-kafka/pull/1","vcsPullRequestAuthor":"kaykhan","vcsPipelineRunId":"5901458303","vcsPullRequestId":"1"},"currency":"USD","projects":[{"name":"redacted/infra-kafka/applications/prod/msk","metadata":{"path":"applications/prod/msk","type":"error","vcsSubPath":"applications/prod/msk","errors":[{"code":0,"message":"No such file or directory applications/prod/msk\n\nTry adding a config-file to configure how Infracost should run. See https://infracost.io/config-file for details and examples.","data":null}]},"pastBreakdown":{"resources":[],"totalHourlyCost":"0","totalMonthlyCost":"0"},"breakdown":{"resources":[],"totalHourlyCost":"0","totalMonthlyCost":"0"},"diff":{"resources":[],"totalHourlyCost":"0","totalMonthlyCost":"0"},"summary":{"totalDetectedResources":0,"totalSupportedResources":0,"totalUnsupportedResources":0,"totalUsageBasedResources":0,"totalNoPriceResources":0,"unsupportedResourceCounts":{},"noPriceResourceCounts":{}}}],"totalHourlyCost":"0","totalMonthlyCost":"0","pastTotalHourlyCost":"0","pastTotalMonthlyCost":"0","diffTotalHourlyCost":"0","diffTotalMonthlyCost":"0","timeGenerated":"2023-08-18T10:15:30.213041174Z","summary":{"totalDetectedResources":0,"totalSupportedResources":0,"totalUnsupportedResources":0,"totalUsageBasedResources":0,"totalNoPriceResources":0,"unsupportedResourceCounts":{},"noPriceResourceCounts":{}}}
/tmp/infracost.json
{"version":"0.2","metadata":{"infracostCommand":"diff","vcsBranch":"dev/modules","vcsCommitSha":"40567b7ed14441c64890b09f0cca994f688a910e","vcsCommitAuthorName":"Kay Khan","vcsCommitAuthorEmail":"redacted","vcsCommitTimestamp":"2023-08-18T10:13:53Z","vcsCommitMessage":"update: debug","vcsRepositoryUrl":"https://github.com/redacted/infra-kafka","vcsProvider":"github","vcsBaseBranch":"main","vcsPullRequestTitle":"feat: kafka + kafkaui","vcsPullRequestUrl":"https://github.com/redacted/infra-kafka/pull/1","vcsPullRequestAuthor":"kaykhan","vcsPipelineRunId":"5901458303","vcsPullRequestId":"1"},"currency":"USD","projects":[{"name":"redacted/infra-kafka/applications/prod/msk","metadata":{"path":"applications/prod/msk","type":"terraform_dir","vcsSubPath":"applications/prod/msk","errors":[{"code":0,"message":"Diff baseline error: No such file or directory applications/prod/msk\n\nTry adding a config-file to configure how Infracost should run. See https://infracost.io/config-file for details and examples.","data":null}],"providers":[{"name":"aws","filename":"applications/prod/msk/versions.tf","startLine":13,"endLine":15}]},"pastBreakdown":null,"breakdown":{"resources":[],"totalHourlyCost":"0","totalMonthlyCost":"0"},"diff":null,"summary":{"totalDetectedResources":30,"totalSupportedResources":6,"totalUnsupportedResources":10,"totalUsageBasedResources":4,"totalNoPriceResources":14,"unsupportedResourceCounts":{"aws_mskconnect_connector":2,"aws_mskconnect_custom_plugin":3,"aws_mskconnect_worker_configuration":2,"aws_s3_object":3},"noPriceResourceCounts":{"aws_appautoscaling_policy":1,"aws_iam_instance_profile":1,"aws_iam_policy":2,"aws_iam_role":4,"aws_iam_role_policy_attachment":4,"aws_msk_configuration":1,"aws_security_group":1}}}],"totalHourlyCost":"0","totalMonthlyCost":"0","pastTotalHourlyCost":null,"pastTotalMonthlyCost":null,"diffTotalHourlyCost":null,"diffTotalMonthlyCost":null,"timeGenerated":"2023-08-18T10:15:33.247471322Z","summary":{"totalDetectedResources":30,"totalSupportedResources":6,"totalUnsupportedResources":10,"totalUsageBasedResources":4,"totalNoPriceResources":14,"unsupportedResourceCounts":{"aws_mskconnect_connector":2,"aws_mskconnect_custom_plugin":3,"aws_mskconnect_worker_configuration":2,"aws_s3_object":3},"noPriceResourceCounts":{"aws_appautoscaling_policy":1,"aws_iam_instance_profile":1,"aws_iam_policy":2,"aws_iam_role":4,"aws_iam_role_policy_attachment":4,"aws_msk_configuration":1,"aws_security_group":1}}}
it appears like it is unable to estiamte the cost if there is no base to compare with?
I think it would be better if it produced the output of infracost breakdown --path . --show-skipped
when there is no base to compare with? How might we do this, has this been considered?
@kaykhan thanks for reporting. In the pipeline what are you passing in as inputs.TF_ROOT
?
@kaykhan thanks for reporting. In the pipeline what are you passing in as
inputs.TF_ROOT
?
To be clear terraform-ci.yaml
is a reusable workflow (updated original post to show that). This reusable workflow is triggered via the below workflow.
TF_ROOT is applications/prod/msk
msk-prod-tf-ci.yaml
name: msk-prod-tf-ci
on:
pull_request:
paths: ['applications/prod/msk/**']
concurrency:
group: terraform
jobs:
ci:
uses: ./.github/workflows/terraform-ci.yaml
with:
TF_ROOT: applications/prod/msk
TF_APP_NAME: msk-prod
secrets: inherit
Thanks @kaykhan. My initial thoughts are since you are passing in applications/prod/msk
as the path to Infracost, then when it runs against the base branch it is actually erroring, instead of producing an empty output since that dir doesn't exist in that branch. Since the base branch is erroring for that project then the diff command will fail as well for that project.
One way to resolve this would be to use an Infracost config file to dynamically find the project directories that exist on each given branch.
Something like this infracost.yml.tmpl
might work for your case:
version: 0.1
projects:
{{- range $project := matchPaths "applications/:env/:name" }}
- path: {{ $project._path }}
name: {{ $project.env }}/{{ $project.name }}
{{- end }}
And then updating your pipeline to generate the config.
For the base run:
infracost generate config --repo-path=. \
--template-path=infracost.yml.tmpl \
--out-file=infracost.yml
infracost breakdown --config-file=infracost.yml \
--format=json \
--out-file=/tmp/infracost-base.json
For the diff run:
infracost generate config --repo-path=. \
--template-path=infracost.yml.tmpl \
--out-file=infracost.yml
infracost diff --config-file=infracost.yml \
--format=json \
--compare-to=/tmp/infracost-base.json \
--out-file=/tmp/infracost.json
Another option that might be simpler to test initially is just to ensure the directory exists when you run the base branch, i.e.:
mkdir -p ${{ inputs.TF_ROOT }}
infracost breakdown --path=${{ inputs.TF_ROOT }} \
--format=json \
--out-file=/tmp/infracost-base.json
Alright i'll have to try the infracost config file
another time.
Another option that might be simpler to test initially is just to ensure the directory exists when you run the base branch, i.e.:
Tested this just now and of course there is an additional error. No valid Terraform files found at path applications/prod/msk
(main branch is emtpy)
What i am thinking of doing is checking for the presence of the folder inputs.TF_ROOT
in an initial step.
- name: Check baseline exists
run: |
Based on the result of that step i can either continue to run the baseline + diff or just run the breakdown directly.
Maybe i was hoping for an extra flag that could be added to infracost diff
(ignore any errors that are in infracost-base.json and run the breakdown) to do this but i dont know if this would introduce some unforeseen conflicts.