In my Terraform configuration, I want to obtain the names of all members of an Azure AD Group called "`VaultUsers`" and then read and display the result from my Terraform output file called `outputs.tf`.
I have two snippets of Terraform configuration that complete part of the above task, as described below.
[ Snippet 1 ]
This first code block will display the object ids of the group members. Unfortunately, the test.group. object does not include a property to display the group member names and so for now, I'm simply displaying the Azure AD object id of each group member by using the notation - data.azuread_group.test_group.members.
data "azuread_group" "test_group" {
display_name = "VaultUsers"
}
output "azuread_groups_root-users" {
value = data.azuread_group.test_group.members
}
The second snippet (below) on the other hand, will display a large chunk of Azure AD metadata for each group member, including the following:
+ mail_nicknames
+ object_ids
+ user_principal_names
[ Snippet 2 ]
data "azuread_users" "users" {
return_all = true
}
output "azuread_groups_root-user-members" {
value = data.azuread_users.users
}
Crucially, it also displays the below additional metadata in a users block and ultimately, it is the display_name value that I'm desperately seeking to extract. Any idea or suggestions on how I can achieve this?
Related
I need to get a list of all VM ids in an Azure subscription using Terraform then read them back one by one and feed one id at a time to a module to perform some tasks on it, how can i do this?
You can use the feature multiple instances of the data source to get the list of all the VM ids. See the data source azurerm_virtual_machine, it requires the name and the resource group name. So if the VMs in the same group, you just need to create a list variable for all the VM names, then the data block will be like this:
variable "vm_names" {
type = list(string)
default = [
...
]
}
data "azurerm_virtual_machine" "example" {
count = length(var.vm_names)
name = element(var.vm_names, count.index)
resource_group_name = var.resource_group_name
}
output "vm_ids" {
value = data.azurerm_virtual_machine.example.*.id
}
The output is all the VM ids.
I am trying to pass multiple values to pricipals's identifiers in the data resource "aws_iam_policy_document". getting the following error
Inappropriate value for attribute "identifiers": element 0: string required.
s3_values variable is defined type = any and set the values as
....
s3_values:
bucket: bucketname1
s3_arns:
- arn:aws:iam::1234567890:root
- arn:aws:iam::2345678901:role/s3-read-role
data "aws_iam_policy_document" "s3_policy" {
count = length(var.s3_arns)
statement {
sid = "1"
effect = "Allow"
principals {
type = "AWS"
identifiers = ["${var.s3_values[count.index]["s3_arns"]}"]
}
actions = ["s3:PutObject"]
resources = ["arn:aws:s3:::${var.s3_values[count.index]["bucket"]}/*"]
}
}
I get the following error
Inappropriate value for attribute "identifiers": element 0: string required.
its working , when only one value is passed , but not working when we pass multiple values to the variable s3_arns.
It looks like you're trying to create multiple policy documents for a single S3 bucket. Rather than using count to create many documents, it would be best if you created a single policy document that gives access to each ARN you pass.
Currently it works for one ARN because the identifiers field gets passed a single string and creates a list with one string element. When you pass a list of ARNs, the identifiers field is instead creating a list with a list element that contains the ARN strings.
I would fix this by making the s3_arns field always be a list of strings, and removing the count field on the data resource. Once you do that you can change the line identifiers to be identifiers = var.s3_values.s3_arns and the resources line to be resources = ["arn:aws:s3:::${var.s3_values.bucket}/*"]
I have a file users.tf file that creates the admin users for aws. It does so by defining a list (e.g. users = ["bob", "john", "tom"])
and then iterating over them with the aws_iam_user resource using the count functionality in terraform, like so:
resource "aws_iam_user" "user" {
count = length(local.users)
name = local.users[count.index]
}
the issue here, is that if I remove the first element of the array ("bob" from the above example), what terraform will suggest to do after issuing terraform plan, instead of deleting bob, is to change bob to john, change john to tom, and delete tom.
like so:
# aws_iam_user.user[0] will be updated in-place
~ resource "aws_iam_user" "user" {
arn = "arn:aws:iam::5555555:user/bob"
force_destroy = false
id = "bob"
~ name = "bob" -> "john"
path = "/"
tags = {}
unique_id = "BLABLABLA11111"
}
# aws_iam_user.user[1] will be updated in-place
~ resource "aws_iam_user" "user" {
arn = "arn:aws:iam::5555555:user/john"
force_destroy = false
id = "john"
~ name = "john" -> "tom"
path = "/"
tags = {}
unique_id = "BLABLABLA22222"
}
# aws_iam_user.user[2] will be destroyed
- resource "aws_iam_user" "user" {
- arn = "arn:aws:iam::5555555:user/tom" -> null
- force_destroy = false -> null
- id = "tom" -> null
- name = "tom" -> null
- path = "/" -> null
- tags = {} -> null
- unique_id = "BLABLABLA3333" -> null
this will result in john getting the arn of bob, and tom getting the arn of john. which is undesirable.
I tried using the very new feature (released 19 hours prior to the writing of this question) of for_each loop instead of count, and defining the keys as the original index numbers, hoping that terraform will consider them as the same resource.
yeah well, no such luck:
...
# aws_iam_user.user[1] will be destroyed
...
# aws_iam_user.user["1"] will be created
...
I will summarize my question:
Is there any way to delete a resource (specifically aws_iam_user), when that resource was created by iterating over a list, such that all the remaining resources stay the way they were?
What you have seen here is the situation that the count documentation warns about in its final paragraph:
Note that the separate resource instances created by count are still identified by their index, and not by the string values in the given list. This means that if an element is removed from the middle of the list, all of the indexed instances after it will see their subnet_id values change, which will cause more remote object changes than were probably intended. The practice of generating multiple instances from lists should be used sparingly, and with due care given to what will happen if the list is changed later.
Fortunately, this is the very problem the for_each feature is intended to solve. Though, in order to use it effectively it's important to choose meaningful unique keys in the map you pass to for_each:
resource "aws_iam_user" "user" {
for_each = { for name in local.users : name => name }
name = each.value
}
This will cause Terraform to track instance identifiers like aws_iam_user.user["john"] rather than aws_iam_user.user[1].
You have existing count-based instances in your state though, so it'll take some migration steps to get there. Unfortunately Terraform doesn't have enough information to automatically correlate your existing index-based addresses with the new name-based ones, but by using your existing list with a separate one-off script you can tell Terraform how to translate these by running a command like this for each entry in the list:
terraform state mv 'aws_iam_user.user[1]' 'aws_iam_user.user["john"]'
After that, Terraform will track these objects by name and thus adding and removing names will affect only the objects relating to the names you changed.
If you aren't ready to do a full switch to for_each right now, you can use a similar strategy with a one-off script to "heal" the hole created by removing an item from your list:
# First, Terraform must "forget" the user that you removed
terraform state rm 'aws_iam_user.user[0]'
# Then renumber the subsequent items to correlate with their new
# positions in the list.
terraform state mv 'aws_iam_user.user[1]' 'aws_iam_user.user[0]'
terraform state mv 'aws_iam_user.user[2]' 'aws_iam_user.user[1]'
# etc, etc
This will of course be a rather tedious, error-prone process to do manually if you have more than a small number of users, so better to write a small program to generate the script.
I am trying to get the security groups associated with the instance with id "i-0abcdefgh1234" but the output gives no result.
terraform.tf
data "aws_instance" "ec2" {
instance_id = "i-0abcdefgh1234"
filter {
name = "tag:Name"
values = ["name-of-the-server"]
}
}
output "sg" {
value = "${data.aws_instance.ec2.*.security_groups}"
}
Output
data.aws_instance.ec2: Refreshing state...
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
Tried with and without * below
value = "${data.aws_instance.ec2.*.security_groups}"
The interpolation syntax for data sources is data.TYPE.NAME.ATTRIBUTE. See this
In your case it will be ${data.aws_instance.ec2.security_groups}
However, as the documentation states - "Some values are not always set and may not be available for interpolation."
I am trying to manage my github organisation using terraform and wanted to implement a team structure.
I have defined the team structure in a map as below:
variable "teams" {
description = "Map of teams with members"
type = "map"
default = {
"TeamA" = ["abc", "xyz", "pqr", "mno"]
"TeamB" = ["abc", "xyz", "mno"]
"TeamC" = ["pqr"]
}
}
I am able to create these teams using following resource code:
resource "github_team" "sub-teams" {
count = "${length(keys(var.teams))}"
name = "${element(keys(var.teams), count.index)} Team"
description = "${element(keys(var.teams), count.index)} team"
privacy = "closed"
}
Now the ask is loop over keys of map and add corresponding team members to the respective teams. How should I achieve this requirement?
I referred this one, but looks like it has both the list constant as against of this said scenario.
Nested Maps are not yet supported by terraform.
You will need to use the variables inside the map rather using arrays. Below link will take you to the git issue page.
https://github.com/hashicorp/terraform/issues/2114