terraformterraform-provider-azureazure-data-lake-gen2terraform-template-file

Update: How do i add additional group or user asigned managed identity and use users/groups names instead of UUIDs?


Main.tf

provider "azurerm" {
      features {}
    }
    locals {
      access_map = {
        owner_other_access = {
          permissions_access = "---"
          type               = "other"
        },
        owner_group_access = {
          permissions_access = "r-x"
          type               = "group"
        },
        owner_mask_access = {
          permissions_access = "rwx"
          type               = "mask"
        },
        owner_user_access = {
          permissions_access = "rwx"
          type               = "user"
        }
      }
    
      default_map = {
        owner_other_default = {
          permissions_default = "---"
          type                = "other"
        },
        owner_group_default = {
          permissions_default = "rwx"
          type                = "group"
        },
        owner_mask_default = {
          permissions_default = "rwx"
          type                = "mask"
        },
        owner_user_default = {
          permissions_default = "rwx"
          type                = "user"
        }
      }
    }
    
    resource "azurerm_storage_data_lake_gen2_filesystem" "this" {
      for_each = var.storage_containers
    
      name               = each.value.sc_name
      storage_account_id = each.value.storage_account_id
    
      properties = {}
    
      dynamic "ace" {
        for_each = merge(local.access_map, jsondecode(each.value.acl_access))
        iterator = item
    
        content {
          type        = item.value.type
          scope       = "access"
          permissions = item.value.permissions_access
          id          = lookup(item.value, "id", null)
        }
      }
    
      dynamic "ace" {
        for_each = merge(local.default_map, jsondecode(each.value.acl_default))
        iterator = item
    
        content {
          type        = item.value.type
          scope       = "default"
          permissions = item.value.permissions_default
          id          = lookup(item.value, "id", null)
        }
      }
    }

teraform.tfvars

storage_containers_fs = {
  raw_fs_container = {
    storage_account_id = "bronze_storage_account"
    sc_name            = "rawfs"
    acl_access         = "{\"owner_group_access\":{\"permissions_access\":\"r-x\",\"type\":\"group\",\"id\":\"3f97daxxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\",\"id\":\"f5509xxxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\"}}"
    acl_default        = "{\"owner_group_default\":{\"permissions_default\":\"rwx\",\"type\":\"group\",\"id\":\"3f97daxxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\",\"id\":\"f5509xxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\"}}"
  }
}

varaibles.tf

variable "storage_containers_fs" {
  description = "Storage Containers settings"
  type = map(object({
    storage_account_id = string
    sc_name            = string
    acl_access         = string
    acl_default        = string
  }))
}

My problem with this code is: When deploying for the very first time, this code works and creates a private container with ACL inside, assigning the correct rights to the two identities, in this case two groups, as seen above

When deploying this code again with a new group added to thte existing ones, the code fails by trying to replace the first group in the list with the new one added.

The new tfvars will look like this:

storage_containers_fs = {
  raw_fs_container = {
    storage_account_id = "bronze_storage_account"
    sc_name            = "rawfs"
    acl_access         = "{\"owner_group_access\":{\"permissions_access\":\"r-x\",\"type\":\"group\",\"id\":\"3f97daxxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\",\"id\":\"f5509xxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\",\"id\":\"deb25xxxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\"}}"
    acl_default        = "{\"owner_group_default\":{\"permissions_default\":\"rwx\",\"type\":\"group\",\"id\":\"3f97daxxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\",\"id\":\"f5509xxxx-xxxxxxx-xxxxx-xxxxxx-xxxxxxxxx\"}}"
  }
}

This is the output from apply:

Terraform will perform the following actions:

  # module.az_bronze_storage_containers_fs.azurerm_storage_data_lake_gen2_filesystem.this["raw_fs_container"] will be updated in-place
  ~ resource "azurerm_storage_data_lake_gen2_filesystem" "this" {
        id                 = "https://xxxxxxxxxxxxxxxxx.dfs.core.windows.net/rawfs"
        name               = "rawfs"
        # (4 unchanged attributes hidden)

      - ace {
          - permissions = "---" -> null
          - scope       = "access" -> null
          - type        = "other" -> null
        }
      - ace {
          - permissions = "---" -> null
          - scope       = "default" -> null
          - type        = "other" -> null
        }
      - ace {
          - permissions = "rwx" -> null
          - scope       = "access" -> null
          - type        = "mask" -> null
        }
      - ace {
          - permissions = "rwx" -> null
          - scope       = "access" -> null
          - type        = "user" -> null
        }
      - ace {
          - permissions = "rwx" -> null
          - scope       = "default" -> null
          - type        = "mask" -> null
        }
      - ace {
          - permissions = "rwx" -> null
          - scope       = "default" -> null
          - type        = "user" -> null
        }
      - ace {
          - id          = "3f97xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx" -> null
          - permissions = "r-x" -> null
          - scope       = "access" -> null
          - type        = "group" -> null
        }
      + ace {
          + id          = "deb25xxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
          + permissions = "r-x"
          + scope       = "access"
          + type        = "group"
        }
      + ace {
          + permissions = "---"
          + scope       = "access"
          + type        = "other"
        }
      + ace {
          + permissions = "---"
          + scope       = "default"
          + type        = "other"
        }
      + ace {
          + permissions = "rwx"
          + scope       = "access"
          + type        = "mask"
        }
      + ace {
          + permissions = "rwx"
          + scope       = "access"
          + type        = "user"
        }
      + ace {
          + permissions = "rwx"
          + scope       = "default"
          + type        = "mask"
        }
      + ace {
          + permissions = "rwx"
          + scope       = "default"
          + type        = "user"
        }

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
module.az_bronze_storage_containers_fs.azurerm_storage_data_lake_gen2_filesystem.this["raw_fs_container"]: Modifying... [id=https://xxxxxxxxxxxxxxxxx.dfs.core.windows.net/rawfs]
╷
│ Error: setting access control for root path in File System "rawfs" in Storage Account "xxxxxxxxxxxxxxxxx": datalakestore.Client#SetAccessControl: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationPermissionMismatch" Message="This request is not authorized to perform this operation using this permission.\nRequestId:e59de7e1-b01f-0066-41cd-3f5884000000\nTime:2024-01-05T11:48:36.0364932Z"
│
│   with module.az_bronze_storage_containers_fs.azurerm_storage_data_lake_gen2_filesystem.this["raw_fs_container"],
│   on ..\templates\az-storage-filesystem\main.tf line 42, in resource "azurerm_storage_data_lake_gen2_filesystem" "this":
│   42: resource "azurerm_storage_data_lake_gen2_filesystem" "this" {

Not sure why this doesn't work.

Ideally is to add a new group without removing the existing one (first problem) AND can we make this template use group names instead of UUIDs in the tfvars (second problem)? Maybe using data.azuread_users, data.azuread_groups, data.azuread_serviceprincipals? Hardcoding IDs in the tfvars doesn't seem like a good idea. I would personally add the group name instead of the ID and let the template resolve the ID for me.

I was expecting a new identity to be added instead of replacing an existing one.


Solution

  • Ideally is to add a new group without removing the existing one (first problem).

    To modify the existing container , you may need Storage Blob Data Owner role. Follow the Ms doc for more details

    enter image description here

    can we make this template use group names instead of UUIDs in thetfvars (second problem)? Maybe using data.azuread_users, data.azuread_groups, data.azuread_serviceprincipals? Hardcoding IDs in the tfvars doesn't seem like a good idea.

    If you create containers using .tfvars, it accepts an ID, not a name. However, you can use the code below to create a container without hardcoding the group ID by using a data block

    terraform.tfvars

    storage_containers = {
      container1 = {
        storage_account_id = ""
        sc_name            = "container1"
        acl_access         = "{\"owner_user_access\":{\"permissions_access\":\"rwx\",\"type\":\"user\"},\"owner_group_access\":{\"permissions_access\":\"r-x\",\"type\":\"group\",\"id\":\"data.azuread_group.example.id\"},\"owner_other_access\":{\"permissions_access\":\"---\",\"type\":\"other\"},\"owner_mask_access\":{\"permissions_access\":\"rwx\",\"type\":\"mask\"}}"
        acl_default        = "{\"owner_user_default\":{\"permissions_default\":\"rwx\",\"type\":\"user\"},\"owner_group_default\":{\"permissions_default\":\"rwx\",\"type\":\"group\",\"id\":\"data.azuread_group.example.id\"},\"owner_other_default\":{\"permissions_default\":\"---\",\"type\":\"other\"},\"owner_mask_default\":{\"permissions_default\":\"rwx\",\"type\":\"mask\"}}"
      },
      container2 = {
        storage_account_id = ""
        sc_name            = "container2"
        acl_access         = "{\"owner_user_access\":{\"permissions_access\":\"rwx\",\"type\":\"user\"},\"owner_group_access\":{\"permissions_access\":\"r-x\",\"type\":\"group\",\"id\":\"Group-ID\"},\"owner_other_access\":{\"permissions_access\":\"---\",\"type\":\"other\"},\"owner_mask_access\":{\"permissions_access\":\"rwx\",\"type\":\"mask\"}}"
        acl_default        = "{\"owner_user_default\":{\"permissions_default\":\"rwx\",\"type\":\"user\"},\"owner_group_default\":{\"permissions_default\":\"rwx\",\"type\":\"group\",\"id\":\"Group-ID\"},\"owner_other_default\":{\"permissions_default\":\"---\",\"type\":\"other\"},\"owner_mask_default\":{\"permissions_default\":\"rwx\",\"type\":\"mask\"}}"
      }
    }
    

    Here is the updated code to create an containers without .tfvars file.

        provider "azurerm" {
          features {}
        }
        
        locals {
          access_map = {
            container1 = {
              permissions_access = "rwx"
              type               = "group"
            },
            container2 = {
              permissions_access = "rwx"
              type               = "user"
            }
          }
        
          default_map = {
            group = {
              permissions_default = "rwx"
              type                = "group"
            },
            user = {
              permissions_default = "rwx"
              type                = "user"
            },
          }
        }
        
        data "azuread_group" "example" {
          display_name = "Azure-AD-Group-Name"
        }
        
        data "azurerm_storage_account" "example" {
          name                = "storage-account-name"
          resource_group_name = "RG-name"
        }
        
        resource "azurerm_storage_data_lake_gen2_filesystem" "example" {
          for_each = local.access_map
        
          name               = each.key
          storage_account_id = data.azurerm_storage_account.example.id
        
          dynamic "ace" {
            for_each = [data.azuread_group.example.id]
        
            content {
              id          = ace.value
              permissions = local.access_map[each.key].permissions_access
              scope       = "access"
              type        = local.access_map[each.key].type
            }
          }
        
          dynamic "ace" {
            for_each = local.default_map
        
            content {
              type        = ace.key
              scope       = "default"
              permissions = ace.value.permissions_default
              id          = data.azuread_group.example.id
            }
          }
        }
    

    Output:

    enter image description here