由于更新的架构,如何更新 google_bigquery_table_iam_member 以指向新的 table

How to update google_bigquery_table_iam_member to point to new table on recreation due to an updated schema

我有一个 BigQuery table 并且将一个服务帐户作为 iam 成员添加到此 table:

resource "google_bigquery_table" "table" {
  dataset_id          = dataset
  table_id            = table
  project             = project
  schema              = "jsonSchema.json"
}

resource "google_bigquery_table_iam_member" "access_right" {
  project    = google_bigquery_table.table.project
  dataset_id = google_bigquery_table.table.dataset_id
  table_id   = google_bigquery_table.table.table_id
  role       = "roles/bigquery.dataEditor"
  member     = "serviceAccount:serviceAccount@GCPserviceAccount.com"
}

jsonSchema.json 中删除一列并应用更改会强制销毁 table 并创建一个新列:

Terraform will perform the following actions:

  # module.module.google_bigquery_table.table must be replaced
-/+ resource "google_bigquery_table" "table" {
      ...
      ~ schema              = jsonencode(
          ~ [ # forces replacement
                # (8 unchanged elements hidden)
                {
                    mode = "REQUIRED"
                    name = "column1"
                    type = "TIMESTAMP"
                },
              - {
                  - mode = "REQUIRED"
                  - name = "column2"
                  - type = "STRING"
                },
            ]
        )
      ...

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

此时创建的google_bigquery_table_iam_member资源仍然指向状态中的旧table。但是,在 GCP 中,服务帐户不再具有对不存在的 table 的访问权限,并且没有向新创建的 table.

授予新的访问权限

运行 terraform apply 第二次它注意到缺少访问权限

Terraform will perform the following actions:

  # module.module.google_bigquery_table_iam_member.access_rights will be created
  + resource "google_bigquery_table_iam_member" "access_rights" {
      + dataset_id = "dataset"
      + etag       = (known after apply)
      + id         = (known after apply)
      + member     = "serviceAccount:serviceAccount@GCPserviceAccount.com"
      + project    = "project"
      + role       = "roles/bigquery.dataEditor"
      + table_id   = "table"
    }

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

是否可以一步实现(应用单个地形)? 即

这是一种奇怪的行为,可能是由于提供者事先知道资源字段的结果并混淆了 terraforms 隐式依赖检测。

您可以尝试通过向 iam 资源添加显式 depends_on 来强制依赖以确保重新创建:

resource "google_bigquery_table_iam_member" "access_right" {
  project    = google_bigquery_table.table.project
  dataset_id = google_bigquery_table.table.dataset_id
  table_id   = google_bigquery_table.table.table_id
  role       = "roles/bigquery.dataEditor"
  member     = "serviceAccount:serviceAccount@GCPserviceAccount.com"

  depends_on = [google_bigquery_table.table]
}

在这种情况下,terraform 应该能够检测到变化,因为您现在还依赖于 etag 等只有在应用后才知道的变化字段。

为了更好地理解问题,初始应用的计划输出会有所帮助。

正如 OP 在评论部分提到的,解决方案是升级 Terraformprovider 版本。然而,当我执行一些测试时,我想分享它们的输出。

解决此问题的另一种方法是使用 recreate。下面的示例是如何工作的。

main.tf

### Creating Service Account
resource "google_service_account" "bigquerytest" {
  project      = "<MyProjectID>"
  account_id   = "bigquery-table"
  display_name = "bigquery-table-test"
  provider     = google
}
### Re-Create table
resource "google_bigquery_table" "table" {
  dataset_id = "test"
  table_id = "bqtable"
  project = "<MyProjectID>"
  schema  = file("/home/<myuser>/terrabq/jsonSchema.json")
  deletion_protection=false
}

### DataEditor binding
resource "google_bigquery_table_iam_member" "access-right" {
  project = "<MyProjectID>"
  dataset_id = "<YourDataset_id>"
  table_id = "bqtable"
  role = "roles/bigquery.dataEditor"
  member = "serviceAccount:${google_service_account.bigquerytest.email}"
}

jsonSchema.json

[
  {
    "mode": "NULLABLE",
    "name": "source",
    "type": "STRING"
  },
  {
    "mode": "NULLABLE",
    "name": "status",
    "type": "STRING"
  },
  {
  "mode": "NULLABLE",
  "name": "test",
  "type": "STRING"
  },
  {
  "mode": "NULLABLE",
  "name": "test4",
  "type": "STRING"
  }
]

场景: 创建一个具有特定架构的新 Table - bqtable,创建 ServiceAccount 并为此 Table.

提供适当的 IAM member 权限

输出:

...
Plan: 3 to add, 0 to change, 0 to destroy.
...
google_service_account.bigquerytest: Creating...
google_bigquery_table.table: Creating...
google_bigquery_table.table: Creation complete after 1s [id=projects/<myproject>/datasets/test/tables/bqtable]
google_service_account.bigquerytest: Creation complete after 1s [id=projects/<myproject>/serviceAccounts/bigquery-table@<myproject>.iam.gserviceaccount.com]
google_bigquery_table_iam_member.access-right: Creating...
google_bigquery_table_iam_member.access-right: Creation complete after 4s [id=projects/<myproject>/datasets/test/tables/bqtable/roles/bigquery.dataEditor/serviceAccount:bigquery-table@<myproject>.iam.gserviceaccount.com]

下一步是更改 jsonSchema.json 中的架构。

注意

  • 在架构中添加列时,不会重新创建 table。它只会更新 table 并且在所有新列中将是 NULL 值。
 # google_bigquery_table.table will be updated in-place
  ~ resource "google_bigquery_table" "table" {

在 BQ 中它看起来像这样:

  • 当您从架构中删除列时
  # google_bigquery_table.table must be replaced
-/+ resource "google_bigquery_table" "table" {

请记住,如果重新创建 Table,其中的所有数据都将被清除。

发布场景:

如果您只是更改架构(删除列),将重新创建 table 但 IAM 规则不会更新。

计划输出可能是这样的:Plan: 1 to add, 0 to change, 1 to destroy

OP 能够通过 Terraformprovider.

的更新版本解决此问题

但是,如果仍有问题,您可以使用 -replace 标记到 re-create 资源。

$ terraform apply -replace=google_bigquery_table_iam_member.access-right

terraform 采取的操作是:

  # google_bigquery_table.table must be replaced
-/+ resource "google_bigquery_table" "table" {
...
  # google_bigquery_table_iam_member.access-right will be replaced, as requested
-/+ resource "google_bigquery_table_iam_member" "access-right" {
      ~ etag       = "<randomString>" -> (known after apply)
      ~ id         = "projects/<myproject>/datasets/<mydataset>/tables/bqtable/roles/bigquery.dataEditor/serviceAccount:bigquery-table@<myproject>.iam.gserviceaccount.com" -> (known after apply)
      ~ table_id   = "projects/<myproject>/datasets/<mydataset>/tables/bqtable" -> "bqtable"
        # (4 unchanged attributes hidden)
    }

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

除了terraform中的depends_on,主要用于资源的排序或推迟创建。

总结一下:

  • 适用于 OP 的解决方案是更新 Terraform 和 Provider 版本
  • 另一个解决方案是使用terraform apply -replace=[resource.resourcename]

运行 此设置具有:

terraform version 1.1.4
google provider version 4.9.0

而不是:

terraform version 1.1.3
google provider version 4.5.0

解决了这个问题。

但是,如果您出于某种原因不能这样做,@PjoterS 有一个