Deploying Azure function With ARM Template

ARM Template

In agile development methodology, we need to repeatedly deploy our solution to the cloud. So we need to automate the deployment process and use the practice of Infrastructure as code. In our code, you can define the infrastructure that needs to be deployed. The infrastructure code is part of our code and we can store it in the repo and version it. 

In Azure we can use the Azure Resource Manage templates to implement the Infrastructure as code concept. The ARM template is a Json file that defines the infrastructure and configuration for your project. In the template, you specify the resources to deploy and the properties for those resources. 

Advantages of ARM Template

Following are the main advantages of using ARM Template
  1. Declarative Syntax: The template will deploy any infrastructure like VM, network infrastructure, storage systems etc
  2. Repeatable results: Repeatedly deploy your infrastructure throughout the development lifecycle and have confidence your resources are deployed in a consistent manner
  3. Built in validation: Resource Manager checks the template before starting the deployment to make sure the deployment will succeed.
  4. CI/CD integration: You can integrate templates into your continuous integration and continuous deployment (CI/CD) tools, which can automate your release pipelines for fast and reliable application and infrastructure updates. 
  5. Tracked deployments: In the Azure portal, you can review the deployment history and get information about the template deployment.

ARM Template Format

A template has the following elements
  1. $schema: Location of the JSON schema file that describes the version of the template language.
  2. contentVersion: Version of the template.
  3. apiProfile: An API version that serves as a collection of API versions for resource types
  4. parameters: Values that are provided when deployment is executed to customize resource deployment.
  5. variables: Values that are used as JSON fragments in the template to simplify template language expressions.
  6. functions: User-defined functions that are available within the template.
  7. resources: Resource types that are deployed or updated in a resource group or subscription.
  8. outputs: Values that are returned after deployment.
Here $schema, contentVersion, resources  are mandatory elements for any template
We can write template expressions that extend the capabilities of JSON file.

ARM Template for deploying Azure Function

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
  "parameters": {
    "environtment": {
      "defaultValue": "dev",
      "type": "String"
    },
    "storageSKU": {
      "defaultValue": "Standard_LRS",
      "allowedValues": [
        "Standard_LRS",
        "Standard_GRS",
        "Standard_RAGRS",
        "Standard_ZRS",
        "Premium_LRS",
        "Premium_ZRS",
        "Standard_GZRS",
        "Standard_RAGZRS"
      ],
      "type": "String"
    },
    "dbConnectionString": {
      "defaultValue": "",
      "type": "String"
    },
    "packageUrl": {
      "defaultValue": "https://gopiportal.blob.core.windows.net/code/AzureFunctions.zip",
      "type": "String"
    }
    
  },
    "variables": {
        "storageAccountName": "[concat('gopitest',parameters('environtment'),'sa')]",
        "servicePlanName": "[concat('gopitest-',parameters('environtment'),'-sp')]",
        "azureFunctionAppName": "[concat('gopitest-',parameters('environtment'),'-af')]",
        "appInsightsName": "[concat('gopitest-',parameters('environtment'),'-ai')]"
    },
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-04-01",
            "name": "[variables('storageAccountName')]",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "[parameters('storageSKU')]"
            },
            "kind": "StorageV2"
        },
        {
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2018-02-01",
            "name": "[variables('servicePlanName')]",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "Y1",
                "tier": "Dynamic"
            },
            "properties": {
                "name": "[variables('servicePlanName')]",
                "computeMode": "Dynamic"
            }
        },
        {
            "type": "Microsoft.Insights/components",
            "apiVersion": "2015-05-01",
            "name": "[variables('appInsightsName')]",
            "location": "[resourceGroup().location]",
            "tags": {
                "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('azureFunctionAppName'))]": "Resource"
            },
            "properties": {
                "ApplicationId": "[variables('azureFunctionAppName')]",
                "Application_Type": "web"
            }
        },
        {
            "type": "Microsoft.Web/sites",
            "apiVersion": "2019-08-01",
            "name": "[variables('azureFunctionAppName')]",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
                "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
    "[resourceId('Microsoft.Storage/components', variables('appInsightsName'))]"
            ],
            "kind": "functionapp",
            "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
                "siteConfig": {
                  "appSettings": [
                    {
                      "name": "AzureWebJobsDashboard",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
                    },
                    {
                      "name": "AzureWebJobsStorage",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
                    },
                    {
                      "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
                    },
                    {
                      "name": "WEBSITE_CONTENTSHARE",
                      "value": "[variables('storageAccountName')]"
                    },
                    {
                      "name": "FUNCTIONS_EXTENSION_VERSION",
                      "value": "~2"
                    },
                    {
                      "name": "FUNCTIONS_WORKER_RUNTIME",
                      "value": "dotnet"
                    },
                    {
                      "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                      "value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
                    },
                    {
                      "name": "dbConnectionString",
                      "value": "[parameters('reportingDBConnectionString')]"
                    }
                  ]
                }
            },
            "resources": [
                {
                    "type": "extensions",
                    "apiVersion": "2015-08-01",
                    "name": "MSDeploy",
                    "location": "[resourceGroup().location]",
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites', variables('azureFunctionAppName'))]"
                    ],
                    "tags": {
                        "displayName": "webappdeploy"
                    },
                    "properties": {
                        "packageUri": "[parameters('packageUrl')]",
                        "dbType": "None",
                        "connectionString": ""
                    }
                }
            ]
        }
    ],
    "outputs": {}
}
The above code will create the following resources.
1) Storage account
For every function app, we need the storage account. So we should create it before function app. Here the storageSKU is a parameter and it has multiple values. We need to choose any one value from the list while deploying the template based on our function app requirement. 
2) Service Plan
After the storage account, we need a service plan for function app. So this part will create Service plan.
3) Applicaiton Insights
The third resource in the above is the Application insights which is required to store the function app insights
4) Function app
The fourth and final one is the Azure function app and it is the place where all the peices are getting together. 
Here, we need the first 3 resources to be created before creating this function app. So we need to tell the azure as this resource is dependsOn those.
"dependsOn": [
 "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
 "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
 "[resourceId('Microsoft.Storage/components', variables('appInsightsName'))]"
]


We can specify the function app settings under properties -> siteConfig -> appSettings as name & value pairs as below

"properties": {
 "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
 "siteConfig": {
   "appSettings": [
  {
    "name": "AzureWebJobsDashboard",
    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
  },
  {
    "name": "AzureWebJobsStorage",
    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
  },
  {
    "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
  },
  {
    "name": "WEBSITE_CONTENTSHARE",
    "value": "[variables('storageAccountName')]"
  },
  {
    "name": "FUNCTIONS_EXTENSION_VERSION",
    "value": "~2"
  },
  {
    "name": "FUNCTIONS_WORKER_RUNTIME",
    "value": "dotnet"
  },
  {
    "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
    "value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
  },
  {
    "name": "dbConnectionString",
    "value": "[parameters('reportingDBConnectionString')]"
  },
  {
    "name": "TestGUID",
    "value": "[parameters('testGuid')]"
  }
   ]
 }
}
The last piece of the process is the sub-resource sourcecontrol inside the FunctionApp. This will define where Azure function code exists. Here I have given packageuri for the function app. 
"resources": [
 {
  "type": "extensions",
  "apiVersion": "2015-08-01",
  "name": "MSDeploy",
  "location": "[resourceGroup().location]",
  "dependsOn": [
   "[resourceId('Microsoft.Web/sites', variables('azureFunctionAppName'))]"
  ],
  "tags": {
   "displayName": "webappdeploy"
  },
  "properties": {
   "packageUri": "[parameters('packageUrl')]",
   "dbType": "None",
   "connectionString": ""
  }
 }
]

Deploying Azure Function

1) Login to the Azure portal
2) Open "Deploy a custom template" (by typing deploy in the search bar)


3) Select "Build your own template in the editor" 
4) Copy the content from Azure ARM template in editor and click on "Save"

5) In the next screen Select the following under Basic settings
Subscription: Azure subscription you want to deploy
Resource Group: Select the existing resource group under which you want to deploy these or Create new 
Location: Select location if you are creating new Resource Group

6) Update the parameters required for the ARM template (Under Settings)  and Accept the Terms and condition by clicking on the checkbox.

7) Click on "Purchase" button. This will deploy the components automatically

Gopikrishna

    Blogger Comment
    Facebook Comment

0 comments:

Post a Comment