Skip to content

feat(azure): Cost management view queries, query views by subscription scope#5898

Merged
disq merged 5 commits intocloudquery:mainfrom
disq:feat/azure-cost-mgmt
Dec 28, 2022
Merged

feat(azure): Cost management view queries, query views by subscription scope#5898
disq merged 5 commits intocloudquery:mainfrom
disq:feat/azure-cost-mgmt

Conversation

@disq
Copy link
Copy Markdown
Member

@disq disq commented Dec 21, 2022

NOTICE Let's keep this unmerged for now it as might need tweaking to make the blog post viable

azure_cost_management_views were coming up empty. Querying with the scope subscription we can get user's previously saved views (in Cost Analysis) and some data.

Unfortunately the main resource only returns metadata about pivots and aggregations, to get the actual table data/metrics we still need to run the query. Not sure if this is the right way to do it in terms of performance.

Example "views" row:

subscription_id | 12345678-1234-1234-1234-123456789012
e_tag           | "fedcda987654321"
properties      | {"kpis": [{"id": "COST_NAVIGATOR.BUDGET_OPTIONS.NONE", "type": "Budget", "enabled": true}, {"type": "Forecast", "enabled": true}], "chart": "Table", "query": {"type": "ActualCost", "dataSet": {"filter": {"and": [{"dimensions": {"name": "PublisherType", "values": ["azure"], "operator": "In"}}, {"dimensions": {"name": "PublisherType", "values": ["azure"], "operator": "In"}}, {"dimensions": {"name": "PublisherType", "values": ["azure"], "operator": "In"}}]}, "sorting": [{"name": "UsageDate", "direction": "Ascending"}], "grouping": [{"name": "ResourceGroupName", "type": "Dimension"}], "aggregation": {"totalCost": {"name": "Cost", "function": "Sum"}, "totalCostUSD": {"name": "CostUSD", "function": "Sum"}}, "granularity": "Daily"}, "timeframe": "None", "includeMonetaryCommitment": false}, "scope": "subscriptions/12345678-1234-1234-1234-123456789012", "metric": "ActualCost", "pivots": [], "currency": "USD", "createdOn": "2022-12-21T14:06:33.1518576Z", "dateRange": "CurrentBillingPeriod", "modifiedOn": "2022-12-21T14:17:14.5524763Z", "accumulated": "False", "displayName": "test2"}
id              | /subscriptions/12345678-1234-1234-1234-123456789012/providers/Microsoft.CostManagement/views/test2
name            | test2
type            | Microsoft.CostManagement/Views

Example "view_queries" row:

properties      | {"rows": [[0.000203782, 0.000203782, 20221206, "er-function-app-resource-group-example-resources", "USD"], [0.374281415, 0.374281415, 20221206, "er-web-app-resource-group-example-resources", "USD"], [0.197516129032258, 0.197516129032258, 20221214, "abc-test-sg", "USD"], [0.526709677419355, 0.526709677419355, 20221215, "abc-test-sg", "USD"], [0.504763440860215, 0.504763440860215, 20221216, "abc-test-sg", "USD"], [0.526709677419355, 0.526709677419355, 20221217, "abc-test-sg", "USD"], [0.131677419354839, 0.131677419354839, 20221218, "abc-test-sg", "USD"]], "columns": [{"name": "Cost", "type": "Number"}, {"name": "CostUSD", "type": "Number"}, {"name": "UsageDate", "type": "Number"}, {"name": "ResourceGroupName", "type": "String"}, {"name": "Currency", "type": "String"}]}
e_tag           |
id              | subscriptions/12345678-1234-1234-1234-123456789012/providers/Microsoft.CostManagement/query/12345678-1234-5678-90ab-123456789012
location        |
name            | 12345678-1234-5678-90ab-123456789012
sku             |
tags            | {}
type            | Microsoft.CostManagement/query

There's a ResourceGuid option which doesn't match anything we have, so we use the "Resource" field which will give us a full id of the resource (/subscriptions/<uuid>/resourcegroups/<groupname>/providers/<provider>/<type>/<name> format) so we'll also need to include that id ("full_id" I'm calling it) in azure_resources.

The query to join all of them to see cost vs. res is:

WITH cost_by_guid AS (
SELECT (r->>(cid.pos::int - 1))::text AS res_id, (r->>(ccost.pos::int - 1))::numeric AS cost_usd
FROM azure_costmanagement_view_queries q
JOIN JSONB_ARRAY_ELEMENTS(q.properties->'rows') AS r ON TRUE
JOIN JSONB_ARRAY_ELEMENTS(q.properties->'columns') WITH ORDINALITY cid(col, pos) ON cid.col->>'name'='ResourceId'
JOIN JSONB_ARRAY_ELEMENTS(q.properties->'columns') WITH ORDINALITY ccost(col, pos) ON ccost.col->>'name'='CostUSD'
JOIN azure_costmanagement_views v ON v._cq_id=q._cq_parent_id
WHERE v.name='test3' -- <-- this is the name of your saved view with "resource" field
)
SELECT
	c.*,
	r.name,
	r.kind,
	r.location
FROM cost_by_guid c JOIN azure_resources r ON lower(r.full_id)=lower(c.res_id);

@github-actions
Copy link
Copy Markdown

This PR has the following changes to source plugin(s) tables:

  • Table azure_costmanagement_view_queries was added

return err
}
var qd armcostmanagement.QueryDefinition
if err := json.Unmarshal(b, &qd); err != nil {
Copy link
Copy Markdown
Member Author

@disq disq Dec 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Types are not compatible (ReportConfigDefinition vs. QueryDefinition) so instead of doing a deep conversion we'll use their marshal/unmarshal methods instead. If the fields were the same type and had equal names, the structs would be compatible with each other (lesser known Go feature) but the field names are different... Luckily their json tags match.

Copy link
Copy Markdown
Member

@erezrokah erezrokah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, I'm assuming this is the data we want for cost analysis

@disq disq force-pushed the feat/azure-cost-mgmt branch from acd0c20 to c909c92 Compare December 22, 2022 09:28
@disq disq merged commit ace315f into cloudquery:main Dec 28, 2022
@disq disq deleted the feat/azure-cost-mgmt branch December 28, 2022 10:23
kodiakhq bot pushed a commit that referenced this pull request Jan 3, 2023
🤖 I have created a release *beep* *boop*
---


## [3.1.0](plugins-source-azure-v3.0.0...plugins-source-azure-v3.1.0) (2023-01-03)


### Features

* Add azure security pricings resource ([#6023](#6023)) ([9648f26](9648f26))
* **azure:** Add storage_blob_services ([#6245](#6245)) ([0620ccd](0620ccd))
* **azure:** Cost management view queries, query views by subscription scope ([#5898](#5898)) ([ace315f](ace315f))


### Bug Fixes

* **deps:** Update module github.com/Azure/azure-sdk-for-go/sdk/internal to v1.1.2 ([#6205](#6205)) ([154fa6f](154fa6f))
* **deps:** Update module github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redhatopenshift/armredhatopenshift to v1.1.0 ([#6183](#6183)) ([3395c09](3395c09))
* **deps:** Update module github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage to v1.2.0 ([#6184](#6184)) ([adc96f3](adc96f3))
* **deps:** Update module github.com/cloudquery/plugin-sdk to v1.14.0 ([#6025](#6025)) ([35b2cfc](35b2cfc))
* **deps:** Update module github.com/cloudquery/plugin-sdk to v1.15.0 ([#6071](#6071)) ([684b525](684b525))
* **deps:** Update module github.com/cloudquery/plugin-sdk to v1.15.1 ([#6079](#6079)) ([650659c](650659c))
* **deps:** Update module github.com/cloudquery/plugin-sdk to v1.16.0 ([#6098](#6098)) ([7bacdf3](7bacdf3))
* **deps:** Update module github.com/cloudquery/plugin-sdk to v1.16.1 ([#6214](#6214)) ([53b2415](53b2415))
* **deps:** Update module github.com/cloudquery/plugin-sdk to v1.17.0 ([#6256](#6256)) ([b19f6cd](b19f6cd))

---
This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
kodiakhq bot pushed a commit that referenced this pull request Jan 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants