├── .github
├── CODE_OF_CONDUCT.md
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── function-app-dedicated-plan
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── main.bicep
├── function-app-deployment-slot
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── main.bicep
├── function-app-linux-consumption-remote-build
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── main.bicep
├── function-app-linux-consumption
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── main.bicep
├── function-app-linux-flex-consumption
├── README.md
├── azuredeploy.json
└── azuredeploy.parameters.json
├── function-app-premium-plan
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── main.bicep
├── function-app-private-endpoints-storage-private-endpoints
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
├── images
│ └── function-app-private-endpoints-storage-private-endpoints.jpg
└── main.bicep
├── function-app-storage-private-endpoints
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
├── images
│ └── function-app-storage-private-endpoints.jpg
└── main.bicep
├── function-app-vnet-integration
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── main.bicep
├── function-app-windows-consumption
├── README.md
├── azuredeploy.json
├── azuredeploy.parameters.json
└── main.bicep
├── images
└── deploytoazure.png
├── zip-deploy-arm-az-cli
├── README.md
├── azuredeploy.json
└── azuredeploy.parameters.json
├── zip-deploy-arm-github-workflow
├── README.md
├── azuredeploy.json
└── workflow.yml
└── zip-deploy-run-from-package
├── README.md
├── azuredeploy.json
└── azuredeploy.parameters.json
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 | > Please provide us with the following information:
5 | > ---------------------------------------------------------------
6 |
7 | ### This issue is for a: (mark with an `x`)
8 | ```
9 | - [ ] bug report -> please search issues before submitting
10 | - [ ] feature request
11 | - [ ] documentation issue or request
12 | - [ ] regression (a behavior that used to work and stopped in a new release)
13 | ```
14 |
15 | ### Minimal steps to reproduce
16 | >
17 |
18 | ### Any log messages given by the failure
19 | >
20 |
21 | ### Expected/desired behavior
22 | >
23 |
24 | ### OS and Version?
25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?)
26 |
27 | ### Versions
28 | >
29 |
30 | ### Mention any other details that might be useful
31 |
32 | > ---------------------------------------------------------------
33 | > Thanks! We'll be in touch soon.
34 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Purpose
2 |
3 | * ...
4 |
5 | ## Does this introduce a breaking change?
6 |
7 | ```
8 | [ ] Yes
9 | [ ] No
10 | ```
11 |
12 | ## Pull Request Type
13 | What kind of change does this Pull Request introduce?
14 |
15 |
16 | ```
17 | [ ] Bugfix
18 | [ ] Feature
19 | [ ] Code style update (formatting, local variables)
20 | [ ] Refactoring (no functional changes, no api changes)
21 | [ ] Documentation content changes
22 | [ ] Other... Please describe:
23 | ```
24 |
25 | ## How to Test
26 | * Get the code
27 |
28 | ```
29 | git clone [repo-address]
30 | cd [repo-name]
31 | git checkout [branch-name]
32 | npm install
33 | ```
34 |
35 | * Test the code
36 |
37 | ```
38 | ```
39 |
40 | ## What to Check
41 | Verify that the following are valid
42 | * ...
43 |
44 | ## Other Information
45 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to [project-title]
2 |
3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a
4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
6 |
7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide
8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
9 | provided by the bot. You will only need to do this once across all repos using our CLA.
10 |
11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
14 |
15 | - [Code of Conduct](#coc)
16 | - [Issues and Bugs](#issue)
17 | - [Feature Requests](#feature)
18 | - [Submission Guidelines](#submit)
19 |
20 | ## Code of Conduct
21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
22 |
23 | ## Found an Issue?
24 | If you find a bug in the source code or a mistake in the documentation, you can help us by
25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can
26 | [submit a Pull Request](#submit-pr) with a fix.
27 |
28 | ## Want a Feature?
29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub
30 | Repository. If you would like to *implement* a new feature, please submit an issue with
31 | a proposal for your work first, to be sure that we can use it.
32 |
33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
34 |
35 | ## Submission Guidelines
36 |
37 | ### Submitting an Issue
38 | Before you submit an issue, search the archive, maybe your question was already answered.
39 |
40 | If your issue appears to be a bug, and hasn't been reported, open a new issue.
41 | Help us to maximize the effort we can spend fixing issues and adding new
42 | features, by not reporting duplicate issues. Providing the following information will increase the
43 | chances of your issue being dealt with quickly:
44 |
45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
46 | * **Version** - what version is affected (e.g. 0.1.2)
47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
48 | * **Browsers and Operating System** - is this a problem with all browsers?
49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps
50 | * **Related Issues** - has a similar issue been reported before?
51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
52 | causing the problem (line of code or commit)
53 |
54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new].
55 |
56 | ### Submitting a Pull Request (PR)
57 | Before you submit your Pull Request (PR) consider the following guidelines:
58 |
59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR
60 | that relates to your submission. You don't want to duplicate effort.
61 |
62 | * Make your changes in a new git fork:
63 |
64 | * Commit your changes using a descriptive commit message
65 | * Push your fork to GitHub:
66 | * In GitHub, create a pull request
67 | * If we suggest changes then:
68 | * Make the required updates.
69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request):
70 |
71 | ```shell
72 | git rebase master -i
73 | git push -f
74 | ```
75 |
76 | That's it! Thank you for your contribution!
77 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | page_type: sample
3 | languages:
4 | - csharp
5 | - python
6 | - java
7 | - nodejs
8 | - typescript
9 | - json
10 | products:
11 | - azure-functions
12 | - azure
13 | ---
14 |
15 | # ARM Templates for Function App Deployment
16 |
17 | This repo contains currently available Azure Resource Manager templates for deploying Function App with recommended settings and best practices.
18 |
19 | ## Flex Consumption
20 |
21 | The examples for the Flex Consumption plan are located in a different place.
22 |
23 | Please visit [FlexConsumption Samples](https://github.com/azure-samples/azure-functions-flex-consumption-samples/) for these scenarios.
24 |
25 | 1. Functions Quickstarts using Azure Developer CLI to create and deploy your code.
26 | 2. ARM Template [Sample](https://github.com/Azure-Samples/azure-functions-flex-consumption-samples/blob/main/IaC/armtemplate/README.md)
27 | 3. Bicep Template [Sample](https://github.com/Azure-Samples/azure-functions-flex-consumption-samples/blob/main/IaC/bicep/README.md)
28 | 4. Terraform Template [Sample](https://github.com/Azure-Samples/azure-functions-flex-consumption-samples/blob/main/IaC/terraform/README.md)
29 |
30 | Create and deploy Functions app for following OS and SKU combinations:
31 |
32 | 1. Create [Function App with Premium Plan](/function-app-premium-plan) on Windows/Linux
33 | 2. Create [Function App with Dedicated Plan](/function-app-dedicated-plan) on Windows/Linux
34 | 3. Create [Function App with Consumption Plan on Windows](/function-app-windows-consumption)
35 | 4. Create Function App with Consumption Plan on Linux:
36 | - [Deploy zip package with Run From Package](/function-app-linux-consumption)
37 | - [Deploy zip package with Remote Build](/function-app-linux-consumption-remote-build)
38 | 5. Create [Function App with Flex Consumption Plan on Linux](/function-app-linux-flex-consumption)
39 |
40 | Create Functions app and resources for following networking scenarios:
41 |
42 | 1. Create [Function App with a Deployment Slot](/function-app-deployment-slot)
43 | 2. Create [Function App with Virtual Network Integration](/function-app-vnet-integration)
44 | 3. Create [Function App with Azure Storage private endpoints](/function-app-storage-private-endpoints)
45 | 4. Create [Function App with private endpoints and Azure Storage with private endpoints](/function-app-private-endpoints-storage-private-endpoints)
46 |
47 | Deploy Functions app code for following scenarios:
48 | 1. Deploy [Function App with ZipDeploy using Run From Package](/zip-deploy-run-from-package)
49 | 2. If Function App with private endpoints and Azure Storage with private endpoints:
50 | - Deploy [Function App with ARM template using Az CLI](/zip-deploy-arm-az-cli)
51 | - Deploy [Function App with ARM template in GitHub Workflow](/zip-deploy-arm-github-workflow)
52 |
53 | This repo also contains wiki pages on the following:
54 |
55 | 1. [Best Practices Guide](../../wiki/Best-Practices-Guide)
56 | 2. [Frequently Asked Questions (FAQs)](../../wiki/Frequently-Asked-Questions-(FAQs))
57 | 3. [Important App Settings for Function Apps](../../wiki/App-Settings-for-Function-Apps)
58 | 4. [ARM Templates for Function App with different Hosting Plans](../../wiki/ARM-Templates-for-Function-Apps-with-different-Hosting-Plans)
59 |
60 | For more information on deploying ARM template, please refer: [Deploy resources with ARM templates](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deploy-portal)
61 |
62 |
63 |
--------------------------------------------------------------------------------
/function-app-dedicated-plan/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a dedicated hosting plan, meaning it will be run and billed just like any App Service site.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-dedicated-plan
8 | languages:
9 | - bicep
10 | - json
11 | ---
12 | # Azure Function App Hosted on Dedicated Plan
13 |
14 | This sample Azure Resource Manager template deploys an Azure Function App hosted on Dedicated plan and required resource including ZipDeploy extension to mount zip package for deployment.
15 |
16 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-dedicated-plan%2Fazuredeploy.json)
17 |
18 | ### OS
19 |
20 | This template has a parameter `functionPlanOS` to choose Windows or Linux OS. Windows is selected by default. If you choose Linux, then parameter `linuxFxVersion` will be required, so you can skip it for Windows.
21 |
22 | ### Dedicated Plan
23 |
24 | The Azure Function app provisioned in this sample uses an [Azure Functions Dedicated plan](https://docs.microsoft.com/en-us/azure/azure-functions/dedicated-plan).
25 |
26 | + **Microsoft.Web/serverfarms**: The Azure Functions Dedicated plan (a.k.a. App Service plan)
27 |
28 | ### Azure Function App
29 |
30 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) app setting to connect to a Storage Account.
31 |
32 | + **Microsoft.Web/sites**: The function app instance.
33 |
34 | ### ZipDeploy Extension
35 |
36 | The Zip Deploy extension is added along with recommended app setting `WEBSITE_RUN_FROM_PACKAGE=1` to mount the zip package for deployment. This is the recommended path for deployment, except for [Linux Consumption Plan](/function-app-linux-consumption)
37 |
38 | + **Microsoft.Web/sites/extensions**: The ZipDeploy extension.
39 |
40 | ### Azure Storage account
41 |
42 | The Storage account that the Function uses for operation and for file contents.
43 |
44 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
45 |
46 | ### Application Insights
47 |
48 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
49 |
50 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
51 |
52 | `Tags: Microsoft.Storage/storageAccounts, microsoft.insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites, Microsoft.Web/sites/extensions`
53 |
--------------------------------------------------------------------------------
/function-app-dedicated-plan/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.10.61.36676",
8 | "templateHash": "16616879859592489661"
9 | }
10 | },
11 | "parameters": {
12 | "functionAppName": {
13 | "type": "string",
14 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
15 | "metadata": {
16 | "description": "The name of the Azure Function app."
17 | }
18 | },
19 | "storageAccountType": {
20 | "type": "string",
21 | "defaultValue": "Standard_LRS",
22 | "allowedValues": [
23 | "Standard_LRS",
24 | "Standard_GRS",
25 | "Standard_RAGRS"
26 | ],
27 | "metadata": {
28 | "description": "Storage Account type"
29 | }
30 | },
31 | "location": {
32 | "type": "string",
33 | "defaultValue": "[resourceGroup().location]",
34 | "metadata": {
35 | "description": "Location for all resources."
36 | }
37 | },
38 | "appInsightsLocation": {
39 | "type": "string",
40 | "defaultValue": "[resourceGroup().location]",
41 | "metadata": {
42 | "description": "Location for Application Insights"
43 | }
44 | },
45 | "functionWorkerRuntime": {
46 | "type": "string",
47 | "defaultValue": "node",
48 | "allowedValues": [
49 | "dotnet",
50 | "node",
51 | "python",
52 | "java"
53 | ],
54 | "metadata": {
55 | "description": "The language worker runtime to load in the function app."
56 | }
57 | },
58 | "functionPlanOS": {
59 | "type": "string",
60 | "defaultValue": "Windows",
61 | "allowedValues": [
62 | "Windows",
63 | "Linux"
64 | ],
65 | "metadata": {
66 | "description": "Specifies the OS used for the Azure Function hosting plan."
67 | }
68 | },
69 | "functionAppPlanSku": {
70 | "type": "string",
71 | "defaultValue": "S1",
72 | "allowedValues": [
73 | "S1",
74 | "S2",
75 | "S3"
76 | ],
77 | "metadata": {
78 | "description": "Specifies the Azure Function hosting plan SKU."
79 | }
80 | },
81 | "packageUri": {
82 | "type": "string",
83 | "metadata": {
84 | "description": "The zip content url."
85 | }
86 | },
87 | "linuxFxVersion": {
88 | "type": "string",
89 | "defaultValue": "",
90 | "metadata": {
91 | "description": "Only required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
92 | }
93 | }
94 | },
95 | "variables": {
96 | "hostingPlanName": "[parameters('functionAppName')]",
97 | "applicationInsightsName": "[parameters('functionAppName')]",
98 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]",
99 | "isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]"
100 | },
101 | "resources": [
102 | {
103 | "type": "Microsoft.Storage/storageAccounts",
104 | "apiVersion": "2022-05-01",
105 | "name": "[variables('storageAccountName')]",
106 | "location": "[parameters('location')]",
107 | "sku": {
108 | "name": "[parameters('storageAccountType')]"
109 | },
110 | "kind": "Storage"
111 | },
112 | {
113 | "type": "Microsoft.Web/serverfarms",
114 | "apiVersion": "2022-03-01",
115 | "name": "[variables('hostingPlanName')]",
116 | "location": "[parameters('location')]",
117 | "sku": {
118 | "tier": "Standard",
119 | "name": "[parameters('functionAppPlanSku')]",
120 | "family": "S",
121 | "capacity": 1
122 | },
123 | "properties": {
124 | "reserved": "[variables('isReserved')]"
125 | }
126 | },
127 | {
128 | "type": "Microsoft.Insights/components",
129 | "apiVersion": "2020-02-02",
130 | "name": "[variables('applicationInsightsName')]",
131 | "location": "[parameters('appInsightsLocation')]",
132 | "tags": {
133 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
134 | },
135 | "properties": {
136 | "Application_Type": "web"
137 | },
138 | "kind": "web"
139 | },
140 | {
141 | "type": "Microsoft.Web/sites",
142 | "apiVersion": "2022-03-01",
143 | "name": "[parameters('functionAppName')]",
144 | "location": "[parameters('location')]",
145 | "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
146 | "properties": {
147 | "reserved": "[variables('isReserved')]",
148 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
149 | "siteConfig": {
150 | "alwaysOn": true,
151 | "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
152 | "appSettings": [
153 | {
154 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
155 | "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName')), '2015-05-01').InstrumentationKey]"
156 | },
157 | {
158 | "name": "AzureWebJobsStorage",
159 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
160 | },
161 | {
162 | "name": "FUNCTIONS_EXTENSION_VERSION",
163 | "value": "~4"
164 | },
165 | {
166 | "name": "FUNCTIONS_WORKER_RUNTIME",
167 | "value": "[parameters('functionWorkerRuntime')]"
168 | },
169 | {
170 | "name": "WEBSITE_NODE_DEFAULT_VERSION",
171 | "value": "~14"
172 | },
173 | {
174 | "name": "WEBSITE_RUN_FROM_PACKAGE",
175 | "value": "1"
176 | }
177 | ]
178 | }
179 | },
180 | "dependsOn": [
181 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
182 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
183 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
184 | ]
185 | },
186 | {
187 | "type": "Microsoft.Web/sites/extensions",
188 | "apiVersion": "2022-03-01",
189 | "name": "[format('{0}/{1}', parameters('functionAppName'), 'zipdeploy')]",
190 | "properties": {
191 | "packageUri": "[parameters('packageUri')]"
192 | },
193 | "dependsOn": [
194 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
195 | ]
196 | }
197 | ]
198 | }
--------------------------------------------------------------------------------
/function-app-dedicated-plan/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-dedicated-plan/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('Storage Account type')
5 | @allowed([
6 | 'Standard_LRS'
7 | 'Standard_GRS'
8 | 'Standard_RAGRS'
9 | ])
10 | param storageAccountType string = 'Standard_LRS'
11 |
12 | @description('Location for all resources.')
13 | param location string = resourceGroup().location
14 |
15 | @description('Location for Application Insights')
16 | param appInsightsLocation string = resourceGroup().location
17 |
18 | @description('The language worker runtime to load in the function app.')
19 | @allowed([
20 | 'dotnet'
21 | 'node'
22 | 'python'
23 | 'java'
24 | ])
25 | param functionWorkerRuntime string = 'node'
26 |
27 | @description('Specifies the OS used for the Azure Function hosting plan.')
28 | @allowed([
29 | 'Windows'
30 | 'Linux'
31 | ])
32 | param functionPlanOS string = 'Windows'
33 |
34 | @description('Specifies the Azure Function hosting plan SKU.')
35 | @allowed([
36 | 'S1'
37 | 'S2'
38 | 'S3'
39 | ])
40 | param functionAppPlanSku string = 'S1'
41 |
42 | @description('The zip content url.')
43 | param packageUri string
44 |
45 | @description('Only required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
46 | param linuxFxVersion string = ''
47 |
48 | var hostingPlanName = functionAppName
49 | var applicationInsightsName = functionAppName
50 | var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
51 | var isReserved = ((functionPlanOS == 'Linux') ? true : false)
52 |
53 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
54 | name: storageAccountName
55 | location: location
56 | sku: {
57 | name: storageAccountType
58 | }
59 | kind: 'Storage'
60 | }
61 |
62 | resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
63 | name: hostingPlanName
64 | location: location
65 | sku: {
66 | tier: 'Standard'
67 | name: functionAppPlanSku
68 | family: 'S'
69 | capacity: 1
70 | }
71 | properties: {
72 | reserved: isReserved
73 | }
74 | }
75 |
76 | resource applicationInsight 'Microsoft.Insights/components@2020-02-02' = {
77 | name: applicationInsightsName
78 | location: appInsightsLocation
79 | tags: {
80 | 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource'
81 | }
82 | properties: {
83 | Application_Type: 'web'
84 | }
85 | kind: 'web'
86 | }
87 |
88 | resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
89 | name: functionAppName
90 | location: location
91 | kind: (isReserved ? 'functionapp,linux' : 'functionapp')
92 | properties: {
93 | reserved: isReserved
94 | serverFarmId: hostingPlan.id
95 | siteConfig: {
96 | alwaysOn: true
97 | linuxFxVersion: (isReserved ? linuxFxVersion : json('null'))
98 | appSettings: [
99 | {
100 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
101 | value: reference(applicationInsight.id, '2015-05-01').InstrumentationKey
102 | }
103 | {
104 | name: 'AzureWebJobsStorage'
105 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
106 | }
107 | {
108 | name: 'FUNCTIONS_EXTENSION_VERSION'
109 | value: '~4'
110 | }
111 | {
112 | name: 'FUNCTIONS_WORKER_RUNTIME'
113 | value: functionWorkerRuntime
114 | }
115 | {
116 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
117 | value: '~14'
118 | }
119 | {
120 | name: 'WEBSITE_RUN_FROM_PACKAGE'
121 | value: '1'
122 | }
123 | ]
124 | }
125 | }
126 | }
127 |
128 | resource zipdeploy 'Microsoft.Web/sites/extensions@2022-03-01' = {
129 | parent: functionApp
130 | name: 'zipdeploy'
131 | properties: {
132 | packageUri: packageUri
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/function-app-deployment-slot/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Premium plan with production slot and an additional deployment slot.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-deployment-slot
8 | languages:
9 | - bicep
10 | - json
11 | ---
12 | # Azure Function App with a Deployment Slot
13 |
14 | This sample Azure Resource Manager template deploys an Azure Function App with production slot and an additional deployment slot.
15 |
16 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-deployment-slot%2Fazuredeploy.json)
17 |
18 | ### OS
19 |
20 | This template has a parameter `functionPlanOS` to choose Windows or Linux OS. Windows is selected by default. If you choose Linux, then parameter `linuxFxVersion` will be parameter, so you can skip it for Windows.
21 |
22 | ### Elastic Premium Plan
23 |
24 | The Azure Function app provisioned in this sample uses an [Azure Functions Elastic Premium plan](https://docs.microsoft.com/azure/azure-functions/functions-premium-plan#features).
25 |
26 | + **Microsoft.Web/serverfarms**: The Azure Functions Premium plan (a.k.a. Elastic Premium plan)
27 |
28 | ### Azure Function App
29 |
30 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) and [WEBSITE_CONTENTAZUREFILECONNECTIONSTRING](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) app settings to connect to a Storage Account.
31 |
32 | + **Microsoft.Web/sites**: The function app instance.
33 |
34 | ### Deployment Slot
35 |
36 | Azure Functions [deployment slots](https://docs.microsoft.com/en-us/azure/azure-functions/functions-deployment-slots) allow your function app to run different instances called "slots". Slots are different environments exposed via a publicly available endpoint. One app instance is always mapped to the production slot, and you can swap instances assigned to a slot on demand.
37 |
38 | Function apps running under the Apps Service plan may have multiple slots, while under the Consumption plan only one slot is allowed.
39 |
40 | For Windows, do not need to set the [WEBSITE_CONTENTSHARE](https://docs.microsoft.com/en-us/azure/azure-functions/functions-app-settings#website_contentshare) setting in a deployment slot. This setting is generated for you when the app is created in the deployment slot.
41 |
42 | + **Microsoft.Web/sites/slots**: The deployment slot for the function app.
43 |
44 | For swapping the 2 slots, it is recommended to have 2 separate templates:
45 | 1. First run this template successfully for deploying to slot using ZipDeploy.
46 | 2. Then run this [template for swapping slots](https://azure.github.io/AppService/2019/10/02/Swap-slots-with-arm-templates.html).
47 |
48 | ### ZipDeploy Extension
49 |
50 | The Zip Deploy extension is added for "deployment" slot along with recommended app setting `WEBSITE_RUN_FROM_PACKAGE=1` to mount the zip package for deployment. This is the recommended path for deployment, except for [Linux Consumption Plan](/function-app-linux-consumption)
51 |
52 | + **Microsoft.Web/sites/slots/extensions**: The ZipDeploy extension for "deployment" slot.
53 |
54 | ### Azure Storage account
55 |
56 | The Storage account that the Function uses for operation and for file contents.
57 |
58 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
59 |
60 | ### Application Insights
61 |
62 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
63 |
64 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
65 |
66 | `Tags: Microsoft.Storage/storageAccounts, microsoft.insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites, Microsoft.Web/sites/slots`
67 |
--------------------------------------------------------------------------------
/function-app-deployment-slot/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "functionAppName": {
6 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
7 | "type": "String",
8 | "metadata": {
9 | "description": "The name of the Azure Function app."
10 | }
11 | },
12 | "SlotName": {
13 | "defaultValue": "deployment",
14 | "type": "String",
15 | "metadata": {
16 | "description": "The name of the slot for Azure Function app."
17 | }
18 | },
19 | "storageAccountType": {
20 | "defaultValue": "Standard_LRS",
21 | "allowedValues": [
22 | "Standard_LRS",
23 | "Standard_GRS",
24 | "Standard_RAGRS"
25 | ],
26 | "type": "String",
27 | "metadata": {
28 | "description": "Storage Account type"
29 | }
30 | },
31 | "location": {
32 | "defaultValue": "[resourceGroup().location]",
33 | "type": "String",
34 | "metadata": {
35 | "description": "Location for all resources."
36 | }
37 | },
38 | "appInsightsLocation": {
39 | "defaultValue": "[resourceGroup().location]",
40 | "type": "String",
41 | "metadata": {
42 | "description": "Location for Application Insights"
43 | }
44 | },
45 | "functionWorkerRuntime": {
46 | "defaultValue": "node",
47 | "allowedValues": [
48 | "dotnet",
49 | "node",
50 | "python",
51 | "java"
52 | ],
53 | "type": "String",
54 | "metadata": {
55 | "description": "The language worker runtime to load in the function app."
56 | }
57 | },
58 | "functionPlanOS": {
59 | "defaultValue": "Windows",
60 | "allowedValues": [
61 | "Windows",
62 | "Linux"
63 | ],
64 | "type": "String",
65 | "metadata": {
66 | "description": "Specifies the OS used for the Azure Function hosting plan."
67 | }
68 | },
69 | "functionAppPlanSku": {
70 | "defaultValue": "EP1",
71 | "allowedValues": [
72 | "EP1",
73 | "EP2",
74 | "EP3"
75 | ],
76 | "type": "String",
77 | "metadata": {
78 | "description": "Specifies the Azure Function hosting plan SKU."
79 | }
80 | },
81 | "packageUri": {
82 | "type": "String",
83 | "metadata": {
84 | "description": "The zip content url."
85 | }
86 | },
87 | "linuxFxVersion": {
88 | "defaultValue": "",
89 | "type": "String",
90 | "metadata": {
91 | "description": "Only required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
92 | }
93 | }
94 | },
95 | "variables": {
96 | "hostingPlanName": "[parameters('functionAppName')]",
97 | "applicationInsightsName": "[parameters('functionAppName')]",
98 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]",
99 | "isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]",
100 | "slotContentShareName": "[format('{0}-{1}', parameters('functionAppName'), parameters('SlotName'))]"
101 | },
102 | "resources": [
103 | {
104 | "type": "Microsoft.Storage/storageAccounts",
105 | "apiVersion": "2022-05-01",
106 | "name": "[variables('storageAccountName')]",
107 | "location": "[parameters('location')]",
108 | "sku": {
109 | "name": "[parameters('storageAccountType')]"
110 | },
111 | "kind": "Storage"
112 | },
113 | {
114 | "type": "Microsoft.Web/serverfarms",
115 | "apiVersion": "2022-03-01",
116 | "name": "[variables('hostingPlanName')]",
117 | "location": "[parameters('location')]",
118 | "sku": {
119 | "tier": "ElasticPremium",
120 | "name": "[parameters('functionAppPlanSku')]",
121 | "family": "EP"
122 | },
123 | "kind": "elastic",
124 | "properties": {
125 | "maximumElasticWorkerCount": 20,
126 | "reserved": "[variables('isReserved')]"
127 | }
128 | },
129 | {
130 | "type": "Microsoft.Insights/components",
131 | "apiVersion": "2020-02-02",
132 | "name": "[variables('applicationInsightsName')]",
133 | "location": "[parameters('appInsightsLocation')]",
134 | "tags": {
135 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
136 | },
137 | "kind": "web",
138 | "properties": {
139 | "Application_Type": "web"
140 | }
141 | },
142 | {
143 | "type": "Microsoft.Web/sites",
144 | "apiVersion": "2022-03-01",
145 | "name": "[parameters('functionAppName')]",
146 | "location": "[parameters('location')]",
147 | "dependsOn": [
148 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
149 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
150 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
151 | ],
152 | "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
153 | "properties": {
154 | "reserved": "[variables('isReserved')]",
155 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
156 | "siteConfig": {
157 | "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
158 | "appSettings": [
159 | {
160 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
161 | "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))).InstrumentationKey]"
162 | },
163 | {
164 | "name": "AzureWebJobsStorage",
165 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
166 | },
167 | {
168 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
169 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
170 | },
171 | {
172 | "name": "WEBSITE_CONTENTSHARE",
173 | "value": "[toLower(parameters('functionAppName'))]"
174 | },
175 | {
176 | "name": "FUNCTIONS_EXTENSION_VERSION",
177 | "value": "~4"
178 | },
179 | {
180 | "name": "FUNCTIONS_WORKER_RUNTIME",
181 | "value": "[parameters('functionWorkerRuntime')]"
182 | },
183 | {
184 | "name": "WEBSITE_NODE_DEFAULT_VERSION",
185 | "value": "~14"
186 | },
187 | {
188 | "name": "WEBSITE_RUN_FROM_PACKAGE",
189 | "value": "1"
190 | }
191 | ]
192 | }
193 | }
194 | },
195 | {
196 | "type": "Microsoft.Web/sites/slots",
197 | "apiVersion": "2022-03-01",
198 | "name": "[format('{0}/{1}', parameters('functionAppName'), parameters('SlotName'))]",
199 | "location": "[parameters('location')]",
200 | "dependsOn": [
201 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
202 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]",
203 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
204 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
205 | ],
206 | "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
207 | "properties": {
208 | "reserved": "[variables('isReserved')]",
209 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
210 | "siteConfig": {
211 | "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
212 | "appSettings": [
213 | {
214 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
215 | "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))).InstrumentationKey]"
216 | },
217 | {
218 | "name": "AzureWebJobsStorage",
219 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
220 | },
221 | {
222 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
223 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
224 | },
225 | {
226 | "name": "WEBSITE_CONTENTSHARE",
227 | "value": "[variables('slotContentShareName')]"
228 | },
229 | {
230 | "name": "FUNCTIONS_EXTENSION_VERSION",
231 | "value": "~4"
232 | },
233 | {
234 | "name": "FUNCTIONS_WORKER_RUNTIME",
235 | "value": "[parameters('functionWorkerRuntime')]"
236 | },
237 | {
238 | "name": "WEBSITE_NODE_DEFAULT_VERSION",
239 | "value": "~14"
240 | },
241 | {
242 | "name": "WEBSITE_RUN_FROM_PACKAGE",
243 | "value": "1"
244 | }
245 | ]
246 | }
247 | }
248 | },
249 | {
250 | "type": "Microsoft.Web/sites/slots/extensions",
251 | "apiVersion": "2021-03-01",
252 | "name": "[format('{0}/{1}/ZipDeploy', parameters('functionAppName'), parameters('SlotName'))]",
253 | "dependsOn": [
254 | "[resourceId('Microsoft.Web/sites/slots', parameters('functionAppName'), parameters('SlotName'))]"
255 | ],
256 | "properties": {
257 | "packageUri": "[parameters('packageUri')]"
258 | }
259 | }
260 | ]
261 | }
262 |
--------------------------------------------------------------------------------
/function-app-deployment-slot/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-deployment-slot/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('Storage Account type')
5 | @allowed([
6 | 'Standard_LRS'
7 | 'Standard_GRS'
8 | 'Standard_RAGRS'
9 | ])
10 | param storageAccountType string = 'Standard_LRS'
11 |
12 | @description('Location for all resources.')
13 | param location string = resourceGroup().location
14 |
15 | @description('Location for Application Insights')
16 | param appInsightsLocation string = resourceGroup().location
17 |
18 | @description('The language worker runtime to load in the function app.')
19 | @allowed([
20 | 'dotnet'
21 | 'node'
22 | 'python'
23 | 'java'
24 | ])
25 | param functionWorkerRuntime string = 'node'
26 |
27 | @description('Specifies the OS used for the Azure Function hosting plan.')
28 | @allowed([
29 | 'Windows'
30 | 'Linux'
31 | ])
32 | param functionPlanOS string = 'Windows'
33 |
34 | @description('Specifies the Azure Function hosting plan SKU.')
35 | @allowed([
36 | 'EP1'
37 | 'EP2'
38 | 'EP3'
39 | ])
40 | param functionAppPlanSku string = 'EP1'
41 |
42 | @description('Only required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
43 | param linuxFxVersion string = ''
44 |
45 | var hostingPlanName = functionAppName
46 | var applicationInsightsName = functionAppName
47 | var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
48 | var isReserved = ((functionPlanOS == 'Linux') ? true : false)
49 | var slotContentShareName = '${functionAppName}-deployment'
50 |
51 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
52 | name: storageAccountName
53 | location: location
54 | sku: {
55 | name: storageAccountType
56 | }
57 | kind: 'Storage'
58 | }
59 |
60 | resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
61 | name: hostingPlanName
62 | location: location
63 | sku: {
64 | tier: 'ElasticPremium'
65 | name: functionAppPlanSku
66 | family: 'EP'
67 | }
68 | properties: {
69 | maximumElasticWorkerCount: 20
70 | reserved: isReserved
71 | }
72 | kind: 'elastic'
73 | }
74 |
75 | resource applicationInsight 'Microsoft.Insights/components@2020-02-02' = {
76 | name: applicationInsightsName
77 | location: appInsightsLocation
78 | tags: {
79 | 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource'
80 | }
81 | properties: {
82 | Application_Type: 'web'
83 | }
84 | kind: 'web'
85 | }
86 |
87 | resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
88 | name: functionAppName
89 | location: location
90 | kind: (isReserved ? 'functionapp,linux' : 'functionapp')
91 | properties: {
92 | reserved: isReserved
93 | serverFarmId: hostingPlan.id
94 | siteConfig: {
95 | linuxFxVersion: (isReserved ? linuxFxVersion : json('null'))
96 | appSettings: [
97 | {
98 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
99 | value: applicationInsight.properties.InstrumentationKey
100 | }
101 | {
102 | name: 'AzureWebJobsStorage'
103 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
104 | }
105 | {
106 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
107 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
108 | }
109 | {
110 | name: 'WEBSITE_CONTENTSHARE'
111 | value: toLower(functionAppName)
112 | }
113 | {
114 | name: 'FUNCTIONS_EXTENSION_VERSION'
115 | value: '~4'
116 | }
117 | {
118 | name: 'FUNCTIONS_WORKER_RUNTIME'
119 | value: functionWorkerRuntime
120 | }
121 | {
122 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
123 | value: '~14'
124 | }
125 | ]
126 | }
127 | }
128 | }
129 |
130 | resource slot 'Microsoft.Web/sites/slots@2022-03-01' = {
131 | parent: functionApp
132 | name: 'deployment'
133 | kind: (isReserved ? 'functionapp,linux' : 'functionapp')
134 | location: location
135 | properties: {
136 | reserved: isReserved
137 | serverFarmId: hostingPlan.id
138 | siteConfig: {
139 | linuxFxVersion: (isReserved ? linuxFxVersion : json('null'))
140 | appSettings: [
141 | {
142 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
143 | value: applicationInsight.properties.InstrumentationKey
144 | }
145 | {
146 | name: 'AzureWebJobsStorage'
147 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
148 | }
149 | {
150 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
151 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
152 | }
153 | {
154 | name: 'WEBSITE_CONTENTSHARE'
155 | value: slotContentShareName
156 | }
157 | {
158 | name: 'FUNCTIONS_EXTENSION_VERSION'
159 | value: '~4'
160 | }
161 | {
162 | name: 'FUNCTIONS_WORKER_RUNTIME'
163 | value: functionWorkerRuntime
164 | }
165 | {
166 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
167 | value: '~14'
168 | }
169 | ]
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/function-app-linux-consumption-remote-build/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Linux Consumption plan, which is a dynamic hosting plan. The app runs on demand and you're billed per execution, with no standing resource committment.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-linux-consumption-remote-build
8 | languages:
9 | - bicep
10 | - json
11 | ---
12 | # Azure Function App Hosted on Linux Consumption Plan
13 |
14 | This sample Azure Resource Manager template deploys an Azure Function App on Linux Consumption plan and required resource including the app setting to deploy using zip package when **remote build** is needed (for example: to get Linux specific packages in python, node.js).
15 |
16 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-linux-consumption-remote-build%2Fazuredeploy.json)
17 |
18 | ### OS
19 |
20 | This template is for Azure Function app hosted on **Linux Consumption plan** only.
21 |
22 | ### Comsumption Plan
23 |
24 | The Azure Function app provisioned in this sample uses an [Azure Functions Consumption plan](https://docs.microsoft.com/en-us/azure/azure-functions/consumption-plan).
25 |
26 | + **Microsoft.Web/serverfarms**: The Azure Functions Consumption plan (a.k.a. Dynamic plan)
27 |
28 | ### Azure Function App
29 |
30 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) app settings to connect to a Storage Account.
31 |
32 | + **Microsoft.Web/sites**: The function app instance.
33 |
34 | ### Deploy using .ZIP package with Remote Build
35 |
36 | To enable the remote build processes with ZipDeploy extension, update the following to your application settings:
37 | + `WEBSITE_RUN_FROM_PACKAGE=0` or Remove WEBSITE_RUN_FROM_PACKAGE app setting
38 | + `SCM_DO_BUILD_DURING_DEPLOYMENT=true`
39 |
40 | NOTE: ZipDeploy extension with the appSetting `SCM_DO_BUILD_DURING_DEPLOYMENT=true` is honored only if `WEBSITE_RUN_FROM_PACKAGE=0`or Remove WEBSITE_RUN_FROM_PACKAGE app setting.
41 |
42 | ### Azure Storage account
43 |
44 | The Storage account that the Function uses for operation and for file contents.
45 |
46 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
47 |
48 | ### Application Insights
49 |
50 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
51 |
52 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
53 |
54 | `Tags: Microsoft.Storage/storageAccounts, microsoft.insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites, Microsoft.Web/sites/extensions`
55 |
--------------------------------------------------------------------------------
/function-app-linux-consumption-remote-build/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.10.61.36676",
8 | "templateHash": "2440974564149075183"
9 | }
10 | },
11 | "parameters": {
12 | "functionAppName": {
13 | "type": "string",
14 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
15 | "metadata": {
16 | "description": "The name of the Azure Function app."
17 | }
18 | },
19 | "storageAccountType": {
20 | "type": "string",
21 | "defaultValue": "Standard_LRS",
22 | "allowedValues": [
23 | "Standard_LRS",
24 | "Standard_GRS",
25 | "Standard_RAGRS"
26 | ],
27 | "metadata": {
28 | "description": "Storage Account type"
29 | }
30 | },
31 | "location": {
32 | "type": "string",
33 | "defaultValue": "[resourceGroup().location]",
34 | "metadata": {
35 | "description": "Location for all resources."
36 | }
37 | },
38 | "appInsightsLocation": {
39 | "type": "string",
40 | "defaultValue": "[resourceGroup().location]",
41 | "metadata": {
42 | "description": "Location for Application Insights"
43 | }
44 | },
45 | "functionWorkerRuntime": {
46 | "type": "string",
47 | "defaultValue": "node",
48 | "allowedValues": [
49 | "dotnet",
50 | "node",
51 | "python",
52 | "java"
53 | ],
54 | "metadata": {
55 | "description": "The language worker runtime to load in the function app."
56 | }
57 | },
58 | "linuxFxVersion": {
59 | "type": "string",
60 | "metadata": {
61 | "description": "Required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
62 | }
63 | },
64 | "packageUri": {
65 | "type": "string",
66 | "metadata": {
67 | "description": "The zip content url."
68 | }
69 | }
70 | },
71 | "variables": {
72 | "hostingPlanName": "[parameters('functionAppName')]",
73 | "applicationInsightsName": "[parameters('functionAppName')]",
74 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]"
75 | },
76 | "resources": [
77 | {
78 | "type": "Microsoft.Storage/storageAccounts",
79 | "apiVersion": "2022-05-01",
80 | "name": "[variables('storageAccountName')]",
81 | "location": "[parameters('location')]",
82 | "sku": {
83 | "name": "[parameters('storageAccountType')]"
84 | },
85 | "kind": "Storage"
86 | },
87 | {
88 | "type": "Microsoft.Web/serverfarms",
89 | "apiVersion": "2021-02-01",
90 | "name": "[variables('hostingPlanName')]",
91 | "location": "[parameters('location')]",
92 | "sku": {
93 | "name": "Y1",
94 | "tier": "Dynamic",
95 | "size": "Y1",
96 | "family": "Y"
97 | },
98 | "properties": {
99 | "computeMode": "Dynamic",
100 | "reserved": true
101 | }
102 | },
103 | {
104 | "type": "Microsoft.Insights/components",
105 | "apiVersion": "2020-02-02",
106 | "name": "[variables('applicationInsightsName')]",
107 | "location": "[parameters('appInsightsLocation')]",
108 | "tags": {
109 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
110 | },
111 | "properties": {
112 | "Application_Type": "web"
113 | },
114 | "kind": "web"
115 | },
116 | {
117 | "type": "Microsoft.Web/sites",
118 | "apiVersion": "2022-03-01",
119 | "name": "[parameters('functionAppName')]",
120 | "location": "[parameters('location')]",
121 | "kind": "functionapp,linux",
122 | "properties": {
123 | "reserved": true,
124 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
125 | "siteConfig": {
126 | "linuxFxVersion": "[parameters('linuxFxVersion')]",
127 | "appSettings": [
128 | {
129 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
130 | "value": "[reference(resourceId('Microsoft.Insights/components', parameters('functionAppName')), '2015-05-01').InstrumentationKey]"
131 | },
132 | {
133 | "name": "AzureWebJobsStorage",
134 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
135 | },
136 | {
137 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
138 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
139 | },
140 | {
141 | "name": "WEBSITE_CONTENTSHARE",
142 | "value": "[toLower(parameters('functionAppName'))]"
143 | },
144 | {
145 | "name": "FUNCTIONS_EXTENSION_VERSION",
146 | "value": "~4"
147 | },
148 | {
149 | "name": "FUNCTIONS_WORKER_RUNTIME",
150 | "value": "[parameters('functionWorkerRuntime')]"
151 | },
152 | {
153 | "name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
154 | "value": "true"
155 | }
156 | ]
157 | }
158 | },
159 | "dependsOn": [
160 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
161 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
162 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
163 | ]
164 | },
165 | {
166 | "type": "Microsoft.Web/sites/extensions",
167 | "apiVersion": "2022-03-01",
168 | "name": "[format('{0}/{1}', parameters('functionAppName'), 'zipdeploy')]",
169 | "properties": {
170 | "packageUri": "[parameters('packageUri')]"
171 | },
172 | "dependsOn": [
173 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
174 | ]
175 | }
176 | ]
177 | }
--------------------------------------------------------------------------------
/function-app-linux-consumption-remote-build/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-linux-consumption-remote-build/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('Storage Account type')
5 | @allowed([
6 | 'Standard_LRS'
7 | 'Standard_GRS'
8 | 'Standard_RAGRS'
9 | ])
10 | param storageAccountType string = 'Standard_LRS'
11 |
12 | @description('Location for all resources.')
13 | param location string = resourceGroup().location
14 |
15 | @description('Location for Application Insights')
16 | param appInsightsLocation string = resourceGroup().location
17 |
18 | @description('The language worker runtime to load in the function app.')
19 | @allowed([
20 | 'dotnet'
21 | 'node'
22 | 'python'
23 | 'java'
24 | ])
25 | param functionWorkerRuntime string = 'node'
26 |
27 | @description('Required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
28 | param linuxFxVersion string
29 |
30 | @description('The zip content url.')
31 | param packageUri string
32 |
33 | var hostingPlanName = functionAppName
34 | var applicationInsightsName = functionAppName
35 | var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
36 |
37 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
38 | name: storageAccountName
39 | location: location
40 | sku: {
41 | name: storageAccountType
42 | }
43 | kind: 'Storage'
44 | }
45 |
46 | resource hostingPlan 'Microsoft.Web/serverfarms@2021-02-01' = {
47 | name: hostingPlanName
48 | location: location
49 | sku: {
50 | name: 'Y1'
51 | tier: 'Dynamic'
52 | size: 'Y1'
53 | family: 'Y'
54 | }
55 | properties: {
56 | computeMode: 'Dynamic'
57 | reserved: true
58 | }
59 | }
60 |
61 | resource insight 'Microsoft.Insights/components@2020-02-02' = {
62 | name: applicationInsightsName
63 | location: appInsightsLocation
64 | tags: {
65 | 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource'
66 | }
67 | properties: {
68 | Application_Type: 'web'
69 | }
70 | kind: 'web'
71 | }
72 |
73 | resource site 'Microsoft.Web/sites@2022-03-01' = {
74 | name: functionAppName
75 | location: location
76 | kind: 'functionapp,linux'
77 | properties: {
78 | reserved: true
79 | serverFarmId: hostingPlan.id
80 | siteConfig: {
81 | linuxFxVersion: linuxFxVersion
82 | appSettings: [
83 | {
84 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
85 | value: reference(resourceId('Microsoft.Insights/components', functionAppName), '2015-05-01').InstrumentationKey
86 | }
87 | {
88 | name: 'AzureWebJobsStorage'
89 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
90 | }
91 | {
92 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
93 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
94 | }
95 | {
96 | name: 'WEBSITE_CONTENTSHARE'
97 | value: toLower(functionAppName)
98 | }
99 | {
100 | name: 'FUNCTIONS_EXTENSION_VERSION'
101 | value: '~4'
102 | }
103 | {
104 | name: 'FUNCTIONS_WORKER_RUNTIME'
105 | value: functionWorkerRuntime
106 | }
107 | {
108 | name: 'SCM_DO_BUILD_DURING_DEPLOYMENT'
109 | value: 'true'
110 | }
111 | ]
112 | }
113 | }
114 | dependsOn: [
115 | insight
116 | ]
117 | }
118 |
119 | resource zipDeploy 'Microsoft.Web/sites/extensions@2022-03-01' = {
120 | parent: site
121 | name: 'zipdeploy'
122 | properties: {
123 | packageUri: packageUri
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/function-app-linux-consumption/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Linux Consumption plan, which is a dynamic hosting plan. The app runs on demand and you're billed per execution, with no standing resource committment.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-linux-consumption
8 | languages:
9 | - bicep
10 | - json
11 | ---
12 | # Azure Function App Hosted on Linux Consumption Plan
13 |
14 | This sample Azure Resource Manager template deploys an Azure Function App on Linux Consumption plan and required resource including the app setting to deploy using zip package.
15 |
16 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-linux-consumption%2Fazuredeploy.json)
17 |
18 | ### OS
19 |
20 | This template is for Azure Function app hosted on **Linux Consumption plan** only.
21 |
22 | ### Comsumption Plan
23 |
24 | The Azure Function app provisioned in this sample uses an [Azure Functions Consumption plan](https://docs.microsoft.com/en-us/azure/azure-functions/consumption-plan).
25 |
26 | + **Microsoft.Web/serverfarms**: The Azure Functions Consumption plan (a.k.a. Dynamic plan)
27 |
28 | ### Azure Function App
29 |
30 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) app settings to connect to a Storage Account.
31 |
32 | + **Microsoft.Web/sites**: The function app instance.
33 |
34 | ### Deploy using .ZIP package
35 |
36 | ZipDeploy extension with the appSetting `WEBSITE_RUN_FROM_PACKAGE=1` is not supported only for Linux Consumption plan. For Linux Consumption plan:
37 | 1. Do not use ZipDeploy extension.
38 | 2. Set appSetting `WEBSITE_RUN_FROM_PACKAGE=URL` for deployment using the .zip package url.
39 |
40 | ### Azure Storage account
41 |
42 | The Storage account that the Function uses for operation and for file contents.
43 |
44 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
45 |
46 | ### Application Insights
47 |
48 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
49 |
50 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
51 |
52 | `Tags: Microsoft.Storage/storageAccounts, microsoft.insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites`
53 |
--------------------------------------------------------------------------------
/function-app-linux-consumption/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.10.61.36676",
8 | "templateHash": "7028085632643346163"
9 | }
10 | },
11 | "parameters": {
12 | "functionAppName": {
13 | "type": "string",
14 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
15 | "metadata": {
16 | "description": "The name of the Azure Function app."
17 | }
18 | },
19 | "storageAccountType": {
20 | "type": "string",
21 | "defaultValue": "Standard_LRS",
22 | "allowedValues": [
23 | "Standard_LRS",
24 | "Standard_GRS",
25 | "Standard_RAGRS"
26 | ],
27 | "metadata": {
28 | "description": "Storage Account type"
29 | }
30 | },
31 | "location": {
32 | "type": "string",
33 | "defaultValue": "[resourceGroup().location]",
34 | "metadata": {
35 | "description": "Location for all resources."
36 | }
37 | },
38 | "appInsightsLocation": {
39 | "type": "string",
40 | "defaultValue": "[resourceGroup().location]",
41 | "metadata": {
42 | "description": "Location for Application Insights"
43 | }
44 | },
45 | "functionWorkerRuntime": {
46 | "type": "string",
47 | "defaultValue": "node",
48 | "allowedValues": [
49 | "dotnet",
50 | "node",
51 | "python",
52 | "java"
53 | ],
54 | "metadata": {
55 | "description": "The language worker runtime to load in the function app."
56 | }
57 | },
58 | "linuxFxVersion": {
59 | "type": "string",
60 | "metadata": {
61 | "description": "Required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
62 | }
63 | },
64 | "packageUri": {
65 | "type": "string",
66 | "metadata": {
67 | "description": "The zip content url."
68 | }
69 | }
70 | },
71 | "variables": {
72 | "hostingPlanName": "[parameters('functionAppName')]",
73 | "applicationInsightsName": "[parameters('functionAppName')]",
74 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]"
75 | },
76 | "resources": [
77 | {
78 | "type": "Microsoft.Storage/storageAccounts",
79 | "apiVersion": "2022-05-01",
80 | "name": "[variables('storageAccountName')]",
81 | "location": "[parameters('location')]",
82 | "sku": {
83 | "name": "[parameters('storageAccountType')]"
84 | },
85 | "kind": "Storage"
86 | },
87 | {
88 | "type": "Microsoft.Web/serverfarms",
89 | "apiVersion": "2022-03-01",
90 | "name": "[variables('hostingPlanName')]",
91 | "location": "[parameters('location')]",
92 | "sku": {
93 | "name": "Y1",
94 | "tier": "Dynamic",
95 | "size": "Y1",
96 | "family": "Y"
97 | },
98 | "properties": {
99 | "reserved": true
100 | }
101 | },
102 | {
103 | "type": "Microsoft.Insights/components",
104 | "apiVersion": "2020-02-02",
105 | "name": "[variables('applicationInsightsName')]",
106 | "location": "[parameters('appInsightsLocation')]",
107 | "tags": {
108 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', parameters('functionAppName')))]": "Resource"
109 | },
110 | "properties": {
111 | "Application_Type": "web"
112 | },
113 | "kind": "web"
114 | },
115 | {
116 | "type": "Microsoft.Web/sites",
117 | "apiVersion": "2022-03-01",
118 | "name": "[parameters('functionAppName')]",
119 | "location": "[parameters('location')]",
120 | "kind": "functionapp,linux",
121 | "properties": {
122 | "reserved": true,
123 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
124 | "siteConfig": {
125 | "linuxFxVersion": "[parameters('linuxFxVersion')]",
126 | "appSettings": [
127 | {
128 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
129 | "value": "[reference(resourceId('Microsoft.Insights/components', parameters('functionAppName')), '2022-03-01').InstrumentationKey]"
130 | },
131 | {
132 | "name": "AzureWebJobsStorage",
133 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
134 | },
135 | {
136 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
137 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
138 | },
139 | {
140 | "name": "WEBSITE_CONTENTSHARE",
141 | "value": "[toLower(parameters('functionAppName'))]"
142 | },
143 | {
144 | "name": "FUNCTIONS_EXTENSION_VERSION",
145 | "value": "~4"
146 | },
147 | {
148 | "name": "FUNCTIONS_WORKER_RUNTIME",
149 | "value": "[parameters('functionWorkerRuntime')]"
150 | },
151 | {
152 | "name": "WEBSITE_RUN_FROM_PACKAGE",
153 | "value": "[parameters('packageUri')]"
154 | }
155 | ]
156 | }
157 | },
158 | "dependsOn": [
159 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
160 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
161 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
162 | ]
163 | }
164 | ]
165 | }
--------------------------------------------------------------------------------
/function-app-linux-consumption/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-linux-consumption/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('Storage Account type')
5 | @allowed([
6 | 'Standard_LRS'
7 | 'Standard_GRS'
8 | 'Standard_RAGRS'
9 | ])
10 | param storageAccountType string = 'Standard_LRS'
11 |
12 | @description('Location for all resources.')
13 | param location string = resourceGroup().location
14 |
15 | @description('Location for Application Insights')
16 | param appInsightsLocation string = resourceGroup().location
17 |
18 | @description('The language worker runtime to load in the function app.')
19 | @allowed([
20 | 'dotnet'
21 | 'node'
22 | 'python'
23 | 'java'
24 | ])
25 | param functionWorkerRuntime string = 'node'
26 |
27 | @description('Required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
28 | param linuxFxVersion string
29 |
30 | @description('The zip content url.')
31 | param packageUri string
32 |
33 | var hostingPlanName = functionAppName
34 | var applicationInsightsName = functionAppName
35 | var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
36 |
37 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
38 | name: storageAccountName
39 | location: location
40 | sku: {
41 | name: storageAccountType
42 | }
43 | kind: 'Storage'
44 | }
45 |
46 | resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
47 | name: hostingPlanName
48 | location: location
49 | sku: {
50 | name: 'Y1'
51 | tier: 'Dynamic'
52 | size: 'Y1'
53 | family: 'Y'
54 | }
55 | properties: {
56 | reserved: true
57 | }
58 | }
59 |
60 | resource applicationInsight 'Microsoft.Insights/components@2020-02-02' = {
61 | name: applicationInsightsName
62 | location: appInsightsLocation
63 | tags: {
64 | 'hidden-link:${resourceId('Microsoft.Web/sites', functionAppName)}': 'Resource'
65 | }
66 | properties: {
67 | Application_Type: 'web'
68 | }
69 | kind: 'web'
70 | }
71 |
72 | resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
73 | name: functionAppName
74 | location: location
75 | kind: 'functionapp,linux'
76 | properties: {
77 | reserved: true
78 | serverFarmId: hostingPlan.id
79 | siteConfig: {
80 | linuxFxVersion: linuxFxVersion
81 | appSettings: [
82 | {
83 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
84 | value: reference(resourceId('Microsoft.Insights/components', functionAppName), '2020-02-02').InstrumentationKey
85 | }
86 | {
87 | name: 'AzureWebJobsStorage'
88 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
89 | }
90 | {
91 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
92 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
93 | }
94 | {
95 | name: 'WEBSITE_CONTENTSHARE'
96 | value: toLower(functionAppName)
97 | }
98 | {
99 | name: 'FUNCTIONS_EXTENSION_VERSION'
100 | value: '~4'
101 | }
102 | {
103 | name: 'FUNCTIONS_WORKER_RUNTIME'
104 | value: functionWorkerRuntime
105 | }
106 | {
107 | name: 'WEBSITE_RUN_FROM_PACKAGE'
108 | value: packageUri
109 | }
110 | ]
111 | }
112 | }
113 | dependsOn: [
114 | applicationInsight
115 | ]
116 | }
117 |
--------------------------------------------------------------------------------
/function-app-linux-flex-consumption/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Flex Consumption plan.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-linux-flex-consumption
8 | languages:
9 | - json
10 | ---
11 | # Azure Function App Hosted on Flex Consumption Plan (Linux)
12 |
13 | This sample Azure Resource Manager template deploys an Azure Function App on Flex Consumption plan (Linux) and required resource including code package deployment with remote build option.
14 |
15 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-linux-flex-consumption%2Fazuredeploy.json)
16 |
17 | ### OS
18 |
19 | This template is for Azure Function app hosted on **Flex Consumption plan (Linux)** only.
20 |
21 | ### Flex Consumption Plan
22 |
23 | The Azure Function app provisioned in this sample uses an [Azure Functions Flex Consumption plan](https://learn.microsoft.com/en-us/azure/azure-functions/flex-consumption-plan).
24 |
25 | + **Microsoft.Web/serverfarms**: The Azure Functions Flex Consumption plan
26 |
27 | ### Azure Function App
28 |
29 | The Function App uses the [AzureWebJobsStorage__accountName](https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings#azurewebjobsstorage__accountname) app setting to connect to a Storage Account and [configured deployment settings](https://learn.microsoft.com/en-us/azure/azure-functions/flex-consumption-how-to?tabs=azure-cli%2Cvs-code-publish&pivots=programming-language-python#configure-deployment-settings) with system-assigned identity.
30 |
31 | + **Microsoft.Web/sites**: The function app instance.
32 |
33 | ### OneDeploy
34 | To deploy/release new code with .ZIP package. It uses storage account and authentication method [configured in deployment settings](../function-app-linux-flex-consumption/azuredeploy.json#L220C5-L231C6)
35 |
36 | + **Microsoft.Web/sites/extensions**: Allows choosing remote build process (for example: to get Linux specific packages in python, node.js) in OneDeploy extension using the following property:
37 | + `"remoteBuild": true` => enable remote build
38 | + `"remoteBuild": false` => disable remote build
39 |
40 | ### Azure Storage account
41 |
42 | The Storage account that the Function uses for operation and for file contents.
43 |
44 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
45 |
46 | ### Application Insights
47 |
48 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
49 |
50 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
51 |
52 | `Tags: Microsoft.Storage/storageAccounts, microsoft.insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites, Microsoft.Web/sites/extensions`
53 |
--------------------------------------------------------------------------------
/function-app-linux-flex-consumption/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "functionAppName": {
6 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
7 | "type": "String",
8 | "metadata": {
9 | "description": "The name of the Azure Function app."
10 | }
11 | },
12 | "storageAccountType": {
13 | "defaultValue": "Standard_LRS",
14 | "allowedValues": [
15 | "Standard_LRS",
16 | "Standard_GRS",
17 | "Standard_RAGRS"
18 | ],
19 | "type": "String",
20 | "metadata": {
21 | "description": "Storage Account type"
22 | }
23 | },
24 | "location": {
25 | "defaultValue": "[resourceGroup().location]",
26 | "type": "String",
27 | "metadata": {
28 | "description": "Location for all resources."
29 | }
30 | },
31 | "appInsightsLocation": {
32 | "defaultValue": "[resourceGroup().location]",
33 | "type": "String",
34 | "metadata": {
35 | "description": "Location for Application Insights"
36 | }
37 | },
38 | "functionAppRuntime": {
39 | "defaultValue": "python",
40 | "allowedValues": [
41 | "dotnet-isolated",
42 | "python",
43 | "java",
44 | "node",
45 | "powerShell"
46 | ],
47 | "type": "String",
48 | "metadata": {
49 | "description": "The language worker runtime to load in the function app."
50 | }
51 | },
52 | "functionAppRuntimeVersion": {
53 | "defaultValue": "3.11",
54 | "allowedValues": [
55 | "3.10",
56 | "3.11",
57 | "7.4",
58 | "8.0",
59 | "10",
60 | "11",
61 | "17",
62 | "20"
63 | ],
64 | "type": "String",
65 | "metadata": {
66 | "description": "The language worker runtime version to load in the function app."
67 | }
68 | },
69 | "maximumInstanceCount": {
70 | "defaultValue": 100,
71 | "type": "Int"
72 | },
73 | "instanceMemoryMB": {
74 | "defaultValue": 2048,
75 | "allowedValues": [
76 | 512,
77 | 2048,
78 | 4096
79 | ],
80 | "type": "Int"
81 | },
82 | "packageUri": {
83 | "type": "String",
84 | "metadata": {
85 | "description": "The zip content url."
86 | }
87 | },
88 | "roleNameGuid": {
89 | "defaultValue": "[newGuid()]",
90 | "type": "String",
91 | "metadata": {
92 | "description": "A new GUID used to identify the role assignment"
93 | }
94 | }
95 | },
96 | "variables": {
97 | "hostingPlanName": "[parameters('functionAppName')]",
98 | "applicationInsightsName": "[parameters('functionAppName')]",
99 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]",
100 | "resourceToken": "[toLower(uniqueString(subscription().id, resourceGroup().name, parameters('location')))]",
101 | "deploymentStorageContainerName": "[concat('app-package-', take(parameters('functionAppName'), 32),'-', take(variables('resourceToken'), 7))]",
102 | "storageBlobContributorRoleId": "[concat(subscription().Id, '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe')]" //Storage Blob Data Contributor role
103 | },
104 | "resources": [
105 | {
106 | "type": "Microsoft.Storage/storageAccounts",
107 | "apiVersion": "2023-01-01",
108 | "name": "[variables('storageAccountName')]",
109 | "location": "[parameters('location')]",
110 | "sku": {
111 | "name": "[parameters('storageAccountType')]"
112 | },
113 | "kind": "StorageV2",
114 | "properties": {
115 | "accessTier": "Hot",
116 | "allowSharedKeyAccess": false
117 | }
118 | },
119 | {
120 | "type": "Microsoft.Storage/storageAccounts/blobServices",
121 | "apiVersion": "2023-01-01",
122 | "name": "[format('{0}/{1}', variables('storageAccountName'), 'default')]",
123 | "dependsOn": [
124 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
125 | ]
126 | },
127 | {
128 | "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
129 | "apiVersion": "2023-01-01",
130 | "name": "[format('{0}/{1}/{2}', variables('storageAccountName'), 'default', variables('deploymentStorageContainerName'))]",
131 | "dependsOn": [
132 | "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]"
133 | ],
134 | "properties": {
135 | "publicAccess": "None"
136 | }
137 | },
138 | {
139 | "type": "Microsoft.Web/serverfarms",
140 | "apiVersion": "2023-12-01",
141 | "name": "[variables('hostingPlanName')]",
142 | "location": "[parameters('location')]",
143 | "sku": {
144 | "tier": "FlexConsumption",
145 | "name": "FC1"
146 | },
147 | "kind": "functionapp",
148 | "properties": {
149 | "reserved": true
150 | }
151 | },
152 | {
153 | "type": "Microsoft.Insights/components",
154 | "apiVersion": "2020-02-02",
155 | "name": "[variables('applicationInsightsName')]",
156 | "location": "[parameters('appInsightsLocation')]",
157 | "tags": {
158 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
159 | },
160 | "kind": "web",
161 | "properties": {
162 | "Application_Type": "web"
163 | }
164 | },
165 | {
166 | "type": "Microsoft.Web/sites",
167 | "apiVersion": "2023-12-01",
168 | "name": "[parameters('functionAppName')]",
169 | "location": "[parameters('location')]",
170 | "dependsOn": [
171 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
172 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
173 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
174 | ],
175 | "kind": "functionapp,linux",
176 | "identity": {
177 | "type": "SystemAssigned"
178 | },
179 | "properties": {
180 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
181 | "functionAppConfig": {
182 | "deployment": {
183 | "storage": {
184 | "type": "blobContainer",
185 | "value": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))).primaryEndpoints.blob, variables('deploymentStorageContainerName'))]",
186 | "authentication": {
187 | "type": "SystemAssignedIdentity"
188 | }
189 | }
190 | },
191 | "scaleAndConcurrency": {
192 | "maximumInstanceCount": "[parameters('maximumInstanceCount')]",
193 | "instanceMemoryMB": "[parameters('instanceMemoryMB')]"
194 | },
195 | "runtime": {
196 | "name": "[parameters('functionAppRuntime')]",
197 | "version": "[parameters('functionAppRuntimeVersion')]"
198 | }
199 | },
200 | "siteConfig": {
201 | "appSettings": [
202 | {
203 | "name": "AzureWebJobsStorage__accountName",
204 | "value": "[variables('storageAccountName')]"
205 | },
206 | {
207 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
208 | "value": "[reference(resourceId('Microsoft.Insights/components', parameters('functionAppName')), '2020-02-02').InstrumentationKey]"
209 | }
210 | ]
211 | }
212 | }
213 | },
214 | { //Gives the function app access to the storage account using system assigned managed identity
215 | "type": "Microsoft.Authorization/roleAssignments",
216 | "apiVersion": "2022-04-01",
217 | "name": "[parameters('roleNameGuid')]",
218 | "dependsOn": [
219 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
220 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
221 | ],
222 | "properties": {
223 | "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2016-08-01', 'Full').identity.principalId]",
224 | "roleDefinitionId": "[variables('storageBlobContributorRoleId')]"
225 | },
226 | "scope": "[concat('Microsoft.Storage/storageAccounts', '/', variables('storageAccountName'))]"
227 | },
228 | { //Wait for 30 seconds before starting OneDeploy to complete role assignment before deployment
229 | "type": "Microsoft.Resources/deploymentScripts",
230 | "apiVersion": "2020-10-01",
231 | "name": "WaitSection",
232 | "location": "[parameters('location')]",
233 | "dependsOn": [
234 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
235 | ],
236 | "kind": "AzurePowerShell",
237 | "properties": {
238 | "azPowerShellVersion": "7.0",
239 | "scriptContent": "start-sleep -Seconds 30",
240 | "cleanupPreference": "Always",
241 | "retentionInterval": "PT1H"
242 | }
243 | },
244 | {
245 | "type": "Microsoft.Web/sites/extensions",
246 | "apiVersion": "2022-09-01",
247 | "name": "[format('{0}/{1}', parameters('functionAppName'), 'onedeploy')]",
248 | "dependsOn": [
249 | "WaitSection"
250 | ],
251 | "properties": {
252 | "packageUri": "[parameters('packageUri')]",
253 | "remoteBuild": true
254 | }
255 | }
256 | ]
257 | }
258 |
--------------------------------------------------------------------------------
/function-app-linux-flex-consumption/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-premium-plan/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Premium plan.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | - bicep
8 | urlFragment: function-app-premium-plan
9 | languages:
10 | - bicep
11 | - json
12 | ---
13 | # Azure Function App Hosted on Premium Plan
14 |
15 | This sample Azure Resource Manager template deploys an Azure Function App hosted on Premium plan and required resource including ZipDeploy extension to mount zip package for deployment.
16 |
17 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-premium-plan%2Fazuredeploy.json)
18 |
19 | ### OS
20 |
21 | This template has a parameter `functionPlanOS` to choose Windows or Linux OS. Windows is selected by default. If you choose Linux, then parameter `linuxFxVersion` will be required, so you can skip it for Windows.
22 |
23 | ### Premium Plan
24 |
25 | The Azure Function app provisioned in this sample uses an [Azure Functions Elastic Premium plan](https://docs.microsoft.com/azure/azure-functions/functions-premium-plan#features).
26 |
27 | + **Microsoft.Web/serverfarms**: The Azure Functions Premium plan (a.k.a. Elastic Premium plan)
28 |
29 | ### Azure Function App
30 |
31 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) and [WEBSITE_CONTENTAZUREFILECONNECTIONSTRING](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) app settings to connect to a Storage Account.
32 |
33 | + **Microsoft.Web/sites**: The function app instance.
34 |
35 | ### ZipDeploy Extension
36 |
37 | The Zip Deploy extension is added along with recommended app setting `WEBSITE_RUN_FROM_PACKAGE=1` to mount the zip package for deployment. This is the recommended path for deployment, except for [Linux Consumption Plan](/function-app-linux-consumption)
38 |
39 | + **Microsoft.Web/sites/extensions**: The ZipDeploy extension.
40 |
41 | ### Azure Storage account
42 |
43 | The Storage account that the Function uses for operation and for file contents.
44 |
45 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
46 |
47 | ### Application Insights
48 |
49 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
50 |
51 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
52 |
53 | `Tags: Microsoft.Storage/storageAccounts, microsoft.insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites, Microsoft.Web/sites/extensions`
54 |
--------------------------------------------------------------------------------
/function-app-premium-plan/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.10.61.36676",
8 | "templateHash": "8819040794260330687"
9 | }
10 | },
11 | "parameters": {
12 | "functionAppName": {
13 | "type": "string",
14 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
15 | "metadata": {
16 | "description": "The name of the Azure Function app."
17 | }
18 | },
19 | "storageAccountType": {
20 | "type": "string",
21 | "defaultValue": "Standard_LRS",
22 | "allowedValues": [
23 | "Standard_LRS",
24 | "Standard_GRS",
25 | "Standard_RAGRS"
26 | ],
27 | "metadata": {
28 | "description": "Storage Account type"
29 | }
30 | },
31 | "location": {
32 | "type": "string",
33 | "defaultValue": "[resourceGroup().location]",
34 | "metadata": {
35 | "description": "Location for all resources."
36 | }
37 | },
38 | "appInsightsLocation": {
39 | "type": "string",
40 | "defaultValue": "[resourceGroup().location]",
41 | "metadata": {
42 | "description": "Location for Application Insights"
43 | }
44 | },
45 | "functionWorkerRuntime": {
46 | "type": "string",
47 | "defaultValue": "node",
48 | "allowedValues": [
49 | "dotnet",
50 | "node",
51 | "python",
52 | "java"
53 | ],
54 | "metadata": {
55 | "description": "The language worker runtime to load in the function app."
56 | }
57 | },
58 | "functionPlanOS": {
59 | "type": "string",
60 | "defaultValue": "Windows",
61 | "allowedValues": [
62 | "Windows",
63 | "Linux"
64 | ],
65 | "metadata": {
66 | "description": "Specifies the OS used for the Azure Function hosting plan."
67 | }
68 | },
69 | "functionAppPlanSku": {
70 | "type": "string",
71 | "defaultValue": "EP1",
72 | "allowedValues": [
73 | "EP1",
74 | "EP2",
75 | "EP3"
76 | ],
77 | "metadata": {
78 | "description": "Specifies the Azure Function hosting plan SKU."
79 | }
80 | },
81 | "packageUri": {
82 | "type": "string",
83 | "metadata": {
84 | "description": "The zip content url."
85 | }
86 | },
87 | "linuxFxVersion": {
88 | "type": "string",
89 | "defaultValue": "",
90 | "metadata": {
91 | "description": "Only required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
92 | }
93 | }
94 | },
95 | "variables": {
96 | "hostingPlanName": "[parameters('functionAppName')]",
97 | "applicationInsightsName": "[parameters('functionAppName')]",
98 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]",
99 | "isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]"
100 | },
101 | "resources": [
102 | {
103 | "type": "Microsoft.Storage/storageAccounts",
104 | "apiVersion": "2022-05-01",
105 | "name": "[variables('storageAccountName')]",
106 | "location": "[parameters('location')]",
107 | "sku": {
108 | "name": "[parameters('storageAccountType')]"
109 | },
110 | "kind": "Storage"
111 | },
112 | {
113 | "type": "Microsoft.Web/serverfarms",
114 | "apiVersion": "2022-03-01",
115 | "name": "[variables('hostingPlanName')]",
116 | "location": "[parameters('location')]",
117 | "sku": {
118 | "tier": "ElasticPremium",
119 | "name": "[parameters('functionAppPlanSku')]",
120 | "family": "EP"
121 | },
122 | "properties": {
123 | "maximumElasticWorkerCount": 20,
124 | "reserved": "[variables('isReserved')]"
125 | },
126 | "kind": "elastic"
127 | },
128 | {
129 | "type": "Microsoft.Insights/components",
130 | "apiVersion": "2020-02-02",
131 | "name": "[variables('applicationInsightsName')]",
132 | "location": "[parameters('appInsightsLocation')]",
133 | "tags": {
134 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
135 | },
136 | "properties": {
137 | "Application_Type": "web"
138 | },
139 | "kind": "web"
140 | },
141 | {
142 | "type": "Microsoft.Web/sites",
143 | "apiVersion": "2022-03-01",
144 | "name": "[parameters('functionAppName')]",
145 | "location": "[parameters('location')]",
146 | "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
147 | "properties": {
148 | "reserved": "[variables('isReserved')]",
149 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
150 | "siteConfig": {
151 | "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
152 | "appSettings": [
153 | {
154 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
155 | "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName')), '2015-05-01').InstrumentationKey]"
156 | },
157 | {
158 | "name": "AzureWebJobsStorage",
159 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
160 | },
161 | {
162 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
163 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
164 | },
165 | {
166 | "name": "WEBSITE_CONTENTSHARE",
167 | "value": "[toLower(parameters('functionAppName'))]"
168 | },
169 | {
170 | "name": "FUNCTIONS_EXTENSION_VERSION",
171 | "value": "~4"
172 | },
173 | {
174 | "name": "FUNCTIONS_WORKER_RUNTIME",
175 | "value": "[parameters('functionWorkerRuntime')]"
176 | },
177 | {
178 | "name": "WEBSITE_NODE_DEFAULT_VERSION",
179 | "value": "~14"
180 | },
181 | {
182 | "name": "WEBSITE_RUN_FROM_PACKAGE",
183 | "value": "1"
184 | }
185 | ]
186 | }
187 | },
188 | "dependsOn": [
189 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
190 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
191 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
192 | ]
193 | },
194 | {
195 | "type": "Microsoft.Web/sites/extensions",
196 | "apiVersion": "2022-03-01",
197 | "name": "[format('{0}/{1}', parameters('functionAppName'), 'zipdeploy')]",
198 | "properties": {
199 | "packageUri": "[parameters('packageUri')]"
200 | },
201 | "dependsOn": [
202 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
203 | ]
204 | }
205 | ]
206 | }
--------------------------------------------------------------------------------
/function-app-premium-plan/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-premium-plan/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('Storage Account type')
5 | @allowed([
6 | 'Standard_LRS'
7 | 'Standard_GRS'
8 | 'Standard_RAGRS'
9 | ])
10 | param storageAccountType string = 'Standard_LRS'
11 |
12 | @description('Location for all resources.')
13 | param location string = resourceGroup().location
14 |
15 | @description('Location for Application Insights')
16 | param appInsightsLocation string = resourceGroup().location
17 |
18 | @description('The language worker runtime to load in the function app.')
19 | @allowed([
20 | 'dotnet'
21 | 'node'
22 | 'python'
23 | 'java'
24 | ])
25 | param functionWorkerRuntime string = 'node'
26 |
27 | @description('Specifies the OS used for the Azure Function hosting plan.')
28 | @allowed([
29 | 'Windows'
30 | 'Linux'
31 | ])
32 | param functionPlanOS string = 'Windows'
33 |
34 | @description('Specifies the Azure Function hosting plan SKU.')
35 | @allowed([
36 | 'EP1'
37 | 'EP2'
38 | 'EP3'
39 | ])
40 | param functionAppPlanSku string = 'EP1'
41 |
42 | @description('The zip content url.')
43 | param packageUri string
44 |
45 | @description('Only required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
46 | param linuxFxVersion string = ''
47 |
48 | var hostingPlanName = functionAppName
49 | var applicationInsightsName = functionAppName
50 | var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
51 | var isReserved = ((functionPlanOS == 'Linux') ? true : false)
52 |
53 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
54 | name: storageAccountName
55 | location: location
56 | sku: {
57 | name: storageAccountType
58 | }
59 | kind: 'Storage'
60 | }
61 |
62 | resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
63 | name: hostingPlanName
64 | location: location
65 | sku: {
66 | tier: 'ElasticPremium'
67 | name: functionAppPlanSku
68 | family: 'EP'
69 | }
70 | properties: {
71 | maximumElasticWorkerCount: 20
72 | reserved: isReserved
73 | }
74 | kind: 'elastic'
75 | }
76 |
77 | resource insight 'Microsoft.Insights/components@2020-02-02' = {
78 | name: applicationInsightsName
79 | location: appInsightsLocation
80 | tags: {
81 | 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource'
82 | }
83 | properties: {
84 | Application_Type: 'web'
85 | }
86 | kind: 'web'
87 | }
88 |
89 | resource site 'Microsoft.Web/sites@2022-03-01' = {
90 | name: functionAppName
91 | location: location
92 | kind: (isReserved ? 'functionapp,linux' : 'functionapp')
93 | properties: {
94 | reserved: isReserved
95 | serverFarmId: hostingPlan.id
96 | siteConfig: {
97 | linuxFxVersion: (isReserved ? linuxFxVersion : json('null'))
98 | appSettings: [
99 | {
100 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
101 | value: reference(insight.id, '2015-05-01').InstrumentationKey
102 | }
103 | {
104 | name: 'AzureWebJobsStorage'
105 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
106 | }
107 | {
108 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
109 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
110 | }
111 | {
112 | name: 'WEBSITE_CONTENTSHARE'
113 | value: toLower(functionAppName)
114 | }
115 | {
116 | name: 'FUNCTIONS_EXTENSION_VERSION'
117 | value: '~4'
118 | }
119 | {
120 | name: 'FUNCTIONS_WORKER_RUNTIME'
121 | value: functionWorkerRuntime
122 | }
123 | {
124 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
125 | value: '~14'
126 | }
127 | {
128 | name: 'WEBSITE_RUN_FROM_PACKAGE'
129 | value: '1'
130 | }
131 | ]
132 | }
133 | }
134 | }
135 |
136 | resource zipDeploy 'Microsoft.Web/sites/extensions@2022-03-01' = {
137 | parent: site
138 | name: 'zipdeploy'
139 | properties: {
140 | packageUri: packageUri
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/function-app-private-endpoints-storage-private-endpoints/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Premium plan that has private endpoints and communicates with Azure Storage over private endpoints.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-private-endpoints-storage-private-endpoints
8 | languages:
9 | - bicep
10 | - json
11 | ---
12 | # Azure Function App with private endpoint and Azure Storage with private endpoints
13 |
14 | This sample Azure Resource Manager template deploys an Azure Function App that communicates with the Azure Storage account referenced by the AzureWebJobsStorage and WEBSITE_CONTENTAZUREFILECONNECTIONSTRING app settings, [via private endpoints](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#private-endpoint-connections).
15 |
16 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-private-endpoints-storage-private-endpoints%2Fazuredeploy.json)
17 |
18 | 
19 |
20 | ### OS
21 |
22 | This template has a parameter `functionPlanOS` to choose Windows or Linux OS. Windows is selected by default. If you choose Linux, then parameter `linuxFxVersion` will be parameter, so you can skip it for Windows.
23 |
24 | ### Elastic Premium Plan
25 |
26 | The Azure Function app provisioned in this sample uses an [Azure Functions Elastic Premium plan](https://docs.microsoft.com/azure/azure-functions/functions-premium-plan#features).
27 |
28 | + **Microsoft.Web/serverfarms**: The Azure Functions Premium plan (a.k.a. Elastic Premium plan)
29 |
30 | ### Azure Function App
31 |
32 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) and [WEBSITE_CONTENTAZUREFILECONNECTIONSTRING](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) app settings to connect to a private endpoint-secured Storage Account.
33 |
34 | + **Microsoft.Web/sites**: The function app instance.
35 |
36 | ### Azure Storage account
37 |
38 | The Storage account that the Function uses for operation and for file contents.
39 |
40 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
41 |
42 | ### Virtual Network
43 |
44 | Azure resources in this sample either integrate with or are placed within a virtual network. The use of private endpoints keeps network traffic contained with the virtual network.
45 |
46 | The sample uses two subnets:
47 |
48 | - Subnet for Azure Function virtual network integration. This subnet is delegated to the Function App.
49 | - Subnet for private endpoints. Private IP addresses are allocated from this subnet.
50 |
51 | ### Private Endpoints
52 |
53 | [Azure Private Endpoints](https://docs.microsoft.com/azure/private-link/private-endpoint-overview) are used to connect to specific Azure resources using a private IP address This ensures that network traffic remains within the designated virtual network, and access is available only for specific resources. This sample configures private endpoints for the following Azure resources:
54 |
55 | - [Azure Funcion App](https://docs.microsoft.com/en-us/azure/app-service/networking/private-endpoint)
56 | - [Azure Storage](https://docs.microsoft.com/azure/storage/common/storage-private-endpoints)
57 | - Azure File storage
58 | - Azure Blob storage
59 | - Azure Queue storage
60 | - Azure Table storage
61 |
62 | ### Private DNS Zones
63 |
64 | Using a private endpoint to connect to Azure resources means connecting to a private IP address instead of the public endpoint. Existing Azure services are configured to use existing DNS to connect to the public endpoint. The DNS configuration will need to be overridden to connect to the private endpoint.
65 |
66 | A private DNS zone will be created for each Azure resource configured with a private endpoint. A DNS A record is created for each private IP address associated with the private endpoint.
67 |
68 | The following DNS zones are created in this sample:
69 |
70 | - privatelink.azurewebsites.net
71 | - privatelink.queue.core.windows.net
72 | - privatelink.blob.core.windows.net
73 | - privatelink.table.core.windows.net
74 | - privatelink.file.core.windows.net
75 |
76 | ### Application Insights
77 |
78 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
79 |
80 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
81 |
82 | ### NOTE:
83 |
84 | + This ARM template will secure your Function App by configuring the Private Endpoint, eliminating public exposure. You can connect to your App from on-premises networks that connects to the VNet using a VPN or ExpressRoute private peering.
85 | + This ARM template will allow access to the storage account through the private endpoints only. So, you will not be able to access the data storage in the storage account through the portal or otherwise.
86 | + You can give access to your secured IP address or virtual network for the data storage in the storage account, by [Managing the default network access rule](https://docs.microsoft.com/en-us/azure/storage/common/storage-network-security?tabs=azure-portal#change-the-default-network-access-rule)
87 |
88 |
89 |
90 | For more information on configuring Azure Storage firewalls and virtual networks, please refer: [Configure Azure Storage firewalls and virtual networks](https://docs.microsoft.com/en-us/azure/storage/common/storage-network-security?tabs=azure-portal)
91 |
92 | For more information on Azure Functions networking options and VNET integration, please refer: [Azure Functions Networking Options](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#restrict-your-storage-account-to-a-virtual-network)
93 |
94 | `Tags: Microsoft.Network/privateDnsZones/virtualNetworkLinks, Microsoft.Network/privateEndpoints/privateDnsZoneGroups, Microsoft.Network/virtualNetworks, Microsoft.Network/privateDnsZones, Microsoft.Network/privateEndpoints, Microsoft.Storage/storageAccounts, Microsoft.Storage/storageAccounts/fileServices/shares, Microsoft.Insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites`
95 |
--------------------------------------------------------------------------------
/function-app-private-endpoints-storage-private-endpoints/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-private-endpoints-storage-private-endpoints/images/function-app-private-endpoints-storage-private-endpoints.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/function-app-arm-templates/fc7311939d3bac5706778af24c8be82a7889a230/function-app-private-endpoints-storage-private-endpoints/images/function-app-private-endpoints-storage-private-endpoints.jpg
--------------------------------------------------------------------------------
/function-app-private-endpoints-storage-private-endpoints/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('The location into which the resources should be deployed.')
5 | param location string = resourceGroup().location
6 |
7 | @description('The language worker runtime to load in the function app.')
8 | @allowed([
9 | 'dotnet'
10 | 'node'
11 | 'python'
12 | 'java'
13 | ])
14 | param functionWorkerRuntime string = 'node'
15 |
16 | @description('Specifies the OS used for the Azure Function hosting plan.')
17 | @allowed([
18 | 'Windows'
19 | 'Linux'
20 | ])
21 | param functionPlanOS string = 'Windows'
22 |
23 | @description('Specifies the Azure Function hosting plan SKU.')
24 | @allowed([
25 | 'EP1'
26 | 'EP2'
27 | 'EP3'
28 | ])
29 | param functionAppPlanSku string = 'EP1'
30 |
31 | @description('The name of the Azure Function hosting plan.')
32 | param functionAppPlanName string = 'plan-${uniqueString(resourceGroup().id)}'
33 |
34 | @description('The name of the backend Azure storage account used by the Azure Function app.')
35 | param functionStorageAccountName string = 'st${uniqueString(resourceGroup().id)}'
36 |
37 | @description('The name of the virtual network for virtual network integration.')
38 | param vnetName string = 'vnet-${uniqueString(resourceGroup().id)}'
39 |
40 | @description('The name of the virtual network subnet to be associated with the Azure Function app.')
41 | param functionSubnetName string = 'snet-func'
42 |
43 | @description('The name of the virtual network subnet used for allocating IP addresses for private endpoints.')
44 | param privateEndpointSubnetName string = 'snet-pe'
45 |
46 | @description('The IP adddress space used for the virtual network.')
47 | param vnetAddressPrefix string = '10.100.0.0/16'
48 |
49 | @description('The IP address space used for the Azure Function integration subnet.')
50 | param functionSubnetAddressPrefix string = '10.100.0.0/24'
51 |
52 | @description('The IP address space used for the private endpoints.')
53 | param privateEndpointSubnetAddressPrefix string = '10.100.1.0/24'
54 |
55 | @description('Only required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
56 | param linuxFxVersion string = ''
57 |
58 | var applicationInsightsName = 'appi-${uniqueString(resourceGroup().id)}'
59 | var privateFunctionAppDnsZoneName = 'privatelink.azurewebsites.net'
60 | var privateEndpointFunctionAppName = '${functionAppName}-private-endpoint'
61 | var privateStorageFileDnsZoneName = 'privatelink.file.${environment().suffixes.storage}'
62 | var privateEndpointStorageFileName = '${functionStorageAccountName}-file-private-endpoint'
63 | var privateStorageTableDnsZoneName = 'privatelink.table.${environment().suffixes.storage}'
64 | var privateEndpointStorageTableName = '${functionStorageAccountName}-table-private-endpoint'
65 | var privateStorageBlobDnsZoneName = 'privatelink.blob.${environment().suffixes.storage}'
66 | var privateEndpointStorageBlobName = '${functionStorageAccountName}-blob-private-endpoint'
67 | var privateStorageQueueDnsZoneName = 'privatelink.queue.${environment().suffixes.storage}'
68 | var privateEndpointStorageQueueName = '${functionStorageAccountName}-queue-private-endpoint'
69 | var functionContentShareName = 'function-content-share'
70 | var isReserved = ((functionPlanOS == 'Linux') ? true : false)
71 |
72 | resource vnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
73 | name: vnetName
74 | location: location
75 | properties: {
76 | addressSpace: {
77 | addressPrefixes: [
78 | vnetAddressPrefix
79 | ]
80 | }
81 | subnets: [
82 | {
83 | name: functionSubnetName
84 | properties: {
85 | privateEndpointNetworkPolicies: 'Enabled'
86 | privateLinkServiceNetworkPolicies: 'Enabled'
87 | delegations: [
88 | {
89 | name: 'webapp'
90 | properties: {
91 | serviceName: 'Microsoft.Web/serverFarms'
92 | }
93 | }
94 | ]
95 | addressPrefix: functionSubnetAddressPrefix
96 | }
97 | }
98 | {
99 | name: privateEndpointSubnetName
100 | properties: {
101 | privateEndpointNetworkPolicies: 'Disabled'
102 | privateLinkServiceNetworkPolicies: 'Enabled'
103 | addressPrefix: privateEndpointSubnetAddressPrefix
104 | }
105 | }
106 | ]
107 | }
108 | }
109 |
110 | resource privateStorageFileDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
111 | name: privateStorageFileDnsZoneName
112 | location: 'global'
113 | }
114 |
115 | resource privateStorageBlobDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
116 | name: privateStorageBlobDnsZoneName
117 | location: 'global'
118 | }
119 |
120 | resource privateStorageQueueDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
121 | name: privateStorageQueueDnsZoneName
122 | location: 'global'
123 | }
124 |
125 | resource privateStorageTableDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
126 | name: privateStorageTableDnsZoneName
127 | location: 'global'
128 | }
129 |
130 | resource privateStorageFileDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
131 | parent: privateStorageFileDnsZone
132 | name: '${privateStorageFileDnsZoneName}-link'
133 | location: 'global'
134 | properties: {
135 | registrationEnabled: false
136 | virtualNetwork: {
137 | id: vnet.id
138 | }
139 | }
140 | }
141 |
142 | resource privateStorageBlobDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
143 | parent: privateStorageBlobDnsZone
144 | name: '${privateStorageBlobDnsZoneName}-link'
145 | location: 'global'
146 | properties: {
147 | registrationEnabled: false
148 | virtualNetwork: {
149 | id: vnet.id
150 | }
151 | }
152 | }
153 |
154 | resource privateStorageTableDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
155 | parent: privateStorageTableDnsZone
156 | name: '${privateStorageTableDnsZoneName}-link'
157 | location: 'global'
158 | properties: {
159 | registrationEnabled: false
160 | virtualNetwork: {
161 | id: vnet.id
162 | }
163 | }
164 | }
165 |
166 | resource privateStorageQueueDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
167 | parent: privateStorageQueueDnsZone
168 | name: '${privateStorageQueueDnsZoneName}-link'
169 | location: 'global'
170 | properties: {
171 | registrationEnabled: false
172 | virtualNetwork: {
173 | id: vnet.id
174 | }
175 | }
176 | }
177 |
178 | resource privateEndpointStorageFile 'Microsoft.Network/privateEndpoints@2022-05-01' = {
179 | name: privateEndpointStorageFileName
180 | location: location
181 | properties: {
182 | subnet: {
183 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
184 | }
185 | privateLinkServiceConnections: [
186 | {
187 | name: 'MyStorageFilePrivateLinkConnection'
188 | properties: {
189 | privateLinkServiceId: storageAccount.id
190 | groupIds: [
191 | 'file'
192 | ]
193 | }
194 | }
195 | ]
196 | }
197 | dependsOn: [
198 | vnet
199 | ]
200 | }
201 |
202 | resource privateEndpointStorageBlob 'Microsoft.Network/privateEndpoints@2022-05-01' = {
203 | name: privateEndpointStorageBlobName
204 | location: location
205 | properties: {
206 | subnet: {
207 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
208 | }
209 | privateLinkServiceConnections: [
210 | {
211 | name: 'MyStorageBlobPrivateLinkConnection'
212 | properties: {
213 | privateLinkServiceId: storageAccount.id
214 | groupIds: [
215 | 'blob'
216 | ]
217 | }
218 | }
219 | ]
220 | }
221 | dependsOn: [
222 | vnet
223 | ]
224 | }
225 |
226 | resource privateEndpointStorageTable 'Microsoft.Network/privateEndpoints@2022-05-01' = {
227 | name: privateEndpointStorageTableName
228 | location: location
229 | properties: {
230 | subnet: {
231 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
232 | }
233 | privateLinkServiceConnections: [
234 | {
235 | name: 'MyStorageTablePrivateLinkConnection'
236 | properties: {
237 | privateLinkServiceId: storageAccount.id
238 | groupIds: [
239 | 'table'
240 | ]
241 | }
242 | }
243 | ]
244 | }
245 | dependsOn: [
246 | vnet
247 | ]
248 | }
249 |
250 | resource privateEndpointStorageQueue 'Microsoft.Network/privateEndpoints@2022-05-01' = {
251 | name: privateEndpointStorageQueueName
252 | location: location
253 | properties: {
254 | subnet: {
255 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
256 | }
257 | privateLinkServiceConnections: [
258 | {
259 | name: 'MyStorageQueuePrivateLinkConnection'
260 | properties: {
261 | privateLinkServiceId: storageAccount.id
262 | groupIds: [
263 | 'queue'
264 | ]
265 | }
266 | }
267 | ]
268 | }
269 | dependsOn: [
270 | vnet
271 | ]
272 | }
273 |
274 | resource privateEndpointStorageFilePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
275 | parent: privateEndpointStorageFile
276 | name: 'filePrivateDnsZoneGroup'
277 | properties: {
278 | privateDnsZoneConfigs: [
279 | {
280 | name: 'config'
281 | properties: {
282 | privateDnsZoneId: privateStorageFileDnsZone.id
283 | }
284 | }
285 | ]
286 | }
287 | }
288 |
289 | resource privateEndpointStorageBlobPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
290 | parent: privateEndpointStorageBlob
291 | name: 'blobPrivateDnsZoneGroup'
292 | properties: {
293 | privateDnsZoneConfigs: [
294 | {
295 | name: 'config'
296 | properties: {
297 | privateDnsZoneId: privateStorageBlobDnsZone.id
298 | }
299 | }
300 | ]
301 | }
302 | }
303 |
304 | resource privateEndpointStorageTablePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
305 | parent: privateEndpointStorageTable
306 | name: 'tablePrivateDnsZoneGroup'
307 | properties: {
308 | privateDnsZoneConfigs: [
309 | {
310 | name: 'config'
311 | properties: {
312 | privateDnsZoneId: privateStorageTableDnsZone.id
313 | }
314 | }
315 | ]
316 | }
317 | }
318 |
319 | resource privateEndpointStorageQueuePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
320 | parent: privateEndpointStorageQueue
321 | name: 'queuePrivateDnsZoneGroup'
322 | properties: {
323 | privateDnsZoneConfigs: [
324 | {
325 | name: 'config'
326 | properties: {
327 | privateDnsZoneId: privateStorageQueueDnsZone.id
328 | }
329 | }
330 | ]
331 | }
332 | }
333 |
334 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
335 | name: functionStorageAccountName
336 | location: location
337 | kind: 'StorageV2'
338 | sku: {
339 | name: 'Standard_LRS'
340 | }
341 | properties: {
342 | publicNetworkAccess: 'Disabled'
343 | allowBlobPublicAccess: false
344 | networkAcls: {
345 | bypass: 'None'
346 | defaultAction: 'Deny'
347 | }
348 | }
349 | }
350 |
351 | resource fileService 'Microsoft.Storage/storageAccounts/fileServices/shares@2022-05-01' = {
352 | name: '${functionStorageAccountName}/default/${functionContentShareName}'
353 | dependsOn: [
354 | storageAccount
355 | ]
356 | }
357 |
358 | resource applicationInsight 'Microsoft.Insights/components@2020-02-02' = {
359 | name: applicationInsightsName
360 | location: location
361 | kind: 'web'
362 | properties: {
363 | Application_Type: 'web'
364 | }
365 | }
366 |
367 | resource functionAppPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
368 | name: functionAppPlanName
369 | location: location
370 | sku: {
371 | tier: 'ElasticPremium'
372 | name: functionAppPlanSku
373 | size: functionAppPlanSku
374 | family: 'EP'
375 | }
376 | kind: 'elastic'
377 | properties: {
378 | maximumElasticWorkerCount: 20
379 | reserved: isReserved
380 | }
381 | }
382 |
383 | resource site 'Microsoft.Web/sites@2022-03-01' = {
384 | name: functionAppName
385 | location: location
386 | kind: (isReserved ? 'functionapp,linux' : 'functionapp')
387 | properties: {
388 | reserved: isReserved
389 | serverFarmId: functionAppPlan.id
390 | siteConfig: {
391 | functionsRuntimeScaleMonitoringEnabled: true
392 | linuxFxVersion: (isReserved ? linuxFxVersion : json('null'))
393 | appSettings: [
394 | {
395 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
396 | value: applicationInsight.properties.InstrumentationKey
397 | }
398 | {
399 | name: 'AzureWebJobsStorage'
400 | value: 'DefaultEndpointsProtocol=https;AccountName=${functionStorageAccountName};AccountKey=${storageAccount.listkeys().keys[0].value}'
401 | }
402 | {
403 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
404 | value: 'DefaultEndpointsProtocol=https;AccountName=${functionStorageAccountName};AccountKey=${storageAccount.listkeys().keys[0].value}'
405 | }
406 | {
407 | name: 'WEBSITE_CONTENTSHARE'
408 | value: functionContentShareName
409 | }
410 | {
411 | name: 'FUNCTIONS_EXTENSION_VERSION'
412 | value: '~4'
413 | }
414 | {
415 | name: 'FUNCTIONS_WORKER_RUNTIME'
416 | value: functionWorkerRuntime
417 | }
418 | {
419 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
420 | value: '~14'
421 | }
422 | {
423 | name: 'WEBSITE_VNET_ROUTE_ALL'
424 | value: '1'
425 | }
426 | {
427 | name: 'WEBSITE_CONTENTOVERVNET'
428 | value: '1'
429 | }
430 | ]
431 | }
432 | }
433 | dependsOn: [
434 | fileService
435 | privateStorageFileDnsZoneLink
436 | privateEndpointStorageFilePrivateDnsZoneGroup
437 | privateStorageBlobDnsZoneLink
438 | privateEndpointStorageBlobPrivateDnsZoneGroup
439 | privateStorageTableDnsZoneLink
440 | privateEndpointStorageTablePrivateDnsZoneGroup
441 | privateStorageQueueDnsZoneLink
442 | privateEndpointStorageQueuePrivateDnsZoneGroup
443 | ]
444 | }
445 |
446 | resource networkConfig 'Microsoft.Web/sites/networkConfig@2022-03-01' = {
447 | parent: site
448 | name: 'virtualNetwork'
449 | properties: {
450 | subnetResourceId: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, functionSubnetName)
451 | swiftSupported: true
452 | }
453 | dependsOn: [
454 | vnet
455 | ]
456 | }
457 |
458 | resource privateFunctionAppDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
459 | name: privateFunctionAppDnsZoneName
460 | location: 'global'
461 | }
462 |
463 | resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
464 | parent: privateFunctionAppDnsZone
465 | name: '${privateFunctionAppDnsZoneName}-link'
466 | location: 'global'
467 | properties: {
468 | registrationEnabled: false
469 | virtualNetwork: {
470 | id: vnet.id
471 | }
472 | }
473 | }
474 |
475 | resource privateEndpoint 'Microsoft.Network/privateEndpoints@2022-05-01' = {
476 | name: privateEndpointFunctionAppName
477 | location: location
478 | properties: {
479 | subnet: {
480 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
481 | }
482 | privateLinkServiceConnections: [
483 | {
484 | name: 'MyFunctionAppPrivateLinkConnection'
485 | properties: {
486 | privateLinkServiceId: site.id
487 | groupIds: [
488 | 'sites'
489 | ]
490 | }
491 | }
492 | ]
493 | }
494 | dependsOn: [
495 | vnet
496 | ]
497 | }
498 |
499 | resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
500 | parent: privateEndpoint
501 | name: 'funcPrivateDnsZoneGroup'
502 | properties: {
503 | privateDnsZoneConfigs: [
504 | {
505 | name: 'config'
506 | properties: {
507 | privateDnsZoneId: privateFunctionAppDnsZone.id
508 | }
509 | }
510 | ]
511 | }
512 | }
513 |
--------------------------------------------------------------------------------
/function-app-storage-private-endpoints/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Premium plan that communicates with Azure Storage over private endpoints.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-storage-private-endpoints
8 | languages:
9 | - bicep
10 | - json
11 | ---
12 | # Azure Function App with Private Endpoint Secured Azure Storage
13 |
14 | This sample Azure Resource Manager template deploys an Azure Function App that communicates with the Azure Storage account referenced by the AzureWebJobsStorage and WEBSITE_CONTENTAZUREFILECONNECTIONSTRING app settings, [via private endpoints](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#private-endpoint-connections).
15 |
16 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-storage-private-endpoints%2Fazuredeploy.json)
17 |
18 | 
19 |
20 | ### OS
21 |
22 | This template has a parameter `functionPlanOS` to choose Windows or Linux OS. Windows is selected by default. If you choose Linux, then parameter `linuxFxVersion` will be parameter, so you can skip it for Windows.
23 |
24 | ### Elastic Premium Plan
25 |
26 | The Azure Function app provisioned in this sample uses an [Azure Functions Elastic Premium plan](https://docs.microsoft.com/azure/azure-functions/functions-premium-plan#features).
27 |
28 | + **Microsoft.Web/serverfarms**: The Azure Functions Premium plan (a.k.a. Elastic Premium plan)
29 |
30 | ### Azure Function App
31 |
32 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) and [WEBSITE_CONTENTAZUREFILECONNECTIONSTRING](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) app settings to connect to a private endpoint-secured Storage Account.
33 |
34 | + **Microsoft.Web/sites**: The function app instance.
35 |
36 | ### Azure Storage account
37 |
38 | The Storage account that the Function uses for operation and for file contents.
39 |
40 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
41 |
42 | ### Virtual Network
43 |
44 | Azure resources in this sample either integrate with or are placed within a virtual network. The use of private endpoints keeps network traffic contained with the virtual network.
45 |
46 | The sample uses two subnets:
47 |
48 | - Subnet for Azure Function virtual network integration. This subnet is delegated to the Function App.
49 | - Subnet for private endpoints. Private IP addresses are allocated from this subnet.
50 |
51 | ### Private Endpoints
52 |
53 | [Azure Private Endpoints](https://docs.microsoft.com/azure/private-link/private-endpoint-overview) are used to connect to specific Azure resources using a private IP address This ensures that network traffic remains within the designated virtual network, and access is available only for specific resources. This sample configures private endpoints for the following Azure resources:
54 |
55 | - [Azure Storage](https://docs.microsoft.com/azure/storage/common/storage-private-endpoints)
56 | - Azure File storage
57 | - Azure Blob storage
58 | - Azure Queue storage
59 | - Azure Table storage
60 |
61 | ### Private DNS Zones
62 |
63 | Using a private endpoint to connect to Azure resources means connecting to a private IP address instead of the public endpoint. Existing Azure services are configured to use existing DNS to connect to the public endpoint. The DNS configuration will need to be overridden to connect to the private endpoint.
64 |
65 | A private DNS zone will be created for each Azure resource configured with a private endpoint. A DNS A record is created for each private IP address associated with the private endpoint.
66 |
67 | The following DNS zones are created in this sample:
68 |
69 | - privatelink.queue.core.windows.net
70 | - privatelink.blob.core.windows.net
71 | - privatelink.table.core.windows.net
72 | - privatelink.file.core.windows.net
73 |
74 | ### Application Insights
75 |
76 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
77 |
78 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
79 |
80 | ### NOTE:
81 |
82 | + This ARM template will allow access to the storage account through the private endpoints only. So, you will not be able to access the data storage in the storage account through the portal or otherwise.
83 | + You can give access to your secured IP address or virtual network for the data storage in the storage account, by [Managing the default network access rule](https://docs.microsoft.com/en-us/azure/storage/common/storage-network-security?tabs=azure-portal#change-the-default-network-access-rule)
84 |
85 |
86 |
87 | For more information on configuring Azure Storage firewalls and virtual networks, please refer: [Configure Azure Storage firewalls and virtual networks](https://docs.microsoft.com/en-us/azure/storage/common/storage-network-security?tabs=azure-portal)
88 |
89 | For more information on Azure Functions networking options and VNET integration, please refer: [Azure Functions Networking Options](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#restrict-your-storage-account-to-a-virtual-network)
90 |
91 | `Tags: Microsoft.Network/privateDnsZones/virtualNetworkLinks, Microsoft.Network/privateEndpoints/privateDnsZoneGroups, Microsoft.Network/virtualNetworks, Microsoft.Network/privateDnsZones, Microsoft.Network/privateEndpoints, Microsoft.Storage/storageAccounts, Microsoft.Storage/storageAccounts/fileServices/shares, Microsoft.Insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites`
92 |
--------------------------------------------------------------------------------
/function-app-storage-private-endpoints/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.10.61.36676",
8 | "templateHash": "12847217906836811677"
9 | }
10 | },
11 | "parameters": {
12 | "functionAppName": {
13 | "type": "string",
14 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
15 | "metadata": {
16 | "description": "The name of the Azure Function app."
17 | }
18 | },
19 | "location": {
20 | "type": "string",
21 | "defaultValue": "[resourceGroup().location]",
22 | "metadata": {
23 | "description": "The location into which the resources should be deployed."
24 | }
25 | },
26 | "functionWorkerRuntime": {
27 | "type": "string",
28 | "defaultValue": "node",
29 | "allowedValues": [
30 | "dotnet",
31 | "node",
32 | "python",
33 | "java"
34 | ],
35 | "metadata": {
36 | "description": "The language worker runtime to load in the function app."
37 | }
38 | },
39 | "functionPlanOS": {
40 | "type": "string",
41 | "defaultValue": "Windows",
42 | "allowedValues": [
43 | "Windows",
44 | "Linux"
45 | ],
46 | "metadata": {
47 | "description": "Specifies the OS used for the Azure Function hosting plan."
48 | }
49 | },
50 | "functionAppPlanSku": {
51 | "type": "string",
52 | "defaultValue": "EP1",
53 | "allowedValues": [
54 | "EP1",
55 | "EP2",
56 | "EP3"
57 | ],
58 | "metadata": {
59 | "description": "Specifies the Azure Function hosting plan SKU."
60 | }
61 | },
62 | "functionAppPlanName": {
63 | "type": "string",
64 | "defaultValue": "[format('plan-{0}', uniqueString(resourceGroup().id))]",
65 | "metadata": {
66 | "description": "The name of the Azure Function hosting plan."
67 | }
68 | },
69 | "functionStorageAccountName": {
70 | "type": "string",
71 | "defaultValue": "[format('st{0}', uniqueString(resourceGroup().id))]",
72 | "metadata": {
73 | "description": "The name of the backend Azure storage account used by the Azure Function app."
74 | }
75 | },
76 | "vnetName": {
77 | "type": "string",
78 | "defaultValue": "[format('vnet-{0}', uniqueString(resourceGroup().id))]",
79 | "metadata": {
80 | "description": "The name of the virtual network for virtual network integration."
81 | }
82 | },
83 | "functionSubnetName": {
84 | "type": "string",
85 | "defaultValue": "snet-func",
86 | "metadata": {
87 | "description": "The name of the virtual network subnet to be associated with the Azure Function app."
88 | }
89 | },
90 | "privateEndpointSubnetName": {
91 | "type": "string",
92 | "defaultValue": "snet-pe",
93 | "metadata": {
94 | "description": "The name of the virtual network subnet used for allocating IP addresses for private endpoints."
95 | }
96 | },
97 | "vnetAddressPrefix": {
98 | "type": "string",
99 | "defaultValue": "10.100.0.0/16",
100 | "metadata": {
101 | "description": "The IP adddress space used for the virtual network."
102 | }
103 | },
104 | "functionSubnetAddressPrefix": {
105 | "type": "string",
106 | "defaultValue": "10.100.0.0/24",
107 | "metadata": {
108 | "description": "The IP address space used for the Azure Function integration subnet."
109 | }
110 | },
111 | "privateEndpointSubnetAddressPrefix": {
112 | "type": "string",
113 | "defaultValue": "10.100.1.0/24",
114 | "metadata": {
115 | "description": "The IP address space used for the private endpoints."
116 | }
117 | },
118 | "linuxFxVersion": {
119 | "type": "string",
120 | "defaultValue": "",
121 | "metadata": {
122 | "description": "Only required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
123 | }
124 | }
125 | },
126 | "variables": {
127 | "applicationInsightsName": "[format('appi-{0}', uniqueString(resourceGroup().id))]",
128 | "privateStorageFileDnsZoneName": "[format('privatelink.file.{0}', environment().suffixes.storage)]",
129 | "privateEndpointStorageFileName": "[format('{0}-file-private-endpoint', parameters('functionStorageAccountName'))]",
130 | "privateStorageTableDnsZoneName": "[format('privatelink.table.{0}', environment().suffixes.storage)]",
131 | "privateEndpointStorageTableName": "[format('{0}-table-private-endpoint', parameters('functionStorageAccountName'))]",
132 | "privateStorageBlobDnsZoneName": "[format('privatelink.blob.{0}', environment().suffixes.storage)]",
133 | "privateEndpointStorageBlobName": "[format('{0}-blob-private-endpoint', parameters('functionStorageAccountName'))]",
134 | "privateStorageQueueDnsZoneName": "[format('privatelink.queue.{0}', environment().suffixes.storage)]",
135 | "privateEndpointStorageQueueName": "[format('{0}-queue-private-endpoint', parameters('functionStorageAccountName'))]",
136 | "functionContentShareName": "function-content-share",
137 | "isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]"
138 | },
139 | "resources": [
140 | {
141 | "type": "Microsoft.Network/virtualNetworks",
142 | "apiVersion": "2022-05-01",
143 | "name": "[parameters('vnetName')]",
144 | "location": "[parameters('location')]",
145 | "properties": {
146 | "addressSpace": {
147 | "addressPrefixes": [
148 | "[parameters('vnetAddressPrefix')]"
149 | ]
150 | },
151 | "subnets": [
152 | {
153 | "name": "[parameters('functionSubnetName')]",
154 | "properties": {
155 | "privateEndpointNetworkPolicies": "Enabled",
156 | "privateLinkServiceNetworkPolicies": "Enabled",
157 | "delegations": [
158 | {
159 | "name": "webapp",
160 | "properties": {
161 | "serviceName": "Microsoft.Web/serverFarms"
162 | }
163 | }
164 | ],
165 | "addressPrefix": "[parameters('functionSubnetAddressPrefix')]"
166 | }
167 | },
168 | {
169 | "name": "[parameters('privateEndpointSubnetName')]",
170 | "properties": {
171 | "privateEndpointNetworkPolicies": "Disabled",
172 | "privateLinkServiceNetworkPolicies": "Enabled",
173 | "addressPrefix": "[parameters('privateEndpointSubnetAddressPrefix')]"
174 | }
175 | }
176 | ]
177 | }
178 | },
179 | {
180 | "type": "Microsoft.Network/privateDnsZones",
181 | "apiVersion": "2020-06-01",
182 | "name": "[variables('privateStorageFileDnsZoneName')]",
183 | "location": "global"
184 | },
185 | {
186 | "type": "Microsoft.Network/privateDnsZones",
187 | "apiVersion": "2020-06-01",
188 | "name": "[variables('privateStorageBlobDnsZoneName')]",
189 | "location": "global"
190 | },
191 | {
192 | "type": "Microsoft.Network/privateDnsZones",
193 | "apiVersion": "2020-06-01",
194 | "name": "[variables('privateStorageQueueDnsZoneName')]",
195 | "location": "global"
196 | },
197 | {
198 | "type": "Microsoft.Network/privateDnsZones",
199 | "apiVersion": "2020-06-01",
200 | "name": "[variables('privateStorageTableDnsZoneName')]",
201 | "location": "global"
202 | },
203 | {
204 | "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
205 | "apiVersion": "2020-06-01",
206 | "name": "[format('{0}/{1}', variables('privateStorageFileDnsZoneName'), format('{0}-link', variables('privateStorageFileDnsZoneName')))]",
207 | "location": "global",
208 | "properties": {
209 | "registrationEnabled": false,
210 | "virtualNetwork": {
211 | "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
212 | }
213 | },
214 | "dependsOn": [
215 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]",
216 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
217 | ]
218 | },
219 | {
220 | "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
221 | "apiVersion": "2020-06-01",
222 | "name": "[format('{0}/{1}', variables('privateStorageBlobDnsZoneName'), format('{0}-link', variables('privateStorageBlobDnsZoneName')))]",
223 | "location": "global",
224 | "properties": {
225 | "registrationEnabled": false,
226 | "virtualNetwork": {
227 | "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
228 | }
229 | },
230 | "dependsOn": [
231 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]",
232 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
233 | ]
234 | },
235 | {
236 | "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
237 | "apiVersion": "2020-06-01",
238 | "name": "[format('{0}/{1}', variables('privateStorageTableDnsZoneName'), format('{0}-link', variables('privateStorageTableDnsZoneName')))]",
239 | "location": "global",
240 | "properties": {
241 | "registrationEnabled": false,
242 | "virtualNetwork": {
243 | "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
244 | }
245 | },
246 | "dependsOn": [
247 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]",
248 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
249 | ]
250 | },
251 | {
252 | "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
253 | "apiVersion": "2020-06-01",
254 | "name": "[format('{0}/{1}', variables('privateStorageQueueDnsZoneName'), format('{0}-link', variables('privateStorageQueueDnsZoneName')))]",
255 | "location": "global",
256 | "properties": {
257 | "registrationEnabled": false,
258 | "virtualNetwork": {
259 | "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
260 | }
261 | },
262 | "dependsOn": [
263 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]",
264 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
265 | ]
266 | },
267 | {
268 | "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups",
269 | "apiVersion": "2022-05-01",
270 | "name": "[format('{0}/{1}', variables('privateEndpointStorageFileName'), 'filePrivateDnsZoneGroup')]",
271 | "properties": {
272 | "privateDnsZoneConfigs": [
273 | {
274 | "name": "config",
275 | "properties": {
276 | "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]"
277 | }
278 | }
279 | ]
280 | },
281 | "dependsOn": [
282 | "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageFileName'))]",
283 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]"
284 | ]
285 | },
286 | {
287 | "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups",
288 | "apiVersion": "2022-05-01",
289 | "name": "[format('{0}/{1}', variables('privateEndpointStorageBlobName'), 'blobPrivateDnsZoneGroup')]",
290 | "properties": {
291 | "privateDnsZoneConfigs": [
292 | {
293 | "name": "config",
294 | "properties": {
295 | "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]"
296 | }
297 | }
298 | ]
299 | },
300 | "dependsOn": [
301 | "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageBlobName'))]",
302 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]"
303 | ]
304 | },
305 | {
306 | "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups",
307 | "apiVersion": "2022-05-01",
308 | "name": "[format('{0}/{1}', variables('privateEndpointStorageTableName'), 'tablePrivateDnsZoneGroup')]",
309 | "properties": {
310 | "privateDnsZoneConfigs": [
311 | {
312 | "name": "config",
313 | "properties": {
314 | "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]"
315 | }
316 | }
317 | ]
318 | },
319 | "dependsOn": [
320 | "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageTableName'))]",
321 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]"
322 | ]
323 | },
324 | {
325 | "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups",
326 | "apiVersion": "2022-05-01",
327 | "name": "[format('{0}/{1}', variables('privateEndpointStorageQueueName'), 'queuePrivateDnsZoneGroup')]",
328 | "properties": {
329 | "privateDnsZoneConfigs": [
330 | {
331 | "name": "config",
332 | "properties": {
333 | "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]"
334 | }
335 | }
336 | ]
337 | },
338 | "dependsOn": [
339 | "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageQueueName'))]",
340 | "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]"
341 | ]
342 | },
343 | {
344 | "type": "Microsoft.Network/privateEndpoints",
345 | "apiVersion": "2022-05-01",
346 | "name": "[variables('privateEndpointStorageFileName')]",
347 | "location": "[parameters('location')]",
348 | "properties": {
349 | "subnet": {
350 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]"
351 | },
352 | "privateLinkServiceConnections": [
353 | {
354 | "name": "MyStorageFilePrivateLinkConnection",
355 | "properties": {
356 | "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
357 | "groupIds": [
358 | "file"
359 | ]
360 | }
361 | }
362 | ]
363 | },
364 | "dependsOn": [
365 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
366 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
367 | ]
368 | },
369 | {
370 | "type": "Microsoft.Network/privateEndpoints",
371 | "apiVersion": "2022-05-01",
372 | "name": "[variables('privateEndpointStorageBlobName')]",
373 | "location": "[parameters('location')]",
374 | "properties": {
375 | "subnet": {
376 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]"
377 | },
378 | "privateLinkServiceConnections": [
379 | {
380 | "name": "MyStorageBlobPrivateLinkConnection",
381 | "properties": {
382 | "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
383 | "groupIds": [
384 | "blob"
385 | ]
386 | }
387 | }
388 | ]
389 | },
390 | "dependsOn": [
391 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
392 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
393 | ]
394 | },
395 | {
396 | "type": "Microsoft.Network/privateEndpoints",
397 | "apiVersion": "2022-05-01",
398 | "name": "[variables('privateEndpointStorageTableName')]",
399 | "location": "[parameters('location')]",
400 | "properties": {
401 | "subnet": {
402 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]"
403 | },
404 | "privateLinkServiceConnections": [
405 | {
406 | "name": "MyStorageTablePrivateLinkConnection",
407 | "properties": {
408 | "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
409 | "groupIds": [
410 | "table"
411 | ]
412 | }
413 | }
414 | ]
415 | },
416 | "dependsOn": [
417 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
418 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
419 | ]
420 | },
421 | {
422 | "type": "Microsoft.Network/privateEndpoints",
423 | "apiVersion": "2022-05-01",
424 | "name": "[variables('privateEndpointStorageQueueName')]",
425 | "location": "[parameters('location')]",
426 | "properties": {
427 | "subnet": {
428 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]"
429 | },
430 | "privateLinkServiceConnections": [
431 | {
432 | "name": "MyStorageQueuePrivateLinkConnection",
433 | "properties": {
434 | "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
435 | "groupIds": [
436 | "queue"
437 | ]
438 | }
439 | }
440 | ]
441 | },
442 | "dependsOn": [
443 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
444 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
445 | ]
446 | },
447 | {
448 | "type": "Microsoft.Storage/storageAccounts",
449 | "apiVersion": "2022-05-01",
450 | "name": "[parameters('functionStorageAccountName')]",
451 | "location": "[parameters('location')]",
452 | "kind": "StorageV2",
453 | "sku": {
454 | "name": "Standard_LRS"
455 | },
456 | "properties": {
457 | "publicNetworkAccess": "Disabled",
458 | "allowBlobPublicAccess": false,
459 | "networkAcls": {
460 | "bypass": "None",
461 | "defaultAction": "Deny"
462 | }
463 | }
464 | },
465 | {
466 | "type": "Microsoft.Storage/storageAccounts/fileServices/shares",
467 | "apiVersion": "2022-05-01",
468 | "name": "[format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName'))]",
469 | "dependsOn": [
470 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]"
471 | ]
472 | },
473 | {
474 | "type": "Microsoft.Insights/components",
475 | "apiVersion": "2020-02-02",
476 | "name": "[variables('applicationInsightsName')]",
477 | "location": "[parameters('location')]",
478 | "kind": "web",
479 | "properties": {
480 | "Application_Type": "web"
481 | }
482 | },
483 | {
484 | "type": "Microsoft.Web/serverfarms",
485 | "apiVersion": "2022-03-01",
486 | "name": "[parameters('functionAppPlanName')]",
487 | "location": "[parameters('location')]",
488 | "sku": {
489 | "tier": "ElasticPremium",
490 | "name": "[parameters('functionAppPlanSku')]",
491 | "size": "[parameters('functionAppPlanSku')]",
492 | "family": "EP"
493 | },
494 | "kind": "elastic",
495 | "properties": {
496 | "maximumElasticWorkerCount": 20,
497 | "reserved": "[variables('isReserved')]"
498 | }
499 | },
500 | {
501 | "type": "Microsoft.Web/sites",
502 | "apiVersion": "2022-03-01",
503 | "name": "[parameters('functionAppName')]",
504 | "location": "[parameters('location')]",
505 | "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
506 | "properties": {
507 | "reserved": "[variables('isReserved')]",
508 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('functionAppPlanName'))]",
509 | "siteConfig": {
510 | "functionsRuntimeScaleMonitoringEnabled": true,
511 | "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
512 | "appSettings": [
513 | {
514 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
515 | "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))).InstrumentationKey]"
516 | },
517 | {
518 | "name": "AzureWebJobsStorage",
519 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}', parameters('functionStorageAccountName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName')), '2022-05-01').keys[0].value)]"
520 | },
521 | {
522 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
523 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}', parameters('functionStorageAccountName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName')), '2022-05-01').keys[0].value)]"
524 | },
525 | {
526 | "name": "WEBSITE_CONTENTSHARE",
527 | "value": "[variables('functionContentShareName')]"
528 | },
529 | {
530 | "name": "FUNCTIONS_EXTENSION_VERSION",
531 | "value": "~4"
532 | },
533 | {
534 | "name": "FUNCTIONS_WORKER_RUNTIME",
535 | "value": "[parameters('functionWorkerRuntime')]"
536 | },
537 | {
538 | "name": "WEBSITE_NODE_DEFAULT_VERSION",
539 | "value": "~14"
540 | },
541 | {
542 | "name": "WEBSITE_VNET_ROUTE_ALL",
543 | "value": "1"
544 | },
545 | {
546 | "name": "WEBSITE_CONTENTOVERVNET",
547 | "value": "1"
548 | }
549 | ]
550 | }
551 | },
552 | "dependsOn": [
553 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
554 | "[resourceId('Microsoft.Web/serverfarms', parameters('functionAppPlanName'))]",
555 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]",
556 | "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageBlobName'), 'blobPrivateDnsZoneGroup')]",
557 | "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageFileName'), 'filePrivateDnsZoneGroup')]",
558 | "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageQueueName'), 'queuePrivateDnsZoneGroup')]",
559 | "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageTableName'), 'tablePrivateDnsZoneGroup')]",
560 | "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageBlobDnsZoneName'), format('{0}-link', variables('privateStorageBlobDnsZoneName')))]",
561 | "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageFileDnsZoneName'), format('{0}-link', variables('privateStorageFileDnsZoneName')))]",
562 | "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageQueueDnsZoneName'), format('{0}-link', variables('privateStorageQueueDnsZoneName')))]",
563 | "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageTableDnsZoneName'), format('{0}-link', variables('privateStorageTableDnsZoneName')))]",
564 | "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', split(format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName')), '/')[0], split(format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName')), '/')[1], split(format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName')), '/')[2])]"
565 | ]
566 | },
567 | {
568 | "type": "Microsoft.Web/sites/networkConfig",
569 | "apiVersion": "2022-03-01",
570 | "name": "[format('{0}/{1}', parameters('functionAppName'), 'virtualNetwork')]",
571 | "properties": {
572 | "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('functionSubnetName'))]",
573 | "swiftSupported": true
574 | },
575 | "dependsOn": [
576 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]",
577 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
578 | ]
579 | }
580 | ]
581 | }
--------------------------------------------------------------------------------
/function-app-storage-private-endpoints/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-storage-private-endpoints/images/function-app-storage-private-endpoints.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/function-app-arm-templates/fc7311939d3bac5706778af24c8be82a7889a230/function-app-storage-private-endpoints/images/function-app-storage-private-endpoints.jpg
--------------------------------------------------------------------------------
/function-app-storage-private-endpoints/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('The location into which the resources should be deployed.')
5 | param location string = resourceGroup().location
6 |
7 | @description('The language worker runtime to load in the function app.')
8 | @allowed([
9 | 'dotnet'
10 | 'node'
11 | 'python'
12 | 'java'
13 | ])
14 | param functionWorkerRuntime string = 'node'
15 |
16 | @description('Specifies the OS used for the Azure Function hosting plan.')
17 | @allowed([
18 | 'Windows'
19 | 'Linux'
20 | ])
21 | param functionPlanOS string = 'Windows'
22 |
23 | @description('Specifies the Azure Function hosting plan SKU.')
24 | @allowed([
25 | 'EP1'
26 | 'EP2'
27 | 'EP3'
28 | ])
29 | param functionAppPlanSku string = 'EP1'
30 |
31 | @description('The name of the Azure Function hosting plan.')
32 | param functionAppPlanName string = 'plan-${uniqueString(resourceGroup().id)}'
33 |
34 | @description('The name of the backend Azure storage account used by the Azure Function app.')
35 | param functionStorageAccountName string = 'st${uniqueString(resourceGroup().id)}'
36 |
37 | @description('The name of the virtual network for virtual network integration.')
38 | param vnetName string = 'vnet-${uniqueString(resourceGroup().id)}'
39 |
40 | @description('The name of the virtual network subnet to be associated with the Azure Function app.')
41 | param functionSubnetName string = 'snet-func'
42 |
43 | @description('The name of the virtual network subnet used for allocating IP addresses for private endpoints.')
44 | param privateEndpointSubnetName string = 'snet-pe'
45 |
46 | @description('The IP adddress space used for the virtual network.')
47 | param vnetAddressPrefix string = '10.100.0.0/16'
48 |
49 | @description('The IP address space used for the Azure Function integration subnet.')
50 | param functionSubnetAddressPrefix string = '10.100.0.0/24'
51 |
52 | @description('The IP address space used for the private endpoints.')
53 | param privateEndpointSubnetAddressPrefix string = '10.100.1.0/24'
54 |
55 | @description('Only required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
56 | param linuxFxVersion string = ''
57 |
58 | var applicationInsightsName = 'appi-${uniqueString(resourceGroup().id)}'
59 | var privateStorageFileDnsZoneName = 'privatelink.file.${environment().suffixes.storage}'
60 | var privateEndpointStorageFileName = '${functionStorageAccountName}-file-private-endpoint'
61 | var privateStorageTableDnsZoneName = 'privatelink.table.${environment().suffixes.storage}'
62 | var privateEndpointStorageTableName = '${functionStorageAccountName}-table-private-endpoint'
63 | var privateStorageBlobDnsZoneName = 'privatelink.blob.${environment().suffixes.storage}'
64 | var privateEndpointStorageBlobName = '${functionStorageAccountName}-blob-private-endpoint'
65 | var privateStorageQueueDnsZoneName = 'privatelink.queue.${environment().suffixes.storage}'
66 | var privateEndpointStorageQueueName = '${functionStorageAccountName}-queue-private-endpoint'
67 | var functionContentShareName = 'function-content-share'
68 | var isReserved = ((functionPlanOS == 'Linux') ? true : false)
69 |
70 | resource vnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
71 | name: vnetName
72 | location: location
73 | properties: {
74 | addressSpace: {
75 | addressPrefixes: [
76 | vnetAddressPrefix
77 | ]
78 | }
79 | subnets: [
80 | {
81 | name: functionSubnetName
82 | properties: {
83 | privateEndpointNetworkPolicies: 'Enabled'
84 | privateLinkServiceNetworkPolicies: 'Enabled'
85 | delegations: [
86 | {
87 | name: 'webapp'
88 | properties: {
89 | serviceName: 'Microsoft.Web/serverFarms'
90 | }
91 | }
92 | ]
93 | addressPrefix: functionSubnetAddressPrefix
94 | }
95 | }
96 | {
97 | name: privateEndpointSubnetName
98 | properties: {
99 | privateEndpointNetworkPolicies: 'Disabled'
100 | privateLinkServiceNetworkPolicies: 'Enabled'
101 | addressPrefix: privateEndpointSubnetAddressPrefix
102 | }
103 | }
104 | ]
105 | }
106 | }
107 |
108 | resource privateStorageFileDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
109 | name: privateStorageFileDnsZoneName
110 | location: 'global'
111 | }
112 |
113 | resource privateStorageBlobDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
114 | name: privateStorageBlobDnsZoneName
115 | location: 'global'
116 | }
117 |
118 | resource privateStorageQueueDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
119 | name: privateStorageQueueDnsZoneName
120 | location: 'global'
121 | }
122 |
123 | resource privateStorageTableDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
124 | name: privateStorageTableDnsZoneName
125 | location: 'global'
126 | }
127 |
128 | resource privateStorageFileDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
129 | parent: privateStorageFileDnsZone
130 | name: '${privateStorageFileDnsZoneName}-link'
131 | location: 'global'
132 | properties: {
133 | registrationEnabled: false
134 | virtualNetwork: {
135 | id: vnet.id
136 | }
137 | }
138 | }
139 |
140 | resource privateStorageBlobDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
141 | parent: privateStorageBlobDnsZone
142 | name: '${privateStorageBlobDnsZoneName}-link'
143 | location: 'global'
144 | properties: {
145 | registrationEnabled: false
146 | virtualNetwork: {
147 | id: vnet.id
148 | }
149 | }
150 | }
151 |
152 | resource privateStorageTableDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
153 | parent: privateStorageTableDnsZone
154 | name: '${privateStorageTableDnsZoneName}-link'
155 | location: 'global'
156 | properties: {
157 | registrationEnabled: false
158 | virtualNetwork: {
159 | id: vnet.id
160 | }
161 | }
162 | }
163 |
164 | resource privateStorageQueueDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
165 | parent: privateStorageQueueDnsZone
166 | name: '${privateStorageQueueDnsZoneName}-link'
167 | location: 'global'
168 | properties: {
169 | registrationEnabled: false
170 | virtualNetwork: {
171 | id: vnet.id
172 | }
173 | }
174 | }
175 |
176 | resource privateEndpointStorageFilePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
177 | parent: privateEndpointStorageFile
178 | name: 'filePrivateDnsZoneGroup'
179 | properties: {
180 | privateDnsZoneConfigs: [
181 | {
182 | name: 'config'
183 | properties: {
184 | privateDnsZoneId: privateStorageFileDnsZone.id
185 | }
186 | }
187 | ]
188 | }
189 | }
190 |
191 | resource privateEndpointStorageBlobPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
192 | parent: privateEndpointStorageBlob
193 | name: 'blobPrivateDnsZoneGroup'
194 | properties: {
195 | privateDnsZoneConfigs: [
196 | {
197 | name: 'config'
198 | properties: {
199 | privateDnsZoneId: privateStorageBlobDnsZone.id
200 | }
201 | }
202 | ]
203 | }
204 | }
205 |
206 | resource privateEndpointStorageTablePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
207 | parent: privateEndpointStorageTable
208 | name: 'tablePrivateDnsZoneGroup'
209 | properties: {
210 | privateDnsZoneConfigs: [
211 | {
212 | name: 'config'
213 | properties: {
214 | privateDnsZoneId: privateStorageTableDnsZone.id
215 | }
216 | }
217 | ]
218 | }
219 | }
220 |
221 | resource privateEndpointStorageQueuePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-05-01' = {
222 | parent: privateEndpointStorageQueue
223 | name: 'queuePrivateDnsZoneGroup'
224 | properties: {
225 | privateDnsZoneConfigs: [
226 | {
227 | name: 'config'
228 | properties: {
229 | privateDnsZoneId: privateStorageQueueDnsZone.id
230 | }
231 | }
232 | ]
233 | }
234 | }
235 |
236 | resource privateEndpointStorageFile 'Microsoft.Network/privateEndpoints@2022-05-01' = {
237 | name: privateEndpointStorageFileName
238 | location: location
239 | properties: {
240 | subnet: {
241 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
242 | }
243 | privateLinkServiceConnections: [
244 | {
245 | name: 'MyStorageFilePrivateLinkConnection'
246 | properties: {
247 | privateLinkServiceId: functionStorageAccount.id
248 | groupIds: [
249 | 'file'
250 | ]
251 | }
252 | }
253 | ]
254 | }
255 | dependsOn: [
256 | vnet
257 | ]
258 | }
259 |
260 | resource privateEndpointStorageBlob 'Microsoft.Network/privateEndpoints@2022-05-01' = {
261 | name: privateEndpointStorageBlobName
262 | location: location
263 | properties: {
264 | subnet: {
265 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
266 | }
267 | privateLinkServiceConnections: [
268 | {
269 | name: 'MyStorageBlobPrivateLinkConnection'
270 | properties: {
271 | privateLinkServiceId: functionStorageAccount.id
272 | groupIds: [
273 | 'blob'
274 | ]
275 | }
276 | }
277 | ]
278 | }
279 | dependsOn: [
280 | vnet
281 | ]
282 | }
283 |
284 | resource privateEndpointStorageTable 'Microsoft.Network/privateEndpoints@2022-05-01' = {
285 | name: privateEndpointStorageTableName
286 | location: location
287 | properties: {
288 | subnet: {
289 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
290 | }
291 | privateLinkServiceConnections: [
292 | {
293 | name: 'MyStorageTablePrivateLinkConnection'
294 | properties: {
295 | privateLinkServiceId: functionStorageAccount.id
296 | groupIds: [
297 | 'table'
298 | ]
299 | }
300 | }
301 | ]
302 | }
303 | dependsOn: [
304 | vnet
305 | ]
306 | }
307 |
308 | resource privateEndpointStorageQueue 'Microsoft.Network/privateEndpoints@2022-05-01' = {
309 | name: privateEndpointStorageQueueName
310 | location: location
311 | properties: {
312 | subnet: {
313 | id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, privateEndpointSubnetName)
314 | }
315 | privateLinkServiceConnections: [
316 | {
317 | name: 'MyStorageQueuePrivateLinkConnection'
318 | properties: {
319 | privateLinkServiceId: functionStorageAccount.id
320 | groupIds: [
321 | 'queue'
322 | ]
323 | }
324 | }
325 | ]
326 | }
327 | dependsOn: [
328 | vnet
329 | ]
330 | }
331 |
332 | resource functionStorageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
333 | name: functionStorageAccountName
334 | location: location
335 | kind: 'StorageV2'
336 | sku: {
337 | name: 'Standard_LRS'
338 | }
339 | properties: {
340 | publicNetworkAccess: 'Disabled'
341 | allowBlobPublicAccess: false
342 | networkAcls: {
343 | bypass: 'None'
344 | defaultAction: 'Deny'
345 | }
346 | }
347 | }
348 |
349 | resource share 'Microsoft.Storage/storageAccounts/fileServices/shares@2022-05-01' = {
350 | name: '${functionStorageAccountName}/default/${functionContentShareName}'
351 | dependsOn: [
352 | functionStorageAccount
353 | ]
354 | }
355 |
356 | resource applicationInsight 'Microsoft.Insights/components@2020-02-02' = {
357 | name: applicationInsightsName
358 | location: location
359 | kind: 'web'
360 | properties: {
361 | Application_Type: 'web'
362 | }
363 | }
364 |
365 | resource functionAppPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
366 | name: functionAppPlanName
367 | location: location
368 | sku: {
369 | tier: 'ElasticPremium'
370 | name: functionAppPlanSku
371 | size: functionAppPlanSku
372 | family: 'EP'
373 | }
374 | kind: 'elastic'
375 | properties: {
376 | maximumElasticWorkerCount: 20
377 | reserved: isReserved
378 | }
379 | }
380 |
381 | resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
382 | name: functionAppName
383 | location: location
384 | kind: (isReserved ? 'functionapp,linux' : 'functionapp')
385 | properties: {
386 | reserved: isReserved
387 | serverFarmId: functionAppPlan.id
388 | siteConfig: {
389 | functionsRuntimeScaleMonitoringEnabled: true
390 | linuxFxVersion: (isReserved ? linuxFxVersion : json('null'))
391 | appSettings: [
392 | {
393 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
394 | value: applicationInsight.properties.InstrumentationKey
395 | }
396 | {
397 | name: 'AzureWebJobsStorage'
398 | value: 'DefaultEndpointsProtocol=https;AccountName=${functionStorageAccountName};AccountKey=${functionStorageAccount.listKeys().keys[0].value}'
399 | }
400 | {
401 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
402 | value: 'DefaultEndpointsProtocol=https;AccountName=${functionStorageAccountName};AccountKey=${functionStorageAccount.listKeys().keys[0].value}'
403 | }
404 | {
405 | name: 'WEBSITE_CONTENTSHARE'
406 | value: functionContentShareName
407 | }
408 | {
409 | name: 'FUNCTIONS_EXTENSION_VERSION'
410 | value: '~4'
411 | }
412 | {
413 | name: 'FUNCTIONS_WORKER_RUNTIME'
414 | value: functionWorkerRuntime
415 | }
416 | {
417 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
418 | value: '~14'
419 | }
420 | {
421 | name: 'WEBSITE_VNET_ROUTE_ALL'
422 | value: '1'
423 | }
424 | {
425 | name: 'WEBSITE_CONTENTOVERVNET'
426 | value: '1'
427 | }
428 | ]
429 | }
430 | }
431 | dependsOn: [
432 | share
433 | privateStorageFileDnsZoneLink
434 | privateEndpointStorageFilePrivateDnsZoneGroup
435 | privateStorageBlobDnsZoneLink
436 | privateEndpointStorageBlobPrivateDnsZoneGroup
437 | privateStorageTableDnsZoneLink
438 | privateEndpointStorageTablePrivateDnsZoneGroup
439 | privateStorageQueueDnsZoneLink
440 | privateEndpointStorageQueuePrivateDnsZoneGroup
441 | ]
442 | }
443 |
444 | resource networkConfig 'Microsoft.Web/sites/networkConfig@2022-03-01' = {
445 | parent: functionApp
446 | name: 'virtualNetwork'
447 | properties: {
448 | subnetResourceId: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, functionSubnetName)
449 | swiftSupported: true
450 | }
451 | dependsOn: [
452 | vnet
453 | ]
454 | }
455 |
--------------------------------------------------------------------------------
/function-app-vnet-integration/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Premium plan with regional virtual network integration enabled to a newly created virtual network.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | - bicep
8 | urlFragment: function-app-vnet-integration
9 | languages:
10 | - bicep
11 | - json
12 | ---
13 | # Azure Function App with Virtual Network Integration
14 |
15 | This sample Azure Resource Manager template deploys an Azure Function Premium plan with [virtual network integration](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#virtual-network-integration) enabled and allows the Azure Function to utilizes resources within the virtual network.
16 |
17 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-vnet-integration%2Fazuredeploy.json)
18 |
19 | ### Virtual Network
20 |
21 | The virtual network into which the Azure Function Premium plan shall be integrated.
22 |
23 | + **Microsoft.Network/virtualNetworks**: The virtual network for which to integrate, and one subnet to which the function app plan is delegated.
24 |
25 | ### OS
26 |
27 | This template has a parameter `functionPlanOS` to choose Windows or Linux OS. Windows is selected by default. If you choose Linux, then parameter `linuxFxVersion` will be parameter, so you can skip it for Windows.
28 |
29 | ### Elastic Premium Plan
30 |
31 | The Azure Function app provisioned in this sample uses an [Azure Functions Elastic Premium plan](https://docs.microsoft.com/azure/azure-functions/functions-premium-plan#features).
32 |
33 | + **Microsoft.Web/serverfarms**: The Azure Functions Premium plan (a.k.a. Elastic Premium plan)
34 |
35 | ### Azure Function App
36 |
37 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) and [WEBSITE_CONTENTAZUREFILECONNECTIONSTRING](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) app settings to connect to a private endpoint-secured Storage Account.
38 |
39 | + **Microsoft.Web/sites**: The function app instance.
40 |
41 | ### Azure Storage account
42 |
43 | The Storage account that the Function uses for operation and for file contents.
44 |
45 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
46 |
47 | ### Application Insights
48 |
49 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
50 |
51 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
52 |
53 | ### NOTE
54 |
55 | + For more information on configuring Azure Storage firewalls and virtual networks, please refer: [Configure Azure Storage firewalls and virtual networks](https://docs.microsoft.com/en-us/azure/storage/common/storage-network-security?tabs=azure-portal)
56 |
57 | + For more information on Azure Functions networking options and VNET integration, please refer: [Azure Functions Networking Options](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#restrict-your-storage-account-to-a-virtual-network)
58 |
59 | `Tags: Microsoft.Network/virtualNetworks, Microsoft.Storage/storageAccounts, Microsoft.Insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites`
60 |
--------------------------------------------------------------------------------
/function-app-vnet-integration/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "metadata": {
5 | "_generator": {
6 | "name": "bicep",
7 | "version": "0.10.61.36676",
8 | "templateHash": "14925466958906821754"
9 | }
10 | },
11 | "parameters": {
12 | "functionAppName": {
13 | "type": "string",
14 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
15 | "metadata": {
16 | "description": "The name of the Azure Function app."
17 | }
18 | },
19 | "storageAccountType": {
20 | "type": "string",
21 | "defaultValue": "Standard_LRS",
22 | "allowedValues": [
23 | "Standard_LRS",
24 | "Standard_GRS",
25 | "Standard_RAGRS"
26 | ],
27 | "metadata": {
28 | "description": "Storage Account type"
29 | }
30 | },
31 | "location": {
32 | "type": "string",
33 | "defaultValue": "[resourceGroup().location]",
34 | "metadata": {
35 | "description": "Location for all resources."
36 | }
37 | },
38 | "appInsightsLocation": {
39 | "type": "string",
40 | "defaultValue": "[resourceGroup().location]",
41 | "metadata": {
42 | "description": "Location for Application Insights"
43 | }
44 | },
45 | "functionWorkerRuntime": {
46 | "type": "string",
47 | "defaultValue": "node",
48 | "allowedValues": [
49 | "dotnet",
50 | "node",
51 | "python",
52 | "java"
53 | ],
54 | "metadata": {
55 | "description": "The language worker runtime to load in the function app."
56 | }
57 | },
58 | "functionPlanOS": {
59 | "type": "string",
60 | "defaultValue": "Windows",
61 | "allowedValues": [
62 | "Windows",
63 | "Linux"
64 | ],
65 | "metadata": {
66 | "description": "Specifies the OS used for the Azure Function hosting plan."
67 | }
68 | },
69 | "functionAppPlanSku": {
70 | "type": "string",
71 | "defaultValue": "EP1",
72 | "allowedValues": [
73 | "EP1",
74 | "EP2",
75 | "EP3"
76 | ],
77 | "metadata": {
78 | "description": "Specifies the Azure Function hosting plan SKU."
79 | }
80 | },
81 | "vnetName": {
82 | "type": "string",
83 | "defaultValue": "[format('vnet-{0}', uniqueString(resourceGroup().id))]",
84 | "metadata": {
85 | "description": "The name of the virtual network to be created."
86 | }
87 | },
88 | "subnetName": {
89 | "type": "string",
90 | "defaultValue": "[format('subnet-{0}', uniqueString(resourceGroup().id))]",
91 | "metadata": {
92 | "description": "The name of the subnet to be created within the virtual network."
93 | }
94 | },
95 | "linuxFxVersion": {
96 | "type": "string",
97 | "defaultValue": "",
98 | "metadata": {
99 | "description": "Only required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
100 | }
101 | }
102 | },
103 | "variables": {
104 | "vnetAddressPrefix": "10.0.0.0/16",
105 | "subnetAddressPrefix": "10.0.0.0/24",
106 | "hostingPlanName": "[parameters('functionAppName')]",
107 | "applicationInsightsName": "[parameters('functionAppName')]",
108 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]",
109 | "isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]"
110 | },
111 | "resources": [
112 | {
113 | "type": "Microsoft.Network/virtualNetworks",
114 | "apiVersion": "2022-05-01",
115 | "name": "[parameters('vnetName')]",
116 | "location": "[parameters('location')]",
117 | "properties": {
118 | "addressSpace": {
119 | "addressPrefixes": [
120 | "[variables('vnetAddressPrefix')]"
121 | ]
122 | },
123 | "subnets": [
124 | {
125 | "name": "[parameters('subnetName')]",
126 | "properties": {
127 | "addressPrefix": "[variables('subnetAddressPrefix')]",
128 | "delegations": [
129 | {
130 | "name": "delegation",
131 | "properties": {
132 | "serviceName": "Microsoft.Web/serverFarms"
133 | }
134 | }
135 | ]
136 | }
137 | }
138 | ]
139 | }
140 | },
141 | {
142 | "type": "Microsoft.Storage/storageAccounts",
143 | "apiVersion": "2022-05-01",
144 | "name": "[variables('storageAccountName')]",
145 | "location": "[parameters('location')]",
146 | "sku": {
147 | "name": "[parameters('storageAccountType')]"
148 | },
149 | "kind": "Storage"
150 | },
151 | {
152 | "type": "Microsoft.Web/serverfarms",
153 | "apiVersion": "2022-03-01",
154 | "name": "[variables('hostingPlanName')]",
155 | "location": "[parameters('location')]",
156 | "sku": {
157 | "tier": "ElasticPremium",
158 | "name": "[parameters('functionAppPlanSku')]",
159 | "family": "EP"
160 | },
161 | "properties": {
162 | "maximumElasticWorkerCount": 20,
163 | "reserved": "[variables('isReserved')]"
164 | },
165 | "kind": "elastic"
166 | },
167 | {
168 | "type": "Microsoft.Insights/components",
169 | "apiVersion": "2020-02-02",
170 | "name": "[variables('applicationInsightsName')]",
171 | "location": "[parameters('appInsightsLocation')]",
172 | "tags": {
173 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
174 | },
175 | "properties": {
176 | "Application_Type": "web"
177 | },
178 | "kind": "web"
179 | },
180 | {
181 | "type": "Microsoft.Web/sites",
182 | "apiVersion": "2022-03-01",
183 | "name": "[parameters('functionAppName')]",
184 | "location": "[parameters('location')]",
185 | "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
186 | "properties": {
187 | "reserved": "[variables('isReserved')]",
188 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
189 | "siteConfig": {
190 | "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
191 | "appSettings": [
192 | {
193 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
194 | "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))).InstrumentationKey]"
195 | },
196 | {
197 | "name": "AzureWebJobsStorage",
198 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix= {1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
199 | },
200 | {
201 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
202 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2};', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
203 | },
204 | {
205 | "name": "WEBSITE_CONTENTSHARE",
206 | "value": "[toLower(parameters('functionAppName'))]"
207 | },
208 | {
209 | "name": "FUNCTIONS_EXTENSION_VERSION",
210 | "value": "~4"
211 | },
212 | {
213 | "name": "FUNCTIONS_WORKER_RUNTIME",
214 | "value": "[parameters('functionWorkerRuntime')]"
215 | },
216 | {
217 | "name": "WEBSITE_NODE_DEFAULT_VERSION",
218 | "value": "~14"
219 | }
220 | ]
221 | }
222 | },
223 | "dependsOn": [
224 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
225 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
226 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
227 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
228 | ]
229 | },
230 | {
231 | "type": "Microsoft.Web/sites/networkConfig",
232 | "apiVersion": "2022-03-01",
233 | "name": "[format('{0}/{1}', parameters('functionAppName'), 'virtualNetwork')]",
234 | "properties": {
235 | "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]",
236 | "swiftSupported": true
237 | },
238 | "dependsOn": [
239 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]",
240 | "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
241 | ]
242 | }
243 | ]
244 | }
--------------------------------------------------------------------------------
/function-app-vnet-integration/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-vnet-integration/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('Storage Account type')
5 | @allowed([
6 | 'Standard_LRS'
7 | 'Standard_GRS'
8 | 'Standard_RAGRS'
9 | ])
10 | param storageAccountType string = 'Standard_LRS'
11 |
12 | @description('Location for all resources.')
13 | param location string = resourceGroup().location
14 |
15 | @description('Location for Application Insights')
16 | param appInsightsLocation string = resourceGroup().location
17 |
18 | @description('The language worker runtime to load in the function app.')
19 | @allowed([
20 | 'dotnet'
21 | 'node'
22 | 'python'
23 | 'java'
24 | ])
25 | param functionWorkerRuntime string = 'node'
26 |
27 | @description('Specifies the OS used for the Azure Function hosting plan.')
28 | @allowed([
29 | 'Windows'
30 | 'Linux'
31 | ])
32 | param functionPlanOS string = 'Windows'
33 |
34 | @description('Specifies the Azure Function hosting plan SKU.')
35 | @allowed([
36 | 'EP1'
37 | 'EP2'
38 | 'EP3'
39 | ])
40 | param functionAppPlanSku string = 'EP1'
41 |
42 | @description('The name of the virtual network to be created.')
43 | param vnetName string = 'vnet-${uniqueString(resourceGroup().id)}'
44 |
45 | @description('The name of the subnet to be created within the virtual network.')
46 | param subnetName string = 'subnet-${uniqueString(resourceGroup().id)}'
47 |
48 | @description('Only required for Linux app to represent runtime stack in the format of \'runtime|runtimeVersion\'. For example: \'python|3.9\'')
49 | param linuxFxVersion string = ''
50 |
51 | var vnetAddressPrefix = '10.0.0.0/16'
52 | var subnetAddressPrefix = '10.0.0.0/24'
53 | var hostingPlanName = functionAppName
54 | var applicationInsightsName = functionAppName
55 | var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
56 | var isReserved = ((functionPlanOS == 'Linux') ? true : false)
57 |
58 | resource vnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
59 | name: vnetName
60 | location: location
61 | properties: {
62 | addressSpace: {
63 | addressPrefixes: [
64 | vnetAddressPrefix
65 | ]
66 | }
67 | subnets: [
68 | {
69 | name: subnetName
70 | properties: {
71 | addressPrefix: subnetAddressPrefix
72 | delegations: [
73 | {
74 | name: 'delegation'
75 | properties: {
76 | serviceName: 'Microsoft.Web/serverFarms'
77 | }
78 | }
79 | ]
80 | }
81 | }
82 | ]
83 | }
84 | }
85 |
86 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
87 | name: storageAccountName
88 | location: location
89 | sku: {
90 | name: storageAccountType
91 | }
92 | kind: 'Storage'
93 | }
94 |
95 | resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
96 | name: hostingPlanName
97 | location: location
98 | sku: {
99 | tier: 'ElasticPremium'
100 | name: functionAppPlanSku
101 | family: 'EP'
102 | }
103 | properties: {
104 | maximumElasticWorkerCount: 20
105 | reserved: isReserved
106 | }
107 | kind: 'elastic'
108 | }
109 |
110 | resource insight 'Microsoft.Insights/components@2020-02-02' = {
111 | name: applicationInsightsName
112 | location: appInsightsLocation
113 | tags: {
114 | 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource'
115 | }
116 | properties: {
117 | Application_Type: 'web'
118 | }
119 | kind: 'web'
120 | }
121 |
122 | resource site 'Microsoft.Web/sites@2022-03-01' = {
123 | name: functionAppName
124 | location: location
125 | kind: (isReserved ? 'functionapp,linux' : 'functionapp')
126 | properties: {
127 | reserved: isReserved
128 | serverFarmId: hostingPlan.id
129 | siteConfig: {
130 | linuxFxVersion: (isReserved ? linuxFxVersion : json('null'))
131 | appSettings: [
132 | {
133 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
134 | value: insight.properties.InstrumentationKey
135 | }
136 | {
137 | name: 'AzureWebJobsStorage'
138 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix= ${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
139 | }
140 | {
141 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
142 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value};'
143 | }
144 | {
145 | name: 'WEBSITE_CONTENTSHARE'
146 | value: toLower(functionAppName)
147 | }
148 | {
149 | name: 'FUNCTIONS_EXTENSION_VERSION'
150 | value: '~4'
151 | }
152 | {
153 | name: 'FUNCTIONS_WORKER_RUNTIME'
154 | value: functionWorkerRuntime
155 | }
156 | {
157 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
158 | value: '~14'
159 | }
160 | ]
161 | }
162 | }
163 | dependsOn: [
164 | vnet
165 | ]
166 | }
167 |
168 | resource functionAppName_virtualNetwork 'Microsoft.Web/sites/networkConfig@2022-03-01' = {
169 | parent: site
170 | name: 'virtualNetwork'
171 | properties: {
172 | subnetResourceId: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnetName)
173 | swiftSupported: true
174 | }
175 | dependsOn: [
176 | vnet
177 | ]
178 | }
179 |
--------------------------------------------------------------------------------
/function-app-windows-consumption/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: This template provisions a function app on a Windows Consumption plan, which is a dynamic hosting plan. The app runs on demand and you're billed per execution, with no standing resource committment.
3 | page_type: sample
4 | products:
5 | - azure
6 | - azure-resource-manager
7 | urlFragment: function-app-windows-consumption
8 | languages:
9 | - bicep
10 | - json
11 | ---
12 | # Azure Function App Hosted on Windows Consumption Plan
13 |
14 | This sample Azure Resource Manager template deploys an Azure Function App on Windows Consumption plan and required resource including ZipDeploy extension to mount zip package for deployment.
15 |
16 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Ffunction-app-windows-consumption%2Fazuredeploy.json)
17 |
18 | ### OS
19 |
20 | This template is for Azure Function app hosted on **Windows Consumption plan** only.
21 |
22 | ### Comsumption Plan
23 |
24 | The Azure Function app provisioned in this sample uses an [Azure Functions Consumption plan](https://docs.microsoft.com/en-us/azure/azure-functions/consumption-plan).
25 |
26 | + **Microsoft.Web/serverfarms**: The Azure Functions Consumption plan (a.k.a. Dynamic plan)
27 |
28 | ### Azure Function App
29 |
30 | The Function App uses the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) and [WEBSITE_CONTENTAZUREFILECONNECTIONSTRING](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) app settings to connect to a Storage Account.
31 |
32 | + **Microsoft.Web/sites**: The function app instance.
33 |
34 | ### ZipDeploy Extension
35 |
36 | The Zip Deploy extension is added along with recommended app setting `WEBSITE_RUN_FROM_PACKAGE=1` to mount the zip package for deployment. This is the recommended path for deployment, except for [Linux Consumption Plan](/function-app-linux-consumption)
37 |
38 | + **Microsoft.Web/sites/extensions**: The ZipDeploy extension.
39 |
40 | ### Azure Storage account
41 |
42 | The Storage account that the Function uses for operation and for file contents.
43 |
44 | + **Microsoft.Storage/storageAccounts**: [Azure Functions requires a storage account](https://docs.microsoft.com/azure/azure-functions/storage-considerations) for the function app instance.
45 |
46 | ### Application Insights
47 |
48 | [Application Insights](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview) is used to provide [monitor the Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-monitoring).
49 |
50 | + **Microsoft.Insights/components**: The Application Insights instance used by the Azure Function for monitoring.
51 |
52 | `Tags: Microsoft.Storage/storageAccounts, microsoft.insights/components, Microsoft.Web/serverfarms, Microsoft.Web/sites, Microsoft.Web/sites/extensions`
53 |
--------------------------------------------------------------------------------
/function-app-windows-consumption/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "functionAppName": {
6 | "type": "string",
7 | "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]",
8 | "metadata": {
9 | "description": "The name of the Azure Function app."
10 | }
11 | },
12 | "storageAccountType": {
13 | "type": "string",
14 | "defaultValue": "Standard_LRS",
15 | "allowedValues": [
16 | "Standard_LRS",
17 | "Standard_GRS",
18 | "Standard_RAGRS"
19 | ],
20 | "metadata": {
21 | "description": "Storage Account type"
22 | }
23 | },
24 | "location": {
25 | "type": "string",
26 | "defaultValue": "[resourceGroup().location]",
27 | "metadata": {
28 | "description": "Location for all resources."
29 | }
30 | },
31 | "functionWorkerRuntime": {
32 | "type": "string",
33 | "defaultValue": "node",
34 | "allowedValues": [
35 | "dotnet",
36 | "node",
37 | "python",
38 | "java"
39 | ],
40 | "metadata": {
41 | "description": "The language worker runtime to load in the function app."
42 | }
43 | },
44 | "packageUri": {
45 | "type": "string",
46 | "metadata": {
47 | "description": "The zip content url."
48 | }
49 | }
50 | },
51 | "variables": {
52 | "hostingPlanName": "[parameters('functionAppName')]",
53 | "applicationInsightsName": "[parameters('functionAppName')]",
54 | "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]"
55 | },
56 | "resources": [
57 | {
58 | "type": "Microsoft.Storage/storageAccounts",
59 | "apiVersion": "2022-05-01",
60 | "name": "[variables('storageAccountName')]",
61 | "location": "[parameters('location')]",
62 | "sku": {
63 | "name": "[parameters('storageAccountType')]"
64 | },
65 | "kind": "Storage"
66 | },
67 | {
68 | "type": "Microsoft.Web/serverfarms",
69 | "apiVersion": "2022-03-01",
70 | "name": "[variables('hostingPlanName')]",
71 | "location": "[parameters('location')]",
72 | "sku": {
73 | "name": "Y1",
74 | "tier": "Dynamic",
75 | "size": "Y1",
76 | "family": "Y"
77 | },
78 | "properties": {
79 | "computeMode": "Dynamic"
80 | }
81 | },
82 | {
83 | "type": "Microsoft.Insights/components",
84 | "apiVersion": "2020-02-02",
85 | "name": "[variables('applicationInsightsName')]",
86 | "location": "[parameters('location')]",
87 | "tags": {
88 | "[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
89 | },
90 | "properties": {
91 | "Application_Type": "web"
92 | },
93 | "kind": "web"
94 | },
95 | {
96 | "type": "Microsoft.Web/sites",
97 | "apiVersion": "2022-03-01",
98 | "name": "[parameters('functionAppName')]",
99 | "location": "[parameters('location')]",
100 | "kind": "functionapp",
101 | "properties": {
102 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
103 | "siteConfig": {
104 | "appSettings": [
105 | {
106 | "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
107 | "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName')), '2020-02-02').InstrumentationKey]"
108 | },
109 | {
110 | "name": "AzureWebJobsStorage",
111 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2021-09-01').keys[0].value)]"
112 | },
113 | {
114 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
115 | "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2021-09-01').keys[0].value)]"
116 | },
117 | {
118 | "name": "WEBSITE_CONTENTSHARE",
119 | "value": "[toLower(parameters('functionAppName'))]"
120 | },
121 | {
122 | "name": "FUNCTIONS_EXTENSION_VERSION",
123 | "value": "~4"
124 | },
125 | {
126 | "name": "FUNCTIONS_WORKER_RUNTIME",
127 | "value": "[parameters('functionWorkerRuntime')]"
128 | },
129 | {
130 | "name": "WEBSITE_NODE_DEFAULT_VERSION",
131 | "value": "~14"
132 | },
133 | {
134 | "name": "WEBSITE_RUN_FROM_PACKAGE",
135 | "value": "1"
136 | }
137 | ]
138 | }
139 | },
140 | "dependsOn": [
141 | "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
142 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
143 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
144 | ]
145 | },
146 | {
147 | "type": "Microsoft.Web/sites/extensions",
148 | "apiVersion": "2022-03-01",
149 | "name": "[format('{0}/{1}', parameters('functionAppName'), 'zipdeploy')]",
150 | "properties": {
151 | "packageUri": "[parameters('packageUri')]"
152 | },
153 | "dependsOn": [
154 | "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
155 | ]
156 | }
157 | ]
158 | }
159 |
--------------------------------------------------------------------------------
/function-app-windows-consumption/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/function-app-windows-consumption/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The name of the Azure Function app.')
2 | param functionAppName string = 'func-${uniqueString(resourceGroup().id)}'
3 |
4 | @description('Storage Account type')
5 | @allowed([
6 | 'Standard_LRS'
7 | 'Standard_GRS'
8 | 'Standard_RAGRS'
9 | ])
10 | param storageAccountType string = 'Standard_LRS'
11 |
12 | @description('Location for all resources.')
13 | param location string = resourceGroup().location
14 |
15 | @description('The language worker runtime to load in the function app.')
16 | @allowed([
17 | 'dotnet'
18 | 'node'
19 | 'python'
20 | 'java'
21 | ])
22 | param functionWorkerRuntime string = 'node'
23 |
24 | @description('The zip content url.')
25 | param packageUri string
26 |
27 | var hostingPlanName = functionAppName
28 | var applicationInsightsName = functionAppName
29 | var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions'
30 |
31 | resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
32 | name: storageAccountName
33 | location: location
34 | sku: {
35 | name: storageAccountType
36 | }
37 | kind: 'Storage'
38 | }
39 |
40 | resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
41 | name: hostingPlanName
42 | location: location
43 | sku: {
44 | name: 'Y1'
45 | tier: 'Dynamic'
46 | size: 'Y1'
47 | family: 'Y'
48 | }
49 | properties: {
50 | computeMode: 'Dynamic'
51 | }
52 | }
53 |
54 | resource applicationInsight 'Microsoft.Insights/components@2020-02-02' = {
55 | name: applicationInsightsName
56 | location: location
57 | tags: {
58 | 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource'
59 | }
60 | properties: {
61 | Application_Type: 'web'
62 | }
63 | kind: 'web'
64 | }
65 |
66 | resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
67 | name: functionAppName
68 | location: location
69 | kind: 'functionapp'
70 | properties: {
71 | serverFarmId: hostingPlan.id
72 | siteConfig: {
73 | appSettings: [
74 | {
75 | name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
76 | value: reference(applicationInsight.id, '2015-05-01').InstrumentationKey
77 | }
78 | {
79 | name: 'AzureWebJobsStorage'
80 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
81 | }
82 | {
83 | name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
84 | value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
85 | }
86 | {
87 | name: 'WEBSITE_CONTENTSHARE'
88 | value: toLower(functionAppName)
89 | }
90 | {
91 | name: 'FUNCTIONS_EXTENSION_VERSION'
92 | value: '~4'
93 | }
94 | {
95 | name: 'FUNCTIONS_WORKER_RUNTIME'
96 | value: functionWorkerRuntime
97 | }
98 | {
99 | name: 'WEBSITE_NODE_DEFAULT_VERSION'
100 | value: '~14'
101 | }
102 | {
103 | name: 'WEBSITE_RUN_FROM_PACKAGE'
104 | value: '1'
105 | }
106 | ]
107 | }
108 | }
109 | }
110 |
111 | resource zipDeploy 'Microsoft.Web/sites/extensions@2022-03-01' = {
112 | parent: functionApp
113 | name: 'zipdeploy'
114 | properties: {
115 | packageUri: packageUri
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/images/deploytoazure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/function-app-arm-templates/fc7311939d3bac5706778af24c8be82a7889a230/images/deploytoazure.png
--------------------------------------------------------------------------------
/zip-deploy-arm-az-cli/README.md:
--------------------------------------------------------------------------------
1 | # Az CLI commands for deployment using ARM Template
2 | This workflow can be used when the following conditions are met:
3 | 1. Function app is locked behind [private endpoint](https://docs.microsoft.com/en-us/azure/app-service/networking/private-endpoint).
4 | 2. Storage account is locked behind [private endpoints](https://docs.microsoft.com/en-us/azure/storage/common/storage-private-endpoints).
5 | 3. Want to deploy from your local machine that is not in the Virtual Network.
6 |
7 | ### PRe-requisite
8 | 1. Build and create [.zip package](https://github.com/Azure-Samples/function-app-arm-templates/wiki/Best-Practices-Guide#deployment-zip-file-requirements) of your code in a folder.
9 | 2. Have a copy of the ARM template [azuredeploy.json](/zip-deploy-arm-az-cli/azuredeploy.json) in the same folder.
10 | 3. Have a copy of the ARM template [azuredeploy.parameters.json](/zip-deploy-arm-az-cli/azuredeploy.parameters.json) in the same folder.
11 |
12 | ### Steps:
13 |
14 | 1. Run the following commands in PowerShell Command prompt:
15 |
16 | ```
17 | az login
18 |
19 | az ad sp create-for-rbac --name --role contributor --scopes /subscriptions//resourceGroups/ --sdk-auth
20 |
21 | az storage account create -n -g
22 |
23 | az storage container create -n --account-name
24 |
25 | az storage blob upload -f --account-name -c -n package.zip --overwrite true
26 |
27 | az storage blob generate-sas --full-uri --permissions r --expiry (get-date).AddMinutes(30).ToString("yyyy-MM-ddTHH:mm:ssZ") --account-name -c -n package.zip
28 | ```
29 |
30 | 2. Copy paste the SAS URL generated above and your function app name in the azuredeploy.parameters.json
31 |
32 | 3. Run rest of the commands in PowerShell Command prompt:
33 |
34 | ```
35 | az deployment group create --name --resource-group --template-file azuredeploy.json --parameters azuredeploy.parameters.json
36 |
37 | az storage container delete -n --account-name
38 | ```
39 |
--------------------------------------------------------------------------------
/zip-deploy-arm-az-cli/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "functionAppName": {
6 | "type": "string",
7 | "metadata": {
8 | "description": "The name of the Azure Function app."
9 | }
10 | },
11 | "location": {
12 | "type": "string",
13 | "defaultValue": "[resourceGroup().location]",
14 | "metadata": {
15 | "description": "The location into which the resources should be deployed."
16 | }
17 | },
18 | "packageUri": {
19 | "type": "string",
20 | "metadata": {
21 | "description": "The zip content url."
22 | }
23 | }
24 | },
25 | "resources": [
26 | {
27 | "name": "[concat(parameters('functionAppName'), '/ZipDeploy')]",
28 | "type": "Microsoft.Web/sites/extensions",
29 | "apiVersion": "2021-02-01",
30 | "location": "[parameters('location')]",
31 | "properties": {
32 | "packageUri": "[parameters('packageUri')]"
33 | }
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/zip-deploy-arm-az-cli/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "functionAppName": {
6 | "value": ""
7 | },
8 | "packageUri": {
9 | "value": ""
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/zip-deploy-arm-github-workflow/README.md:
--------------------------------------------------------------------------------
1 | # Github Workflow for deployment using ARM Template
2 | This workflow can be used when the following conditions are met:
3 | 1. Function app is locked behind [private endpoint](https://docs.microsoft.com/en-us/azure/app-service/networking/private-endpoint).
4 | 2. Storage account is locked behind [private endpoints](https://docs.microsoft.com/en-us/azure/storage/common/storage-private-endpoints).
5 | 3. Want to setup [continuous deployment](https://docs.microsoft.com/en-us/azure/azure-functions/functions-continuous-deployment) pipeline for GitHub repo.
6 |
7 | ### Pre-requisite
8 | 1. Have a copy of the ARM template [azuredeploy.json](/zip-deploy-arm-github-workflow/azuredeploy.json) in the root of the repo.
9 | 2. Setup [Azure Service Principle for RBAC as Deployment Credential](/zip-deploy-arm-github-workflow#Azure-Service-Principle-for-RBAC-as-Deployment-Credential) using the steps below.
10 | 3. Update evironment variables in the [workflow.yml](/zip-deploy-arm-github-workflow/workflow.yml) with those of your app.
11 | 4. This template is for DotNet function app on Windows OS. Please refer [sample templates](https://github.com/Azure/actions-workflow-samples/tree/master/FunctionApp) for other languages and OS to modify this template accordingly.
12 |
13 | ### Azure Service Principle for RBAC as Deployment Credential
14 | You have to create an [Azure Service Principal for RBAC](https://docs.microsoft.com/en-us/azure/role-based-access-control/overview) and add them as a GitHub Secret in your repository.
15 | 1. Download Azure CLI from [here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest), run `az login` to login with your Azure credentials.
16 | 2. Run Azure CLI command
17 | ```
18 | az ad sp create-for-rbac --name "myApp" --role contributor \
19 | --scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Web/sites/{app-name} \
20 | --sdk-auth
21 |
22 | # Replace {subscription-id}, {resource-group}, and {app-name} with the names of your subscription, resource group, and Azure function app.
23 | # The command should output a JSON object similar to this:
24 |
25 | {
26 | "clientId": "",
27 | "clientSecret": "",
28 | "subscriptionId": "",
29 | "tenantId": "",
30 | (...)
31 | }
32 | ```
33 | 3. Paste the json response from above Azure CLI to your Github Repository > Settings > Secrets > Add a new secret > **AZURE_CREDENTIALS**
34 |
35 | ### Dependencies on other Github Actions
36 | * [Checkout](https://github.com/actions/checkout) Checkout your Git repository content into GitHub Actions agent.
37 | * [Azure Login](https://github.com/Azure/actions) Login with your Azure credentials for function app deployment authentication.
38 | * To build app code in a specific language based environment, use setup actions:
39 | * [Setup DotNet](https://github.com/actions/setup-dotnet) Build your DotNet core function app or function app extensions.
40 | * [Setup Node](https://github.com/actions/setup-node) Resolve Node function app dependencies using npm.
41 | * [Setup Python](https://github.com/actions/setup-python) Resolve Python function app dependencies using pip.
42 | * [Setup Java](https://github.com/actions/setup-java) Resolve Java function app dependencies using maven.
43 |
44 | If you are looking for a GitHub Action to deploy your customized container image into an Azure Functions container, use [`azure/functions-container-action`](https://github.com/Azure/functions-container-action).
45 |
--------------------------------------------------------------------------------
/zip-deploy-arm-github-workflow/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "functionAppName": {
6 | "type": "string",
7 | "metadata": {
8 | "description": "The name of the Azure Function app."
9 | }
10 | },
11 | "location": {
12 | "type": "string",
13 | "defaultValue": "[resourceGroup().location]",
14 | "metadata": {
15 | "description": "The location into which the resources should be deployed."
16 | }
17 | },
18 | "packageUri": {
19 | "type": "string",
20 | "metadata": {
21 | "description": "The zip content url."
22 | }
23 | }
24 | },
25 | "resources": [
26 | {
27 | "name": "[concat(parameters('functionAppName'), '/ZipDeploy')]",
28 | "type": "Microsoft.Web/sites/extensions",
29 | "apiVersion": "2021-02-01",
30 | "location": "[parameters('location')]",
31 | "properties": {
32 | "packageUri": "[parameters('packageUri')]"
33 | }
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/zip-deploy-arm-github-workflow/workflow.yml:
--------------------------------------------------------------------------------
1 | name: Build and deploy dotnet core app to Azure Function App - cp-win-dotnet
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_dispatch:
8 |
9 | env:
10 | AZURE_RESOURCE_GROUP: 'your-resource-group-name' # set this to your Azure Resource group's name
11 | FUNCTION_APP: 'your-function-app-name' # set this to your Azure Function app's name
12 | STORAGE_ACCOUNT: 'new-storage-for-deployment' # storage will be created for deployment if not exists. Requirements: unique name, 3 to 24 characters in length, use numbers and lower-case letters only
13 | CONTAINER: 'temporarydeploymentcontainer' # temporary container needed for deployment, can keep name as it is
14 | AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root
15 | DOTNET_VERSION: '6.0.x' # set this to the dotnet version to use
16 |
17 | jobs:
18 | build-and-deploy:
19 | runs-on: windows-latest
20 | steps:
21 |
22 | - name: Azure Login
23 | uses: azure/login@v1
24 | with:
25 | creds: ${{ secrets.AZURE_CREDENTIALS }}
26 |
27 | - name: Checkout GitHub Action
28 | uses: actions/checkout@v2
29 |
30 | - name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment
31 | uses: actions/setup-dotnet@v1
32 | with:
33 | dotnet-version: ${{ env.DOTNET_VERSION }}
34 |
35 | - name: Resolve Project Dependencies Using Dotnet
36 | shell: pwsh
37 | run: |
38 | pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
39 | dotnet build --configuration Release --output ./output
40 | popd
41 |
42 | - name: Zip Function App Content
43 | shell: pwsh
44 | run: |
45 | Compress-Archive -Path ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output/* -DestinationPath ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/package.zip
46 |
47 | - name: Setup Storage, Container & Blob for Deployment
48 | shell: pwsh
49 | run: |
50 | az storage account create -n ${{ env.STORAGE_ACCOUNT }} -g ${{ env.AZURE_RESOURCE_GROUP }}
51 | az storage container create -n ${{ env.CONTAINER }} --account-name ${{ env.STORAGE_ACCOUNT }}
52 | az storage blob upload -f ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/package.zip --account-name ${{ env.STORAGE_ACCOUNT }} -c ${{ env.CONTAINER }} -n package.zip --overwrite true
53 |
54 | - name: Zip Deployment using ARM Template
55 | shell: pwsh
56 | run: |
57 | $Env:Expiry=(get-date).AddMinutes(30).ToString("yyyy-MM-ddTHH:mm:ssZ")
58 | $Env:ZIP_URL=$(az storage blob generate-sas --full-uri --permissions r --expiry $Env:Expiry --account-name ${{ env.STORAGE_ACCOUNT }} -c ${{ env.CONTAINER }} -n package.zip)
59 | az deployment group create --name deploy --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --template-file azuredeploy.json --parameters functionAppName='${{ env.FUNCTION_APP }}' packageUri=$Env:ZIP_URL
60 |
61 | - name: Delete Temporary Container & Blob
62 | shell: pwsh
63 | run: |
64 | az storage container delete -n ${{ env.CONTAINER }} --account-name ${{ env.STORAGE_ACCOUNT }}
--------------------------------------------------------------------------------
/zip-deploy-run-from-package/README.md:
--------------------------------------------------------------------------------
1 | # Function App Deployment with ZipDeploy Run From Package
2 |
3 | ### ZipDeploy with ARM template
4 |
5 | ZipDeploy is intended for xcopy or ftp style deployment. By default, It unzips the artifacts and lay them out exactly to d:\home\site\wwwroot. You can use any tooling (such as one coming with Windows) to zip your content.
6 |
7 | It is recommended to set appSettings `WEBSITE_RUN_FROM_PACKAGE=1`, to allow Zip package deployed with ZipDeploy to mount as read-only virtual filesystem directly without deflating or extracting. The advantage is to allow atomic and reliable deployment (no more files being locked).
8 |
9 | NOTE: ZipDeploy with the appSetting `WEBSITE_RUN_FROM_PACKAGE=1` is not supported for Linux Consumption plan.
10 |
11 | If you have an existing Function App with right appSettings and want to perform deployment with ZipDeploy extension, then here is the template:
12 |
13 | [](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Ffunction-app-arm-templates%2Fmain%2Fzip-deploy-run-from-package%2Fazuredeploy.json)
14 |
15 | The following example shows declartion of ZipDeploy extension in site resources along with the recommended WEBSITE_RUN_FROM_PACKAGE appSetting:
16 |
17 | NOTE: Please include rest of the existing app settings in the template to preserve them.
18 |
19 | ```json
20 | {
21 | "name": "[parameters('siteName')]",
22 | "type": "Microsoft.Web/sites",
23 | "apiVersion": "2021-02-01",
24 | "location": "[parameters('location')]",
25 | "properties": {
26 | "siteConfig": {
27 | "appSettings": [
28 | {
29 | "name": "WEBSITE_RUN_FROM_PACKAGE",
30 | "value": "1"
31 | }
32 | ]
33 | }
34 | },
35 | "resources": [
36 | {
37 | "name": "ZipDeploy",
38 | "type": "Extensions",
39 | "apiVersion": "2021-02-01",
40 | "dependsOn": [
41 | "[concat('Microsoft.Web/sites/', parameters('siteName'))]"
42 | ],
43 | "properties": {
44 | "packageUri": "[parameters('packageUri')]"
45 | }
46 | }
47 | ]
48 | }
49 | ```
50 |
51 | Since it will mount as read-only, app runtime will not be able to create or modify files under d:\home\site\wwwroot. In addition, Azure Functions Portal will also prevent you from modifying the Function Apps.
52 |
53 | ### For Linux Consumption plan:
54 |
55 | 1. Do not use ZipDeploy extension.
56 | 2. Set appSetting `WEBSITE_RUN_FROM_PACKAGE=URL` for deployment using the .zip package url. For example:
57 |
58 | NOTE: Please include rest of the existing app settings in the template to preserve them.
59 |
60 | ```json
61 | {
62 | "name": "[parameters('siteName')]",
63 | "type": "Microsoft.Web/sites",
64 | "apiVersion": "2021-02-01",
65 | "location": "[parameters('location')]",
66 | "properties": {
67 | "siteConfig": {
68 | "appSettings": [
69 | {
70 | "name": "WEBSITE_RUN_FROM_PACKAGE",
71 | "value": "[parameters('packageUri')]"
72 | }
73 | ]
74 | }
75 | }
76 | }
77 | ```
78 |
--------------------------------------------------------------------------------
/zip-deploy-run-from-package/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "functionAppName": {
6 | "type": "string",
7 | "metadata": {
8 | "description": "The name of the Azure Function app."
9 | }
10 | },
11 | "location": {
12 | "type": "string",
13 | "defaultValue": "[resourceGroup().location]",
14 | "metadata": {
15 | "description": "The location into which the resources should be deployed."
16 | }
17 | },
18 | "packageUri": {
19 | "type": "string",
20 | "metadata": {
21 | "description": "The zip content url."
22 | }
23 | }
24 | },
25 | "resources": [
26 | {
27 | "name": "[concat(parameters('functionAppName'), '/ZipDeploy')]",
28 | "type": "Microsoft.Web/sites/extensions",
29 | "apiVersion": "2021-02-01",
30 | "location": "[parameters('location')]",
31 | "properties": {
32 | "packageUri": "[parameters('packageUri')]"
33 | }
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/zip-deploy-run-from-package/azuredeploy.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------