I have a scenario where I have in an existing root module in terraform/main.tf the below code, in which the data entity is used for the locals { } in entity_ids:
# terraform/main.tf of root module
data "newrelic_entity" "my_application" {
name = var.nr_entity_name
type = "APPLICATION"
domain = "APM"
}
# locals for passing customized/dynamic widgets data to the child module `nr_dashboard`
locals {
# mappings for multiple widgets
dynamic_widgets = [
{
title = "Requests per minute"
visualization = "billboard"
entity_ids = [data.newrelic_entity.my_application.application_id, ]
nrql = "SELECT rate(count(*), 1 minute) FROM Transaction WHERE appName = '${var.nr_entity_name}'"
row = 1
column = 1
},
{
...
In the child module - the module source is via local path - I also declare the data entity as well:
# terraform/modules/terraform-nr-dashboard/main.tf
# Already existing APM in NewRelic, for your application
data "newrelic_entity" "my_application" {
name = var.nr_entity_name
type = "APPLICATION"
domain = "APM"
}
I think ideally, I would like to not have to declare the data "newrelic_entity" "my_application" in both my root module and in my child module. Is there a cleaner way to do this so I don’t have to declare it in both the root module and the child module? I know a best practice is understandable code, so it’s better to style the code like that then to get too sophisticated/complicated.
Technically, in the future after further development, I may not need the data entity in the child module, but I do right now in at least a child module folder on the same level as the child module above.
I think what you are talking about here is the situation that the Module Composition section talks about under “Dependency Inversion”: you have a module that needs some data that it isn’t directly managing, and that module doesn’t know what is managing that data. The root module can therefore take responsibility for either declaring or reading the object and then passing the information about it into the child module via its input variables.
The first step here would be to determine what information about this “entity” your child module needs and to declare an input variable with an object type constraint in order to receive that data:
variable "newrelic_application" {
type = object({
name = string
id = string
})
}
I’m not familiar with New Relic so I’m not sure which attributes your module might need, but I just picked name and id here because they were the two I saw in your existing examples. Inside your module you can refer to the application ID as var.newrelic_application.id, for example.
Typically when I design module interfaces like this I try to make the variable type constraint a common subset of the corresponding managed resource type and data source that the provider declared, for the convenience of just being able to pass in both objects whole, but it seems like the newrelic provider isn’t really designed with that pattern in mind, because it has this generic newrelic_entity data source and doesn’t have a corresponding managed resource type. No matter though, because we can still use the same pattern just with some extra ceremony inside the module block to reshape the data source result into the object type that the module expects:
data "newrelic_entity" "application" {
name = var.nr_entity_name
type = "APPLICATION"
domain = "APM"
}
module "nr_dashboard" {
source = "./modules/terraform-nr-dashboard"
newrelic_application = {
name = data.newrelic_entity.application.name
id = data.newrelic_entity.application.application_id
}
}
Now both your root module and your nr_dashboard module have access to that data, but only the root module actually directly depends on it.
From the New Relic provider docs it doesn’t look like you can use this provider to declare a new application, but if there were a way and you later refactored so that this configuration were responsible for managing that object, then you could replace the data block with a resource block and pass equivalent data into the module and the module would not need to be modified at all, because it’s agnostic to where that data came from as long as the root module is able to somehow obtain the necessary data to pass in.
data "newrelic_entity" "my_application" {
name = var.nr_entity_name
type = "APPLICATION"
domain = "APM"
}
in both the root and the child module, the child module in fact didn’t referenced that newrelic_entity.my_applicationanywhere else in the child module, meaning it wasn’t apparently even needed for my needs and child module.
Therefore I removed that data declaration entirely from the child module, and left it in the parent module, and it works.
I guess it’s a unique situation where the data entity is used by my root module to get some data which is used by locals - not shown in entirety but somewhat in my original poast - and then those local values get passed to the child module via the the module { } block in the root module, which I didn’t share in my original post (sorry if that would have changed your response a bit):
I’m glad you found a working solution. It sounds like what you’re doing here is essentially like what I was proposing, just with an extra local value in between the data source and the module. The general principle that the module doesn’t know where the data came from still holds, though!