I have a custom policy initiative I would like to assign using terraform, one of the policies does have parameters.
I know that for single policy assignment, we could use something like this, and pass variable as parameter.
parameters = jsonencode({
"operationName": {
"value": var.allowed_values,
}
})
however, I tried this with initiative, this doesn't seem to like it keeps complaining parameter is missing.
Infact, I tried to hardcode parameter into the body of azurerm_policy_assignment, I don't think even this is working.
resource "azurerm_policy_assignment" "example" {
name = "mcs_governance_policy_assignment"
scope = var.scopes
policy_definition_id = azurerm_policy_set_definition.iam.id
description = "example"
display_name = "Governance Policy"
metadata = <<METADATA
{
"category": "General"
}
METADATA
parameters = <<PARAMETERS
{
"operationName": {
"value": "Microsoft.Authorization/policyAssignments/write"
}
}
PARAMETERS
}
the policy initiative definition policy is as following, it uses built in policy, I have not included all the policies in this initiative, just for the one that complaining about the parameter
resource "azurerm_policy_set_definition" "iam" {
name = "mcs_iam"
policy_type = "Custom"
display_name = "MCS IAM Governance Policy Set"
description = "Contains MCS Governance policies"
metadata = <<METADATA
{
"category": "General"
}
METADATA
policy_definition_reference {
# An activity log alert should exist for specific Policy operations
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/c5447c04-a4d7-4ba8-a263-c9ee321a6858"
reference_id = "MCS IAM 3.1"
}
As I see, the azurerm_policy_set_definition iam only reference the policy definition, but it does not set the parameters in the policy_definition_reference block for the property parameter_values. You don't show how the policy definition configured, so you need to check how do the parameters setting in the policy definition and then add the parameter setting in the policy set definition.
Related
We are using Terraform to store secrets inside AWS secrets manager. We would like to expand our Terraform to add resource access policy to each secret to only allow certain IAM roles or user access the secret and get it is value. We are defining each secret and it is metadata using YAML. Terraform will then decode the yaml and store all the contents in a map. We then have a for_each to iterate through each map and create the secrets. Below is the yaml definition for a secret
nonprod:
- name: my-super-secret
metadata:
description: my-super-secret
value: somesecret
policy: true #This is the feature we trying to add. It will tell TF to add resource access policy
iam_roles:
- "arn:aws:iam::account-id:role/sagemaker"
- "arn:aws:iam::account-id:user/jon.doe"
- "arn:aws:iam::account-id:role/test"
tags:
purpose: sagemaker
The YAML is decoded and then stored in a var.secrets map. Using TF console this is what TF store in var.secrets after decoding YAML.
{
"metadata" = {
"description" = "my-super-secret"
}
"name" = "my-super-secret"
"policy" = true
"iam_roles" = [
"arn:aws:iam::account-id:role/sagemaker",
"arn:aws:iam::account-id:user/jane.doe",
"arn:aws:iam::account-id:role/test",
]
"tags" = {
"purpose" = "sagemkaer"
}
"value" = "somesecret"
}
on the main.tf file, I added the following code for the IAM policy document:
data "aws_iam_policy_document" "example" {
for_each = { for item in var.secrets : item.name => item }
statement {
sid = "EnableAccessFor${each.value.name}"
principals {
type = "AWS"
identifiers = [lookup(each.value, "iam_roles")]
}
actions = [
"secretsmanager:GetSecretValue",
]
resources = [
"*",
]
}
}
then I am passing the policy document to aws_secretsmanager_secret_policy resource to attach the policy to the secret that has policy set as true
resource "aws_secretsmanager_secret_policy" "policy" {
depends_on = [aws_secretsmanager_secret.secret]
for_each = { for item in var.secrets : item.name => item }
secret_arn = each.key
policy = data.aws_iam_policy_document.example.json
}
no matter what way I use, I always get errors when I run a plan. I have used the following functions with no luck:
join, concat, toset, splat, jsonencode and for expressions
I get the following errors:
using for expression identifiers = [for r in lookup(each.value, "iam_roles") : r] produces this error: Invalid value for "inputMap" parameter: the given object has no attribute "iam_roles"
using splat identifiers = "${each.value[*].iam_roles}" produces this error: Inappropriate value for attribute "identifiers": element 0: string required.
using toset with lookup identifiers = "${toset(lookup(each.value, "iam_roles", ""))}" produces this error Invalid value for "v" parameter: cannot convert string to set of any single type.
using join with lookup identifiers = ["${join(", ", lookup(each.value, "iam_roles", ""))}"] produces this error Invalid value for "lists" parameter: list of string required.
using jsonencode identifiers = [jsonencode(each.value.iam_roles)] produces this error This object does not have an attribute named "iam_roles".
using just each.value without lookup identifiers = [each.value.iam_roles] produces this error Inappropriate value for attribute "identifiers": element 0: string required.
Any idea?
Update
I got rid of the iam_policy_document and instead opt-in to use the aws_secretsmanager_secret_policy resource with a json-policy. See example below:
resource "aws_secretsmanager_secret_policy" "policy" {
depends_on = [aws_secretsmanager_secret.secret]
for_each = { for item in var.secrets : item.name => item if try(item.policy, false)}
secret_arn = aws_secretsmanager_secret.secret[each.key].arn
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableAccessFor${each.value.name}",
"Effect": "Allow",
"Principal": {
"AWS": [
"${join(",", formatlist("\"%s\"", each.value.iam_roles))}"
]
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}
]
}
POLICY
}
The part I am having issue is with the each.value.iam_roles as that is a tuple vs. a string. I tried multiple ways to convert that into string but it is not working. Perhaps someone can help me with that.
issue was resolved with encoding the entire policy to JSON using the `jsonencode function in Terraform.
Working on a code to apply CIS policies set via code to track changes. What I am trying to do is create a custom policy set that contains the policies within CIS Microsoft Azure Foundation Benchmark v1.4.0 initiative definition. I am using the Terraform’s azurerm_policy_set_definition but repeatedly encounter a listofAllowedLocations issue.
Code:
data "azurerm_management_group" "Standard" {
display_name = "Standard"
}
resource "azurerm_policy_set_definition" "cis_benchmark" {
name = var.cis_policy_name
policy_type = "Custom"
display_name = var.cis_display_name
lifecycle {
create_before_destroy = true
}
parameters = local.parameters
metadata = local.metadata
policy_definition_reference {
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988"
parameter_values = <<VALUE
{
"allowedLocation": {"value": ["eastus"]}
}
VALUE
}
}
Error:
Error: creating Policy Set Definition "CIS Benchmark v1.4.0": policy.SetDefinitionsClient#CreateOrUpdate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="MissingPolicyParameter" Message="The policy set definition 'CIS Benchmark v1.4.0' is missing the parameter(s) 'listOfAllowedLocations' as defined in the policy definition 'e765b5de-1225-4ba3-bd56-1ac6695af988'."
│
│ with azurerm_policy_set_definition.cis_benchmark,│ on cisbenchmark.tf line 22, in resource "azurerm_policy_set_definition" "cis_benchmark":
│ 22: resource "azurerm_policy_set_definition" "cis_benchmark" {
│
You misspelled the parameter name allowedLocation, which should be listOfAllowedLocations. Take a close look at the example below, this should solve your issue. I agree it is a bit confusing that the names differ.
resource "azurerm_policy_set_definition" "example" {
name = "testPolicySet"
policy_type = "Custom"
display_name = "Test Policy Set"
parameters = <<PARAMETERS
{
"allowedLocations": {
"type": "Array",
"metadata": {
"description": "The list of allowed locations for resources.",
"displayName": "Allowed locations",
"strongType": "location"
}
}
}
PARAMETERS
policy_definition_reference {
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988"
parameter_values = <<VALUE
{
"listOfAllowedLocations": {"value": "[parameters('allowedLocations')]"}
}
VALUE
}
}
I got this example from the official policy_set_definition documentation.
I’m creating a new Alias and Subscription for an Enrollment Account by using the following terraform code:
data "azurerm_billing_enrollment_account_scope" "example" {
billing_account_name = "1234567890"
enrollment_account_name = "0123456"
}
resource "azurerm_subscription" "example" {
subscription_name = "My Example EA Subscription"
billing_scope_id = data.azurerm_billing_enrollment_account_scope.example.id
}
Next, manages a policy set definition and subscription policy assignment by using the following TF code:
data "azurerm_subscriptions" "availablesubscriptions" {
display_name_prefix = var.subscription_name
}
resource "azurerm_policy_set_definition" "tag_definition" {
name = "${var.subscription_name}-tag-def"
display_name = "${var.subscription_name}-tag-def"
description = "Append the default tags definition"
policy_type = "Custom"
policy_definitions = templatefile("${path.module}/templates/tag_definitions.json",
{ environmentType = var.environment_Type})
metadata = <<METADATA
{ "category" : "Tags" }
METADATA
lifecycle { ignore_changes = [metadata] }
}
resource "azurerm_subscription_policy_assignment" "tag_assignment" {
name = "${var.subscription_name}-tag-assignment"
display_name = "${var.subscription_name}-tag-assignment"
description = "Append the default tags assignment"
subscription_id = data.azurerm_subscriptions.availablesubscriptions.subscriptions[0].subscription_id
policy_definition_id = azurerm_policy_set_definition.tag_definition.id
}
I have configured the release pipeline in Azure DevOps, for creating above infrastructure. But in the release, terraform apply task giving the following error:
Note: The service connection/Active Directory application having the Owner access.
Error: creating Policy Set Definition "XXXX-tag-def": policy.SetDefinitionsClient#CreateOrUpdate: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client 'XXXXXXXXXXXXX' with object id 'XXXXXXXXXX' does not have authorization to perform action 'Microsoft.Authorization/policySetDefinitions/write' over scope '/subscriptions/***' or the scope is invalid. If access was recently granted, please refresh your credentials."
I have a Vault instance and I manage policies and secrets in it with Terraform. There are a couple of repeated steps when creating approle authentication, policy and policy documents for newly onboarded teams, because each team has several applications they work on. I'd like to modularize the repeated parts ( policy document, policy creation and approle for the team-app), though each application has a slightly different rule set.
Is there a way to create policy documents in a way that some rules are only included if a bool is set to true?
for example:
I have a module that creates policies and policy documents as below:
I would pass a bool variable named enable_metadata_rule and based on it's value I would create the 2nd rule or not:
resource "vault_policy" "example_policy" {
for_each = var.environments
provider = vault
name = "${var.team}-${var.application}-${each.key}"
policy = data.vault_policy_document.policy_document["${each.key}"].hcl
}
data "vault_policy_document" "policy_document" {
for_each = var.environments
rule {
path = "engines/${var.team}-kv/data/${each.key}/services/${var.application}/*"
capabilities = ["read", "list"]
description = "Read secrets for ${var.application}"
}
rule {
# IF enable_metadata_rule == true
path = "engines/${var.team}-kv/metadata/*"
capabilities = ["list"]
description = "List metadata for kv store"
}
}
If there isn't such thing, is there an option for merging separately created policy documents?
You should be able to do it using dynamic blocks:
data "vault_policy_document" "policy_document" {
for_each = var.environments
rule {
path = "engines/${var.team}-kv/data/${each.key}/services/${var.application}/*"
capabilities = ["read", "list"]
description = "Read secrets for ${var.application}"
}
dynamic "rule" {
for_each = var.enable_metadata_rule == true ? [1]: []
content {
path = "engines/${var.team}-kv/metadata/*"
capabilities = ["list"]
description = "List metadata for kv store"
}
}
}
We have a release pipeline using the Terraform 0.12.3 series of tasks to create a VM, part of which involves creating a policy for automatically tagging the resources. Here's the relevant Terraform code that's giving an error:
# Assign tagging policy
resource "azurerm_policy_assignment" "tag_policy" {
count = length(var.tagNames)
name = "Apply${var.tagNames[count.index]}Tag"
scope = azurerm_resource_group.rsg.id
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498"
description = "Assign policy for Tag '${var.tagNames[count.index]}' Value '${var.tagValues[count.index]}'"
display_name = "Apply${var.tagNames[count.index]}Tag"
parameters = <<PARAMETERS
{
"tagName": {
"value": "${var.tagNames[count.index]}"
},
"tagValue": {
"value": "${var.tagValues[count.index]}"
}
}
PARAMETERS
}
(the "count" parts of the code are due to having 3 Tags to apply, which are defined in variables.tf)
The error we receive is:
2020-04-17T15:40:29.5769619Z Error: cannot parse "policy_definition_id" as a Policy Definition ID: unable to parse Policy Definition ID "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498": unable to parse Remediation Scope ID: ID is empty
2020-04-17T15:40:29.5771388Z
2020-04-17T15:40:29.5771960Z on main.tf line 62, in resource "azurerm_policy_assignment" "tag_policy":
2020-04-17T15:40:29.5772430Z 62: resource "azurerm_policy_assignment" "tag_policy" {
I've double checked the policy definition ID is correct:
Name: Append a tag and its value to resources
Definition ID: /providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498
The guide at Hashicorp for the azurerm_policy_assignment makes no mention of requiring a Remediation Scope ID, or setting a flag to ignore it.
For this issue, I think the introduction for the property policy_definition_id of the resource azurerm_policy_assignment misleading you in the wrong way. You can see it quote the azurerm_policy_definition.example.id for the property, but when you output that id, you would know that it's the resource Id of the policy definition, not the Id you provided. So the policy_definition_id looks like this:
/subscriptions/xxxxxxxx/providers/Microsoft.Authorization/policyDefiniti
ons/my-policy-definition