terraform-provider-snowflake icon indicating copy to clipboard operation
terraform-provider-snowflake copied to clipboard

REVOKE runs before DROP ROLE when role is deleted

Open dlouseiro opened this issue 3 years ago • 8 comments

Provider Version

0.25.32

Terraform Version

v1.1.5

Describe the bug

When a snowflake_role resource and all grants associated with it are removed, terraform fails with SQL compilation error: Role '<role_name>' does not exist or not authorized.

This happens because the DROP ROLE command runs before the REVOKE command.

Expected behavior

Either the REVOKE command should run before the DROP ROLE or the REVOKE should be ignored if the role was already dropped.

Code samples and commands

Example of the initial state (already applied in the database):

resource "snowflake_role" "x" {
  name    = "X"
}

resource "snowflake_role" "y" {
  name    = "Y"
}

resource "snowflake_role" "z" {
  name    = "Z"
}

resource "snowflake_role_grants" "x" {
  role_name = snowflake_role.x.name

  roles = [
    snowflake_role.y.name,
    snowflake_role.z.name
  ]
}

Example of changes made (y role and all its dependent grants deleted):

resource "snowflake_role" "x" {
  name    = "X"
}

resource "snowflake_role" "z" {
  name    = "Z"
}

resource "snowflake_role_grants" "x" {
  role_name = snowflake_role.x.name

  roles = [
    snowflake_role.z.name
  ]
}

Code executed in the database (in this order):

DROP ROLE y;
REVOKE ROLE x FROM ROLE y;

dlouseiro avatar Feb 11 '22 11:02 dlouseiro

Any updates on this one? I have the same issue.

ricardobf avatar Apr 06 '22 11:04 ricardobf

Have you tried adding a depends_on argument to role_grants to make it depend on the roles?

danu165 avatar Apr 06 '22 17:04 danu165

I did, still doesn't worked.

ricardobf avatar Apr 06 '22 17:04 ricardobf

Also having this same issue. REVOKE is being called after DROP

bfalk-phr avatar Jul 27 '22 15:07 bfalk-phr

Same issue but on a case of a role name change, here is a print of snowflake queries.

I will say pattern error is same, revoke is after drop or rename.

Capture d’écran, le 2022-08-02 à 21 57 33

remymaillot avatar Aug 03 '22 02:08 remymaillot

Any update on when this will be fixed?

hanss0n avatar Aug 25 '22 08:08 hanss0n

I was having this issue when I renamed roles, the way I solved the issue was to include a lifecycle block in all my role_grant blocks to force resource re-creation upon change in roles. For more on lifecycles see https://www.terraform.io/language/meta-arguments/lifecycle.

This will not work in OPs case where he actually deletes the entire terraform resource definition but the following will work if you rename roles.

resource "snowflake_role" "y" {
  name    = "Y"
}

resource "snowflake_role" "x" {
  name    = "X"
}
resource "snowflake_role_grants" "x" {
  role_name = snowflake_role.x.name

  roles = [
    snowflake_role.y.name,
  ]
  depends_on = [
    snowflake_role.x,
    snowflake_role.y,
  ]
  lifecycle {
    replace_triggered_by = [
      snowflake_role.x,
      snowflake_role.y,
    ]
  }

}

I am not 100% sure the depends_on is needed for the lifecycle to work haven't tested it, but I create most of my roles using for_each for a large number of roles so I always have it anyways.

Kaniel12 avatar Sep 06 '22 14:09 Kaniel12

This issue makes running the snowflake provider in CI difficult. User is forced to either run these types of plans twice, first with failure but state syncing to desired state, then again so you can report a successful deploy.

Alternatively, you could make the change to remove the role in 2 phases. First apply removal of all the grants, then apply a removal the role, but that seems pretty cludgy to me.

The provider should be aware of the dependency between these resources and plan accordingly, not require the user to jump through hoops to get successful applies.

rjoelnorgren avatar Sep 26 '22 21:09 rjoelnorgren

Alternatively check that the user or role exists before attempting to revoke the permission seeing as the grant can't possibly exist if the user/role doesn't.

matt-tyler avatar Jan 03 '23 05:01 matt-tyler

Same issue is mentioned in https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1204.

I think this probably affects most grants.

The grant resources represent some object x being granted to another object y.

There was a PR to fix revokes on resource changes when granting role x to user y, where y has been deleted (https://github.com/Snowflake-Labs/terraform-provider-snowflake/pull/1142).

This issue is now referring to role x to role y.

Compare

https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/0dff06583e4dd25d44508c997521774adc9475a2/pkg/resources/role_grants.go#L219

with

https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/0dff06583e4dd25d44508c997521774adc9475a2/pkg/resources/role_grants.go#L225

I'm unsure as to whether simply checking for an error and swallowing it is the right way to go - but it probably is.

Grant creation obviously has to fail if either sides of the grant relationship do not exist. On update or delete, if this resolves in needing to perform a revoke, and the revoke fails from either side not existing, that should be fine - as non-existence means that the revoke has already happened anyway.

As a revoke always happens as a result of removing a grant, it probably makes sense to ignore failed revokes where either side does not exist. However, the error that is returned does mention potential auth failure, so there would need to be some way of distinguishing between something not existing and simply an auth failure. I presume this is why in the 'user' issue above, why a list call is made. This might make it difficult to handle non-existence generically.

Of note, there are some generic handlers that seem to have been created for handling revokes, of which certain grant types are (database_grant for example https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/pkg/resources/database_grant.go) but these are not being used for role_grant, despite an implementation existing for role_grant. I assume there was a refactoring effort at some point that did not complete (role_grant predates database_grant).

@sfc-gh-swinkler you seem to be most active on this repository at the moment - do you have any thoughts?

matt-tyler avatar Jan 09 '23 22:01 matt-tyler

Fixing this bug would take away a lot of pain points where we have to fiddle with terraform state to get around this error. As mentioned above it might be easy enough to check if the role exists, if it doesn't then revoking the grant means nothing and can be ignored

zmaleki avatar Jan 10 '23 01:01 zmaleki

@matt-tyler a simple fix would be to check if the role exists before attempting to revoke a role that has already been dropped.

If you really want this done then please file a support ticket with your Snowflake account representative. When you file a support ticket it gets priority above other backlog issues. It also helps us to make the case to allocate more engineering resources toward supporting the provider in the future.

sfc-gh-swinkler avatar Jan 10 '23 18:01 sfc-gh-swinkler

Thanks @sfc-gh-swinkler. I created a Snowflake support case for this purpose!

dlouseiro avatar Jan 11 '23 10:01 dlouseiro

I am having the same problem. For a revoke, ultimately the logic should be that if the role no longer exists then the revoke is already complete so no error should be raised.

In general, the problem I see most in this provider relates to the behavior when a resource is changed or destroyed.

DustinMoriarty avatar Feb 24 '23 17:02 DustinMoriarty

I have reported this in a snowflake support case and it has become Jira task SNOW-750421.

DustinMoriarty avatar Mar 03 '23 18:03 DustinMoriarty

Also, note that the fix should include all grant resources since they all probably follow a similar pattern.

DustinMoriarty avatar Mar 03 '23 18:03 DustinMoriarty

Here is an example in which a role name is changed.

terraform {
  required_version = ">=1.3.4,<1.4"
  required_providers {
    snowflake = {
      source  = "Snowflake-Labs/snowflake"
      version = ">=0.56.5,<0.57"
    }
  }
}


provider "snowflake" {
  role    = "SECURITYADMIN"
}

resource "snowflake_role" "parent" {
  name = "FOO_PARENT"
}

resource "snowflake_role" "child" {
  name = "FOO_CHILD"
}

resource "snowflake_role_grants" "child_to_parent" {
  role_name = snowflake_role.parent.name
  roles = [snowflake_role.child.name]
}

Logs after changing the name of the parent resource.

% terraform apply
snowflake_role.child: Refreshing state... [id=FOO_CHILD]
snowflake_role.parent: Refreshing state... [id=FOO_PARENT]
snowflake_role_grants.child_to_parent: Refreshing state... [id=FOO_PARENT❄️FOO_CHILD❄️]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # snowflake_role.parent will be updated in-place
  ~ resource "snowflake_role" "parent" {
        id   = "FOO_PARENT"
      ~ name = "FOO_PARENT" -> "FOO_PARENT_2"
    }

  # snowflake_role_grants.child_to_parent will be updated in-place
  ~ resource "snowflake_role_grants" "child_to_parent" {
        id                     = "FOO_PARENT❄️FOO_CHILD❄️"
      ~ role_name              = "FOO_PARENT" -> "FOO_PARENT_2"
        # (3 unchanged attributes hidden)
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

snowflake_role.parent: Modifying... [id=FOO_PARENT]
snowflake_role.parent: Modifications complete after 3s [id=FOO_PARENT_2]
snowflake_role_grants.child_to_parent: Modifying... [id=FOO_PARENT❄️FOO_CHILD❄️]
╷
│ Error: 002003 (02000): SQL compilation error:
│ Role 'FOO_PARENT' does not exist or not authorized.
│
│   with snowflake_role_grants.child_to_parent,
│   on [main.tf](http://main.tf/) line 25, in resource "snowflake_role_grants" "child_to_parent":
│   25: resource "snowflake_role_grants" "child_to_parent" {
│
╵

DustinMoriarty avatar Mar 03 '23 18:03 DustinMoriarty

I believe this has been fixed as of v0.58.0. See here: https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/9087220af85f08880f7ad453cbe9d13dd3bc11ec/pkg/resources/role_grants.go#L220

KPGR avatar Mar 16 '23 15:03 KPGR

@KPGR and @dlouseiro: I have tested this with snowflake provider 0.59.0 and terraform 1.4.2 for both the use case of changing the name of the parent role and terraform destroy and the bug is no longer present.

Here is the configuration I used for testing.

terraform {
  required_version = ">=1.4.2,<1.5"
  required_providers {
    snowflake = {
      source  = "Snowflake-Labs/snowflake"
      version = ">=0.59.0,<0.60"
    }
  }
}


provider "snowflake" {
  role    = "SECURITYADMIN"
}

resource "snowflake_role" "parent" {
  name = "FOO_PARENT"
}

resource "snowflake_role" "child" {
  name = "FOO_CHILD"
}

resource "snowflake_role_grants" "child_to_parent" {
  role_name = snowflake_role.parent.name
  roles = [snowflake_role.child.name]
}

DustinMoriarty avatar Mar 27 '23 14:03 DustinMoriarty

@DustinMoriarty thank you for verifying that this bug is no longer found. I am going to go ahead and close this issue then. If it is still a problem, please feel free to reopen the ticket / create a new ticket.

sfc-gh-swinkler avatar Mar 28 '23 23:03 sfc-gh-swinkler