Search code examples
terraformterraform-provider-azure

How to compare two Terraform plans for differences?


There is a pipeline that generates a Terraform plan and pauses until a manager approves the changes. It can pass an undetermined period of time for it to be approved (one second, three hours, etc) so the proposed Terraform plan could differ from the Terraform plan executed after the approval due to many reasons like the infrastructure being manually modified (not intended but possible).

The pipeline after the approval runs the second Terraform plan and compares it with the first one generated in the pre-approval stage. The pipeline does the comparison with a git diff and fails if there is a difference. That is not working as expected because the plans differ even if generated one after the other, in a section called relevant_attributes, but the differences are the order in which the JSON is generated, not the content or effective changes. enter image description here

The following scripts are being used to generate the JSONs and compare them out:

terraform show -json expected.tfplan | jq . > expected.json
terraform show -json actual.tfplan | jq . > actual.json
git diff --no-index expected.json actual.json || exit 1

Is there a fix for this approach? Alternatively, are there better ways to compare two Terraform plans for differences in this approval scenario?


Solution

  • You can export your plan with -out=PLAN_FILE and then only apply it when you want.

    For example, you can run

    terraform plan -out tfplan.zip
    

    To show the plan you can run

    terraform show tfplan.zip
    

    Then when you want to apply you just run terraform apply PLAN_FILE

    terraform apply tfplan.zip
    

    Or you can go for other approaches, for example by using approval features on some CI/CD platforms (circleci, or GitHub Actions's deployments for example) where you block the apply step and keep your pipeline pending.

    Or simply treat your IaC as we would treat our code, only deploy when merging to some branches; makes your main branches protected and only require approvals from authorized people. When opening a PR run a simple Terraform plan, and when it gets merged you run terraform apply.


    Edit:

    Apparently, these solutions might not fit your use case, so your approach might be better, you just need to sort your resource_change array with jq in case it gets shuffled between each plan

     terraform show -json expected.tfplan | jq '.resource_changes | sort_by(.address)'