Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/iamfrench/terraform-azurerm-billing-export

Terraform Module witch creates billing export, including FOCUS, on Azure.
https://github.com/iamfrench/terraform-azurerm-billing-export

azure azure-billing billing-export finops finops-focus focus terraform terraform-module

Last synced: 8 days ago
JSON representation

Terraform Module witch creates billing export, including FOCUS, on Azure.

Awesome Lists containing this project

README

        

# Microsoft Azure Billing export Terraform Module

Terraform module witch creates billing export on Azure.

FOCUS v1.0 billing export for Azure now available!

This module will create a storage account for Azure billing exports.

## What is FOCUS™?

The FinOps Cost and Usage Specification (FOCUS™) is an open-source specification that defines clear requirements for cloud vendors to produce consistent cost and usage datasets.

Supported by the FinOps Foundation, FOCUS™ aims to reduce complexity for FinOps Practitioners so they can drive data-driven decision-making and maximize the business value of cloud, while making their skills more transferable across clouds, tools, and organizations.

Learn more about FOCUS in this [FinOps Foundation Insights article](https://www.finops.org/insights/focus-1-0-available/).

## Usage

Detailed examples are available under the [`./examples`](./examples/) directory.

Create a FOCUS export for a billing account with a new resource group, storage account and container

```hcl
module "azurerm_billing_export" {
source = "IAmFrench/billing-export/azurerm"

version = "" # change this to your desired version, https://www.terraform.io/language/expressions/version-constraints

create_resource_group = true
resource_group_name = "rg-focus-export-001"
resource_group_location = "Switzerland North"

create_storage_account = true
storage_account_name = "billingexportokjdlksa"

create_storage_container = true
storage_container_name = "focus"

export_type = "FOCUS"
export_version = "1.0r2"

export_scope_and_id = {
scope = "billing-account"
id = "123456789"
}

export_start_date = local.export_start_date
export_creation_date = "2024-10-16"
export_end_date = "2050-01-01"

enable_backfill = true

export_directory = "billing_account_123456789"

export_name = "focus-export-for-billing-account-123456789"
}
```

Create a FOCUS export for a billing account with an existing resource group, storage account and container

```hcl
module "azurerm_billing_export" {
source = "IAmFrench/billing-export/azurerm"

version = "" # change this to your desired version, https://www.terraform.io/language/expressions/version-constraints

create_resource_group = false
resource_group_name = "rg-focus-export-001"
resource_group_location = "Switzerland North"

create_storage_account = false
storage_account_name = "billingexportokjdlksa"

create_storage_container = false
storage_container_name = "focus"

export_type = "FOCUS"
export_version = "1.0r2"

export_scope_and_id = {
scope = "billing-account"
id = "123456789"
}

export_start_date = local.export_start_date
export_creation_date = "2024-10-16"
export_end_date = "2050-01-01"

enable_backfill = true

export_directory = "billing_account_123456789"

export_name = "focus-export-for-billing-account-123456789"
}
```

Create a FOCUS export for a subcription with a existing resource group but new storage account and container

```hcl
module "azurerm_billing_export" {
source = "IAmFrench/billing-export/azurerm"

version = "" # change this to your desired version, https://www.terraform.io/language/expressions/version-constraints

create_resource_group = false
resource_group_name = "rg-focus-export-001"
resource_group_location = "Switzerland North"

create_storage_account = true
storage_account_name = "billingexportzfcxfd"

create_storage_container = true
storage_container_name = "focus"

export_type = "FOCUS"
export_version = "1.0r2"

export_scope_and_id = {
scope = "subscription"
id = "12345-uuid-6789"
}

export_start_date = local.export_start_date
export_creation_date = "2024-10-16"
export_end_date = "2050-01-01"

enable_backfill = true

export_directory = "subscription_12345-uuid-6789"

export_name = "focus-export-for-subscription-12345-uuid-6789"
}
```

## Roadmap & Features

- [X] FOCUS `1.0` & `1.0r2` export ([Improved export experience](https://learn.microsoft.com/en-us/azure/cost-management-billing/costs/tutorial-improved-exports))
- [X] Subscription export (`export_scope_and_id` = `subscription`)
- [X] Billing Account export (`export_scope_and_id` = `billing-account`)
- [X] Automatic backfill from `export_start_date` to export's creation date (`export_creation_date`)
- [X] Retry implementation (if we hit the rate limit `429 Too Many Requests`)

## Limitations

- Id of backfill jobs is not returned by the [microsoft API](https://learn.microsoft.com/en-us/rest/api/cost-management/exports/execute) on job submission, therefore returned id is incorrect

## Common errors

### FocusCost is not supported

FocusCost is not supported

```bash

│ Error: Failed to create/update resource

│ with module.azurerm_billing_export.azapi_resource.focus_export[0],
│ on ../../main.tf line 100, in resource "azapi_resource" "focus_export":
│ 100: resource "azapi_resource" "focus_export" {

│ creating/updating Resource: (ResourceId
│ "/subscriptions/xxxx-xxxx-xxxx-xxxx/providers/Microsoft.CostManagement/exports/focus-export-for-subscription-xxxx-xxxx-xxxx-xxxx"
│ / Api Version "2023-07-01-preview"): PUT
│ https://management.azure.com/subscriptions/xxxx-xxxx-xxxx-xxxx/providers/Microsoft.CostManagement/exports/focus-export-for-subscription-xxxx-xxxx-xxxx-xxxx
│ --------------------------------------------------------------------------------
│ RESPONSE 400: 400 Bad Request
│ ERROR CODE: BadRequest
│ --------------------------------------------------------------------------------
│ {
│ "error": {
│ "code": "BadRequest",
│ "message": "Request properties validation failed: Export type: FocusCost is not supported for Agreement Type: WebDirect and Subscription."
│ }
│ }
│ --------------------------------------------------------------------------------


```

Check if your subscription type is supported here: https://learn.microsoft.com/en-us/azure/cost-management-billing/costs/tutorial-improved-exports#understand-data-types

## Requirements

| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.1.0 |
| [azapi](#requirement\_azapi) | >= 2.0.0-beta, < 3.0 |
| [azurerm](#requirement\_azurerm) | >= 3.113.0, < 5.0 |
| [time](#requirement\_time) | >= 0.12.1, < 1.0.0 |

## Providers

| Name | Version |
|------|---------|
| [azapi](#provider\_azapi) | >= 2.0.0-beta, < 3.0 |
| [azurerm](#provider\_azurerm) | >= 3.113.0, < 5.0 |
| [time](#provider\_time) | >= 0.12.1, < 1.0.0 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| [months\_to\_backfill](#module\_months\_to\_backfill) | ./modules/months_to_backfill | n/a |

## Resources

| Name | Type |
|------|------|
| [azapi_resource.focus_export](https://registry.terraform.io/providers/azure/azapi/latest/docs/resources/resource) | resource |
| [azapi_resource_action.backfill_job](https://registry.terraform.io/providers/azure/azapi/latest/docs/resources/resource_action) | resource |
| [azurerm_resource_group.main](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource |
| [azurerm_storage_account.export](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account) | resource |
| [azurerm_storage_container.focus](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource |
| [time_static.export_creation_date](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/static) | resource |
| [azurerm_resource_group.main](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group) | data source |
| [azurerm_storage_account.export](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_account) | data source |
| [azurerm_storage_container.focus](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/storage_container) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [create\_resource\_group](#input\_create\_resource\_group) | Option to create or not the Resource Group for the billing export.

If set to `false`, this module will not create the resource group and will
instead lookup for a resource group with the name `var.resource_group_name`.

E.g.: `true`, `false` | `bool` | `false` | no |
| [create\_storage\_account](#input\_create\_storage\_account) | Option to create or not the storage account for the billing export.

If set to `false`, this module will not create the storage account.

E.g.: `true`, `false` | `bool` | `true` | no |
| [create\_storage\_container](#input\_create\_storage\_container) | Option to create or not the Storage Container for the billing export.

If set to `false`, this module will not create the storage Container and will
instead lookup for a storage container with `var.storage_container_name` in
the `var.storage_account_name` Storage Account.

Note: If `var.create_storage_account` is set to `true`, then this variable
MUST be set to `true`.

E.g.: `true`, `false` | `bool` | `true` | no |
| [enable\_backfill](#input\_enable\_backfill) | Option to enable or not a backfill for the export.

E.g.: `true`, `false` | `bool` | `false` | no |
| [export\_creation\_date](#input\_export\_creation\_date) | Creation date of the export.

E.g.: `2024-07-22` | `string` | n/a | yes |
| [export\_directory](#input\_export\_directory) | Directory to place the billing export in.

Validation: Directory name cannot end with a forward slash(/) or dot(.)

E.g.: `subscription_63aa77b3-5e14-4c6d-a895-27f9d8443e37` with
`63aa77b3-5e14-4c6d-a895-27f9d8443e37` being the subscription id | `string` | n/a | yes |
| [export\_end\_date](#input\_export\_end\_date) | End date of the export.

Validation: Date should be in the future and it must be the first day of the month.

E.g.: `2050-01-01` | `string` | `"2050-01-01"` | no |
| [export\_name](#input\_export\_name) | Name of the billing export.

Validation: Export name must be alphanumeric, without whitespace, and 3 to
64 characters in length.

E.g.: `focus-export-for-sub-63aa77b3-5e14-4c6d-a895-27f9d8443e37` (57
characters) | `string` | n/a | yes |
| [export\_scope\_and\_id](#input\_export\_scope\_and\_id) | Scope and the corresponding id for the billing export.

Valid values for scope are:
- `billing-account` for an export at the billing account level (recommended)
- `subscription` for an export at the subscription level

E.g.:

{
scope = "billing-account"
id = "1234567890"
}
|
object({
scope = string
id = string
})
| n/a | yes |
| [export\_start\_date](#input\_export\_start\_date) | Start date of the export.
You can go as far as 9 years in the past.

Validation: Date should be in the past and it must be the first day of the month.

E.g.: `2024-01-01` | `string` | `"2020-01-01"` | no |
| [export\_type](#input\_export\_type) | Version of the billing export.

Valid values:
- `FOCUS` for Cost and usage details (FOCUS),
- `AMORTIZED` for Cost and usage details (amortized),
- `ACTUAL` for Cost and usage details (usage only)

Learn more about export types: https://learn.microsoft.com/en-us/azure/cost-management-billing/costs/tutorial-improved-exports#schedule-frequency

E.g.: `FOCUS`, `AMORTIZED` or `ACTUAL` | `string` | n/a | yes |
| [export\_version](#input\_export\_version) | Version of the billing export. Should be use with `export_type`.

Valid values are:
- `1.0r2` & `1.0` for `FOCUS`
- `2023-12-01-preview` for Cost and usage details (EA, MCA, MPA and CSP)
- `2019-11-01` for Cost and usage details (MOSA)

E.g.: `1.0r2`, `1.0`, `2023-12-01-preview`, `2019-11-01` | `string` | n/a | yes |
| [resource\_group\_location](#input\_resource\_group\_location) | Location of the Storage Account.

Note: if `var.create_resource_group` is set to `true`, then this variable MUST
be set.

E.g.: `Switzerland North` | `string` | `null` | no |
| [resource\_group\_name](#input\_resource\_group\_name) | Name of the resource group where the Storage account is located in.

E.g.: `rg-finops-export-001` | `string` | n/a | yes |
| [storage\_account\_location](#input\_storage\_account\_location) | Location of the Storage Account.

If `null`, the Storage Account will be created in the location linked to the resource group.

E.g.: `Switzerland North` | `string` | `null` | no |
| [storage\_account\_name](#input\_storage\_account\_name) | Name of the Storage Account.

The Storage Account will be created with this name if `var.create_storage_account` is `true`.

E.g.: `billingexports` | `string` | n/a | yes |
| [storage\_container\_name](#input\_storage\_container\_name) | Name of the Storage Container.

The Storage Container will be created with this name if `var.create_storage_container` is `true`.

E.g.: `focus-v1.0` | `string` | n/a | yes |
| [tags](#input\_tags) | Tags to apply to all created resources.

E.g.:
{
createdBy = "Terraform"
}
| `map(string)` |
{
"createdBy": "Terraform"
}
| no |

## Outputs

| Name | Description |
|------|-------------|
| [backfill\_job\_id](#output\_backfill\_job\_id) | List of Ids of backfill jobs.

E.g.:

[
"/providers/Microsoft.Billing/billingAccounts/123456789/providers/Microsoft.CostManagement/exports/focus-export-for-billing-account-123456789/Run/e8102b07-9d1e-4185-95fe-fe60d8d6ad5a",
"/providers/Microsoft.Billing/billingAccounts/123456789/providers/Microsoft.CostManagement/exports/focus-export-for-billing-account-123456789/Run/1089e775-8098-4d50-ae69-c22fd26ae7ef"
]
|
| [export\_id](#output\_export\_id) | Id of the export.

E.g.: `/providers/Microsoft.Billing/billingAccounts/123456789/providers/Microsoft.CostManagement/exports/focus-export-for-billing-account-123456789` |
| [months\_to\_backfill](#output\_months\_to\_backfill) | List of months to backfill.

E.g.:
[
{
start = "2023-01-01T00:00:00Z"
end = "2023-01-31T00:00:00Z"
},
{
start = "2023-02-01T00:00:00Z"
end = "2023-02-28T00:00:00Z"
}
]
|