Search code examples
terraformterraform-modules

Comparing two maps and storing the result in a variable, 1 for yes, 0 for no


I have two map(objects) that I want to compare and then return the result of that comparison to a value stored within a local variable in my terraform module.

{
   "boot_volume_name": {
      "id": "some id",
      "size": "100GB"
   },
   "block_volume_name": {
      "id": "some id",
      "size": "300GB"
   },
   "block_volume_name2": {
      "id": "some id",
      "size": "1000GB"
   }
}
{
   "boot_volume_name": {
      "id": "some id",
      "size": "100GB"
   },
   "block_volume_name": {
      "id": "some id",
      "size": "400GB"
   },
   "block_volume_name2": {
      "id": "some id",
      "size": "1000GB"
   }
}

In this example, the first JSON map(object) is created based on what is found within the remote state. The second is created based on what is found using given variables that are defined in an auto.tfvars file. What I want to do with this information is return a value that I can use with a terraform resource to either run or not run based on this information. The real reason for this is based on a issue with the cloud provider and their inability to disable replication for drive expansions using their normal resource terraform provider. Ideally how it would work would look something kind of like this (it may not be exact, this is just what's in my head at the moment:

locals {
  volumes = {
    map of volumes generated from the auto.tfvars file
  }
  
  volumes_from_state = {
    map of volumes generated from the state outputs
  }

  variables_with_comparison = {
    logic that I'm trying to figure out, which effectively contains a key, value of the first block volume
  }

  volumes_with_changes = [
    { for key, value in local.volumes : key => value if contains(local.varaibles_with_comparision, key) }
  ]
  
  volumes_no_changes = [
    { for key, value in local.volumes : key => value if !contains(local.varaibles_with_comparision, key) }
  ]

}

resource "null_resource" "disable_replication" {
  for_each = local.volumes_with_changes 
  provisioner "local-exec" {
    command = "command to run api update of volume group to disable replication for each.value.id"
  }
}

resource "null_resource" "disable_replication" {
  for_each =  "local.volumes_no_changes
  provisioner "local-exec" {
    command = "command to run api update of volume group to enable replication for this volume"
  }
}

As you can see I have this conceptually mostly thought out...but I'm not exactly sure on the comparison part of the equation. Any help on this would be greatly appreciated.

Also, believe me, I would rather just use the terraform resource that provisions the volumes to handle the changes because that would be the most ideal situation...however we're in this mess because of the cloud provider...

It is important to note that we are using Terraform version 1.1.7, so anything that involves newer features than that version wont work in our envioronment.


Solution

  • I didn't understand exactly what kind of comparison you need:

    • check if the maps are equal OR
    • get the changed elements between maps

    I've created a sample PoC that hopefully can help you. I'm assuming that both maps have the same keys:

    locals {
      volumes = {
        "boot_volume_name" : {
          "id" : "a",
          "size" : "100GB"
        },
        "block_volume_name" : {
          "id" : "b",
          "size" : "300GB"
        },
        "block_volume_name2" : {
          "id" : "c",
          "size" : "1000GB"
        }
      }
    
      volumes_from_state = {
        "boot_volume_name" : {
          "id" : "a",
          "size" : "100GB"
        },
        "block_volume_name" : {
          "id" : "b",
          "size" : "400GB"
        },
        "block_volume_name2" : {
          "id" : "c",
          "size" : "1000GB"
        }
      }
    
      changed_volumes = {
        for key, value in local.volumes : key => value
        if value != local.volumes_from_state[key]
      }
    
      are_maps_equal = length(local.volumes) == 0
    }
    
    output "changed_volumes" {
      value       = local.changed_volumes
      description = "A map containing the volumes that have changed"
    }
    
    output "are_maps_equal" {
      value       = local.are_maps_equal
      description = "A boolean value indicating whether the two maps are equal or not"
    }
    

    Running terraform apply ... command:

    Changes to Outputs:
      + are_maps_equal  = false
      + changed_volumes = {
          + block_volume_name = {
              + id   = "b"
              + size = "300GB"
            }
        }
    
    You can apply this plan to save these new output values to the Terraform
    state, without changing any real infrastructure.
    
    Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
    
    Outputs:
    
    are_maps_equal = false
    changed_volumes = {
      "block_volume_name" = {
        "id" = "b"
        "size" = "300GB"
      }
    }
    

    Please note that I've used Terraform v1.7, but hopefully the code is compatible with older versions (including v1.1).