I have an Azure Resource group that contains an Azure Logic App that calls into an Azure Function.
I exported this Resource Group as an ARM template so I can re-import the resources to another Azure Subscription. This works fine, but the problem is, the Azure Function Code (100+ line c# file) is all included on one line of the JSON ARM template file. This makes is really hard to read or modify the Azure Function from the template itself.
Is there an easy way to work around this? Ideally my Azure Function would be in it's own file (run.csx), and the Azure JSON ARM template would just reference that external file.
Here is my JSON blob for the Function Resource in the ARM template. The line that contains run.csx for a key is my concern, how can I make this code more readable and easy for devs to edit?
{
"apiVersion": "2015-08-01",
"name": "[concat(parameters('test_site_name'),'\/ProvisionUser')]",
"type": "Microsoft.Web\/sites\/functions",
"properties": {
"config": {
"bindings": [
{
"authLevel": "function",
"name": "req",
"type": "httpTrigger",
"direction": "in"
},
{
"name": "return",
"direction": "out",
"type": "http"
}
]
},
"files": {
"run.csx": "LOTS OF C# CODE HERE - LOTS OF C# CODE HERE FROM MY AZURE FUNCTION - LOTS OF C# CODE HERE FROM MY AZURE FUNCTION - LOTS OF C# CODE HERE FROM MY AZURE FUNCTION - LOTS OF C# CODE HERE FROM MY AZURE FUNCTION - LOTS OF C# CODE HERE FROM MY AZURE FUNCTION - LOTS OF C# CODE HERE FROM MY AZURE FUNCTION - ",
"project.json": "{\r\n \"frameworks\": {\r\n \"net46\": {\r\n \"dependencies\": {\r\n \"Microsoft.IdentityModel.Clients.ActiveDirectory\": \"3.13.8\",\r\n \"Newtonsoft.Json\": \"10.0.2\",\r\n \"Microsoft.Sdk.CoreAssemblies\" : \"8.2.0.2\"\r\n }\r\n }\r\n }\r\n}"
}
}
}
You have some options:
Quick fix to your question: Run your ARM template through some code formatter. You may be in luck if you try copy-paste the template in to a json file in Visual Studio and then CTRL-K,CTRL-D to auto format it. I have not tried this but it may work. You can also cut the code out and format it using any one of a number of online formatting tools or using Visual Studio.
Deploy your functions from a source control system. Treat your infrastructure and code separately. I.e. Create your functions PaaS service from your ARM templates, but then use a CI/CD process to deploy your code and configuration (the functions).
Wrap your code in to an assembly, deploy the assembly to your function host and reference it in your function. This is called an external reference (documentation here) and will limit the amount of code in your function to plumbing, with your logic kept in a separate assembly. You will still need to work out how to deploy the assembly through script or your CI/CD process.
In short, and in line with the comments on your question, you need to support your Azure function development with a little more diligence from a development process perspective. This becomes even more critical if you will have a number of developers working on your functions.
Good luck!
Related
I have a class library, timer-based Azure Function that is deployed using an ARM template. Everything works fine except I would like a slightly different behavior based on the target environment. When deploying to a test environment I would like the function to be initially disabled but in production it should always be enabled. Is this possible?
My current workaround is to have an app setting that tells the function to immediately exit when set to a specific value. However, this seems like a poor solution, especially since the timer-triggered function is executed quite frequently. To solve this I manually disables the function using the following switch in the Azure portal:
Is there perhaps possible to specify the desired state of this switch from the ARM template?
Seems you don't need to set to a specific value in the app settings, azure function has a built-in property.
Try to use the setting in the template snippet below to disable the function, it should work.
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobs.MessageQueueMonitorFunction.Disabled",
"value": "true"
}
]
}
Expanding on Joy's answer, which worked like a charm!
For the benefit of others, the "name" property above is composed like so:
AzureWebJobs.<YouFunctionName>.Disabled
where <YouFunctionName> is specified in your template.json here:
{
"resources": [
"name": "<YourFunctionName>",
"type": "functions",
"properties": {
"config": {
"bindings": [
{
...
}
]
}
}
]
}
I am deploying a bunch of resources from Arm template. I am trying to provide the resource name unique by using this "[uniqueString(subscription().subscriptionId)]" . I have the templates hosted in github and using the Deploy to Azure button am trying to deploy, but the site just shows the plain string with the function and not the value. Any idea would be appreciated.
Between here's my code
"parameters": {
"functionAppName": {
"type": "string",
"metadata": {
"description": "Name of the function app"
},
"defaultValue": "[concat('asfnapp',uniqueString(resourceGroup().id))]"
}
}
I have the rest of the parameters in the same way.
Edit : Added repository URL - GITHUB
Ok, I thought you were referring to one of the templates in the QuickStart repo - they all (by default) go through this UX: https://ms.portal.azure.com/#create/Microsoft.Template
it looks like you're not using that UX - and I suspect that what you're using does not handle expressions in parameters (just assumes they are strings). So nothing you can do to fix that (your template is fine).
A workaround would be to use this:
https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fhariharan618%2Ftest%2Fmaster%2Fazuredeploy.json
I'm trying to create an Azure function that will output to a table. I'm using the Azure Function App, and so, as I currently understand it, the function.json is generated for me by the SDK. My function definition is as follows:
public static HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req,
TraceWriter log,
[StorageAccount("table_storage")] ICollector<TableItem> outputTable)
I've defined TableItem as a class that inherits from TableEntity. When I deploy this and look at the generated function.json, it doesn't mention the output parameter binding:
{
"generatedBy": "Microsoft.NET.Sdk.Functions.Generator-1.0.7",
"configurationSource": "attributes",
"bindings": [
{
"type": "httpTrigger",
"methods": [
"post"
],
"authLevel": "function",
"name": "req"
}
],
"disabled": false,
"scriptFile": "../bin/FunctionApp5.dll",
"entryPoint": "FunctionApp5.DeliveryComplete.Run"
}
If I run this from Visual Studio, I get the following error:
Cannot bind parameter 'outputTable' to type ICollector`1
I have a few questions about this behaviour: the first and main one is, why is function.json not showing the output binding? Secondly, I understand why this can't be edited when you deploy from VS, but is there a way to manage the bindings without guesswork (I came across using ICollector in this post), but I can't find anywhere else that says it should or shouldn't be there.
Finally, how does (or does) running this from the desktop interact with the published function: does it connect to the published version of the function, or does it generate the function.json locally?
That's a common source of confusion, but input and output bindings are not visible in generated function.json, only trigger does. They will still work normally.
If you are trying to write to Table Storage, you should use Table attribute instead of StorageAccount. ICollector is mentioned in Azure Table storage bindings for Azure Functions.
When running locally, the files stay locally and run in local runtime, without deployment to Azure. They might still interact with real Azure services via bindings.
In my ARM template i have piece of code:
"name": "[variables('logicappname')]",
"type": "Microsoft.Logic/workflows",
"location": "[resourceGroup().location]",
"apiVersion": "2016-06-01",
"dependsOn": [
"[resourceId('Microsoft.Web/connections', variables('servicebusConnectionName'))]",
"[resourceId('Microsoft.Web/sites/sourcecontrols', variables('functionAppName'), 'web')]"
],
"tags": {
"displayName": "display-name"
},
And in resources array:
{
"apiVersion": "2015-08-01",
"name": "web",
"type": "sourcecontrols",
"dependsOn": [
"[resourceId('Microsoft.Web/Sites', variables('functionAppName'))]"
],
"properties": {
"RepoUrl": "[parameters('repoURL')]",
"branch": "[variables('branch')]",
"IsManualIntegration": true
}
}
variables('branch') = 'master-dev'
variables('repoUrl') = https://user:token#MYREPO.visualstudio.com/DefaultCollection/PROJECTNAME/_git/REPO
In my repo I have azure functions project, which has this structure:
function-1, function-1/function.json, function-1/run.csx
function-2, function-2/function.json, function-2/run.csx
.gitignore
.appsettings.json
host.json
local.settings.json
read_me.html
README.md
In this case everything works well.
Now I need to create few new projects in the same solution - Core, tests.
The structure now looks like that (let's ignore the tests proj):
Core
Core/Properties/AssemblyInfo.cs
Core/bin/Debug/Core.dll
Core/Core.csproj
OrdersService.cs
function-1, function-1/function.json, function-1/run.csx
function-2, function-2/function.json, function-2/run.csx
.gitignore
.appsettings.json
host.json
local.settings.json
read_me.html
README.md
Once i have Core.dll which has some code I can include the dll in my function-1 (run.csx):
#r ".\..\Core\bin\Debug\Core.dll"
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
var service = Core.OrdersService.GetInstance();
return req.CreateResponse(HttpStatusCode.OK, service.GetOrderHistory());
}
Now when I deploying the azure function into the portal (by current arm template) I can't finish deployment. I have an error that the function "function-1" doesn't exist. When I go to the portal it's the same - the azure function has been created, but inside the function, there are no any methods.
P.S. Changes on local work well - I can call endpoints http://localhost:7071/api/function-1 (in debug and normal mode).
I guess, the problem is with "Core" folder, which does not match an azure function/method and by this way, i can't publish my repo into the azure portal.
My questions:
It's possible to specify in a repoUrl or by Microsoft.sourcecontrols resource, that I want to create Azure functions with some method, but from a specified subfolder? By this way, i'll solve it, just by copying the azure function project into the specific folder and then start to use them in azure function.
It's possible to specify that I want to use some specific files/folders in deployment from my VSO repo into the azure function?
My first suggestion would be that you can move to just using class libraries for everything. .csx is only needed if you want the ability to edit in the portal, but you can use normal .dll's for your functions. Check out this blog post: https://blogs.msdn.microsoft.com/webdev/2017/11/15/improvements-to-azure-functions-in-visual-studio/
Git deploy will copy all files over, so not sure why it's not including everything.
If you could share your exact error logs you see from the deployment, that'd help to debug this specific case, but I think the new VS tooling would have a cleaner experience for you than just using C# script.
I've been having some issues deploying a dscExtension to an Azure virtual machine scale set (VMSS) using a deployment template.
Here's how I've added it to my template:
{
"name": "dscExtension",
"properties": {
"publisher": "Microsoft.Powershell",
"type": "DSC",
"typeHandlerVersion": "2.9",
"autoUpgradeMinorVersion": true,
"settings": {
"ModulesUrl": "[concat(parameters('_artifactsLocation'), '/', 'MyDscPackage.zip', parameters('_artifactsLocationSasToken'))]",
"ConfigurationFunction": "CmvmProcessor.ps1\\CmvmProcessor",
"Properties": [
{
"Name": "ServiceCredentials",
"Value": {
"UserName": "parameters('administratorLogin')",
"Password": "parameters('administratorLoginPassword')"
},
"TypeName": "System.Management.Automation.PSCredential"
}
]
}
}
}
The VMSS itself is successfully deploying, but when I browse the InstanceView of the individual VMs, the dscExtension shows the failed status with an error message.
The problems I'm having are as follows:
The ARM deployment does not try to update the dscExtension upon redeploy. I am used to MSDeploy web app extensions where the artifacts are updated and the code is redeployed on each new deployment. I do not know how to force it to update the dscExtension with new binaries. In fact it only seems to give an error on the first deploy of the VMSS, then it won't even try again.
The error I'm getting is for old code that doesn't exist anymore.
I had a bug previously in a custom DSC Powershell script where I tried to use the -replace operator which is supposed to create a $Matches variable but it was saying $Matches didn't exist.
In any case, I've since refactored the code and deleted the entire resource group then redeployed. The dscExtension is still giving the same error. I've verified the blob storage account where my DSC .zip is located no longer has the code which is capable of producing this error message. Azure must be caching the dscExtension somewhere. I can't get it to use my new blob .zip that I upload before each deployment.
Any insight into the DSC Extension and how to force it to update on deploy?
It sounds like you may be running into multiple things here, so trying the simple one first. In order to get a VM extension to run on a subsequent deployment you have to "seed" it. (and you're right this is different than the rest of AzureRM) Take a look at this template:
https://github.com/bmoore-msft/AzureRM-Samples/blob/master/VMDSCInstallFile/azuredeploy.json
There is a property on the DSC extension called:
"forceUpdateTag" : "changeThisToEnsureScriptRuns-maxlength=50",
The property value must be different if you ever want the extension to run again. So for example, if you wanted it to run every time you'd seed it with a random number or a guid. You could also use version numbers if you wanted to version it somehow. The point is, if the value in the template is the same as the one you're passing in, the extension won't run again.
That sample uses a VM, but the VMSS syntax should be the same. That property also applies to other extensions (e.g. custom script).
The part that seems odd is that you said you deleted the entire RG and couldn't get it to accept the new package... That sounds bad (i.e. like a bug). If the above doesn't fix it, we may need to dig deeper into the template and script. LMK...