Trying to create a rule to make sure we are only using company authorise providers

I’ve been wanting to for a while start writing policies for our Terraform Cloud deployments.
Yesterday I started to pick up Sentinel and run over some of the tutorials but being honest, this structure and finding all the syntax key words and meanings is turning out to be very hard.

As Indicated above the end goal for this piece of work is to have a policy, I can attach to workspaces that will identify if non approved providers are being used.

I thought I would start simple and just see if I can pass on a single provider such as aws but I’m having no luck.

Mock I have is

providers = {
	"aws": {
		"alias": "",
		"config": {
			"region": {
				"references": [
					"var.region",
				],
			},
		},
		"full_name":           "registry.terraform.io/hashicorp/aws",
		"module_address":      "",
		"name":                "aws",
		"provider_config_key": "aws",
		"version_constraint":  ">= 3.26.0",
	},
	"random": {
		"alias":               "",
		"config":              {},
		"full_name":           "registry.terraform.io/hashicorp/random",
		"module_address":      "",
		"name":                "random",
		"provider_config_key": "random",
		"version_constraint":  "3.0.1",
	},
}

Code I’m currently trying is

# Imports mock data
import "tfconfig/v2" as tfconfig


# Retrive all providers 
providers = tfconfig.providers

#confirm the provider matches
allowed_providers = rule {
  all providers as _, provider{
    provider.provider_config_key is "aws"
  }
}


# Main rule that requires other rules to be true
main = rule {
    (allowed_providers) else true
}

It doesn’t matter what I put in " provider.provider_config_key is “aws” " it always comes back as false and I can’t work out why as it seems correct.

What has I got wrong here, ta ?

Hello John-

It looks like the issue here is that in the all statement, you’re probably wanting to actually look at providers[provider].provider_config_key rather than provider.provider_config_key.

This modified example passes, and I added print statements that showed me a selector was being used against a string (provider in provider.provider_config_key):

https://play.sentinelproject.io/p/Khl96lx883T

Let me know if you have additional questions around the functionality of this- if you’re testing locally, you can see the values of print by applying the -trace flag to the CLI.

1 Like

Hi @john.ward

I’m the Product Manager for the policy integration in Terraform Cloud/Enterprise. I’m sorry to hear that you are finding the process of getting started harder than it should be. I would love the opportunity to speak with you about your experience as I am in the process of researching how we can improve the onboarding process for folks that are new to Sentinel. If this is of interest, please reach out via DM and I can set this up.

Regarding the issue you are experiencing, I believe @sean.meininger has provided a working example but I thought I should dig into it a bit to try and help explain what is happening.

You have used an all expression in your allowed_providers. This expression will loop through the configuration for each provider and check that the value of provider_config_key is equal to aws.

allowed_providers = rule {
  all providers as _, provider{
    provider.provider_config_key is "aws"
  }
}

If we look at the contents of the providers map you’ll see that you have two providers listed only one of which has a provider_config_key that is equal to aws and therefore the all expression returns a value of false. This is because only one of the providers is an aws provider.

providers = {
	"aws": {
        ...
		"provider_config_key": "aws",  // Will evaluate as True
        ...
	},
	"random": {
        ...
		"provider_config_key": "random", // Will evaluate as False
        ...
	},
}

If you want to play around with this idea a bit more, change your rule to an any expression which would result in a passing rule as we are checking that at least one of the providers that have been configured has a provider_config_key that is equal to aws

There is another expression that you may be interested in which is called a filter expression. You can use this expression to filter out the providers that you don’t care about.

providers = filter tfconfig.providers as _, provider {
	provider.name is "aws"
}

Once you have filtered out the providers, your policy will behave in the manner that you were expecting.

https://play.sentinelproject.io/p/386r7HMu6Ey

I hope this helps with your understanding of how Sentinel works. If you have any feedback, do get in touch.

1 Like

Thank guys, I totally missed that there was a “random” provider, when I looked at it in my code I thought it wasn’t in line with providers and instead an argument for the aws provider :(.

I have 2 great pieces of sentinel code I can know build on and understand a lot more.

Thanks again.

I’ll make sure to drop my code in here when finished so it can be reused.

Hi @hcrhall, I’ve written the policy that I think should work now for our need, it is located here
https://play.sentinelproject.io/p/CZslZLH6mMT I’ve put sean’s code in to print all the providers but I thought it would be good if I could also add some code in so if a provider triggered a “false” in the allowed_providers we could print that out as well.

Do you have a suggestion on a resource I could read to understand how to implement this?

I think the simplest way of doing so would be to change allowed_providers to a function and then add logic that would iterate through all providers as input, check the configuration, print the values and return true or false.

In this example, I have changed allowed_providers to a policy parameter.

https://play.sentinelproject.io/p/16l8pMjT9Tl

:heart: I altered it just slightly but it just what I was looking for, cheer.
https://play.sentinelproject.io/p/ZS3kHhkQjdO Is there a beginner’s page for functions you could point me to hcrhall, know that I don’t know the syntax hence need to learn that as well?

The Sentinel functions documentation is the best place to start. The language specification is also a great resource.

Don’t forget that there are also a few policies in the registry that can help get you started

1 Like