CompositionalIT / farmer

Repeatable Azure deployments with ARM templates - made easy!
https://compositionalit.github.io/farmer
MIT License
525 stars 156 forks source link

Event Hub as dependency is invalid #1086

Open BrianVallelunga opened 9 months ago

BrianVallelunga commented 9 months ago

I'm having issues in setting an Event Hub instance as a dependency of a web app. The issue seems to be the way the Event Hub is referenced. Here's the code:

let myEventHub =
    eventHub {
        namespace_name "my-event-hubs-ns"
        sku EventHub.Standard
        name "my-event-hub"
    }

Then I reference it in a web app:

let myWebApp = webApp {
    name "my-web-app"
    depends_on myEventHub 
    setting "EventHubs" myEventHub.DefaultKey
}

The resulting ARM template has the following:

"dependsOn": [
    "[resourceId('Microsoft.EventHub/namespaces', 'my-event-hubs-ns')]",
    "[resourceId('Microsoft.EventHub/namespaces/eventhubs', 'my-event-hub')]"
]

This results in the following error:

Unable to evaluate template language function 'resourceId': the type 'Microsoft.EventHub/namespaces/eventhubs' requires '2' resource name argument(s).

From what I can tell, the issue is in the second reference to the Event Hub itself, not the namespace. Changing it to include the Event Hub namespace seems to solve the issue:

"dependsOn": [
    "[resourceId('Microsoft.EventHub/namespaces', 'my-event-hubs-ns')]",
    "[resourceId('Microsoft.EventHub/namespaces/eventhubs', 'my-event-hubs-ns', 'my-event-hub')]"
]

I'm happy to take a stab at trying to fix this, but am not quite sure where to look in the code.

BrianVallelunga commented 9 months ago

I've been attempting to fix this issue, but am coming up short. The tricky thing about the EventHub builder is that unlike the ServiceBus builder, the Hub and the Namespace are combined. What I think needs to happen is for the EventHub record to add a Namespace: LinkedResource field that points back to the EventHubNamespace, much like the Queue record for the ServiceBus. This can then be used to generate the correct dependency.

That said, I've been unsuccessful in getting the generation fixed so that the three-part version is output as a dependency. I thought replacing the Create method name with both the namespace name and hub name would work, but I've had no success. This produces the exact same resourceId reference.

// In type EventHub
interface IArmResource with
    member this.ResourceId = eventHubs.resourceId (this.Namespace.Name / this.Name)

    member this.JsonModel =
        {| eventHubs.Create(this.Namespace.Name / this.Name, this.Location, this.Dependencies, this.Tags) with
            properties =
isaacabraham commented 9 months ago

Let me take a look at this one.

BrianVallelunga commented 9 months ago

@isaacabraham Thanks for checking this out.

In an effort to support this properly, I first added a Namespace field to the EventHub type in the Namespaces module.

    type EventHub =
        {
            Name: ResourceName
            Namespace: LinkedResource
            Location: Location
            MessageRetentionDays: int option
            Partitions: int
            Dependencies: ResourceId Set
            CaptureDestination: CaptureDestination option
            Tags: Map<string, string>
        }

Then, in the BuildResources method, I assign it the value:

Namespace = Managed(namespaces.resourceId this.EventHubNamespaceName)

I think this is the correct approach and is modeled after the Service Bus. I believe the problem is in the ARM generation.

BrianVallelunga commented 3 months ago

Thank you. I'll test it out.