将 Makefile 与 Terraform 结合使用并拆分项目布局

Using Makefile with Terraform and split project layout

我有一个类似于

的 Terraform 项目布局
stage
  └ Makefile
  └ terraform.tfvars
  └ vpc
  └ services
      └ frontend-app
      └ backend-app
          └ vars.tf
          └ outputs.tf
          └ main.tf
  └ data-storage
      └ mysql
      └ redis

其中Makefile的内容与

相似
.PHONY: all plan apply destroy

all: plan

plan:
    terraform plan -var-file terraform.tfvars -out terraform.tfplan

apply:
    terraform apply -var-file terraform.tfvars

destroy:
    terraform plan -destroy -var-file terraform.tfvars -out terraform.tfplan
    terraform apply terraform.tfplan

据我了解,Terraform 只会在当前目录中的模板上 运行。所以我需要 cd stage/services/backend-app 和 运行 terraform apply 那里。

但是我希望能够从 Makefile 管理整个堆栈。我还没有看到将参数传递给 make 的好方法。

我的目标是

make s3 plan # verify syntax
make s3 apply # apply plan

除非有更好的方法从父目录 运行 terraform?有没有类似于:

make all plan  # create stage plan
make all apply # apply stage plan

我们使用 shell 脚本来处理这个确切的用例,它可以更好地处理 cd 周围。

但是,您可以通过使用环境变量或直接在目标后面的命令行上设置 Make 变量,如下所示:

make target FOO=bar

所以在你的情况下,你可能需要这样的东西:

ifndef LOCATION
$(error LOCATION is not set)
endif

.PHONY: all plan apply destroy

all: plan

plan:
    cd $(LOCATION) && \
    terraform plan -var-file terraform.tfvars -out terraform.tfplan

apply:
    cd $(LOCATION) && \
    terraform apply -var-file terraform.tfvars

destroy:
    cd $(LOCATION) && \
    terraform plan -destroy -var-file terraform.tfvars -out terraform.tfplan
    terraform apply terraform.tfplan

我可能倾向于有一个运行 terraform get 并且还配置远程状态的目标,但现在设置起来应该很简单。

另一种解决方案是在每个 运行 上创建一个 tmp 文件夹并使用 terraform init ...terraform get...,就像这样(该示例还显示了使用部分配置的远程状态管理) :

readonly orig_path=$(pwd) && \
mkdir tmp && \
cd tmp && \
terraform init -backend=true -backend-config="$tf_backend_config" -backend-config="key=${account}/${envir}/${project}.json" $project_path && \
terraform get $project_path && \
terraform apply && \
cd $orig_path && \
rm -fR tmp

或者将上面的代码包装到 shell 脚本中,然后从 "apply" 等下的 make 文件中调用它

-- 添加此部分以解决来自 Sam Hammamy 的 comment/question --

总的来说,根据当前版本的 Terraform 处理项目的方式,我们确实想提前考虑如何构建我们的项目,以及如何将它们分解成可管理的、仍然有效的部分。这就是为什么我们通常将它们分解为 "foundational" 项目,如 VPC、VPN、SecurityGroups、IAM-Policies、Bastions 等,而不是像 "db"、"web-cluster" 等“功能性”项目。我们通常 run/deploy/modify "fundamental" 件一次或偶尔,而 "functional" 件我们可能每天重新部署几次。

这意味着随着我们的 IaC 代码的碎片化,我们也将相应地结束远程状态的碎片化,以及项目部署的执行。

对于反映"philosophy"的项目结构,我们通常会得到类似这样的项目结构(未显示公共模块):

├── projects
│   └── application-name
│       ├── dev
│       │   ├── bastion
│       │   ├── db
│       │   ├── vpc
│       │   └── web-cluster
│       ├── prod
│       │   ├── bastion
│       │   ├── db
│       │   ├── vpc
│       │   └── web-cluster
│       └── backend.config
└── run-tf.sh

每个项目都是一个子文件夹,对于每个 application_name/env/component = 文件夹(即 dev/vpc),我们添加了一个占位符后端配置文件:backend.tf:

terraform {
    backend "s3" {
    }
}

每个组件的文件夹内容将包含类似于以下文件:

│       ├── prod
│       │   ├── vpc
│       │   │   ├── backend.tf
│       │   │   ├── main.tf
│       │   │   ├── outputs.tf
│       │   │   └── variables.tf

在 "application_name/" 或 "application_name/env" 级别,我们添加了一个 backend.config 文件,内容为:

bucket     = "BUCKET_NAME"
region     = "region_name"
lock       = true
lock_table = "lock_table_name"
encrypt    = true

我们的包装器 shell 脚本需要参数应用程序名称、环境、组件和实际的 terraform cmd 运行。

运行-tf.sh脚本内容(简体):

#!/bin/bash

application=
envir=
component=
cmd=

tf_backend_config="root_path/$application/$envir/$component/backend.config"

terraform init -backend=true -backend-config="$tf_backend_config" -backend-config="key=tfstate/${application}/${envir}/${component}.json" 

terraform get

terraform $cmd

这是典型的 运行-tf.sh 调用的样子(从 Makefile 执行):

$ run-tf.sh application_name dev vpc plan

$ run-tf.sh application_name prod bastion apply