Mock_outputs and complex outputs - lower maintenance overhead?

tl;dr - Has anyone come up with a way to programmatically generate mock_outputs when complex objects are in play?

Hey all, I’m using terragrunt for a multi-account setup and have hit a concern around mocking outputs. In general, I like the functionality as it keeps plan-all available when dependencies aren’t yet in place. That said, with Terraform 0.12 I have been taking to using complex outputs from foundational modules like VPCs. Therein will nested objects such as subnet lists, security groups, etc.

To keep mocks up to date I’ve been mocking the pieces of the output needed for each module. For a basic CodeBuild module though, I am already using five separate dependency areas so my mocks are getting verbose (at least that’s my opinion :wink: ).

I thought to JSON encode them and load from a file into a local variable. This works, though I’m still on the hook for keeping the mocks up to date manually. Then I tried running terraform output on my source VPC module, saving to JSON and then loading that into a local variable. Unfortunately, Terraform’s JSONified output doesn’t match as there are extra type and value keys. At this point I’m back to my tl;dr, which is my desire to not handcode my mock_outputs. Does anyone have a sophisticated approach here?

Thanks!
haylo75

1 Like

I’m also interested in a solution here. I need to run plans for basic config like Network Firewall that requires subnetwork ip cidr ranges, which are nested like below.

pub_subnw_range         = dependency.vpc.outputs.public_subnetwork.ip_cidr_range
  pub_subnw_range_scndry  = dependency.vpc.outputs.public_subnetwork.secondary_ip_range[0].ip_cidr_range

I’ve tried using parenthesis in the mock_outputs block but does not work either.

(public_subnetwork.ip_cidr_range) = "10.1.10.0/24"

@haylo75 can you share how you worked around this with locals? did you create separate input vars for plan that uses local.x vars? I believe we need to maintain the dependency.x.outputs.x format for when the data actually exists.

I’ve thrown away my code which does the locals, though all you need to do is create a JSON file with the same structure as the outputs you wish to mock. Then load that file into a variable and translate it with the jsondecode function, and pass the decoded object to the mock_outputs argument.

thanks @haylo75 . While implementing your workaround, I realized below:

  1. pre-terragrunt, I was using the data resource to grab subnet ip cidr ranges for use by other modules.
  2. post-terragrunt, I am making use of the dependency block, which makes data resource unnecessary, so I don’t think I need to grab the same objects in the same way as before. Instead, I can create output vars in each module (for instance VPC) for instance below which will give me the otherwise nested cidr block in a variable, for instance one is called “public_subnetwork_secondary_cidr_block”.
output "public_subnetwork_secondary_cidr_block" {
  value = google_compute_subnetwork.vpc_subnetwork_public.secondary_ip_range[0].ip_cidr_range
}
  1. Now in the dependency block, I can reference this variable by its name as saved in the tfstate file. This way, I don’t need to worry about grabbing things in a list in the dependency block.

This is not a solution to your original request…which I am still interested in.

If I understand the request correctly, are you looking for a way to format the outputs for use with mock_outputs?

E.g, if terragrunt had a command: terragrunt output-mock which would output a json file in the format understood by dependency blocks that you can then load using jsondecode, would that suffice?

If so, I think that is workable. Can you create an issue on the terragrunt repo with this suggestion?

Yori