Extending existing resource with custom fields
I've looked at Convert from JSON to an F# anonymous record and Migrating to IArmResource, and (correct me if I am wrong) it looks like it does not allow adding fields/properties to existing resources (but rather only creating new resources from scratch). Is there a way to just add custom JSON or anonymous record to e.g. a webApp?
More specifically I am looking to mount Azure Storage as a local share in a container app in App Service by adding the following under "properties" (next to e.g. siteConfig that is already generated by Farmer):
"azureStorageAccounts": {
"my-storage-mount": {
"type": "AzureFiles",
"accountName": "storageAccountName",
"shareName": "sharename",
"mountPath": "/data",
"accessKey": "..."
}
}
Would I have to recreate the whole webApp or Microsoft.Web/sites/config JSON manually in order to add this section, or is there a way to just "jack in" a custom field for azureStorageAccounts?
@Yakimych great question. For the moment, it's unfortunately the former - you can only add in entirely new resources, rather than modifying an object. I've been thinking about ways of letting you "plug in" to the very bottom of the pipeline though to modify the JSON that is output as well, but unfortunately nothing that's ready for testing out.
Ok, good to know - closing this then, thanks @isaacabraham! Feel free to ping me in case you would like to test out a PoC or share ideas on how to do this going forward.
Feel free to create an issue specifically on that so we don't forget!
Oh, in that case, is it ok to just reopen this one? What kind of API do you have in mind? I am thinking naively, something like a customJson or mergeWithCustomJson field on a webSite builder that would take the following:
$"""{{
"properties": {{
"azureStorageAccounts": {{
"my-storage-mount": {{
"type": "AzureFiles",
"accountName": "storageAccountName",
"shareName": "shareName",
"mountPath": "/data",
"accessKey": "..."
}}
}}
}}
}}"""
|> Resource.ofJson
But I assume the above would have to be "merged" with the JSON structure that the webApp builder generates, since it might already have a properties section with other fields?
@Yakimych exactly right. We'd have to have some way of splicing the JSON together. A crude option would be that we give you access to the final JSON as a JObject (or whatever the System Text JSON equivalent of that is) and it's your responsibility to return back some valid JSON.
@Yakimych I know it's been a while, but I actually had a bash at this this evening and quite quickly had something that seems like it could be workable:
- Given some ARM resources e.g. a storage account:
let store = storageAccount {
name "storage"
}
- Standard
arm { }block, except notice the extrarewriterkeyword:
let x = arm {
location Location.NorthEurope
add_resources [
store
]
rewriter mapper // rewrite JSON using a function called mapper (see below).
}
-
mapperabove is a function that has the following signature:IArmResource -> obj option. In other words, the function takes in every resource that has been created and it's your responsibility to either rewrite the JSON however you want, or to do nothing (return None).
Here's an example rewriter that looks for Storage Accounts called "store" and adds a new field into the JSON:
let mapper (resource:IArmResource) =
match resource with
| ResourceType storageAccounts & ResourceName "storage" ->
// Convert the object into JSON first...
let json = resource.JsonModel |> JsonSerializer.Serialize |> JsonNode.Parse
// Make any required changes
json["properties"]["defaultToOAuthAuthentication"] <- true
// Return back out as a .NET object
Some (JsonSerializer.Deserialize json)
| _ ->
None
In this prototype I've made a couple of active patterns so that you can more easily find the resource that you want to modify (based on type and name).
This is fairly low-level but it is a complete escape-hatch, you can modify the JSON however you want.
What do you think?
cc @ninjarobot
Would it be possible to map them in the context of the resource being added rather than the whole deployment if there was some new interface like IMappedResource that holds a specific resource and a mapping function that will be able to operate on the JSONModel record for the specific resource? It might make it easier to use without having to dig into lists of resources (could also be in nested templates).
@isaacabraham - nice! It's been a while indeed, but next week I'll try to dig up the use-case I had and double-check if it is solvable this way (but I can't see why it wouldn't).