我用Ruby尝试在Terraform上做一些调整的备忘录
最近才开始接触Terraform的原始人,你好。
忽略 tfstate 了嗎?還是不忽略?這是個問題。
terraform.tfstate 是由Terraform创建的巨大的JSON文件,用来维护所创建资源的状态。
你不觉得对于用于某个项目的Terraform存储库和这个文件来说,它们应该是一体的吗?所以,将这个文件也用git进行管理不是一个很好的集中管理的方式吗?你不这么想吗?我是这么想的。
然而,如果使用aws_iam_access_key,IAM用户的凭据将明文写入terraform.tfstate。我不想将其添加到git!!
在我写作的过程中我第一次知道,似乎可以使用S3或Atlas来管理terraform.tfstate,所以我收到一个建议就是照着做。但是,在进行这项工作的人无论如何都不愿意放弃在代码库中进行管理的梦想,这真是像石器时代的人一样。
如果 `terraform.tfstate` 文件中包含敏感信息,我们可以考虑加密并使用 `git add` 来处理。就像使用 Chef 的 Encrypted Data Bag Item 一样。
没错,我们来加密吧
terraform.tfstate
terraform.tfstate.backup
因为这些家伙有问题,所以我不会添加他们。而是将 terraform.tfstate.encrypted 加入到 Git 管理中。
然而,当然,这意味着在执行terraform plan/apply之前和之后需要进行加密和解密。
-
- terraform.tfstate.encrypted → 使用某种方法进行解密 → terraform.tfstate
执行terraform plan/apply
terraform.tfstate的内容将会更新
terraform.tfstate → 使用某种方法进行加密 → terraform.tfstate.encrypted
除非这个流程被庄严地执行,否则 terraform.tfstate.encrypted 将无法保持最新。
-
- 古い状態の terraform.tfstate を使って terraform plan/apply が行われてしまう
terraform.tfstate.encryptedが最新化されないまま *.tf の変更だけが git commit されてしまう
发生了不利的情况。
好吧,我們停止直接使用 terraform 了吧。
只需将前面1至4节的流程作为任务依次执行即可。这就是rake所做的事情。
# 略
namespace :terraform do
# 略
desc "decrypt terraform.tfstate.encrypted to terraform.tfstate"
task :decrypt_state do
# 略
# terraform.tfstate.encrypted を terraform.tfstate に復号化する
end
desc "encrypt terraform.tfstate to terraform.tfstate.encrypted"
task :encrypt_state do
# 略
# terraform.tfstate を terraform.tfstate.encrypted に暗号化する
end
desc "terraform plan"
task :plan do
# 略
# terraform plan を実行する
Rake::Task["terraform:encrypt_state"].invoke
end
desc "terraform apply"
task :apply do
# 略
# terraform apply を実行する
Rake::Task["terraform:encrypt_state"].invoke
end
task :plan => :decrypt_state
task :apply => :decrypt_state
# 略
end
task default: ["terraform:plan"]
出于方便之考虑,我们也选择使用Ruby来进行加密和解密,这样就能顺便利用到symmetric_encryption这个宝石了。
$ rake -T
rake terraform:apply # terraform apply
rake terraform:decrypt_state # decrypt terraform.tfstate.encrypted to terraform.tfstate
rake terraform:encrypt_state # encrypt terraform.tfstate to terraform.tfstate.encrypted
rake terraform:plan # terraform plan
应用时,执行 $ rake terraform:apply。 计划也是相同的,但由于是默认任务,所以只执行 rake 就可以。
禁止直接使用terraform命令。
我们既然都这么辛苦,就来让ERB模板可以使用吧。
Terraform的定义文件*.tf并不是很可编程化。复制粘贴相似的资源定义是相当麻烦的,对吧?
既然带了Ruby过来,就试试用ERB自动生成*.tf文件吧。
require "erb"
# 略
namespace :terraform do
# 略
desc "build *.tf.erb to .tf"
task :build do
Dir.glob("*.tf.erb").each do |path|
File.open path.sub(/\.erb\z/, ""), "w" do |file|
file.write ERB.new(File.read(path)).result
end
end
end
# 略
task :plan => :build
task :apply => :build
end
请确保在执行 terraform:plan 和 terraform:apply 之前,使用 $ rake terraform:build 将当前目录中的所有 *.tf.erb 文件编译成 *.tf 文件。
我现在可以这样描述了。
<%
user_names = %w(
user_a
user_b
user_c
user_d
user_e
user_f
user_g
)
%>
resource "aws_iam_group" "users" {
name = "users"
path = "/users/"
}
<% user_names.each do |user_name| %>
resource "aws_iam_user" "<%= user_name %>" {
name = "<%= user_name %>"
path = "/users/"
}
resource "aws_iam_access_key" "<%= user_name %>" {
user = "${aws_iam_user.<%= user_name %>.name}"
}
<% end %>
resource "aws_iam_group_membership" "users_membership" {
name = "users_membership"
group = "${aws_iam_group.users.name}"
users = [
<% user_names.each do |user_name| %>
"${aws_iam_user.<%= user_name %>.name}",
<% end %>
]
}
不要说难读啊!
为了防止 .tf 和 .tf.erb 文件的内容不一致而无法提交到 git 中,
*.tf
哎呀,竟然把 Terraform 的存储库中的 .tf 文件给排除掉了。
既然这样,那就让我们能够安装Terraform本身吧。
Terraform是一个还很年轻的产品,版本更新也经常进行。毫无疑问,即使是相同的 .tf 文件,如果使用不同版本的Terraform会导致不同的行为。
你想要把这个仓库的Terraform版本固定下来吧?
所以,我考虑在当前目录下放置一个固定版本的Terraform主体,并创建相应的任务。
namespace :terraform do
desc "install terraform to this directory"
task :install do
terraform_version = File.read(".terraform_version").chomp
download_url = "https://releases.hashicorp.com/terraform/#{terraform_version}/terraform_#{terraform_version}_#{`uname`.match(/darwin/i) ? "darwin" : "linux"}_amd64.zip"
system "curl -L #{download_url} -o terraform.zip"
system "unzip terraform.zip -d vendor/"
system "rm -f terraform.zip"
end
# 略
end
0.6.8
如果运行 $ rake terraform:install,terraform程序将被安装到vendor/目录下。太棒了!
然后,terrafrom: plan terraform: apply命令将应用于此terraform配置。
# 略
desc "terraform plan"
task :plan do
system "vendor/terraform plan"
Rake::Task["terraform:encrypt_state"].invoke
end
# 略
順便提一句
有一个叫做direnv的工具,它非常好用。
我們強制使用這個方式來管理AWS的credentials和terraform.tfstate加解密所使用的金鑰文件資訊,這樣做很不容易。
- direnvを使おう – Qiita
变成了这样
- https://github.com/y13i/terraform-ruby-something