@veecode-platform/backstage-plugin-infracost v0.3.2
Infracost Plugin
Intro 💡
The Infracost plugin provides a graphic representation of the application's cost estimate in its respective provider. The Plugin offers a generalized approach with all the resources used and also presents a more detailed approach with the cost components that each resource has.
Our community
💬 Join Us
Join our community to resolve questions about our Plugins. We look forward to welcoming you!
Getting Started:
Before installing the plugin, there are some prerequisites to ensure its functionality:
- Have a locally installed Backstage project, :heavy_check_mark: How to create a Backstage app :page_with_curl: .
- Have the
Infracost-backend
plugin installed on your Backstage, see how to install here. - The project's infrastructure must be provided via terraform.
- Have an Infracost API KEY. Here's how to generate one here.
Installation
If you are using yarn 3.x:
yarn workspace app add @veecode-platform/backstage-plugin-infracost
If you are using other versions:
yarn add --cwd packages/app @veecode-platform/backstage-plugin-infracost
Configuration
Bearing in mind that the infracost-backend
plugin is already configured and the kind Infracost
is already available in your backstage, the next step is to generate the file infracost-base.json and relate it to the main project and the kind infracost.
We can generate an estimate for any component in the catalog, as long as this estimate is referenced with the same name and the same repository as the main component, and the estimate file must have the name infracost-base.json and be at the same level as the estimate file, which should have the kind Infracost.
The illustration shows a case where the component Cluster-ec2 has in its repository a main entity of kind Cluster and an entity of kind Infracost with the infracost-base.json
referenced at the same level.
To begin with, we need to add the annotation infracost/project
to the catalog-info.yaml of your component:
apiVersion: veecode.backstage.io/v1alpha1
kind: Cluster
metadata:
name: "cluster-ec2"
annotations:
github.com/project-slug: test/cluster-ec2
backstage.io/techdocs-ref: dir:.
+ infracost/project: cluster-ec2
spec:
type: ec2
lifecycle: experimental
owner: "group:default/admin"
Note in the example that the name that the annotation will receive will have the same name as the component, because it will be referencing the component to the estimate.
Generating the contents of the infracost-base.json file:
For this step, we need to consider several possible scenarios for organizing the Infracost entity. We could implement the file within the component's own repository, create an .infracost folder and generate the entity content there, add a .content folder in the main repository with a catalog-info.yaml file of kind Location in the project root pointing to all the entities in the folder, or even create a separate repository just for the generated files. The best approach depends on the strategy adopted.
We'll cover an example of introducing estimation manually, and an example of adding a workflow to automate the process.
Manual process
Taking into account that the project is already provisioned with terraform and the terraform apply process has already been done, just run the following command:
infracost breakdown --path plan_cache_cli.json --format json --out-file infracost-base.json
This command needs to be run in the project's root folder, it will generate a file called infracost-base.json.
Now create a folder in the project root called .content
, move the file infracost-base.json to the folder and create the file infracost.yaml, with the following content:
apiVersion: veecode.backstage.io/v1alpha1
kind: Infracost
metadata:
name: "cluster-ec2" // here it should have the same name as the main project
annotations:
backstage.io/techdocs-ref: dir:.
spec:
type: FinOps
lifecycle: experimental
owner: "group:default/admin" # your group
estimate:
$text: ./infracost-base.json
Now the next step is to move catalog-info.yaml
, which is in the root of the project, to the folder .content
folder, rename the file to reference your kind, as in the example we are dealing with a cluster we rename it to cluster.yaml;
Obs: When moving the entities to the .content folder, we need to change any annotations that refer to the repository's relative path, such as the techdocs annotation:
- backstage.io/techdocs-ref: dir:.
+ backstage.io/techdocs-ref: dir:..
In the project root, we'll create a new catalog-info.yaml, this time with the Location kind that will reference the entities in the .content folder:
apiVersion: backstage.io/v1alpha1
kind: Location
metadata:
name: cluster-ec2-location
description: Importing components
spec:
targets:
- ./.content/*.yaml
Automated process
The organization of folders is up to you, but let's assume that this rule is adopted:
.
├── .content
│ ├── cluster.yaml
│
└── catalog-info.yaml
The catalog-info.yaml is a location that will retrieve all the other entities present in the repository as long as they are referenced in it:
apiVersion: backstage.io/v1alpha1
kind: Location
metadata:
name: cluster-ec2-location
description: Importing components
spec:
targets:
- ./.content/*.yaml
This way, when it is registered in the Backstage, the location will scan both the Cluster entity and the Infracost entity (when it is generated).
ℹ️Remember that the Cluster is an example and can be used for any other kind.
Generating the contents of the file infracost-base.json
:
An important step to note is that the project needs to be provisioned by Terraform, it will be via Terraform that the provider will be defined and the necessary secrets will be generated.
Taking into account that the infracost.yaml files are already created within the repository of the main component, and the catalog-info.yaml
already follows the kind model Location, in your project's repository, create a new workflow to run the estimate of the Infracost and commit the file infracost-base.json in your repository:
ℹ️ This example is based on github, if your git provider is not github, feel free to adapt this job, note that it is very simple and open to adaptation.
.github > workflows > infracost-estimate.yml
name: infracost-estimate
on:
push:
branches:
- "*"
- "*/*"
- "**"
workflow_dispatch:
env:
PATH_INFRACOST: ./.content
jobs:
infracost:
name: Infracost
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: hashicorp/setup-terraform@v2
- name: Setup Infracost
uses: infracost/actions/setup@v2
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
path: |
.terraform/**
.terraform.lock.hcl
plan_cache.json
key: terraform-lock-${{ steps.extract_branch.outputs.branch }}
- name: Checkout base branch
uses: actions/checkout@v3
with:
ref: '${{ github.event.pull_request.base.ref }}'
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Terraform Init
id: init
run: |
terraform init
terraform plan -no-color -out plan_cache.json
# Generate Infracost JSON file as the baseline.
- name: Generate Infracost cost estimate baseline
run: |
infracost breakdown --show-skipped --path plan_cache.json
- name: Generate Infracost cost estimate Json
run: |
infracost breakdown --path plan_cache.json --format json --out-file ${{ env.PATH_INFRACOST }}/infracost-base.json
- name: Generate component infracost.yaml
run: |
echo 'apiVersion: veecode.backstage.io/v1alpha1
kind: Infracost
metadata:
name: cluster-ec2 # component name
annotations:
backstage.io/techdocs-ref: dir:.
spec:
type: FinOps
lifecycle: experimental
owner: "group:default/admin"
estimate:
$text: ./infracost-base.json' > ${{ env.PATH_INFRACOST }}/infracost.yaml
- name: Publish generated artifacts
uses: stefanzweifel/git-auto-commit-action@v5
with:
file_pattern: "${{ env.PATH_INFRACOST }}"
commit_user_name: ${{ secrets.GH_USERNAME }}
commit_user_email: ${{ secrets.GH_EMAIL }}
commit_author: ${{ secrets.GH_USERNAME }}<${{ secrets.GH_EMAIL }}>
commit_message: "Publish infracost estimate"
push_options: '--force'
ℹ️ Note that Infracost's Path is set to "./.content" because we are giving the example so that it is generated in the
.content
folder, but if the approach adopted is different, then the folder reference in the catalog-info.yaml of kind Location, and in the variable INFRACOST_PATH of the workflow.ℹ️ Another important observation is that in the example we used terraform with aws as the provider, but nothing prevents other providers from being used. Just be aware of the changes to the workflow that this will entail.
Considerations
With the entities in the repository, following the rules stated above, and with the file infracost-base.json generated. Simply register the location of the catalog-info.yaml
in the Backstage, and the backend processor will save the data in the database.
Next, we'll look at the UI components to get the most out of the Infracost plugin.
UI 🎨
To start, we need to go to our EntityPage.tsx
and add the tab Infracost to the page of the entity you prefer, in the example we'll add it to a clusterPage:
... other imports
+ import { InfracostOverviewPage, isInfracostAvailable } from '@veecode-platform/backstage-plugin-infracost';
...
const clusterPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
<ClusterOverviewPage />
</EntityLayout.Route>
...
+ <EntityLayout.Route if={isInfracostAvailable} path="/infracost" title="Infracost">
+ <InfracostOverviewPage/>
+ </EntityLayout.Route>
...
</EntityLayout>
);
This way your component, if it has the estimate correctly configured, will have a view of the data coming from Infracost, with an estimate of the costs of the infrastructure, according to your provider: