Terraform- This value does not have any attributes - terraform

I Want to add the users to the team and assigned them roles as per the yaml
but the error is value does not have any attributes.
i will have multiple users in single team with different roles
this is
variables.tf
variable "admin_role_id" {
description = "The id to give access admin role to the user"
type = string
default = "1111111111111"
}
variable "user_role_id" {
description = "The id to give access user role to the user"
type = string
default = "22222222222222222"
}
this is my yaml file
TEAM1:
- users:
- joe#gmail.com
- pa#gmail.com
roles:
- ${user_role}
- ${admin_role}
- users:
- test#gmail.com
roles:
- ${user_role}
TEAM2:
- users:
- joe#gmail.com
roles:
- ${test_user_role}
This is TERRAFORM CODE
i am using local variable and i flatten the values there
locals {
render_membership = templatefile("${path.module}/teammembers.yaml",
{
admin_role = var.admin_role_id
user_role = var.user_role_id
}
)
membership_nested = yamldecode(local.render_membership)
membership_flat = flatten(
[
for team_key, team in local.membership_nested : [
for user in team.users : {
team_name = team_key
user_name = user
roles = team.roles
}
]
]
)
}
resource "squadcast_team_member" "membership" {
for_each = { for i, v in local.membership_flat : i => v }
team_id = data.squadcast_team.teams[each.value.team_name].id
user_id = data.squadcast_user.users[each.value.user_name].id
role_ids = each.value.roles
}
data "squadcast_team" "teams" {
for_each = { for i, v in local.membership_flat : i => v }
name = each.value.team_name
}
data "squadcast_user" "users" {
for_each = { for i, v in local.membership_flat : i => v }
email = each.value.user_name
}
output "rendered_yaml" {
value = local.membership_nested
}
Error:
│ Error: Unsupported attribute
│
│ on teammembers.tf line 112, in locals:
│ 112: for user in team.users : {
│
│ This value does not have any attributes.

Decoding the YAML document you showed would produce the following Terraform value:
{
TEAM1 = [
{
users = [
"joe#gmail.com",
"pa#gmail.com",
]
roles = [
"...",
"...",
]
},
]
TEAM2 = [
{
users = [
"joe#gmail.com",
]
roles = [
"...",
],
},
]
}
Notice that the values assigned to TEAM1 and TEAM2 are lists -- or more accurately: tuples -- that each contain one object.
That means that in your membership_flat expression the value of team in the first for expression is not an object, and so it's not valid to write team.users: the . operator for accessing an attribute is only valid for object types or map types.
If the structure of this YAML is fixed and you need to change the Terraform configuration to work with it then one way would be to access the first element of the tuple and then take the users attribute of that object:
for user in team[0].users : {
This solution will only work if your team values are always single-element sequences. If you had a team with more than one object assigned to it then the above expression would ignore the other elements, and if you had a team with zero objects assigned to it then it would fail because there would be no index zero.
If you are able to change the YAML structure instead, then I would suggest removing the YAML sequences so that each "team" is represented by only a single mapping, which Terraform will then decode as a single object:
TEAM1:
users:
- joe#gmail.com
- pa#gmail.com
roles:
- ${user_role}
- ${admin_role}
TEAM2:
users:
- joe#gmail.com
roles:
- ${test_user_role}
Terraform's yamldecode would decode the above to the following value instead:
{
TEAM1 = {
users = [
"joe#gmail.com",
"pa#gmail.com",
]
roles = [
"...",
"...",
]
}
TEAM2 = {
users = [
"joe#gmail.com",
]
roles = [
"...",
],
}
}
Notice that now each team is just a single object rather than a tuple of objects, and so team.users would now be a valid way to access the users attribute of each single object.

Related

how to get ONLY values form all KEYS members and form a list - Terraform

below is a teams.yaml file in the Terraform directory.
---
TEAMS:
- name: dx-example-team2
roles:
- GROUP_DATA_ACCESS_READ_WRITE:
members:
- ab#ad.com
- cd#ad.com
- GROUP_OWNER:
members:
- pr#ad.com
Hello all,
can someone help me with gettting only values from nested members KEY from above YAML file and how to form a list merge of all the values.
TRIED Terraform Code , but below is just sample i have:
locals {
teams_file = yamldecode(file("${path.cwd}/teams.yaml"))["TEAMS"]
all_members = flatten([for team in local.teams_file : [
for role in team.roles : {
"name" = role
}
]
])
}
output "sample" {
value = local.all_members
}
EXPECTED OUTPUT:
all_members = [ "ab#ad.com" , "cd#ad.com", "pr#ad.com"]
In your case you can just use spat expression:
all_members = flatten(local.teams_file[*].roles[*].members)

Terraform data block only if it is dev env

I am trying to get the list of users in an IAM group. The group only exists in dev account and not prod
# lookup for user accounts in Developers group only if its dev env
data "aws_iam_group" "developers" {
count = var.profile == "dev" ? 1 : 0
group_name = "Developers"
}
When I have the below
locals = {
mapdevelopers = [
for index, x in data.aws_iam_group.developers[count.index].users : {
username = x.user_name
userarn = x.arn
groups = ["system:masters"]
}
]
}
I am getting error
│ The "count" object can only be used in "module", "resource", and "data"
│ blocks, and only when the "count" argument is set.
╵
so, I tried my locals without count.index like
locals = {
mapdevelopers = [
for index, x in data.aws_iam_group.developers.users : {
username = x.user_name
userarn = x.arn
groups = ["system:masters"]
}
]
}
Now I am getting an error
│ Because data.aws_iam_group.developers has "count" set, its attributes must
│ be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ data.aws_iam_group.developers[count.index]
How can I obtain mapdevelopers local variable?
Since you are using count for aws_iam_group, this will transform this resource into an array of resources. If you want to iterate over it and access certain item, you would want to use the splat. Moreover, in your case you need to flatten the users property to get the correct values:
locals {
mapdevelopers = [
for index, x in flatten(data.aws_iam_group.developers[*].users) : {
username = x.user_name
userarn = x.arn
groups = ["system:masters"]
}
]
}
Output will be something like:
mapdevelopers = [
{
"groups" = [
"system:masters",
]
"userarn" = "arn:aws:iam::069700690111:user/random-user"
"username" = "random-username"
},
]
This will work even if count = var.profile == "dev" ? 1 : 0 evaluates to 0.
Just update this data.aws_iam_group.developers[count.index].users to this data.aws_iam_group.developers[0].users

Terraform Combine tuple elements as list

Can someone suggest how to combine terraform tuple with elements to list as shown below.
Scenario:
I have bunch of yaml files structure as below, I flatten them with local values and assign to the resource attributes.
On my resource i need to assign roles values as a list , example below
roles = local.acs_tup #["READER,OWNERSHIP","OWNERSHIP1","READER1,DEVELOPER1"]
yaml files content format
schemagrants :
- schema: BDV
privilege: USAGE
roles:
- READER
- OWNERSHIP
environment: DEV
- schema: BDV
privilege: USAGE
roles:
- OWNERSHIP1
- DEVELOPER1
environment: DEV
- schema: CDV
privilege: USAGE
roles:
- DEVELOPER2
environment: DEV
locals {
acs_files = fileset("./objects/schema-accessgrant", "*.yml")
acs_data = [for f in local.acs_files : yamldecode(file("./objects/schema-accessgrant/${f}"))]
acs_flatten = flatten([for access in local.acs_data :
[for sgr in access.schemagrants : {
id = "${sch.schema}.${sch.privilege}.${sch.environment}"
roles = sgr.roles
environment = sgr.environment
}
]]
)
acs_tup = [for roles in local.acs_flatten :
{
role = join(",", roles.roles)
}
]
}
resource "snowflake_database_grant" "database-access" {
database_name = "database"
privilege = "USAGE"
roles = local.acs_tup #["READER,OWNERSHIP","OWNERSHIP1","DEVELOPER1","DEVELOPER2"]
shares = []
with_grant_option = false
}
# Current Output
[ + {
+ role = "READER,OWNERSHIP"
},
+ {
+ role = "ONWERSHIP1,DEVELOPER1"
},
+ {
+ role = "DEVELOPER2"
},
]
# Expected Output
["READER,OWNERSHIP","OWNERSHIP1","DEVELOPER1","DEVELOPER2"]
Assuming that your expected output should be ["READER","OWNERSHIP","OWNERSHIP1","DEVELOPER1","DEVELOPER2"], your acs_tup must be:
acs_tup = flatten([for roles in local.acs_flatten: roles.roles])

Parse Internal array in list(object) data type in terraform 0.12

how can I parse this data type in terraform 0.12
variable "groups" {
type = list(object({
group_id = string
permissions = list(string)
}))
}
Example:
groups = [
{
group_id = "gcp-org-admin"
permissions = [ "roles/resourcemanager.organizationAdmin",
"roles/resourcemanager.folderViewer",
"roles/viewer",
"roles/iam.organizationRoleViewer",
"roles/orgpolicy.policyViewer"
]
},
{
group_id = "gcp-security-ops"
permissions = [ "roles/resourcemanager.folderViewer",
"roles/logging.viewer",
"roles/monitoring.editor",
"roles/iam.securityReviewer"
]
}]
for each of the groups, I would like to pair group_id and each permissions
that is like
{
group_id = "gcp-org-admin"
permissions = "roles/resourcemanager.organizationAdmin"
},
{
group_id = "gcp-org-admin"
permissions = "roles/resourcemanager.folderViewer"
},
{
group_id = "gcp-org-admin"
permissions = "roles/viewer"
}
Would like to create organization_iam_resource
for each of the permissions within each group_id, I have to create a resource.
Is there any way to do this
Take a look at this example as it shows you a possible answer and some problems associated with nested lists: https://github.com/hashicorp/terraform/issues/11036

Iterate over list of list of maps in terraform

Consider I have a variable that is a list of list of maps.
Example:
processes = [
[
{start_cmd: "a-server-start", attribute2:"type_a"},
{start_cmd: "a-worker-start", attribute2:"type_b"}
{start_cmd: "a--different-worker-start", attribute2:"type_c"}
],
[
{start_cmd: "b-server-start", attribute2:"type_a"},
{start_cmd: "b-worker-start", attribute2:"type_b"}
]
]
In each iteration, I need to take out the array of maps, then iterate over that array and take out the values of the map. How do I achieve this in terraform?
I have considered having two counts and doing some arithmetic to trick terraform into performing a lookalike nested iteration Check reference here. But in our case the number of maps in the inner array can vary.
Also we are currently using the 0.11 terraform version but dont mind using the alpha 0.12 version of terraform if it is possible to achieve this in that version.
Edit:
Added how I would use this variable:
resource “create_application” “applications” {
// Create a resource for every array in the variable processes. 2 in this case
name = ""
migration_command = ""
proc {
// For every map create this attribute for the resource.
name = ““
init_command = “a-server-start”
type = “server”
}
}
Not sure if this clears up the requirement. Please do ask if it is still not clear.
Using terraform 0.12.x
locals {
processes = [
[
{ start_cmd: "a-server-start", type: "type_a", name: "inglorious bastards" },
{ start_cmd: "a-worker-start", type: "type_b", name: "kill bill" },
{ start_cmd: "a--different-worker-start", type: "type_c", name: "pulp fiction" },
],
[
{ start_cmd: "b-server-start", type: "type_a", name: "inglorious bastards" },
{ start_cmd: "b-worker-start", type: "type_b", name: "kill bill" },
]
]
}
# just an example
data "archive_file" "applications" {
count = length(local.processes)
type = "zip"
output_path = "applications.zip"
dynamic "source" {
for_each = local.processes[count.index]
content {
content = source.value.type
filename = source.value.name
}
}
}
$ terraform apply
data.archive_file.applications[0]: Refreshing state...
data.archive_file.applications[1]: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
If a create_application resource existed, it can be modeled like so
resource "create_application" "applications" {
count = length(local.processes)
name = ""
migration_command = ""
dynamic "proc" {
for_each = local.processes[count.index]
content {
name = proc.value.name
init_command = proc.value.start_cmd
type = proc.value.type
}
}
}
Here is my solution that work like charm. Just note the tricks google_service_account.purpose[each.value["name"]].name where I can retrieve the named array element by using its name.
variable "my_envs" {
type = map(object({
name = string
bucket = string
}))
default = {
"dev" = {
name = "dev"
bucket = "my-bucket-fezfezfez"
}
"prod" = {
name = "prod"
bucket = "my-bucket-ezaeazeaz"
}
}
}
resource "google_service_account" "purpose" {
for_each = var.my_envs
display_name = "blablabla (terraform)"
project = each.value["name"]
account_id = "purpose-${each.value["name"]}"
}
resource "google_service_account_iam_binding" "purpose_workload_identity_binding" {
for_each = var.my_envs
service_account_id = google_service_account.purpose[each.value["name"]].name
role = "roles/iam.whatever"
members = [
"serviceAccount:${each.value["name"]}.svc.id.goog[purpose/purpose]",
]
}
resource "google_storage_bucket_iam_member" "purpose_artifacts" {
for_each = var.my_envs
bucket = each.value["bucket"]
role = "roles/storage.whatever"
member = "serviceAccount:${google_service_account.purpose[each.value["name"]].email}"
}

Resources