Alright, let’s face it, Azure Policies are not easy to setup.
Why you say that?
Because Policies and Tags in general they look like the half baked products that Microsoft didn’t want to release.
But do you want to know what is my favorite Azure Policy?
No, tell me, please.
It’s Require a tag on resource groups. Without it you cannot really do FinOps.
Why?
Because when you want to track the consumption of a customer or a specific product I bet you go for the biggest offenders: SQL Databases, App Services, VMs, etc…
Yes, and what’s wrong with that?
Most of the time people forget that there is a galaxy of other small resources that fluctuate around biggest offenders: Microsoft Defender, Log Analytics, Virtual Networks, Network Watcher, etc…
Yeah, but they don’t cost much, right?
Well, it’s true, it’s just $16 per Resource Group.
You see!
But when you have 400 Resource Groups it can account for $6’400 at the end of the month.
…and then the CFO comes to your desk with a stick in his hands.
So how to avoid this?
By using a good combination of tagging and policy. Or tagging through policies.
And why Require a tag on resource groups is not enough?
Because sometimes people just put “Hello World” in the tag and the tag becomes unusable.
You cannot track money any more.
What you need is my Custom Azure Policy…
Require a tag and its specific values on Resource Groups
But Azure already has a Policy which name sounds very similar: Require a tag and its value on resource groups
Yes, but that is limited to 1 value.
We want to have a list of values!
For example let’s say that we want to allow our user to only put on the tag myTag this list of values:
- Apple
- Banana
- Kiwi
- Mango
- Peach
Everything else will not be allowed.
How do you do that?
You have to create a new Policy definition with this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
{ "properties": { "displayName": "Custom Policy :: Require a tag and its specific values on Resource Groups", "policyType": "Custom", "mode": "All", "description": "This is a custom policy that makes the tag myTag mandatory and the allowed values to be in the rage of: \n- Apple\n- Banana\n- Kiwi\n- Mango\n- Peach", "metadata": { "createdBy": "d5ebb475-1d4e-4633-b888-3d6f54264ef7", "createdOn": "2023-11-09T09:12:37.0236155Z", "updatedBy": "d5ebb475-1d4e-4633-b888-3d6f54264ef7", "updatedOn": "2023-11-20T13:33:09.2452726Z", "category": "" }, "version": "1.0.0", "parameters": {}, "policyRule": { "if": { "allOf": [ { "field": "type", "equals": "Microsoft.Resources/subscriptions/resourceGroups" }, { "not": { "field": "tags['myTag']", "in": [ "Apple", "Banana", "Kiwi", "Mango", "Peach" ] } } ] }, "then": { "effect": "deny" } }, "versions": [ "1.0.0" ] }, "id": "/providers/Microsoft.Management/managementGroups/363c9387-f81b-4c87-8f64-93d71a8c1e9b/providers/Microsoft.Authorization/policyDefinitions/d005ac64-f849-4ebb-a1f6-b558889bb7f6", "type": "Microsoft.Authorization/policyDefinitions", "name": "d005ac64-f849-4ebb-a1f6-b558889bb7f6", "systemData": { "createdBy": "name.surname@me.com", "createdByType": "User", "createdAt": "2023-11-09T09:12:36.9950422Z", "lastModifiedBy": "name.surname@me.com", "lastModifiedByType": "User", "lastModifiedAt": "2023-11-20T13:33:09.1887144Z" } } |
And voilà, you are good to go.
Any problems with Terraform?
Yes, you need to coordinate Terraform accordingly otherwise they will compete and overwrite one each other.