使用 tflint 对 Terraform 进行静态分析
我叫寺冈,是一名基础设施工程师。
这次,我想使用 tflint 对 Terraform 代码进行静态分析。
什么是 tflint?
这是一个专门针对OSS发布的tf文件的linter工具。
这存储库。
通过使用 tflint,您可以在编写已弃用的语法或未使用的声明时发出警告,并
检测 AWS 等云平台上可能发生的错误。
安装
对于Mac,您可以使用brew 安装。
$ 酿造安装 tflint
对于 Linux,提供了安装脚本,因此您可以使用它。
$ 卷曲 https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh |
如何使用
基本上是在tf文件所在目录运行命令。
只需执行以下命令即可读取当前目录下的文件并进行分析,
如果有问题则显示错误。
$tflint
让我们仔细看看。
在当前目录下将以下代码写入main.tf。
变量“role_arn”{描述=“AWS角色Arn”}提供商“aws”{region=“ap-northeast-1”假设_角色{role_arn=var.role_arn}}资源“aws_instance”“test”{ami=“ami-0ca38c7440de1749a” ”instance_type =“t3.micro”标签={名称=“tflint-test”}}
当我检查与 terraform plan 的差异时,我尝试创建一个 EC2 实例。
$ 地形计划 ---------------------------------------------------------- ------------------------- 执行计划已生成,如下所示,资源操作用以下符号表示: + create Terraform 将执行。以下操作: # aws_instance.test 将被创建 + resource "aws_instance" "test" { + ami = "ami-0ca38c7440de1749a" + arn = (应用后已知) + Associate_public_ip_address = (应用后已知) +availability_zone = (应用后已知) apply) + cpu_core_count = (apply后已知) + cpu_threads_per_core = (apply后已知) + get_password_data = false + host_id = (apply后已知) + id = (apply后已知) + instance_state = (apply后已知) + instance_type = " t3.micro" + ipv6_address_count =(应用后已知)+ ipv6_addresses =(应用后已知)+ key_name =(应用后已知)+ outpost_arn =(应用后已知)+password_data =(应用后已知)+placement_group =(应用后已知) apply) + Primary_network_interface_id = (应用后已知) + private_dns = (应用后已知) + private_ip = (应用后已知) + public_dns = (应用后已知) + public_ip = (应用后已知) + secondary_private_ips = (应用后已知) + security_groups =(应用后已知)+ source_dest_check = true +subnet_id =(应用后已知)+tags = { + "Name" = "tflint-test" } + tenancy =(应用后已知)+ vpc_security_group_ids =(应用后已知) ) + ebs_block_device { + delete_on_termination = (应用后已知) + device_name = (应用后已知) + 加密 = (应用后已知) + iops = (应用后已知) + kms_key_id = (应用后已知) + snapshot_id = (应用后已知)应用)+标签=(应用后已知)+吞吐量=(应用后已知)+volume_id=(应用后已知)+volume_size=(应用后已知)+volume_type=(应用后已知)}+enclave_options{+enabled=( + ephemeral_block_device { + device_name = (应用后已知) + no_device = (应用后已知) + virtual_name = (应用后已知) } +metadata_options { + http_endpoint = (应用后已知) + http_put_response_hop_limit = (应用后已知) apply) + http_tokens = (应用后已知) } + network_interface { + delete_on_termination = (应用后已知) + device_index = (应用后已知) + network_interface_id = (应用后已知) } + root_block_device { + delete_on_termination = (应用后已知) + device_name =(应用后已知)+加密=(应用后已知)+ iops =(应用后已知)+ kms_key_id =(应用后已知)+标签=(应用后已知)+吞吐量=(应用后已知)+volume_id =(应用后已知)+volume_size =(应用后已知)+volume_type =(应用后已知)} } 计划:1 添加,0 更改,0 销毁。 -------------------------------------------------- ---------- 注意:您没有指定“-out”参数来保存此计划,因此 Terraform 无法保证随后运行“terraform apply”时将执行这些操作。
让我们用 terraform apply 来实际反映它。
$ terraform apply 已生成执行计划,如下所示: + create Terraform 将执行以下操作: # 将创建 aws_instance.test + resource "aws_instance" "test" { + ami =“ami-0ca38c7440de1749a”+ arn =(应用后已知)+ Associate_public_ip_address =(应用后已知)+availability_zone =(应用后已知)+ cpu_core_count =(应用后已知)+ cpu_threads_per_core =(应用后已知)+ get_password_data = false + host_id = (应用后已知) + id = (应用后已知) + instance_state = (应用后已知) + instance_type = "t3.micro" + ipv6_address_count = (应用后已知) + ipv6_addresses = (应用后已知) + key_name =(应用后已知)+ outpost_arn =(应用后已知)+password_data =(应用后已知)+placement_group =(应用后已知)+primary_network_interface_id =(应用后已知)+private_dns =(应用后已知)+private_ip =( + public_dns =(应用后已知)+ public_ip =(应用后已知)+ secondary_private_ips =(应用后已知)+ security_groups =(应用后已知)+ source_dest_check = true +subnet_id =(应用后已知)+标签= { + "Name" = "tflint-test" } + tenancy = (应用后已知) + vpc_security_group_ids = (应用后已知) + ebs_block_device { + delete_on_termination = (应用后已知) + device_name = (应用后已知) + 加密=(应用后已知)+ iops =(应用后已知)+ kms_key_id =(应用后已知)+ snapshot_id =(应用后已知)+标签=(应用后已知)+吞吐量=(应用后已知)+volume_id =( + Volume_size =(应用后已知)+ Volume_type =(应用后已知)} + enclave_options { +enabled =(应用后已知)} + ephemeral_block_device { + device_name =(应用后已知)+ no_device =(应用后已知) apply) + virtual_name = (应用后已知) } +metadata_options { + http_endpoint = (应用后已知) + http_put_response_hop_limit = (应用后已知) + http_tokens = (应用后已知) } + network_interface { + delete_on_termination = (应用后已知) + device_index = (应用后已知) + network_interface_id = (应用后已知) } + root_block_device { + delete_on_termination = (应用后已知) + device_name = (应用后已知) + 加密 = (应用后已知) + iops = (应用后已知)应用)+ kms_key_id =(应用后已知)+标签=(应用后已知)+吞吐量=(应用后已知)+volume_id =(应用后已知)+volume_size =(应用后已知)+volume_type =(应用后已知)计划:1 表示添加,0 表示更改,0 表示销毁。 Terraform 将执行上述操作。输入值:yes aws_instance.test。 :正在创建... aws_instance.test:仍在创建... [已过 10 秒] aws_instance.test:仍在创建... [已过 20 秒] aws_instance.test:仍在创建... [已过 30 秒] aws_instance.test:仍在创建... [已过 40 秒] aws_instance.test:仍在创建... [已过 50 秒] aws_instance.test:53 秒后创建完成 [id=i-085049c8fe2c58383] 应用完成! 资源:1 个添加,0 个更改,0 个销毁。
代码没有什么特别的问题,所以可以毫无问题地反映出来。
接下来,让我们重写一些代码。
再次检查与计划的差异。
您正在尝试将 t3.micro 更改为 t4.micro。
% terraform plan 正在计划之前刷新内存中的 Terraform 状态...刷新的状态将用于计算此计划,但不会持久保存到本地或远程状态存储中:正在刷新状态... [id= i-085049c8fe2c58383] ---------------------------------------------------------- --- -------------------------- 执行计划已生成,如下所示,资源操作用以下符号表示:~就地更新 Terraform 将执行以下操作: # aws_instance.test 将就地更新 ~ resource "aws_instance" "test" { ami = "ami-0ca38c7440de1749a" arn = "arn:aws:ec2:ap-northeast- 1:485076298277:instance / i-085049c8fe2c58383”associate_public_ip_address = trueavailability_zone =“ap-northeast-1a”cpu_core_count = 1 cpu_threads_per_core = 2disable_api_termination = false ebs_optimized = false get_password_data = false休眠= false id =“i-0850 49c8fe2c58383“实例状态=”运行” ~ instance_type = “t3.micro” -> “t4.micro” ipv6_address_count = 0 ipv6_addresses = [] 监控 = false Primary_network_interface_id = “eni-0c78d105cbfaddc16” private_dns = “ip-172-31-40-11.ap-northeast -1.compute.internal" private_ip = "172.31.40.11" public_dns = "ec2-54-249-80-70.ap-northeast-1.compute.amazonaws.com" public_ip = "54.249.80.70" secondary_private_ips = [] security_groups = [ "default", ] source_dest_check = true subnet_id = "subnet-9621c1de" Tags = { "Name" = "tflint-test" } tenancy = "default" vpc_security_group_ids = [ "sg-485f1735", ] Credit_specation { cpu_credits = “无限制” } enclave_options { 启用 = false }metadata_options { http_endpoint = “启用” http_put_response_hop_limit = 1 http_tokens = “可选” } root_block_device { delete_on_termination = true device_name = “/dev/xvda” 加密 = false iops = 100 个标签 = {} 吞吐量= 0volume_id =“vol-012c9259f69420c1c”volume_size = 8volume_type =“gp2”}}计划:0添加,1更改,0销毁 ----------------- --- ---------------------------------------------------------- --- -- 注意:您没有指定“-out”参数来保存此计划,因此 Terraform 无法保证随后运行“terraform apply”时将执行这些操作。
让我们应用这个来反映它。
% terraform apply aws_instance.test: 正在刷新状态... [id=i-085049c8fe2c58383] 执行计划已生成,如下所示,资源操作用以下符号指示: ~ 就地更新 Terraform 将执行以下操作。 : # aws_instance.test 将就地更新 ~ resources "aws_instance" "test" { ami = "ami-0ca38c7440de1749a" arn = "arn:aws:ec2:ap-northeast-1:485076298277:instance/i-085049c8fe2c58383" Associate_public_ip_address = trueavailability_zone =“ap-northeast-1a”cpu_core_count = 1 cpu_threads_per_core = 2disable_api_termination = false ebs_optimized = false get_password_data = false 休眠 = false id =“i-085049c8fe2c58383”instance_state =“正在运行”〜instance_type =“t3.micro” ->“t4.micro”ipv6_address_count = 0 ipv6_addresses = []监控= false Primary_network_interface_id =“eni-0c78d105cbfaddc16”private_dns =“ip-172-31-40-11.ap-northeast-1.compute.internal”private_ip =“ 172.31.40.11" public_dns = "ec2-54-249-80-70.ap-northeast-1.compute.amazonaws.com" public_ip = "54.249.80.70" secondary_private_ips = [] security_groups = [ "default", ] source_dest_check = true subnet_id = "subnet-9621c1de" 标签 = { "Name" = "tflint-test" } tenancy = "default" vpc_security_group_ids = [ "sg-485f1735", ] Credit_specation { cpu_credits = "unlimited" } enclave_options {enabled = false } metadata_options {http_endpoint =“启用”http_put_response_hop_limit = 1 http_tokens =“可选”} root_block_device {delete_on_termination = true device_name =“/dev/xvda”加密= false iops = 100个标签= {}吞吐量= 0volume_id =“vol-012c9259f69420c1c”volume_size = 8 volume_type = "gp2" } } 计划:0 表示添加,1 表示更改,0 表示销毁。 Terraform 将执行上述操作。值:是 aws_instance.test:正在修改... [id=i-085049c8fe2c58383] aws_instance.test:仍在修改... [id=i-085049c8fe2c58383,已过 10 秒] aws_instance.test:仍在修改... [id= I-085049C8FE2C58383,20S] AWS_INSTANCE.TEST:仍在修改... [ID = I-085049C8FE2C58383,30s已播放] AWS_INSTANCE.TEST.TEST.TEST.TEST.TEST.TEST:仍在正在修改... [id=i-085049c8fe2c58383,已过 50 秒] aws_instance.test:仍在修改... [id=i-085049c8fe2c58383,已过 1 秒] 错误:Client.InvalidParameterValue:InstanceType 状态值“t4.micro”无效。代码:400,请求 ID:326502a4-5564-449f-a37d-73f651ffb151,位于 main.tf 第 13 行,资源“aws_instance”“test”中:13:资源“aws_instance”“test”{
发生错误。
由于 AWS 上没有名为 t4.micro 的实例类型,因此正确的行为是收到错误,但是
在 AWS 上更改实例类型时,您需要停止有问题的实例,因此
在 Terraform 上运行时执行相同的操作。顺序是暂停 -> 更改类型 -> 开始。
在这种情况下,更改类型时会发生错误,Terraform 执行会在实例停止时结束。
这不是一个好主意...如果可能的话,我想在计划阶段发出错误。
现在让我们运行 tflint。
% tflint 发现 1 个问题:错误:instance_type 不是 main.tf 第 15 行上的有效值 (aws_instance_invalid_type):15:instance_type = "t4.micro"
它给了我一个错误。
Plan只是Terraform上的执行计划,因此
如果tf文件的语法有问题,它会发出错误,但
它无法检测依赖于目标云平台规范的错误。
在这种情况下,首先运行 tflint 将极大地提高更新之前检查和测试差异的准确性。
概括
如上所述,如果您在无法检测到取决于目标云平台规格的错误的情况下进行应用,
即使执行过程中发生错误,Terraform也不会回滚,因此可能会影响正在运行的服务
或导致您出错。将需要调查这种情况是否正在发生。
因此,有必要尽可能使用我们这次介绍的plan或tflint来提高反射前确认的准确性。
tflint 是一个非常有用的工具,所以请尝试一下。