mito’s blog

IT技術メインの雑記。思い立ったが吉日。

[Snyk][Terraform] Snykで実リソースとtfstateファイルの差分を検出してみた

はじめに

実リソースとコードの差異を検出することをドリフト検出と言うそうです。

今回は、Terraformで作成したAWSリソースとtfstateファイルの差異を検出します。
Snykで試したかったことがこれです!


まとめ

ドリフト検出を試して、おおむね満足のいく結果になりました。SGのインバウンドルール追加は検出されませんでしたが。理由は調査中です。
使いこなすには、ポリシーで何を検出しないか決めたり、コードの定義もデフォルトに任せず明記したほうがいいかも。
あと、結果をJSON形式で出力すれば、tfstateファイルに管理外のリソースをインポートしやすくなるかもしれないですね。


目次


環境

  • Cloud9: t3.small
  • Snyk CLI: 1.992.0
  • Terraform: 1.2.8


試すこと

TerraformでEC2インスタンスを構築した後、手動でEIPとSGのインバウンドルールを追加し、ドリフト検出します。
実行コマンドは以下です。

$ snyk iac describe
IaC describe
Usage
  Note: This feature is available in Snyk CLI version v1.876.0 or greater.

  snyk iac describe [<OPTIONS>]

Description
  The snyk iac describe command detects infrastructure drift and unmanaged resources. It compares
  resources in your Terraform state file against actual resources in your cloud provider and outputs
  a report.

  -  Resources in your Terraform state files are managed resources.
  -  Changes to managed resources not reflected in the Terraform state file are drifts.
  -  Resources that exist but are not in your Terraform state file are unmanaged resources.

  For detailed information and examples, see IaC describe command examples https://docs.snyk.io/prod
  ucts/snyk-infrastructure-as-code/detect-drift-and-manually-created-resources/iac-describe-command-
  examples

  For a list of related commands see the snyk iac help; iac --help

Exit codes
  Possible exit codes and their meaning:

  0: success, no drift found
  1: drifts or unmanaged resources found
  2: failure

Required options
  Note: To use the describe command, you must use one of these options:

  --only-unmanaged
    Report resources not found in any Terraform states.

  --only-managed or --drift
    Scan managed resources found in Terraform states for changes.

  --all
    Scan both managed and unmanaged resources.


リソース作成前にドリフト検出する

tfstateファイルは存在しないので、これといった結果はありません。
「IaC Coverage: 0%」が重要そうですね。

$ snyk iac describe --drift
Scanned states (1) 
Scan duration: 16s     
Provider version used to scan: 4.28.0. Use --tf-provider-version to use another version.
Snyk Scanning Infrastructure As Code Discrepancies...

  Info:    Resources under IaC, but different to terraform states.
  Resolve: Reapply IaC resources or update into terraform.

Test Summary


  IaC Coverage: 0%
  Info: To reach full coverage, remove resources or move it to Terraform.

  Tip: Run --help to find out about commands and flags.
      Scanned with aws provider version 4.28.0. Use --tf-provider-version to update.


TerraformでEC2インスタンスを構築する

ドリフト検出するために、以下のtfファイルでEC2インスタンスを構築します。

provider "aws" {
    region        = "ap-northeast-1"
}

resource "aws_instance" "mito-ec2" {
    ami           = "ami-0f36dcfcc94112ea1"
    instance_type = "t2.micro"
    key_name      = "キー名"
    associate_public_ip_address = "true"
    
    vpc_security_group_ids = [
        aws_security_group.mito_sg.id
    ]
  
    tags = {
        Name      = "mito-ec2"
    }
}

resource "aws_security_group" "mito_sg" {
  name            = "mito_sg"

  # インバウンド
  dynamic "ingress" {
    for_each      = [22, 80]

    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }

  # アウトバウンド
  egress {
    from_port     = 0
    to_port       = 0
    protocol      = "-1"
    cidr_blocks   = ["0.0.0.0/0"]
  }
}


EC2インスタンスを変更する前に、ドリフト検出する

Changed Resourcesが2となっているけど、まぁ今回は気にしない。
Ignore resourcesで、検出したくないリソースが指定できそうです。

$ snyk iac describe --drift
Scanned states (1) 
Scan duration: 14s     
Provider version used to scan: 4.28.0. Use --tf-provider-version to use another version.
Snyk Scanning Infrastructure As Code Discrepancies...

  Info:    Resources under IaC, but different to terraform states.
  Resolve: Reapply IaC resources or update into terraform.

Changed resources: 2

State: Generated [ Changed Resources: 1 ]

  Resource Type: aws_ebs_volume
    ID: vol-056e51aa5a6148297
    + timeouts: null => {}

State: tfstate://terraform.tfstate [ Changed Resources: 1 ]

  Resource Type: aws_instance
    ID: i-0683a97d236667e6f
    ~ arn: arn:aws:ec2:ap-northeast-1:xxxx:instance/i-0683a97d236667e6f => arn:aws:ec2:ap-northeast-1::instance/i-0683a97d236667e6f
    - user_data_replace_on_change: false

Test Summary

  Managed Resources: 3
  Changed Resources: 2

  IaC Coverage: 100%
  Info: To reach full coverage, remove resources or move it to Terraform.

  Tip: Run --help to find out about commands and flags.
      Scanned with aws provider version 4.28.0. Use --tf-provider-version to update.


EIPを付与した後に、ドリフト検出する

手動でEC2インスタンスに付与し、ドリフト検出してみました。
結果、「public_dns」と「public_ip」が検出されました。想定通りです。

$ snyk iac describe --drift
Scanned states (1) 
Scan duration: 7s      
Provider version used to scan: 4.28.0. Use --tf-provider-version to use another version.
Snyk Scanning Infrastructure As Code Discrepancies...

  Info:    Resources under IaC, but different to terraform states.
  Resolve: Reapply IaC resources or update into terraform.

Changed resources: 2

State: Generated [ Changed Resources: 1 ]

  Resource Type: aws_ebs_volume
    ID: vol-056e51aa5a6148297
    + timeouts: null => {}

State: tfstate://terraform.tfstate [ Changed Resources: 1 ]

  Resource Type: aws_instance
    ID: i-0683a97d236667e6f
    ~ arn: arn:aws:ec2:ap-northeast-1:xxxx:instance/i-0683a97d236667e6f => arn:aws:ec2:ap-northeast-1::instance/i-0683a97d236667e6f
    ~ public_dns: ec2-54-65-55-184.ap-northeast-1.compute.amazonaws.com => ec2-52-69-152-84.ap-northeast-1.compute.amazonaws.com
    ~ public_ip: 54.65.55.184 => 52.69.152.84
    - user_data_replace_on_change: false

Test Summary

  Managed Resources: 3
  Changed Resources: 2

  IaC Coverage: 100%
  Info: To reach full coverage, remove resources or move it to Terraform.

  Tip: Run --help to find out about commands and flags.
      Scanned with aws provider version 4.28.0. Use --tf-provider-version to update.


SGのインバウンドルールを追加した後に、ドリフト検出する

インバウンドルールに443を追加しましたが、これは検出されませんでした。

$ snyk iac describe --drift
Scanned states (1) 
Scan duration: 7s      
Provider version used to scan: 4.28.0. Use --tf-provider-version to use another version.
Snyk Scanning Infrastructure As Code Discrepancies...

  Info:    Resources under IaC, but different to terraform states.
  Resolve: Reapply IaC resources or update into terraform.

Changed resources: 2

State: Generated [ Changed Resources: 1 ]

  Resource Type: aws_ebs_volume
    ID: vol-056e51aa5a6148297
    + timeouts: null => {}

State: tfstate://terraform.tfstate [ Changed Resources: 1 ]

  Resource Type: aws_instance
    ID: i-0683a97d236667e6f
    ~ arn: arn:aws:ec2:ap-northeast-1:xxxx:instance/i-0683a97d236667e6f => arn:aws:ec2:ap-northeast-1::instance/i-0683a97d236667e6f
    ~ public_dns: ec2-54-65-55-184.ap-northeast-1.compute.amazonaws.com => ec2-52-69-152-84.ap-northeast-1.compute.amazonaws.com
    ~ public_ip: 54.65.55.184 => 52.69.152.84
    - user_data_replace_on_change: false

Test Summary

  Managed Resources: 3
  Changed Resources: 2

  IaC Coverage: 100%
  Info: To reach full coverage, remove resources or move it to Terraform.

  Tip: Run --help to find out about commands and flags.
      Scanned with aws provider version 4.28.0. Use --tf-provider-version to update.


EC2インスタンスを削除して、ドリフト検出する

EC2インスタンスを削除し、SGは残したままでドリフト検出しました。
結果、Missing Resourceとaws_instanceは、Missing Resourcesとして検出されました。 残ったManaged ResourcesはSGのようです。

$ snyk iac describe --drift
Scanned states (1) 
Scan duration: 8s      
Provider version used to scan: 4.28.0. Use --tf-provider-version to use another version.
Snyk Scanning Infrastructure As Code Discrepancies...

  Info:    Resources under IaC, but different to terraform states.
  Resolve: Reapply IaC resources or update into terraform.

Missing resources: 2

State: Generated [ Missing Resources: 1 ]

  Resource Type: aws_ebs_volume
    ID: vol-056e51aa5a6148297

State: tfstate://terraform.tfstate [ Missing Resources: 1 ]

  Resource Type: aws_instance
    ID: i-0683a97d236667e6f

Test Summary

  Managed Resources: 1
  Missing Resources: 2

  IaC Coverage: 33%
  Info: To reach full coverage, remove resources or move it to Terraform.

  Tip: Run --help to find out about commands and flags.
      Scanned with aws provider version 4.28.0. Use --tf-provider-version to update.


JSON形式で、ドリフト検出の結果を出力する

JSON形式で出力した場合、出管理下にあるリソース、管理下にないリソースが分かりやすいですね。

$ snyk iac describe --json --drift
Scanned states (1) 
Scan duration: 7s      
Provider version used to scan: 4.28.0. Use --tf-provider-version to use another version.
{
        "options": {
                "deep": true,
                "only_managed": true,
                "only_unmanaged": false
        },
        "summary": {
                "total_resources": 3,
                "total_changed": 2,
                "total_unmanaged": 0,
                "total_missing": 0,
                "total_managed": 3,
                "total_iac_source_count": 1
        },
        "managed": [
                {
                        "id": "vol-02205209f17fcac8b",
                        "type": "aws_ebs_volume"
                },
                {
                        "id": "i-01b5dbaa8fadcd935",
                        "type": "aws_instance",
                        "human_readable_attributes": {
                                "Name": "mito-ec2"
                        },
                        "source": {
                                "source": "tfstate://terraform.tfstate",
                                "namespace": "",
                                "internal_name": "mito-ec2"
                        }
                },
                {
                        "id": "sg-0354feb133037f2f0",
                        "type": "aws_security_group",
                        "source": {
                                "source": "tfstate://terraform.tfstate",
                                "namespace": "",
                                "internal_name": "mito_sg"
                        }
                }
        ],
        "unmanaged": null,
        "missing": null,
        "differences": [
                {
                        "res": {
                                "id": "vol-02205209f17fcac8b",
                                "type": "aws_ebs_volume"
                        },
                        "changelog": [
                                {
                                        "type": "create",
                                        "path": [
                                                "timeouts"
                                        ],
                                        "from": null,
                                        "to": {},
                                        "computed": false
                                }
                        ]
                },
                {
                        "res": {
                                "id": "i-01b5dbaa8fadcd935",
                                "type": "aws_instance",
                                "human_readable_attributes": {
                                        "Name": "mito-ec2"
                                },
                                "source": {
                                        "source": "tfstate://terraform.tfstate",
                                        "namespace": "",
                                        "internal_name": "mito-ec2"
                                }
                        },
                        "changelog": [
                                {
                                        "type": "update",
                                        "path": [
                                                "arn"
                                        ],
                                        "from": "arn:aws:ec2:ap-northeast-1:xxxx:instance/i-01b5dbaa8fadcd935",
                                        "to": "arn:aws:ec2:ap-northeast-1::instance/i-01b5dbaa8fadcd935",
                                        "computed": true
                                },
                                {
                                        "type": "update",
                                        "path": [
                                                "public_dns"
                                        ],
                                        "from": "ec2-35-77-229-240.ap-northeast-1.compute.amazonaws.com",
                                        "to": "ec2-54-65-228-246.ap-northeast-1.compute.amazonaws.com",
                                        "computed": true
                                },
                                {
                                        "type": "update",
                                        "path": [
                                                "public_ip"
                                        ],
                                        "from": "35.77.229.240",
                                        "to": "54.65.228.246",
                                        "computed": true
                                },
                                {
                                        "type": "delete",
                                        "path": [
                                                "user_data_replace_on_change"
                                        ],
                                        "from": false,
                                        "to": null,
                                        "computed": false
                                }
                        ]
                }
        ],
        "coverage": 100,
        "alerts": {
                "": [
                        {
                                "message": "You have unmanaged security group rules that could be false positives, find out more at https://docs.driftctl.com/limitations"
                        },
                        {
                                "message": "You have diffs on computed fields, check the documentation for potential false positive drifts: https://docs.driftctl.com/limitations"
                        }
                ]
        },
        "provider_name": "aws",
        "provider_version": "4.28.0",
        "scan_duration": 7,
        "date": "2022-08-29T08:11:36.782188621Z"
}


オプション「--all」による検出

全リソースが11sで検出されました。早いですね。

$ snyk iac describe --all
Scanned states (1) 
Scan duration: 11s     
Provider version used to scan: 4.28.0. Use --tf-provider-version to use another version.
Snyk Scanning Infrastructure As Code Discrepancies...

  Info:    Resources under IaC, but different to terraform states.
  Resolve: Reapply IaC resources or update into terraform.

Changed resources: 2

State: Generated [ Changed Resources: 1 ]

  Resource Type: aws_ebs_volume
    ID: vol-02205209f17fcac8b
    + timeouts: null => {}

State: tfstate://terraform.tfstate [ Changed Resources: 1 ]

  Resource Type: aws_instance
    ID: i-01b5dbaa8fadcd935
    ~ arn: arn:aws:ec2:ap-northeast-1:xxxx:instance/i-01b5dbaa8fadcd935 => arn:aws:ec2:ap-northeast-1::instance/i-01b5dbaa8fadcd935
    ~ public_dns: ec2-35-77-229-240.ap-northeast-1.compute.amazonaws.com => ec2-54-65-228-246.ap-northeast-1.compute.amazonaws.com
    ~ public_ip: 35.77.229.240 => 54.65.228.246
    - user_data_replace_on_change: false

Unmanaged resources: 71

Service: Unidentified [ Unmanaged Resources: 8 ]

  Resource Type: aws_cloudformation_stack
    ID: xxxx

  Resource Type: aws_iam_group
    ID: xxxx

  Resource Type: aws_iam_group_policy
    ID: xxxx

  Resource Type: aws_network_acl_rule
    ID: xxxx

  Resource Type: aws_s3_bucket_public_access_block
    ID: xxxx

Service: aws_ec2 [ Unmanaged Resources: 13 ]

  Resource Type: aws_ebs_snapshot
    ID: xxxx

  Resource Type: aws_ebs_volume
    ID: xxxx
  
  Resource Type: aws_eip
    ID: xxxx

  Resource Type: aws_eip_association
    ID: xxxx

  Resource Type: aws_instance
    ID: xxxx

  Resource Type: aws_key_pair
    ID: xxxx

Service: aws_iam [ Unmanaged Resources: 26 ]

  Resource Type: aws_iam_access_key
    ID: xxxx

  Resource Type: aws_iam_policy
    ID: xxxx

  Resource Type: aws_iam_policy_attachment
    ID: xxxx

  Resource Type: aws_iam_role
    ID: AWSCloud9SSMAccessRole

  Resource Type: aws_iam_role_policy
    ID: xxxx

  Resource Type: aws_iam_user
    ID: xxxx

Service: aws_route53 [ Unmanaged Resources: 1 ]

  Resource Type: aws_route53_zone
    ID: xxxxx

Service: aws_s3 [ Unmanaged Resources: 1 ]

  Resource Type: aws_s3_bucket
    ID: cf-templates-xxxx-ap-northeast-1

Service: aws_vpc [ Unmanaged Resources: 22 ]

  Resource Type: aws_security_group
    ID: sg-xxxxx

  Resource Type: aws_security_group_rule
    ID: sgrule-xxxx

Test Summary

  Managed Resources: 3
  Changed Resources: 2
  Unmanaged Resources: 71

  IaC Coverage: 4%
  Info: To reach full coverage, remove resources or move it to Terraform.

  Tip: Run --help to find out about commands and flags.
      Scanned with aws p


参考

Snyk IaCでTerraformのドリフト検出をやってみた | DevelopersIO