Terraform 在创建资源之前检查资源是否存在
Terraform check if resource exists before creating it
Terraform 中是否有一种方法可以在尝试创建资源之前检查 Google 云中的资源是否存在?
我想在工作期间检查我的 CircleCI CI/CD 管道中是否存在以下资源。我可以访问终端命令、bash 和 gcloud 命令。如果资源确实存在,我想使用它们。如果它们不存在,我想创建它们。我在 CircleCI 的 config.yml 中执行此逻辑作为我可以访问终端命令和 bash 的步骤。我的目标是在需要时在 GCP 中创建必要的基础设施(资源),否则在创建时使用它们,而不会在我的 CI/CD 构建中出现 Terraform 错误。
如果我尝试创建一个已经存在的资源,Terraform apply 将导致错误提示,例如“您已经拥有此资源”,现在我的 CI/CD 作业失败了。
下面是描述我试图获取的资源的伪代码。
resource "google_artifact_registry_repository" "main" {
# this is the repo for hosting my Docker images
# it does not have a data source afaik because it is beta
}
对于我的 google_artifact_registry_repository 资源。我的一种方法是使用数据源块执行 Terraform 应用,然后查看值是否为 returned。问题在于 google_artifact_registry_repository 没有数据源块。因此,我必须使用资源块创建此资源一次,此后每个 CI/CD 构建都可以依赖它。是否有解决方法来读取它的存在?
resource "google_storage_bucket" "bucket" {
# bucket containing the folder below
}
resource "google_storage_bucket_object" "content_folder" {
# folder containing Terraform default.tfstate for my Cloud Run Service
}
对于我的 google_storage_bucket 和 google_storage_bucket_object 资源。如果我使用数据源块执行 Terraform 应用以查看这些是否存在,我 运行 遇到的一个问题是当找不到资源时,Terraform 会永远 return 该状态。如果我能在 10-15 秒左右的时间内确定资源是否存在,如果不存在则假设这些资源不存在,那就太好了。
data "google_storage_bucket" "bucket" {
# bucket containing the folder below
}
output bucket {
value = data.google_storage_bucket.bucket
}
当资源存在时,我可以使用 Terraform 输出桶来获取该值。如果不存在,Terraform 需要很长时间才能 return 做出响应。对此有什么想法吗?
TF 没有任何内置工具来检查是否有预先存在的资源,因为这不是 TF 的本意。但是,您可以创建自己的 custom data source.
使用自定义数据源,您可以编写任何您想要的逻辑,包括检查预先存在的资源和return将该信息发送给 TF 以供将来使用。
感谢 Marcin 的建议,我有了一个工作示例,说明如何使用 Terraform external data sources 解决检查 GCP 中是否存在资源的问题。这是一种可行的方法。我相信还有其他方法。
我有一个 CircleCI config.yml,我的工作使用 运行 命令和 bash。从 bash,我将 init/apply 一个 Terraform 脚本来检查我的资源是否存在,如下所示。
data "external" "get_bucket" {
program = ["bash","gcp.sh"]
query = {
bucket_name = var.bucket_name
}
}
output "bucket" {
value = data.external.get_bucket.result.name
}
然后在我的 gcp.sh 中,我使用 gsutil 获取我的存储桶(如果它存在)。
#!/bin/bash
eval "$(jq -r '@sh "BUCKET_NAME=\(.bucket_name)"')"
bucket=$(gsutil ls gs://$BUCKET_NAME)
if [[ ${#bucket} -gt 0 ]]; then
jq -n --arg name "" '{name:"'$BUCKET_NAME'"}'
else
jq -n --arg name "" '{name:""}'
fi
然后在我的 CircleCI config.yml 中,我把它们放在一起。
terraform init
terraform apply -auto-approve -var bucket_name=my-bucket
bucket=$(terraform output bucket)
此时我检查是否返回了存储桶名称,并据此确定如何继续。
这对我有用:
- 创建数据
data "gitlab_user" "user" {
for_each = local.users
username = each.value.user_name
}
- 创建资源
resource "gitlab_user" "user" {
for_each = local.users
name = each.key
username = data.gitlab_user.user[each.key].username != null ? data.gitlab_user.user[each.key].username : split("@", each.value.user_email)[0]
email = each.value.user_email
reset_password = data.gitlab_user.user[each.key].username != null ? false : true
}
P.S.
变量
variable "users_info" {
type = list(
object(
{
name = string
user_name = string
user_email = string
access_level = string
expires_at = string
group_name = string
}
)
)
description = "List of users and their access to team's groups for newcommers"
}
本地人
locals {
users = { for user in var.users_info : user.name => user }
}
Terraform 中是否有一种方法可以在尝试创建资源之前检查 Google 云中的资源是否存在?
我想在工作期间检查我的 CircleCI CI/CD 管道中是否存在以下资源。我可以访问终端命令、bash 和 gcloud 命令。如果资源确实存在,我想使用它们。如果它们不存在,我想创建它们。我在 CircleCI 的 config.yml 中执行此逻辑作为我可以访问终端命令和 bash 的步骤。我的目标是在需要时在 GCP 中创建必要的基础设施(资源),否则在创建时使用它们,而不会在我的 CI/CD 构建中出现 Terraform 错误。
如果我尝试创建一个已经存在的资源,Terraform apply 将导致错误提示,例如“您已经拥有此资源”,现在我的 CI/CD 作业失败了。
下面是描述我试图获取的资源的伪代码。
resource "google_artifact_registry_repository" "main" {
# this is the repo for hosting my Docker images
# it does not have a data source afaik because it is beta
}
对于我的 google_artifact_registry_repository 资源。我的一种方法是使用数据源块执行 Terraform 应用,然后查看值是否为 returned。问题在于 google_artifact_registry_repository 没有数据源块。因此,我必须使用资源块创建此资源一次,此后每个 CI/CD 构建都可以依赖它。是否有解决方法来读取它的存在?
resource "google_storage_bucket" "bucket" {
# bucket containing the folder below
}
resource "google_storage_bucket_object" "content_folder" {
# folder containing Terraform default.tfstate for my Cloud Run Service
}
对于我的 google_storage_bucket 和 google_storage_bucket_object 资源。如果我使用数据源块执行 Terraform 应用以查看这些是否存在,我 运行 遇到的一个问题是当找不到资源时,Terraform 会永远 return 该状态。如果我能在 10-15 秒左右的时间内确定资源是否存在,如果不存在则假设这些资源不存在,那就太好了。
data "google_storage_bucket" "bucket" {
# bucket containing the folder below
}
output bucket {
value = data.google_storage_bucket.bucket
}
当资源存在时,我可以使用 Terraform 输出桶来获取该值。如果不存在,Terraform 需要很长时间才能 return 做出响应。对此有什么想法吗?
TF 没有任何内置工具来检查是否有预先存在的资源,因为这不是 TF 的本意。但是,您可以创建自己的 custom data source.
使用自定义数据源,您可以编写任何您想要的逻辑,包括检查预先存在的资源和return将该信息发送给 TF 以供将来使用。
感谢 Marcin 的建议,我有了一个工作示例,说明如何使用 Terraform external data sources 解决检查 GCP 中是否存在资源的问题。这是一种可行的方法。我相信还有其他方法。
我有一个 CircleCI config.yml,我的工作使用 运行 命令和 bash。从 bash,我将 init/apply 一个 Terraform 脚本来检查我的资源是否存在,如下所示。
data "external" "get_bucket" {
program = ["bash","gcp.sh"]
query = {
bucket_name = var.bucket_name
}
}
output "bucket" {
value = data.external.get_bucket.result.name
}
然后在我的 gcp.sh 中,我使用 gsutil 获取我的存储桶(如果它存在)。
#!/bin/bash
eval "$(jq -r '@sh "BUCKET_NAME=\(.bucket_name)"')"
bucket=$(gsutil ls gs://$BUCKET_NAME)
if [[ ${#bucket} -gt 0 ]]; then
jq -n --arg name "" '{name:"'$BUCKET_NAME'"}'
else
jq -n --arg name "" '{name:""}'
fi
然后在我的 CircleCI config.yml 中,我把它们放在一起。
terraform init
terraform apply -auto-approve -var bucket_name=my-bucket
bucket=$(terraform output bucket)
此时我检查是否返回了存储桶名称,并据此确定如何继续。
这对我有用:
- 创建数据
data "gitlab_user" "user" {
for_each = local.users
username = each.value.user_name
}
- 创建资源
resource "gitlab_user" "user" {
for_each = local.users
name = each.key
username = data.gitlab_user.user[each.key].username != null ? data.gitlab_user.user[each.key].username : split("@", each.value.user_email)[0]
email = each.value.user_email
reset_password = data.gitlab_user.user[each.key].username != null ? false : true
}
P.S.
变量
variable "users_info" {
type = list(
object(
{
name = string
user_name = string
user_email = string
access_level = string
expires_at = string
group_name = string
}
)
)
description = "List of users and their access to team's groups for newcommers"
}
本地人
locals {
users = { for user in var.users_info : user.name => user }
}