Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_storage_data_lake_gen2_(filesystem,path) default acl mask clashing with tf state #11435

Closed
matkins-uk opened this issue Apr 22, 2021 · 4 comments · Fixed by #18494
Closed

Comments

@matkins-uk
Copy link

matkins-uk commented Apr 22, 2021

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Terraform (and AzureRM Provider) Version

Terraform v0.15.0
on windows_amd64
provider registry.terraform.io/hashicorp/azurerm v2.56.0

Affected Resource(s)

azurerm_storage_data_lake_gen2_filesystem
azurerm_storage_data_lake_gen2_path

Terraform Configuration Files

main .tf


terraform {

  required_version = "=0.15.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "2.56.0"
    }
  }
}

provider "azurerm" {
  features {}
}

data "azurerm_client_config" "current_user" {}

resource "azurerm_storage_account" "storage_account" {

  resource_group_name       = var.resource_group_name
  location                  = var.region
  
  name                      = var.storage_account_name
  account_tier              = "Standard"
  account_replication_type  = "LRS"
  account_kind              = "StorageV2"
  is_hns_enabled            = "true"
  
}

resource "azurerm_storage_data_lake_gen2_filesystem" "filesystem" {

  storage_account_id = azurerm_storage_account.storage_account.id

  name = "test"
  
 # default mask acls
  ace { 
    permissions = "---"
    scope       = "access"
    type        = "other"
  }
  ace {
      permissions = "r-x"
      scope       = "access"
      type        = "group"
  }
  ace {
      permissions = "rwx"
      scope       = "access"
      type        = "user"
  }
 # custom acls
  ace {
    scope       = "default"
    type        = "user"
    id          = data.azurerm_client_config.current_user.object_id
    permissions = "rwx"
  }
  
}

resource "azurerm_storage_data_lake_gen2_path" "path" {

  storage_account_id = azurerm_storage_account.storage_account.id
  filesystem_name    = azurerm_storage_data_lake_gen2_filesystem.filesystem.name
  path      = "test"
  resource  = "directory"
  
 # default mask acls
  ace { 
    permissions = "---"
    scope       = "access"
    type        = "other"
  }
  ace {
      permissions = "r-x"
      scope       = "access"
      type        = "group"
  }
  ace {
      permissions = "rwx"
      scope       = "access"
      type        = "user"
  }
 # custom acls
  ace {
    scope       = "default"
    type        = "user"
    id          = data.azurerm_client_config.current_user.object_id
    permissions = "rwx"
  }
  
}

variables.tf

variable "resource_group_name" {
  default = "" # your rg name
}

variable "region" {
  default = "" # your prefrred region
}

variable "storage_account_name" {
  default = "" # your sa name
}

Expected Behaviour

Once storage account is created, the ACLs declared above should match with the default mask so rerunning "terraform apply" should return:

No changes. Infrastructure is up-to-date.

Actual Behaviour

Once the; storage account, filesystem and path are created then rerunning terraform apply detects a state change between auto applied the Azure default mask ACLs and declared ace blocks despite them matching correctly:

Terraform will perform the following actions:

  # azurerm_storage_data_lake_gen2_filesystem.filesystem will be updated in-place
  ~ resource "azurerm_storage_data_lake_gen2_filesystem" "filesystem" {
        id                 = "https://*****.dfs.core.windows.net/test"
        name               = "test"
        # (2 unchanged attributes hidden)

      - ace {
          - permissions = "---" -> null
          - scope       = "access" -> null
          - type        = "other" -> null
        }
      - ace {
          - permissions = "---" -> null
          - scope       = "default" -> null
          - type        = "other" -> null
        }
      - ace {
          - permissions = "r-x" -> null
          - scope       = "access" -> null
          - type        = "group" -> null
        }
      - ace {
          - permissions = "r-x" -> null
          - scope       = "default" -> null
          - type        = "group" -> 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 {
          + permissions = "---"
          + scope       = "access"
          + type        = "other"
        }
      + ace {
          + permissions = "r-x"
          + scope       = "access"
          + type        = "group"
        }
      + ace {
          + permissions = "rwx"
          + scope       = "access"
          + type        = "user"
        }
        # (1 unchanged block hidden)
    }

  # azurerm_storage_data_lake_gen2_path.path will be updated in-place
  ~ resource "azurerm_storage_data_lake_gen2_path" "path" {
        id                 = "https://*****.dfs.core.windows.net/test/test"
        # (6 unchanged attributes hidden)

      - ace {
          - permissions = "---" -> null
          - scope       = "access" -> null
          - type        = "other" -> null
        }
      - ace {
          - permissions = "---" -> null
          - scope       = "default" -> null
          - type        = "other" -> null
        }
      - ace {
          - permissions = "r-x" -> null
          - scope       = "access" -> null
          - type        = "group" -> null
        }
      - ace {
          - permissions = "r-x" -> null
          - scope       = "default" -> null
          - type        = "group" -> 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 {
          + permissions = "---"
          + scope       = "access"
          + type        = "other"
        }
      + ace {
          + permissions = "r-x"
          + scope       = "access"
          + type        = "group"
        }
      + ace {
          + permissions = "rwx"
          + scope       = "access"
          + type        = "user"
        }
        # (1 unchanged block hidden)
    }

Plan: 0 to add, 2 to change, 0 to destroy.

Therefor every time you run terraform apply after creating the initial resources, Terraform will attempt to update and change the resource ACls even though no changes are required leading to a permanent out of sync state and slowing down deployments to make invalid changes.

Steps to Reproduce

  1. update variables.tf
  2. terraform init
  3. terraform apply
  4. wait for resources to be created
  5. terraform apply

Important Factoids

For a new Data Lake Storage Gen2 container, the mask for the access ACL of the root directory ("/") defaults to 750 for directories and 640 for files (see Azure docs link below). These ACLs clash with the state of the ace blocks which are declared in Terraform leading to a permanent out of sync state.

References

@matkins-uk matkins-uk changed the title azurerm_storage_data_lake_gen2(filesystem,path) default acl mask not clashing with tf state azurerm_storage_data_lake_gen2_(filesystem,path) default acl mask clashing with tf state Apr 22, 2021
@favoretti
Copy link
Collaborator

// cc @stuartleeks One more example of #10814

@matkins-uk
Copy link
Author

@favoretti thanks for linking, sorry I didn't realise this was a duplicate.

So I did some further investigation on my test case above, and have managed to semi resolve my issue.

After the resources are created....
Run az storage fs access show --file-system "test" --path "/test" --account-name "$storage_account_name" --auth-mode=login
Returns :

{
  "acl": "user::rwx,group::r-x,other::---,default:user::rwx,default:user:********-****-47e2-be64-4cde4fd4ce9c:rwx,default:group::r-x,default:mask::rwx,default:other::---",
  "group": "********-****-47e2-be64-4cde4fd4ce9c",
  "owner": "********-****-47e2-be64-4cde4fd4ce9c",
  "permissions": "rwxr-x---+"
}

Breaking down the acl:

user::rwx,
group::r-x,
other::---,
default:user::rwx,
default:user:********-****-****-be64-4cde4fd4ce9c:rwx,
default:group::r-x,
default:mask::rwx,
default:other::---

Therefore if you declare your ace blocks for the resource as such:

  # default mask acls
  ace {
    scope       = null
    type        = "user"
    id          = null
    permissions = "rwx"
  }
  ace {
    scope       = null
    type        = "group"
    id          = null
    permissions = "r-x"
  }
  ace {
    scope       = null
    type        = "other"
    id          = null
    permissions = "---"
  }
  ace {
    scope       = "default"
    type        = "user"
    id          = null
    permissions = "rwx"
  }
  ace {
    scope       = "default"
    type        = "group"
    id          = null
    permissions = "r-x"
  }
  ace {
    scope       = "default"
    type        = "mask"
    id          = null
    permissions = "rwx"
  }
  ace {
    scope       = "default"
    type        = "other"
    id          = null
    permissions = "---"
  }
  # custom acls
  ace {
    scope       = "default"
    type        = "user"
    id          = data.azurerm_client_config.current_user.object_id
    permissions = "rwx"
  }

Then running terraform apply correctly returns No changes. Infrastructure is up-to-date.

So it would appear the ordering of the ace blocks is not an issue, as reordering the ace blocks does not cause a state mismatch. However, all of the default ACLs must be explicitly declared in your module to avoid a state mismatch. Whilst this works around the issue it makes the resource declaration extremely verbose.

@github-actions
Copy link

This functionality has been released in v3.39.0 of the Terraform Provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.

For further feature requests or bug reports with this functionality, please create a new GitHub issue following the template. Thank you!

@github-actions
Copy link

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.