I'm trying to assign the Object ID of my User_Managed_Identity to the KeyVault_Access_Policy
The Managed_Identity will need to Get & List the Certificates from my KeyVault. I've been following Terraform Documentation.
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment
# User Assigned Identity
resource "azurerm_user_assigned_identity" "user_assigned_identity" {
location = "west europe"
name = "Identity_Name"
resource_group_name = "Resource_Group_Name"
}
# Access Policy
data "azurerm_client_config" "example" {
}
resource "azurerm_key_vault_access_policy" "access_policy" {
key_vault_id = "000000-000000-000000-000000"
tenant_id = data.azurerm_client_config.example.tenant_id
object_id = azurerm_user_assigned_identity.user_assigned_identity.id
certificate_permissions = [
"Get","List",
]
}
I'm getting the following error:
Error: expected "object_id" to be a valid UUID, got /subscriptions/0000000-0000-0000-0000-0000000000/resourceGroups/Resource_Group_Name/providers/Microsoft.ManagedIdentity/userAssignedIdentities/Identity_Name
I'd think it should be:
azurerm_user_assigned_identity.user_assigned_identity.principal_id
what you are doing right now - is using resource id, not object id
Related
I receive an error, value must be a string, when trying to set an ssm parameter with type=stringlist to a variable of type list using terraform.
resource "aws_ssm_parameter" "customer_list_stg" {
name = "/corp/stg/customer_list"
type = "StringList"
value = var.customer_list
tags = {
environment = var.environment
}
}
customer_list = ["sar", "smi", "heath","first","human","stars","ther","ugg","stars","well"]
terraform apply: expecting an ssm parameter with a list of 10 strings
received an error: Inappropriate value for attribute "value": string required.
I have tried tostring, jsonencode and flatten without success.
Just use list as the type, StringList isn't a valid HCL type. Please see the documentation here:
[https://developer.hashicorp.com/terraform/language/expressions/types][1]
Regardless of the attribute type = "StringList", the value attribute of aws_ssm_parameter always expects a string literal.
Please refer to the AWS API docs
Hence the correct code would be as follow
resource "aws_ssm_parameter" "customer_list_stg" {
name = "/corp/stg/customer_list"
type = "StringList"
value = join(",", var.customer_list) ## if you want to use list(string) input
# value = "sar,smi,heath,first,human,stars,ther,ugg,stars,well" ## as a string literal (you can put this value in a variable too)
tags = {
environment = var.environment # define this variable in your config too.
}
}
variable "customer_list" {
type = list(string)
description = "(optional) Customer List"
default = ["sar", "smi", "heath", "first", "human", "stars", "ther", "ugg", "stars", "well"]
}
I have a list of users with characteristics like this and I want to create a local variable that includes the names of the users in the "maker" group.
variable "users" {
type = map(object({
groups = list(string)
}))
default = {
"kevin.mccallister" = {
groups = ["kids", "maker"],
},
"biff" = {
groups = ["kids", "teens", "bully"],
},
}
}
I want to write the local like this, but it complains
Error: Invalid 'for' expression ... Key expression is required when
building an object.
locals {
makers_list = flatten({
for user, attr in var.users: user
if contains(attr.groups, "makers")
})
}
How can I take that map of objects and get out a list of names based on group affiliation?
flatten() is not required for this. Also, the {} is pushing this to build an object. You can instead build a list using [] and then it will create a list of the users filtered by their group association.
makers_list = [
for user, attr in var.users: user
if contains(attr.groups, "makers")
]
I am preparing to deploy Aurora MySQL Cluster, I have defined the following in the “variable.tf” under root:
variable "db_subnet_id" {
type = list
description = `DB Subnet IDs`
default = [“subnet-02ddc8565555aaa9b”,“subnet-1d30f3a41ce19635d”] #db-sub-eu-central-1a , db-sub-eu-central-1b
}
variable `db-sg` {
type = list
description = “List of standard security groups”
default = [`“sg-00cfed28101ea95ab”,“sg-08017f86bc12348e8”,“sg-0e8c67c7a3cd79a79”`]
}
variable `db_az` {
type = list
description = “Frankfurt Availability Zones”
default = [“eu-central-1a”,“eu-central-1b”]
}
how can I use those variables with the following parameters:
availability_zones
db_subnet_group_name
vpc_security_group_ids
I have tried to use this vailability_zones = var.db_az[count.index] but the following error shows up:
The “count” object can only be used in “module”, “resource”, and “data” blocks, and only when the “count” argument is set.
please advise, how can I use those variables with those parameters ?
Thanks
I am interested to see if anyone knows of any better alternative to using conditional count statements in Terraform. By "conditional count statement", I mean a statement where depending a condition like a variable input, count will evaluate to create either 0 or 1 of a resource.
A simple example:
resource "xxxxxxxx" "example" {
count = var.example != null ? 1 : 0
}
Here, the resource will only be created if var.example has a value (is not null), otherwise it will not get created.
Conditional counts usually work ok in practice, but sometimes in more complex uses than the one above it introduces a risk of getting an error during Terraform Plan where it cannot evaluate the result of the count pre-apply.
The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.
Is there any better way to achieve the same effect of creating resources on a conditional basis in Terraform?
Keeping in mind that your terraform config not shown I've used a general example of for_each instead of count.
This is an example of how 2 CNAME records will be created using & in this example terraform modules are used but the same can be done directly on terraform resources.
locals {
cname_records = {
"email" = ["email.domain.net."]
"imap" = ["imap.domain.net."]
}
}
module "aws_route53_record_CNAME" {
source = "app.terraform.io/terraform-cloud-org/route53-record/aws"
version = "1.0.0"
for_each = local.cname_records
records = each.value
name = each.key
zone_id = "YOUR-HOSTED-ZONE-ID"
type = "CNAME"
ttl = "300"
}
It varies by case, but there are some cases where you need to get around this. There is a clever trick, but it seems obtuse in the single item (binary exists or not) case. But in a case where there are actually multiple items, this should help.
This is a completely contrived example but I actually use the method quite a bit. In short, a list of things not known until apply, can be replaced by a list of objects with a key you don't care about. The purpose is that for_each doesn't mind if the value is unknown at plan-time, only if the key is.
Consider the following root module with these four modules.
main.tf
resource "aws_s3_bucket" "this" {
bucket = "h4s-test-bucket"
}
# module "with_count" { # cannot be determined until apply
# source = "./with-count"
# x = aws_s3_bucket.this.id
# }
module "with_for_each_over_item" { # handy workaround
source = "./with-for-each-over-item"
x = aws_s3_bucket.this.id
}
output "with_for_each_over_item" {
value = module.with_for_each_over_item
}
# module "with_for_each_list" { # cannot be determined until apply
# source = "./with-for-each-list"
# x = [aws_s3_bucket.this.id]
# }
module "with_for_each_list_better" { # handy workaround
source = "./with-for-each-list-better"
x = [{ y = aws_s3_bucket.this.id }]
}
output "with_for_each_list_better" {
value = module.with_for_each_list_better
}
module "with_for_each_list_best" { # handier workaround
source = "./with-for-each-list-best"
x = [aws_s3_bucket.this.id]
}
output "with_for_each_list_best" {
value = module.with_for_each_list_best
}
with-count/main.tf (problematic)
variable "x" {
type = string
default = null
}
resource "null_resource" "this" {
count = var.x != null ? 1 : 0
}
output "this" {
value = null_resource.this
}
with-for-each-over-item/main.tf (handy workaround)
variable "x" {
type = string
default = null
}
resource "null_resource" "this" {
for_each = { for i, v in [var.x] : i => v }
}
output "this" {
value = null_resource.this
}
with-for-each-list/main.tf (problematic)
variable "x" {
type = list(string)
default = []
}
resource "null_resource" "this" {
for_each = toset(var.x)
}
output "this" {
value = null_resource.this
}
with-for-each-list-better/main.tf (handy workaround)
variable "x" {
type = list(object({ y = string }))
default = []
}
resource "null_resource" "this" {
for_each = { for i, v in var.x : i => v }
}
output "this" {
value = null_resource.this
}
with-for-each-list-best/main.tf (handiest workaround)
variable "x" {
type = list(string)
default = []
}
resource "null_resource" "this" {
for_each = { for i, v in var.x : i => v }
}
output "this" {
value = null_resource.this
}
Summary
In cases where the variable has a value not known at plan-time, consider using an object where the key is known.
I am using the aws provider and trying to create an aws_workspaces_workspace with encrypted volumes.
I created an aws_kms_key with an associated alias (aws_kms_alias).
I specified the key alias (as a string) for volume_encryption_key.
The resource is created as expected and I can verify in the console that the volumes are encrypted with the specified key.
My issue is that every time I re-run terraform apply, terraform reports that the aws_workspaces_workspace needs to be replaced because of an update in the key value (from a key id to the alias)
How can I prevent this form happening? Is this a bug? Am I doing something incorrectly? Some of the relevant code is below.
resource "aws_workspaces_workspace" "workspace" {
directory_id = aws_workspaces_directory.ws-ad.id
bundle_id = var.bundle_id
user_name = var.username
root_volume_encryption_enabled = true
user_volume_encryption_enabled = true
volume_encryption_key = "alias/workspace-volume"
workspace_properties {
compute_type_name = "POWER"
user_volume_size_gib = 80
root_volume_size_gib = 50
running_mode = "AUTO_STOP"
running_mode_auto_stop_timeout_in_minutes = 60
}
}
resource "aws_kms_key" "kms-ws-volume" {
description = "Workspace Volume Encryption Key"
key_usage = "ENCRYPT_DECRYPT"
deletion_window_in_days = 30
is_enabled = true
}
resource "aws_kms_alias" "kms-ws-volume-alias" {
name = "alias/workspace-volume"
target_key_id = aws_kms_key.kms-ws-volume.key_id
}
Here's what terraform apply reports:
# aws_workspaces_workspace.workspace["1"] must be replaced
-/+ resource "aws_workspaces_workspace" "workspace" {
~ computer_name = "WSAMZN-T34E23BK" -> (known after apply)
~ id = "ws-v98b0y17z" -> (known after apply)
~ ip_address = "10.0.0.45" -> (known after apply)
~ state = "STOPPED" -> (known after apply)
tags = {
"Name" = "workspace-user1-env1"
"Owner" = "mario"
"Profile" = "dev"
"Stack" = "env1"
}
~ volume_encryption_key = "arn:aws:kms:us-west-2:927743275319:key/09de3db9-ecdd-4be1-a781-705fdd0294f9" -> "alias/workspace-volume" # forces replacement
# (6 unchanged attributes hidden)
# (1 unchanged block hidden)
}
Use the ARN of the key: aws_kms_key.kms-ws-volume.arn
volume_encryption_key is storing the ARN of the key, and therefore the plan detects a change.
The example on https://registry.terraform.io/providers/hcavarsan/aws/latest/docs/resources/workspaces_workspace might be misleading in this regard, despite an alias will also work.
Something similar happens with kms_key_id of aws_instance, in that it stores the ARN and not the key_id , and the plan always requires a volume replacement when using key_id instead of ARN. https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#kms_key_id