Thanks, @bruun963 this is helpful.
What I am going to do is show you how to do it without the need for functions. The reason being, functions are an advanced topic and it sounds like you are just starting out so don’t want to abstract away a lot of the code because it can make it hard to follow. Hope that’s okay!
Regarding Availability Sets, these are kind of tricky to determine because they are usually computed resources and therefore cannot be determined at the time of the plan. More information on that here. Happy to unpack this later once we have covered a simpler use case. Anyway onto the example…
First, we need to tell Sentinel which import version we are using, you do so by specifying the following at the top of your policy file:
import "tfplan/v2" as tfplan
Next we create a collection of resources based on type and save the properties for all of the resources in a variable called allVirtualMachines
. In this example, we only care about the azurerm_virtual_machine
type:
allVirtualMachines = filter tfplan.resource_changes as _, resource_changes {
resource_changes.type is "azurerm_virtual_machine" and
resource_changes.mode is "managed" and
resource_changes.change.actions is ["create"]
}
Then we define our rule. In this example, we will write a rule that will determine if boot diagnostics has been enabled:
boot_diagnostics_enabled_is_true = rule {
all allVirtualMachines as _, vm {
all vm.change.after.boot_diagnostics as bd {
bd.enabled is true
}
}
}
Then to close off we evaluate our newly defined rule within main
as follows:
main = rule {
boot_diagnostics_enabled_is_true
}
When we are done our policy should appear as follows:
import "tfplan/v2" as tfplan
allVirtualMachines = filter tfplan.resource_changes as _, resource_changes {
resource_changes.type is "azurerm_virtual_machine" and
resource_changes.mode is "managed" and
resource_changes.change.actions is ["create"]
}
boot_diagnostics_enabled_is_true = rule {
all allVirtualMachines as _, vm {
all vm.change.after.boot_diagnostics as bd {
bd.enabled is true
}
}
}
main = rule {
boot_diagnostics_enabled_is_true
}
So that is the standard flow for writing a policy. Essentially you are:
- Filtering out the resource type that you care about through a filter expression which is more often than not based on the
resource_changes
map which is touched on in more detail here and here.
- Then you author a rule that evaluates whether or not a particular property has been set accordingly.
We have detailed documentation for the Sentinel language as well as the specification. I would also encourage you to familiarise yourself with the list of functions that are builtin to Sentinel as well as the standard imports library. In the example above I have used the all
expression quite a bit which you can read more about here.
Finally, debugging and logging is achieved using the print
function but use these sparingly because things can get unruly pretty quickly. If you want to see a practical example of how to use print
you can change the main
rule to the following which should print out the contents of allVirtualMachines
.
main = rule {
boot_diagnostics_enabled_is_true and
print(allVirtualMachines)
}
I realise that this is a rather large braindump and I am probably oversimplifying things a great deal. If you have any further question or you would like me to dig deeper into certain things, please let me know. I’ve tried to provide the most relevant documentation in the form of links but if there is anything I have missed please call it out. I didn’t mention that I work in the Sentinel product team so if you have any feedback related to documentation etc. please send it my way.
I hope you find this helpful.
Regards,
Ryan