CDKTF C# Get value from JSON object result of Terraform resource (TFToken)

545 views Asked by At

TLDR Using CDKTF for C#, how do I use the outputContent of the ResourceGroupTemplateDeployment resource in downstream resources? The JSON structure is

{
  apiKey: {
     value: <the value>
  },
  queryKey: {
     value: <the query key value>
  }
}

So I would like to be able to pass this value to say, a Function App's config, but any attempts to get apiKey in C# lead to an error because C# sees the value as a Token and not the JSON object.


I am currently using CDKTF with C# to build out infrastructure. I am using a resource ResourceGroupTemplateDeployment which returns an Output object which is a string that is actually a JSON object. I would like to retrieve two values from this to use in downstream resources. In HCL this is trivial to do, I would just do something like sensitive(jsondecode(azurerm_resource_group_template_deployment.service.output_content).myKey.value) and that would get me what I need. In CDKTF though this is not so straightforward.

While running the initial synth (building and converting C# into json) the Output value is actually a TFToken. This means that it's essentially a placeholder for the real value which is currently unknown because Terraform hasn't actually run the plan/apply. more info on that here.

So it returns a string when the code is first run, but when terraform plan/apply runs it has the real values. I need the values from the JSON object to assign them to the downstream resources, how can I possibly do this if they're undefined when the code is building? Here is my code:

ResourceGroupTemplateDeployment searchService = new ResourceGroupTemplateDeployment(this, "search-service", new ResourceGroupTemplateDeploymentConfig
{
                Name = $"{searchName}-{suffix.Result}",
                ResourceGroupName = rg.Name,
                DeploymentMode = "Incremental",
                ParametersContent = JsonSerializer.Serialize(new Dictionary<string, Dictionary<string,string>>{
                    {"searchServices_dev_request_search_name", new Dictionary<string, string>{
                        { "value", $"{searchName}-{suffix.Result}" }
                        }
                    }
                }),
                TemplateContent = template,
});

SearchTemplateOutput resultObj = JsonSerializer.Deserialize<SearchTemplateOutput>((searchService.OutputContent));
apiKey = resultObj.apiKey;

class SearchTemplateOutput {
        public string apiKey {get; set;}
        public string queryKey {get; set;}
}

The above code does not work and gets an error Unhandled exception. System.Text.Json.JsonException: '$' is an invalid start of a value. because the value is a Token, it's not an actual JSON object yet... but it will be :/

I also tried adding Token.AsString

SearchTemplateOutput resultObj = JsonSerializer.Deserialize<SearchTemplateOutput>(Token.AsString(searchService.OutputContent));

but the result is the same. It blows up trying to deserialize the result because it's a Token, not the actual values that I need to reference later.

I'm not sure how to handle this, the documentation just gives incredibly simple examples where you're getting something simple returned to you and you use the entire object that's returned, but in this situation it's a JSON object with multiple values that gets returned, so I am at a loss for how to access those values.

1

There are 1 answers

0
Jake Boomgaarden On

The answer to my question was found in the AWS documentation which was linked to from the TF documentation. Essentially it said that you can't manipulate JSON lists/strings, and so the solution was to just create the object using the resource group template (since there is an outstanding bug > 1 year in Azure where their API doesn't conform to their own standard which breaks the search service through terraform if using the free tier) and then after it is created, use the associated data object to get the values.


For what it's worth, here is the final setup I have done to be able to get the values safely using C#

DataAzurermSearchService searchSvcData = new DataAzurermSearchService(this, "search-service-data", new DataAzurermSearchServiceConfig
{
     Name = searchService.Name,
     ResourceGroupName = rg.Name
});

apiKey = searchSvcData.PrimaryKey;
queryKey = searchSvcData.QueryKeys.Get(0).GetStringAttribute("key");

This solution might be obvious to someone more well versed with C#, but it took me some time to figure it out. Hopefully it helps someone else someday