AWS Resource Cost Estimation via Terraform with Infracost and Atlantis on Github PR

Infracost lets you see a cost breakdown and understand costs before making changes. On integration with Atlantis, can see cloud cost estimates for Terraform in pull requests. So teams can understand better the cost of changes before deploy.

Implementation:

  1. Update your setup to use the infracost-atlantis Docker image
  2. If you haven't done so already, download Infracost and run infracost auth login to get a free API key.
  3. Retrieve your Infracost API key by running infracost configure get api_key.
  4. You'll need to pass the following custom env var into the container.
 INFRACOST_API_KEY=<your-infracost-api-token>

5. Add infracost yaml spec to repos.yml config file

        repos:
          - id: /.*/
            apply_requirements: [approved, mergeable]
            workflow: terragrunt
            allowed_overrides: [apply_requirements, workflow]
            allow_custom_workflows: true

        # workflows lists server-side custom workflows
        workflows:
          terragrunt:
            plan:
              steps:
                - run: terragrunt init
                - run: terragrunt plan -no-color -input=false -out=$SHOWFILE
                - env:
                    name: INFRACOST_OUTPUT
                    command: 'echo "/tmp/$BASE_REPO_OWNER-$BASE_REPO_NAME-$PULL_NUM-$WORKSPACE-${REPO_REL_DIR//\//-}-infracost.json"'
                # Run Infracost breakdown and save to a tempfile, namespaced by this project, PR, workspace and dir
                - run: |
                    infracost breakdown --path=$SHOWFILE \
                                        --format=json \
                                        --log-level=info \
                                        --out-file=$INFRACOST_OUTPUT \
                                        --project-name=$REPO_REL_DIR
                - run: |
                    # Read the breakdown JSON and get costs using jq.
                    # Note jq comes as standard as part of infracost-atlantis Docker images. If you are using the base atlantis
                    # image you'll need to manually install jq. e.g:
                    # curl https://stedolan.github.io/jq/download/linux64/jq > /usr/local/bin/jq; chmod +x /usr/local/bin/jq
                    past_total_monthly_cost=$(cat $INFRACOST_OUTPUT | jq -r "(.pastTotalMonthlyCost // 0) | tonumber")
                    total_monthly_cost=$(cat $INFRACOST_OUTPUT | jq -r "(.totalMonthlyCost // 0) | tonumber")
                    diff_total_monthly_cost=$(cat $INFRACOST_OUTPUT | jq -r "(.diffTotalMonthlyCost // 0) | tonumber")
                    currency=$(cat $INFRACOST_OUTPUT | jq -r '.currency | select (.!=null)')
                    if [ "$currency" = "" ] || [ "$currency" = "USD" ]; then
                      currency="$"
                    elif [ "$currency" = "EUR" ]; then
                      currency="€"
                    elif [ "$currency" = "GBP" ]; then
                      currency="£"
                    else
                      currency="$currency " # Space is needed so output is "INR 123"
                    fi
                    # Run infracost output to get the diff output
                    diff_output=$(infracost output --no-color --format diff --show-skipped --path=$INFRACOST_OUTPUT)
                    change_symbol () {
                      local old=$1
                      local new=$2
                      local change_symbol="+"
                      if [ "$(echo "$new < $old" | bc -l)" = 1 ]; then
                        change_symbol=""
                      fi
                      printf "%s" "$change_symbol"
                    }
                    percent_display () {
                      local old=$1
                      local new=$2
                      local percent
                      local sym
                      percent=$(calculate_percentage "$old" "$new")
                      sym=$(change_symbol "$old" "$new")
                      local s=""
                      if [ -n "$percent" ]; then
                        s="$(printf "%.0f" "$percent")"
                        s=" ($sym$s%%)"
                      fi
                      printf "%s" "$s"
                    }
                    calculate_percentage () {
                      local old=$1
                      local new=$2
                      local percent=""
                      # If both old and new costs are greater than 0
                      if [ "$(echo "$old > 0" | bc -l)" = 1 ] && [ "$(echo "$new > 0" | bc -l)" = 1 ]; then
                        percent="$(echo "scale=6; $new / $old * 100 - 100" | bc)"
                      fi
                      # If both old and new costs are less than or equal to 0
                      if [ "$(echo "$old <= 0" | bc -l)" = 1 ] && [ "$(echo "$new <= 0" | bc -l)" = 1 ]; then
                        percent="0"
                      fi
                      printf "%s" "$percent"
                    }
                    format_cost () {
                      cost=$1
                      if [ -z "$cost" ] || [ "$cost" = "null" ]; then
                        echo "-"
                      elif [ "$(echo "$cost < 100" | bc -l)" = 1 ]; then
                        printf "$currency%0.2f" "$cost"
                      else
                        printf "$currency%0.0f" "$cost"
                      fi
                    }
                    percent=$(percent_display "$past_total_monthly_cost" "$total_monthly_cost" | sed "s/%/%%/g")
                    change_word="increase"
                    if [ "$(echo "$past_total_monthly_cost > $total_monthly_cost" | bc -l)" = 1 ]; then
                      change_word="decrease"
                    fi
                    msg="##### Infracost estimate #####"
                    msg="${msg}\n\n"
                    msg="${msg}Monthly cost will $change_word by $(format_cost $diff_total_monthly_cost)$percent\n"
                    msg="${msg}\n"
                    msg="${msg}Previous monthly cost: $(format_cost $past_total_monthly_cost)\n"
                    msg="${msg}New monthly cost: $(format_cost $total_monthly_cost)\n"
                    msg="${msg}\n"
                    msg="${msg}Infracost output:\n"
                    msg="${msg}\n"
                    msg="${msg}$(echo "$diff_output" | sed 's/^/    /' | sed "s/%/%%/g")\n"
                    printf "$msg"
            apply:
              steps:
                - run: terragrunt apply -no-color -input=false $PLANFILE

Infracost comment on PR:

Increase the allocated_storage from 20 to 100 for one of the db. On running atlantis plan, following details will get append at the end of terraform plan

Infracost calculates the cost of infrastructure already deployed (23€) , the cost of the infrastructure with new changes (34€) and the difference between both (11€).

View Comments