Dynamically add AzureAD users to AzureAD group in terraform - terraform

I recently started working with terraform and am currently working on adding people to groups for access management. I've got a bunch of user_principal_names, basically unique identifiers within AzureAD, of people that are already added to AzureAD. I now need to add these people to azuread_group objects. I've got it working by requesting each person's azuread_user object in the top of my main.tf file, and then adding them to groups. This isn't a really scalable solution however, and I was wondering if there is a more dynamic method, can I for example work with an array containing these user_principal_name objects? Or do I always first have individually get each azuread_user object?
data "azuread_user" "jack" {
user_principal_name = "JackDoe#Company.Com"
}
data "azuread_user" "john" {
user_principal_name = "JohnDoe#Company.Com"
}
resource "azuread_group" "admins" {
display_name = "admins"
owners = [
data.azuread_user.jack.object_id,
]
security_enabled = true
prevent_duplicate_names = true
members = [
data.azuread_user.john.object_id,
]
}
This is an example of how I've got it working right now.

main.tf:
data "azuread_users" "users" {
ignore_missing = false
user_principal_names = var.ad_users
}
resource "azuread_group" "ad_group" {
display_name = "New_AD_Group"
security_enabled = true
members = data.azuread_users.users.object_ids
}
terraform.tfvars.json:
{
"ad_users": [
"user_1#domain.com",
"user_2#domain.com"
]
}
variables.tf:
variable "ad_users" {
type = list(any)
}
Also, here could be more complex solution for variable construction like:
{
"assignments": {
"AD_GROUP_1": {
"users": [
"user_1#domain.com",
"user_2#domain.com"
]
},
"AD_GROUP_2": {
"users": [
"user_3#domain.com",
"user_4#domain.com"
]
}
}
}
For this way you have to use for_each

Related

Terraform Use Map for a for_each statement

So I have an issue, and need a bit of help. My knowledge of Terraform isn't all that great, and I need some advice on how to make this happen. I have a map variable with the following information in it:
In variables.tf:
variable "users" {
type = "map"
}
in the Terraform.tfvars:
users = {
"user1" = {
},
"user2" = {
instance = ["instance_size"]
},
"user3" = {
bucket = ["bucket1"]
},
"user4" = {
bucket = ["bucket1", "bucket2"]
},
"user5" = {
instance = ["instance_size"]
bucket = ["bucket1", "bucket2", "bucket3"]
}
And what I want to do is to take the bucket information out of that variable and apply a AWS policy kinda like so:
data "aws_iam_policy_document" "sm_s3_bucket" {
for_each = var.users
statement {
actions = [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket",
"s3:GetBucketLocation"
]
resources = formatlist("arn:aws:s3:::%s", each.value.bucket)
}
}
How do I go about getting only the bucket info for each user out of the variable and into the resource command? Thanks!
If the question is how to create a policy document for users who have bucket specified, it could be done for example like this:
for_each = {
for user, attr in var.users : user => attr if contains(keys(attr), "bucket")
}

Terraform access map

I am trying to access all groups and create groups in the below terraform code. But I am facing error This object does not have an attribute named "groups". Is there any logic I am missing here in the resource "og" "example"
for_each=toset(flatten(local.instances[*].groups)). Thanks
locals {
instances = {
test1 = {
baseUrl = "url1"
subDomain = "sd1"
groups = [
"app1",
"app2",
],
}
test2 = {
baseUrl = "url2"
subDomain = "sd2"
groups = [
"t1",
"t2",
],
}
}
}
resource "og" "example" {
for_each = toset(flatten(local.instances[*].groups))
name = each.value
description = "${each.value}-access"
}
Your local variable is a map, not a list. So it should be:
for_each = toset(flatten(values(local.instances)[*].groups))

How to get Terraform azuread_groups to accept UPN instead of object_id?

I am trying to use azuread_group to add users to the AAD group by using UPN instead of user object_ID. Below is what I have:
Group.json
[
{
"team_name": "Test Group1",
"members": [
"user1#mydomain.com",
"user2#mydomain.com"
]
},
{
"team_name": "Test Group2",
"members": [
"User3#mydomain.com",
"User4#mydomain.com"
]
}
]
My main.tf
locals {
team_name_list = jsondecode(file("${path.module}/group.json"))
}
resource "azuread_group" "provision-aad-group" {
count = length(local.team_name_list)
display_name = local.team_name_list[count.index].team_name
prevent_duplicate_names = true
members = var.aad_group_display_name[count.index].members
}
Unfortunately, the above TF code complain that the member string needs to be object_id. Any suggestion on how I can covert the upn into object_id dynamicatlly?
Thanks in advance.

Get resources based on a value created using count

I am using Terraform v12.19 with the aws provider v2.34.0.
Imagine, I have a resource generated with a count value:
resource "aws_iam_role" "role" {
count = length(var.somevariable)
name = var.somevariable[count.index]
}
Later on, I want to reference one specific resource instance in that way, e. g.:
resource "aws_iam_role_policy_attachment" "polatt" {
role = aws_iam_role.role["TheRoleNameIWant"].id
policy_arn = "arn:aws:iam::aws:policy/..."
}
I don't know the index, I can just rely on the name, provided by the variable. Thats because the values of the variable are provided by an external source and the order could change...
Any ideas how to do this?
You should be able to accomplish this using the index terraform function.
Here's a minimal example using null_resources to test it out
locals {
role_names = [
"role-a",
"role-b",
"role-c",
"role-d",
]
target_role_name = "role-c"
}
resource "null_resource" "hi" {
count = length(local.role_names)
}
output "target_resource" {
value = null_resource.hi[index(local.role_names, local.target_role_name)].id
}
output "all_resources" {
value = [for r in null_resource.hi : r.id]
}
This outputs, for example
all_resources = [
"4350570701002192774",
"9173388682753384584",
"1634695740603384613",
"2098863759573339880",
]
target_resource = 1634695740603384613
So your example, I suppose, would look like
resource "aws_iam_role_policy_attachment" "polatt" {
role = aws_iam_role.role[index(var.somevariable, "TheRoleNameIWant")].id
policy_arn = "arn:aws:iam::aws:policy/..."
}
Update
Your comment below mentions that you actually have a more complicated data structure than just a list of names. I just wanted to mention that you can derive names from your JSON structure.
Assuming you have something like the following
variable "role_values" {
value = [
{
name = "foo",
other = "details",
fields = 3
},
{
name = "bar",
other = "yet more details",
fields = 3
}
]
}
you could derive just the names by using a local and the newer for loops TF 0.12 offers
locals {
role_names = [for role in var.role_values: role.name]
}
That way you don't have to store the names twice.

Iterating through a list of resource values inside a list in a Terraform local

I am trying to get an array of arrays to use in a Terraform template_file data field:
data "template_file" "dashboard" {
template = "${file("${path.module}/files/dashboard.json")}"
vars {
metrics = "${jsonencode(local.metrics)}"
}
}
But I am not finding the proper way to get what I want. I have an aws_instance resource with a count of 3, and I am trying to generate 3 arrays inside a local based on each one of the resource counts. The only thing I've come up with so far is:
locals {
metrics = [
"collectd", "GenericJMX.gauge.50thPercentile", "Host", "${aws_instance.instance.*.id}", "PluginInstance", "cassandra_client_request-latency"
]
}
Obviously what this does, is put all the instances one after the other in the same array. What I am trying to achieve is a result array that would look like:
["collectd", "GenericJMX.gauge.50thPercentile", "Host", "the id of instance 0", PluginInstance", "cassandra_client_request-latency"],
["collectd", "GenericJMX.gauge.50thPercentile", "Host", "the id of instance 1", PluginInstance", "cassandra_client_request-latency"],
["collectd", "GenericJMX.gauge.50thPercentile", "Host", "the id of instance 3", PluginInstance", "cassandra_client_request-latency"]
And this would be expanded in the template ${metrics} variable.
Is there any way to achieve what I want, inside a local, and make it usable in the template?
terraform data source supports count as well.
It is a hide feature, and never be documented (https://github.com/hashicorp/terraform/pull/8635)
Do some adjustments on your dashboard.json, then use below codes to generate number of template_file data source resources.
data "template_file" "dashboard" {
count = "${length(aws_instance.instance.*.id)}"
template = "${file("${path.module}/files/dashboard.json")}"
vars {
metrics = "${element(aws_instance.instance.*.id, count.index)}"
}
}
You can reference it as terraform count resources
count = "${length(aws_instance.instance.*.id)}"
${data.template_file.dashboard.*.rendered[count.index]}"
Here are the full test data.
$ cat main.tf
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "instance" {
count = 2
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "t2.micro"
tags = {
Name = "HelloWorld"
}
}
data "template_file" "dashboard" {
count = "${length(aws_instance.instance.*.id)}"
template = "${file("${path.module}/files/dashboard.json")}"
vars {
metric = "${element(aws_instance.instance.*.id, count.index)}"
}
}
output "aws_instances" {
value = "${length(aws_instance.instance.*.id)}"
}
$ cat files/dashboard.json
["collectd", "GenericJMX.gauge.50thPercentile", "Host", "${metric}", PluginInstance", "cassandra_client_request-latency"]
After you apply the change, check the tfstate file, the data sources are
data.template_file.dashboard.0
data.template_file.dashboard.1
Sample tfstate:
"data.template_file.dashboard.1": {
"type": "template_file",
"depends_on": [
"aws_instance.instance.*"
],
"primary": {
"id": "8e05e7c115a8d482b9622a1eddf5ee1701b8cc4695da5ab9591899df5aeb703d",
"attributes": {
"id": "8e05e7c115a8d482b9622a1eddf5ee1701b8cc4695da5ab9591899df5aeb703d",
# the date is here ==> "rendered": "[\"collectd\", \"GenericJMX.gauge.50thPercentile\", \"Host\", \"i-015961b744ff55da4\", PluginInstance\", \"cassandra_client_request-latency\"]\n",
"template": "[\"collectd\", \"GenericJMX.gauge.50thPercentile\", \"Host\", \"${metric}\", PluginInstance\", \"cassandra_client_request-latency\"]\n",
"vars.%": "1",
"vars.metric": "i-015961b744ff55da4"
},
"meta": {},
"tainted": false
},
"deposed": [],
"provider": "provider.template"
}

Resources