From 2ca732a051eda014c52caeb974be05169d639cd2 Mon Sep 17 00:00:00 2001
From: pragnagopa <pgopa@microsoft.com>
Date: Mon, 30 Jan 2017 10:13:32 -0800
Subject: [PATCH 1/2] Azure Serverless plugin

---
 .gitignore                                    |    4 +
 .npmignore                                    |   36 +
 LICENSE                                       |   21 +
 README.md                                     |   77 +-
 deploy/azureDeploy.js                         |   36 +
 deploy/azureDeployFunction.js                 |   28 +
 .../lib/CreateResourceGroupAndFunctionApp.js  |   10 +
 deploy/lib/cleanUpFunctions.js                |    9 +
 deploy/lib/createFunction.js                  |   13 +
 deploy/lib/createFunctions.js                 |   19 +
 index.js                                      |   31 +
 invoke/azureInvoke.js                         |   45 +
 invoke/lib/invokeFunction.js                  |   14 +
 logs/azureLogs.js                             |   29 +
 logs/lib/retrieveLogs.js                      |   11 +
 package.json                                  |   42 +
 provider/armTemplates/azuredeploy.json        |   83 +
 provider/armTemplates/azuredeployWithGit.json |  102 +
 provider/azureProvider.js                     |  607 ++++++
 remove/azureRemove.js                         |   28 +
 remove/lib/deleteResourceGroup.js             |    9 +
 shared/bindings.json                          | 1741 +++++++++++++++++
 shared/getAdminKey.js                         |    7 +
 shared/loginToAzure.js                        |    9 +
 shared/parseBindings.js                       |   43 +
 shared/utils.js                               |  204 ++
 26 files changed, 3256 insertions(+), 2 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 .npmignore
 create mode 100644 LICENSE
 create mode 100644 deploy/azureDeploy.js
 create mode 100644 deploy/azureDeployFunction.js
 create mode 100644 deploy/lib/CreateResourceGroupAndFunctionApp.js
 create mode 100644 deploy/lib/cleanUpFunctions.js
 create mode 100644 deploy/lib/createFunction.js
 create mode 100644 deploy/lib/createFunctions.js
 create mode 100644 index.js
 create mode 100644 invoke/azureInvoke.js
 create mode 100644 invoke/lib/invokeFunction.js
 create mode 100644 logs/azureLogs.js
 create mode 100644 logs/lib/retrieveLogs.js
 create mode 100644 package.json
 create mode 100644 provider/armTemplates/azuredeploy.json
 create mode 100644 provider/armTemplates/azuredeployWithGit.json
 create mode 100644 provider/azureProvider.js
 create mode 100644 remove/azureRemove.js
 create mode 100644 remove/lib/deleteResourceGroup.js
 create mode 100644 shared/bindings.json
 create mode 100644 shared/getAdminKey.js
 create mode 100644 shared/loginToAzure.js
 create mode 100644 shared/parseBindings.js
 create mode 100644 shared/utils.js

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..ab3d6d3b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+*.dll
+.eslintrc.js
+.eslintrc.json
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 00000000..1f7f2257
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,36 @@
+# Logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules
+jspm_packages
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..39c786a0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Serverless
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index fa14d8bb..bcb5f7e5 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,75 @@
-# serverless-azure-functions
-WIP – Coming soon...
+# Azure Functions Plugin 
+
+This plugin enables Azure Functions support within the Serverless Framework.
+
+## Getting started
+
+
+### Get a Serverless Service with Azure as the Provider
+
+1. Clone gitrepo: `git clone -b dev https://github.com/pragnagopa/boilerplate-azurefunctions.git`.
+2. npm install
+
+### Get an Azure Subscription
+ - <a href="https://azure.microsoft.com/en-us/free/?b=17.01" target="_blank">Create your free Azure account today</a>
+
+### Create Service Principal User for your Azure subscription
+1. Create a Service Principal User with <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal" target="_blank">portal</a> or <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal-cli" target="_blank">Azure CLI</a>
+2. <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-tenant-id" target="_blank">Get tenant ID</a>
+3. <a href="https://blogs.msdn.microsoft.com/mschray/2015/05/13/getting-your-azure-guid-subscription-id/" target="_blank">Get Azure subscription ID</a>
+4. <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#get-application-id-and-authentication-key" target="_blank">Get application ID</a>. Note this is also referred to as the client id.
+
+
+### Set the following environment variables:
+  
+- azureSubId: YourAzureSubscriptionID
+- azureServicePrincipalTenantId: servicePrincipalTenantId
+- azureservicePrincipalClientId: servicePrincipalClientId
+- azureServicePrincipalPassword: servicePrincipalPassword
+
+**Note:** If you created Service Principal User from Portal, servicePrincipalPassword is the authentication key
+
+### Update the config in `serverless.yml`
+
+Open up your `serverless.yml` file and update the following information:
+
+#### `service` property
+
+```yml
+service: my-azure-functions-app # Name of the Azure function App you want to create
+```
+### Quick Start
+
+1. **Deploy a Service:**
+
+  Use this when you have made changes to your Functions or you simply want to deploy all changes within your Service at the same time.
+  ```bash
+  serverless deploy
+  ```
+
+2. **Deploy the Function:**
+
+  Use this to quickly upload and overwrite your Azure function, allowing you to develop faster.
+  ```bash
+  serverless deploy function -f httpjs
+  ```
+
+3. **Invoke the Function:**
+
+  Invokes an Azure Function on Azure
+  ```bash
+  serverless invoke --path httpQueryString.json -f httpjs
+  ```
+
+4. **Stream the Function Logs:**
+
+  Open up a separate tab in your console and stream all logs for a specific Function using this command.
+  ```bash
+  serverless logs -f httpjs -t
+  ```
+
+5. **Remove the Service:**
+
+  Removes all Functions and Resources from your Azure subscription.
+  ```bash
+  serverless remove
diff --git a/deploy/azureDeploy.js b/deploy/azureDeploy.js
new file mode 100644
index 00000000..649352c1
--- /dev/null
+++ b/deploy/azureDeploy.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const CreateResourceGroupAndFunctionApp = require('./lib/CreateResourceGroupAndFunctionApp');
+const createFunctions = require('./lib/createFunctions');
+const cleanUpFunctions = require('./lib/cleanUpFunctions');
+const loginToAzure = require('../shared/loginToAzure');
+
+class AzureDeploy {
+  constructor (serverless, options) {
+    this.serverless = serverless;
+    this.options = options;
+    this.provider = this.serverless.getProvider('azure');
+
+    Object.assign(
+      this,
+      loginToAzure,
+      cleanUpFunctions,
+      CreateResourceGroupAndFunctionApp,
+      createFunctions
+    );
+
+    this.hooks = {
+      'before:deploy:deploy': () => BbPromise.bind(this)
+        .then(this.loginToAzure)
+        .then(this.cleanUpFunctions),
+
+      'deploy:deploy': () => BbPromise.bind(this)
+        .then(this.CreateResourceGroupAndFunctionApp)
+        .then(this.createFunctions)
+        .then(() => this.serverless.cli.log('Successfully created Function App'))
+    };
+  }
+}
+
+module.exports = AzureDeploy;
diff --git a/deploy/azureDeployFunction.js b/deploy/azureDeployFunction.js
new file mode 100644
index 00000000..7bc3b247
--- /dev/null
+++ b/deploy/azureDeployFunction.js
@@ -0,0 +1,28 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const createFunction = require('./lib/createFunction');
+const loginToAzure = require('../shared/loginToAzure');
+
+class AzureDeployFunction {
+  constructor (serverless, options) {
+    this.serverless = serverless;
+    this.options = options;
+    this.provider = this.serverless.getProvider('azure');
+
+    Object.assign(
+      this,
+      loginToAzure,
+      createFunction
+    );
+
+    this.hooks = {
+      'deploy:function:deploy': () => BbPromise.bind(this)
+        .then(this.loginToAzure)
+        .then(this.createFunction)
+        .then(() => this.serverless.cli.log('Successfully uploaded Function'))
+    };
+  }
+}
+
+module.exports = AzureDeployFunction;
diff --git a/deploy/lib/CreateResourceGroupAndFunctionApp.js b/deploy/lib/CreateResourceGroupAndFunctionApp.js
new file mode 100644
index 00000000..3c3e4cd1
--- /dev/null
+++ b/deploy/lib/CreateResourceGroupAndFunctionApp.js
@@ -0,0 +1,10 @@
+'use strict';
+
+module.exports = {
+  CreateResourceGroupAndFunctionApp () {
+
+return this.provider.CreateResourceGroup()
+      .then(() => this.provider.CreateFunctionApp());
+  }
+};
+
diff --git a/deploy/lib/cleanUpFunctions.js b/deploy/lib/cleanUpFunctions.js
new file mode 100644
index 00000000..7485d89c
--- /dev/null
+++ b/deploy/lib/cleanUpFunctions.js
@@ -0,0 +1,9 @@
+'use strict';
+
+module.exports = {
+  cleanUpFunctions () {
+    return this.provider.isExistingFunctionApp()
+      .then(() => this.provider.getDeployedFunctionsNames())
+      .then(() => this.provider.cleanUpFunctionsBeforeDeploy(this.serverless.service.getAllFunctions()));
+  }
+};
diff --git a/deploy/lib/createFunction.js b/deploy/lib/createFunction.js
new file mode 100644
index 00000000..a0c22700
--- /dev/null
+++ b/deploy/lib/createFunction.js
@@ -0,0 +1,13 @@
+'use strict';
+
+const utils = require('../../shared/utils');
+
+module.exports = {
+  createFunction () {
+    const functionName = this.options.function;
+    const metaData = utils.getFunctionMetaData(functionName, this.provider.getParsedBindings(), this.serverless);
+
+return this.provider.createZipObject(functionName, metaData.entryPoint, metaData.handlerPath, metaData.params)
+      .then(() => this.provider.createAndUploadZipFunctions());
+  }
+};
diff --git a/deploy/lib/createFunctions.js b/deploy/lib/createFunctions.js
new file mode 100644
index 00000000..9247ce0d
--- /dev/null
+++ b/deploy/lib/createFunctions.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const utils = require('../../shared/utils');
+
+module.exports = {
+  createFunctions () {
+    const createFunctionPromises = [];
+
+    this.serverless.service.getAllFunctions().forEach((functionName) => {
+      const metaData = utils.getFunctionMetaData(functionName, this.provider.getParsedBindings(), this.serverless);
+
+      createFunctionPromises.push(this.provider.createZipObject(functionName, metaData.entryPoint, metaData.handlerPath, metaData.params));
+    });
+
+    return BbPromise.all(createFunctionPromises)
+            .then(() => this.provider.createAndUploadZipFunctions());
+  }
+};
diff --git a/index.js b/index.js
new file mode 100644
index 00000000..f2a8d8ce
--- /dev/null
+++ b/index.js
@@ -0,0 +1,31 @@
+'use strict';
+
+/*
+NOTE: this plugin is used to add all the differnet provider related plugins at once.
+This way only one plugin needs to be added to the service in order to get access to the
+whole provider implementation.
+*/
+
+const AzureDeploy = require('./deploy/azureDeploy');
+const AzureDeployFunction = require('./deploy/azureDeployFunction');
+const AzureProvider = require('./provider/azureProvider');
+const AzureInvoke = require('./invoke/azureInvoke');
+const AzureLogs = require('./logs/azureLogs');
+const AzureRemove = require('./remove/azureRemove');
+
+
+class AzureIndex {
+  constructor(serverless, options) {
+    this.serverless = serverless;
+    this.options = options;
+
+    this.serverless.pluginManager.addPlugin(AzureProvider);
+    this.serverless.pluginManager.addPlugin(AzureDeploy);
+    this.serverless.pluginManager.addPlugin(AzureDeployFunction);
+    this.serverless.pluginManager.addPlugin(AzureInvoke);
+    this.serverless.pluginManager.addPlugin(AzureLogs);
+    this.serverless.pluginManager.addPlugin(AzureRemove);
+  }
+}
+
+module.exports = AzureIndex;
diff --git a/invoke/azureInvoke.js b/invoke/azureInvoke.js
new file mode 100644
index 00000000..5c2f6c63
--- /dev/null
+++ b/invoke/azureInvoke.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const invokeFunction = require('./lib/invokeFunction');
+const getAdminKey = require('../shared/getAdminKey');
+const loginToAzure = require('../shared/loginToAzure');
+const path = require('path');
+
+class AzureInvoke {
+  constructor (serverless, options) {
+    this.serverless = serverless;
+    this.options = options;
+    this.provider = this.serverless.getProvider('azure');
+
+    Object.assign(
+      this,
+      loginToAzure,
+      getAdminKey,
+      invokeFunction
+    );
+
+    if (this.options.path) {
+      const absolutePath = path.isAbsolute(this.options.path)
+        ? this.options.path
+        : path.join(this.serverless.config.servicePath, this.options.path);
+
+      if (!this.serverless.utils.fileExistsSync(absolutePath)) {
+        throw new this.serverless.classes.Error('The file you provided does not exist.');
+      }
+      this.options.data = this.serverless.utils.readFileSync(absolutePath);
+    }
+
+    this.hooks = {
+
+      'before:invoke:invoke': () => BbPromise.bind(this)
+        .then(this.loginToAzure)
+        .then(this.getAdminKey),
+
+      'invoke:invoke': () => BbPromise.bind(this)
+        .then(this.invokeFunction)
+    };
+  }
+}
+
+module.exports = AzureInvoke;
diff --git a/invoke/lib/invokeFunction.js b/invoke/lib/invokeFunction.js
new file mode 100644
index 00000000..45d4e1a2
--- /dev/null
+++ b/invoke/lib/invokeFunction.js
@@ -0,0 +1,14 @@
+'use strict';
+
+module.exports = {
+  invokeFunction () {
+    const func = this.options.function;
+    const functionObject = this.serverless.service.getFunction(func);
+    const eventType = Object.keys(functionObject.events[0])[0];
+    
+    return this.provider.invoke(func, eventType, this.options.data);
+    // TODO: Github issue: https://github.com/Azure/azure-webjobs-sdk-script/issues/1122
+    // .then(() => this.provider.getInvocationId(func))
+    // .then(() => this.provider.getLogsForInvocationId());
+  }
+};
diff --git a/logs/azureLogs.js b/logs/azureLogs.js
new file mode 100644
index 00000000..7d252d6f
--- /dev/null
+++ b/logs/azureLogs.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const retrieveLogs = require('./lib/retrieveLogs');
+const loginToAzure = require('../shared/loginToAzure');
+
+class AzureLogs {
+  constructor (serverless, options) {
+    this.serverless = serverless;
+    this.options = options;
+    this.provider = this.serverless.getProvider('azure');
+
+    Object.assign(
+      this,
+      loginToAzure,
+      retrieveLogs
+    );
+
+    this.hooks = {
+      'before:logs:logs': () => BbPromise.bind(this)
+      .then(this.loginToAzure),
+
+      'logs:logs': () => BbPromise.bind(this)
+        .then(this.retrieveLogs)
+    };
+  }
+}
+
+module.exports = AzureLogs;
diff --git a/logs/lib/retrieveLogs.js b/logs/lib/retrieveLogs.js
new file mode 100644
index 00000000..ad63c14c
--- /dev/null
+++ b/logs/lib/retrieveLogs.js
@@ -0,0 +1,11 @@
+'use strict';
+
+module.exports = {
+  retrieveLogs () {
+    const functionName = this.options.function;
+
+return this.provider.getAdminKey()
+      .then(() => this.provider.pingHostStatus(functionName))
+      .then(() => this.provider.getLogsStream(functionName));
+  }
+};
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..0c3e2fe4
--- /dev/null
+++ b/package.json
@@ -0,0 +1,42 @@
+{
+  "name": "serverless-azure-functions",
+  "version": "0.1.0",
+  "description": "Provider plugin for the Serverless Framework v1.x which adds support for Azure Functions.",
+  "license": "MIT",
+  "main": "index.js",
+  "author": "Azure Functions",
+  "repository": {
+    "git": "https://github.com/serverless/serverless-azure-functions"
+  },
+  "homepage": "https://github.com/serverless/serverless-azure-functions",
+  "keywords": [
+    "serverless",
+    "serverless framework",
+    "serverless applications",
+    "serverless modules",
+    "azure functions",
+    "iot",
+    "internet of things",
+    "serverless.com"
+  ],
+  "dependencies": {
+    "async": "^2.1.2",
+    "bluebird": "^3.4.6",
+    "chalk": "^1.1.3",
+    "lodash": "^4.16.6",
+    "fs-extra": "^2.0.0",
+    "jszip": "^3.1.3",
+    "request": "2.79.0",
+    "ms-rest-azure": "^1.15.4",
+    "azure-arm-resource": "1.6.1-preview"
+  },
+  "devDependencies": {
+    "eslint": "^3.15.0",
+    "eslint-config-standard": "^6.2.1",
+    "eslint-plugin-promise": "^3.4.1",
+    "eslint-plugin-standard": "^2.0.1"
+  },
+  "engines": {
+    "node": ">= 6.5.0"
+  }
+}
\ No newline at end of file
diff --git a/provider/armTemplates/azuredeploy.json b/provider/armTemplates/azuredeploy.json
new file mode 100644
index 00000000..2481445e
--- /dev/null
+++ b/provider/armTemplates/azuredeploy.json
@@ -0,0 +1,83 @@
+{
+    "$schema": "http://schemas.management.azure.com/schemas/2015-01-01-preview/deploymentTemplate.json#",
+    "contentVersion": "1.0.0.0",
+    "parameters": {
+        "functionAppName": {
+            "type": "string",
+            "metadata": {
+                "description": "The name of the function app that you wish to create."
+            }
+        },
+        "storageAccountType": {
+            "type": "string",
+            "defaultValue": "Standard_LRS",
+            "allowedValues": [
+                "Standard_LRS",
+                "Standard_GRS",
+                "Standard_ZRS",
+                "Premium_LRS"
+            ],
+            "metadata": {
+                "description": "Storage Account type"
+            }
+        }        
+    },
+    "variables": {
+        "functionAppName": "[parameters('functionAppName')]",
+        "hostingPlanName": "[parameters('functionAppName')]",
+        "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'azfunctions')]"
+    },
+    "resources": [
+        {
+            "type": "Microsoft.Storage/storageAccounts",
+            "name": "[variables('storageAccountName')]",
+            "apiVersion": "2015-06-15",
+            "location": "[resourceGroup().location]",
+            "properties": {
+                "accountType": "[parameters('storageAccountType')]"
+            }
+        },
+        {
+            "type": "Microsoft.Web/serverfarms",
+            "apiVersion": "2015-04-01",
+            "name": "[variables('hostingPlanName')]",
+            "location": "[resourceGroup().location]",
+            "properties": {
+                "name": "[variables('hostingPlanName')]",
+                "computeMode": "Dynamic",
+                "sku": "Dynamic"
+            }
+        },
+        {
+            "apiVersion": "2015-08-01",
+            "type": "Microsoft.Web/sites",
+            "name": "[variables('functionAppName')]",
+            "location": "[resourceGroup().location]",
+            "kind": "functionapp",
+            "properties": {
+                "name": "[variables('functionAppName')]",
+                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]"
+            },
+            "dependsOn": [
+                "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
+                "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
+            ],
+            "resources": [
+                {
+                    "apiVersion": "2016-03-01",
+                    "name": "appsettings",
+                    "type": "config",
+                    "dependsOn": [
+                        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
+                        "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
+                    ],
+                    "properties": {
+                        "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2015-05-01-preview').key1,';')]",
+                        "AzureWebJobsDashboard": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2015-05-01-preview').key1,';')]",
+                        "FUNCTIONS_EXTENSION_VERSION": "~1"
+                    }
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/provider/armTemplates/azuredeployWithGit.json b/provider/armTemplates/azuredeployWithGit.json
new file mode 100644
index 00000000..38dad76d
--- /dev/null
+++ b/provider/armTemplates/azuredeployWithGit.json
@@ -0,0 +1,102 @@
+{
+    "$schema": "http://schemas.management.azure.com/schemas/2015-01-01-preview/deploymentTemplate.json#",
+    "contentVersion": "1.0.0.0",
+    "parameters": {
+        "functionAppName": {
+            "type": "string",
+            "metadata": {
+                "description": "The name of the function app that you wish to create."
+            }
+        },
+        "storageAccountType": {
+            "type": "string",
+            "defaultValue": "Standard_LRS",
+            "allowedValues": [
+                "Standard_LRS",
+                "Standard_GRS",
+                "Standard_ZRS",
+                "Premium_LRS"
+            ],
+            "metadata": {
+                "description": "Storage Account type"
+            }
+        },
+        "gitUrl": {
+            "type": "string",
+            "metadata": {
+                "description": "Git URL"
+            }
+        }
+    },
+    "variables": {
+        "functionAppName": "[parameters('functionAppName')]",
+        "hostingPlanName": "[parameters('functionAppName')]",
+        "gitRepoUrl": "[parameters('gitUrl')]",
+        "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'azfunctions')]"
+    },
+    "resources": [
+        {
+            "type": "Microsoft.Storage/storageAccounts",
+            "name": "[variables('storageAccountName')]",
+            "apiVersion": "2015-06-15",
+            "location": "[resourceGroup().location]",
+            "properties": {
+                "accountType": "[parameters('storageAccountType')]"
+            }
+        },
+        {
+            "type": "Microsoft.Web/serverfarms",
+            "apiVersion": "2015-04-01",
+            "name": "[variables('hostingPlanName')]",
+            "location": "[resourceGroup().location]",
+            "properties": {
+                "name": "[variables('hostingPlanName')]",
+                "computeMode": "Dynamic",
+                "sku": "Dynamic"
+            }
+        },
+        {
+            "apiVersion": "2015-08-01",
+            "type": "Microsoft.Web/sites",
+            "name": "[variables('functionAppName')]",
+            "location": "[resourceGroup().location]",
+            "kind": "functionapp",
+            "properties": {
+                "name": "[variables('functionAppName')]",
+                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]"
+            },
+            "dependsOn": [
+                "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
+                "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
+            ],
+            "resources": [
+                {
+                    "apiVersion": "2016-03-01",
+                    "name": "appsettings",
+                    "type": "config",
+                    "dependsOn": [
+                        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
+                        "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
+                    ],
+                    "properties": {
+                        "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2015-05-01-preview').key1,';')]",
+                        "AzureWebJobsDashboard": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2015-05-01-preview').key1,';')]",
+                        "FUNCTIONS_EXTENSION_VERSION": "~1"
+                    }
+                },
+                {
+                    "apiVersion": "2016-03-01",
+                    "name": "web",
+                    "type": "sourcecontrols",
+                    "dependsOn": [
+                        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
+                    ],
+                    "properties": {
+                        "repoUrl": "[variables('gitRepoUrl')]",
+                        "IsManualIntegration": true
+                    }
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/provider/azureProvider.js b/provider/azureProvider.js
new file mode 100644
index 00000000..b5ed7543
--- /dev/null
+++ b/provider/azureProvider.js
@@ -0,0 +1,607 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const _ = require('lodash');
+const msRestAzure = require('ms-rest-azure');
+const resourceManagement = require('azure-arm-resource');
+const path = require('path');
+const fs = require('fs');
+const fse = require('fs-extra');
+const https = require('https');
+const JSZip = require('jszip');
+const request = require('request');
+const dns = require('dns');
+const parseBindings = require('../shared/parseBindings');
+
+let resourceGroupName;
+let deploymentName;
+let functionAppName;
+let subscriptionId;
+let servicePrincipalTenantId;
+let servicePrincipalClientId;
+let servicePrincipalPassword;
+let functionsAdminKey;
+let invocationId;
+let oldLogs = '';
+let principalCredentials;
+let functionsFolder;
+let existingFunctionApp = false;
+let parsedBindings;
+const zipArray = [];
+const deployedFunctionNames = [];
+
+const constants = {
+  'authorizationHeader': 'Authorization',
+  'bearer': 'Bearer ',
+  'contentTypeHeader': 'Content-Type',
+  'functionAppApiPath': '/api/',
+  'functionAppDomain': '.azurewebsites.net',
+  'functionsAdminApiPath': '.azurewebsites.net/admin/functions/',
+  'functionsApiPath': '/api/functions',
+  'jsonContentType': 'application/json',
+  'logInvocationsApiPath': '/azurejobs/api/functions/definitions/',
+  'logOutputApiPath': '/azurejobs/api/log/output/',
+  'logStreamApiPath': '/api/logstream/application/functions/function/',
+  'masterKeyApiApth': '/api/functions/admin/masterkey',
+  'providerName': 'azure',
+  'scmDomain': '.scm.azurewebsites.net',
+  'scmVfsPath': '.scm.azurewebsites.net/api/vfs/site/wwwroot/',
+  'scmZipApiPath': '.scm.azurewebsites.net/api/zip/site/wwwroot/'
+};
+
+const azureCredentials = {
+  'azureSubId': 'azureSubId',
+  'azureServicePrincipalTenantId': 'azureServicePrincipalTenantId',
+  'azureservicePrincipalClientId': 'azureservicePrincipalClientId',
+  'azureServicePrincipalPassword': 'azureServicePrincipalPassword',
+};
+
+class AzureProvider {
+  static getProviderName () {
+    return constants.providerName;
+  }
+
+  constructor (serverless) {
+    this.serverless = serverless;
+    this.provider = this;
+    this.serverless.setProvider(constants.providerName, this);
+    subscriptionId = process.env[azureCredentials.azureSubId];
+    servicePrincipalTenantId = process.env[azureCredentials.azureServicePrincipalTenantId];
+    servicePrincipalClientId = process.env[azureCredentials.azureservicePrincipalClientId];
+    servicePrincipalPassword = process.env[azureCredentials.azureServicePrincipalPassword];
+
+    functionAppName = this.serverless.service.service;
+    resourceGroupName = `${functionAppName}-rg`;
+    deploymentName = `${resourceGroupName}-deployment`;
+    functionsFolder = path.join(this.serverless.config.servicePath, 'functions');
+  }
+
+  getParsedBindings () {
+    if (!this.parsedBindings) {
+      this.parsedBindings = parseBindings.getBindingsMetaData(this.serverless);
+    }
+
+return this.parsedBindings;
+  }
+
+  LoginWithServicePrincipal () {
+    return new BbPromise((resolve, reject) => {
+      msRestAzure.loginWithServicePrincipalSecret(servicePrincipalClientId, servicePrincipalPassword, servicePrincipalTenantId, (error, credentials) => {
+        if (error) {
+          reject(error);
+        } else {
+          principalCredentials = credentials;
+          resolve(credentials);
+        }
+      });
+    });
+  }
+
+  CreateResourceGroup () {
+    const groupParameters = {
+'location': this.serverless.service.provider.location,
+'tags': {'sampletag': 'sampleValue'}
+};
+
+    this.serverless.cli.log(`Creating resource group: ${resourceGroupName}`);
+    const resourceClient = new resourceManagement.ResourceManagementClient(principalCredentials, subscriptionId);
+
+return new BbPromise((resolve, reject) => {
+      resourceClient.resourceGroups.createOrUpdate(resourceGroupName,
+        groupParameters, (error, result, createOrUpdateRequest, response) => {
+          if (error) {
+            reject(error);
+          } else {
+            resolve(result);
+          }
+        });
+    });
+  }
+
+  CreateFunctionApp (method, params) {
+    this.serverless.cli.log(`Creating function app: ${functionAppName}`);
+    const resourceClient = new resourceManagement.ResourceManagementClient(principalCredentials, subscriptionId);
+    let parameters = {'functionAppName': {'value': functionAppName}};
+
+    const gitUrl = this.serverless.service.provider.gitUrl;
+
+    if (gitUrl) {
+      parameters = {
+        'functionAppName': {'value': functionAppName},
+        'gitUrl': {'value': gitUrl}
+      };
+    }
+
+    let templateFilePath = path.join(__dirname, 'armTemplates', 'azuredeploy.json');
+
+    if (gitUrl) {
+      templateFilePath = path.join(__dirname, 'armTemplates', 'azuredeployWithGit.json');
+    }
+    if (this.serverless.service.provider.armTemplate) {
+      templateFilePath = path.join(this.serverless.config.servicePath, this.serverless.service.provider.armTemplate.file);
+      const userParameters = this.serverless.service.provider.armTemplate.parameters;
+      const userParametersKeys = Object.keys(userParameters);
+
+      for (let paramIndex = 0; paramIndex < userParametersKeys.length; paramIndex++) {
+        const item = {};
+
+        item[userParametersKeys[paramIndex]] = {'value': userParameters[userParametersKeys[paramIndex]]};
+        parameters = _.merge(parameters, item);
+      }
+    }
+
+    const template = JSON.parse(fs.readFileSync(templateFilePath, 'utf8'));
+    const deploymentParameters = {
+      'properties': {
+        'mode': 'Incremental',
+        parameters,
+        template
+      }
+    };
+
+return new BbPromise((resolve, reject) => {
+      resourceClient.deployments.createOrUpdate(resourceGroupName,
+        deploymentName,
+        deploymentParameters, (error, result, createOrUpdateRequest, response) => {
+          if (error) {
+            reject(error);
+          } else {
+            this.serverless.cli.log('Waiting for Kudu endpoint...');
+            setTimeout(() => {
+              resolve(result);
+            }, 10000);
+          }
+        });
+    });
+  }
+
+  DeleteDeployment () {
+    this.serverless.cli.log(`Deleting deployment: ${deploymentName}`);
+    const resourceClient = new resourceManagement.ResourceManagementClient(principalCredentials, subscriptionId);
+
+return new BbPromise((resolve, reject) => {
+      resourceClient.deployments.deleteMethod(resourceGroupName,
+        deploymentName, (error, result, deleteRequest, response) => {
+          if (error) {
+            reject(error);
+          } else {
+            resolve(result);
+          }
+        });
+    });
+  }
+
+  DeleteResourceGroup () {
+    this.serverless.cli.log(`Deleting resource group: ${resourceGroupName}`);
+    const resourceClient = new resourceManagement.ResourceManagementClient(principalCredentials, subscriptionId);
+
+return new BbPromise((resolve, reject) => {
+      resourceClient.resourceGroups.deleteMethod(resourceGroupName, (error, result, deleteRequest, response) => {
+        if (error) {
+          reject(error);
+        } else {
+          resolve(result);
+        }
+      });
+    });
+  }
+
+  getAdminKey () {
+    const options = {
+      'host': functionAppName + constants.scmDomain,
+      'port': 443,
+      'path': constants.masterKeyApiApth,
+      'headers': {
+        'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+        'Content-Type': constants.jsonContentType
+      }
+    };
+
+    return new BbPromise((resolve, reject) => {
+      https.get(options, (res) => {
+        let body = '';
+
+        res.on('data', (data) => {
+          body += data;
+        });
+        res.on('end', () => {
+          const parsed = JSON.parse(body);
+
+          functionsAdminKey = parsed.masterKey;
+          resolve(res);
+        });
+        res.on('error', (responseError) => {
+          reject(responseError);
+        });
+      });
+    });
+  }
+
+  pingHostStatus (functionName) {
+    const requestUrl = `https://${functionAppName}${constants.functionAppDomain}/admin/functions/${functionName}/status`;
+    const options = {
+      'host': functionAppName + constants.functionAppDomain,
+      'method': 'get',
+      'url': requestUrl,
+      'headers': {
+        'x-functions-key': functionsAdminKey,
+        'Accept': 'application/json,*/*'
+      }
+    };
+
+  return new BbPromise((resolve, reject) => {
+      this.serverless.cli.log('Pinging host status...');
+      request(options, (err, res, body) => {
+        if (err) {
+          reject(err);
+        } else {
+          resolve(res);
+        }
+      });
+    });
+  }
+
+  isExistingFunctionApp () {
+    const host = functionAppName + constants.scmDomain;
+
+return new BbPromise((resolve, reject) => {
+      dns.resolve4(host, (err, addresses) => {
+        if (err) {
+          if (err.message.includes('ENOTFOUND')) {
+            resolve(existingFunctionApp);
+          } else {
+            reject(err);
+          }
+        } else {
+          existingFunctionApp = true;
+          resolve(existingFunctionApp);
+        }
+      });
+    });
+  }
+
+  getDeployedFunctionsNames () {
+    const requestUrl = `https://${functionAppName}${constants.scmDomain}${constants.functionsApiPath}`;
+    const options = {
+      'host': functionAppName + constants.scmDomain,
+      'method': 'get',
+      'url': requestUrl,
+      'headers': {
+        'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+        'Accept': 'application/json,*/*'
+      }
+    };
+
+return new BbPromise((resolve, reject) => {
+      if (existingFunctionApp) {
+        this.serverless.cli.log('Looking for deployed functions that are not part of the current deployment...');
+        request(options, (err, res, body) => {
+          if (err) {
+            if (err.message.includes('ENOTFOUND')) {
+              resolve(res);
+            } else {
+              reject(err);
+            }
+          } else {
+            if (res.statusCode === 200) {
+              const parsed = JSON.parse(body);
+
+              for (let functionNamesIndex = 0; functionNamesIndex < parsed.length; functionNamesIndex++) {
+                deployedFunctionNames.push(parsed[functionNamesIndex].name);
+              }
+            }
+            resolve(res);
+          }
+        });
+      } else {
+        resolve('New service...');
+      }
+    });
+  }
+
+  getLogsStream (functionName) {
+    const logOptions = {
+      'host': functionAppName + constants.scmDomain,
+      'port': 443,
+      'path': constants.logStreamApiPath + functionName,
+      'headers': {
+        'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+        'Accept': '*/*'
+      }
+    };
+
+    https.get(logOptions, (res) => {
+      let body = '';
+
+      res.on('data', (data) => {
+        body += data;
+        const currentLogs = body.substring(oldLogs.length, body.length - 1);
+
+        console.log(currentLogs);
+        oldLogs += currentLogs;
+      });
+      res.on('end', function () {
+        console.log(body);
+        this.getLogsStream(functionName);
+      });
+      res.on('error', (responseError) => {
+        console.log(`Got error: ${responseError.message}`);
+      });
+    });
+  }
+
+  getInvocationId (functionName) {
+    const options = {
+      'host': functionAppName + constants.scmDomain,
+      'port': 443,
+      'path': `${constants.logInvocationsApiPath + functionAppName}-${functionName}/invocations?limit=5`,
+      'headers': {
+        'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+        'Content-Type': constants.jsonContentType
+      }
+    };
+
+return new BbPromise((resolve, reject) => {
+      https.get(options, (res) => {
+        let body = '';
+
+        res.on('data', (data) => {
+          body += data;
+        });
+        res.on('end', () => {
+          const parsed = JSON.parse(body);
+
+          invocationId = parsed.entries[0].id;
+          resolve(res);
+        });
+        res.on('error', (responseError) => {
+          reject(responseError);
+        });
+      });
+    });
+  }
+
+  getLogsForInvocationId () {
+    this.serverless.cli.log(`Logs for InvocationId: ${invocationId}`);
+    const options = {
+      'host': functionAppName + constants.scmDomain,
+      'port': 443,
+      'path': constants.logOutputApiPath + invocationId,
+      'headers': {
+        'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+        'Content-Type': constants.jsonContentType
+      }
+    };
+
+return new BbPromise((resolve, reject) => {
+      https.get(options, (res) => {
+        let body = '';
+
+        res.on('data', (data) => {
+          body += data;
+        });
+        res.on('end', () => {
+          console.log(body);
+        });
+        res.on('error', (e) => {
+          reject(e);
+        });
+        resolve(res);
+      });
+    });
+  }
+
+  invoke (functionName, eventType, eventData) {
+    let options = {};
+
+    if (eventType === 'http') {
+      let queryString = '';
+
+      if (eventData) {
+        Object.keys(eventData).forEach((key) => {
+          const value = eventData[key];
+
+          queryString = `${key}=${value}`;
+        });
+      }
+      options = {
+        'host': functionAppName + constants.functionAppDomain,
+        'port': 443,
+        'path': `${constants.functionAppApiPath + functionName}?${queryString}`
+      };
+
+return new BbPromise((resolve, reject) => {
+        https.get(options, (res) => {
+          let body = '';
+
+          res.on('data', (data) => {
+            body += data;
+          });
+          res.on('end', () => {
+            console.log(body);
+          });
+          res.on('error', (e) => {
+            reject(e);
+          });
+          resolve(res);
+        });
+      });
+    }
+      const requestUrl = `https://${functionAppName}${constants.functionsAdminApiPath}${functionName}`;
+      
+      options = {
+        'host': constants.functionAppDomain,
+        'method': 'post',
+        'body': eventData,
+        'url': requestUrl,
+        'json': true,
+        'headers': {
+          'x-functions-key': functionsAdminKey,
+          'Accept': 'application/json,*/*'
+        }
+      };
+
+return new BbPromise((resolve, reject) => {
+        request(options, (err, res, body) => {
+          if (err) {
+            reject(err);
+          }
+          this.serverless.cli.log(`Invoked function at: ${requestUrl}. \nResponse statuscode: ${res.statusCode}`);
+          resolve(res);
+        });
+      });
+
+  }
+
+  cleanUpFunctionsBeforeDeploy (serverlessFunctions) {
+    const deleteFunctionPromises = [];
+
+    deployedFunctionNames.forEach((functionName) => {
+      if (serverlessFunctions.indexOf(functionName) < 0) {
+        this.serverless.cli.log(`Deleting function : ${functionName}`);
+        deleteFunctionPromises.push(this.deleteFunction(functionName));
+      }
+    });
+
+return BbPromise.all(deleteFunctionPromises);
+  }
+
+  deleteFunction (functionName) {
+    const requestUrl = `https://${functionAppName}${constants.scmVfsPath}${functionName}/?recursive=true`;
+    const options = {
+      'host': functionAppName + constants.scmDomain,
+      'method': 'delete',
+      'url': requestUrl,
+      'headers': {
+        'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+        'Accept': '*/*',
+        'Content-Type': constants.jsonContentType
+      }
+    };
+
+return new BbPromise((resolve, reject) => {
+      request(options, (err, res, body) => {
+        if (err) {
+          reject(err);
+        } else {
+          resolve(res);
+        }
+      });
+    });
+  }
+
+  createZipObject (functionName, entryPoint, filePath, params) {
+    return new BbPromise((resolve, reject) => {
+      this.serverless.cli.log(`Packaging function: ${functionName}`);
+      const folderForJSFunction = path.join(functionsFolder, functionName);
+      const handlerPath = path.join(this.serverless.config.servicePath, filePath);
+
+      if (!fs.existsSync(folderForJSFunction)) {
+        fs.mkdirSync(folderForJSFunction);
+      }
+      fse.copySync(handlerPath, path.join(folderForJSFunction, 'index.js'));
+      const functionJSON = params.functionsJson;
+
+      functionJSON.entryPoint = entryPoint;
+      fs.writeFileSync(path.join(folderForJSFunction, 'function.json'), JSON.stringify(functionJSON, null, 4));
+      fs.readdirSync(functionsFolder).filter((folder) => {
+        const folderName = path.basename(folder);
+
+        if (fs.statSync(path.join(functionsFolder, folder)).isDirectory() && functionName == folderName) {
+          const zip = new JSZip();
+
+          fs.readdir(path.join(functionsFolder, folder), (err, files) => {
+            if (err) {
+              reject(err);
+            } else {
+              let filesInFolder = 0;
+
+              for (let i = 0; i < files.length; i++) {
+                const filepathtobezipped = path.join(functionsFolder, folder, files[i]);
+                const data = fs.readFileSync(filepathtobezipped);
+
+                filesInFolder++;
+                zip.folder(path.basename(folder)).folder(path.basename(folder)).file(path.basename(filepathtobezipped), data);
+                if (filesInFolder === files.length) {
+                  zipArray.push({
+                    'key': path.basename(folder),
+                    'value': zip
+                  });
+                  resolve(`done folder..${folder}`);
+                }
+              }
+            }
+          });
+        }
+      });
+    });
+  }
+
+  createZipFileAndUploadFunction (folder, zip) {
+    return new BbPromise((resolve, reject) => {
+      const generateOptions = {
+        'type': 'nodebuffer',
+        'streamFiles': true
+      };
+      const zipFileName = `${path.basename(folder)}.zip`;
+      const outputZipPath = path.join(functionsFolder, zipFileName);
+
+      zip.folder(path.basename(folder)).generateNodeStream(generateOptions)
+        .pipe(fs.createWriteStream(outputZipPath))
+        .on('error', (error) => {
+          reject(error);
+        })
+        .on('finish', () => {
+          const requestUrl = `https://${functionAppName}${constants.scmZipApiPath}`;
+          const options = {
+            'url': requestUrl,
+            'headers': {
+              'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+              'Accept': '*/*'
+            }
+          };
+
+          fs.createReadStream(outputZipPath)
+            .pipe(request.put(options, (err, res, body) => {
+              if (err) {
+                reject(err);
+              } else {
+                resolve('ZipFileCreated and uploaded');
+              }
+              fse.removeSync(outputZipPath);
+            }));
+        });
+    });
+  }
+
+  createAndUploadZipFunctions () {
+    const zipFunctions = [];
+
+    for (let j = 0; j < zipArray.length; j++) {
+      zipFunctions.push(this.createZipFileAndUploadFunction(zipArray[j].key, zipArray[j].value));
+    }
+
+return BbPromise.all(zipFunctions);
+  }
+}
+module.exports = AzureProvider;
diff --git a/remove/azureRemove.js b/remove/azureRemove.js
new file mode 100644
index 00000000..cbbc36d0
--- /dev/null
+++ b/remove/azureRemove.js
@@ -0,0 +1,28 @@
+'use strict';
+
+const BbPromise = require('bluebird');
+const deleteResourceGroup = require('./lib/deleteResourceGroup');
+const loginToAzure = require('../shared/loginToAzure');
+
+class AzureRemove {
+  constructor (serverless, options) {
+    this.serverless = serverless;
+    this.options = options;
+    this.provider = this.serverless.getProvider('azure');
+
+    Object.assign(
+      this,
+      loginToAzure,
+      deleteResourceGroup
+    );
+
+    this.hooks = {
+      'remove:remove': () => BbPromise.bind(this)
+        .then(this.loginToAzure)
+        .then(this.deleteResourceGroup)
+        .then(() => this.serverless.cli.log('Service successfully removed'))
+    };
+  }
+}
+
+module.exports = AzureRemove;
diff --git a/remove/lib/deleteResourceGroup.js b/remove/lib/deleteResourceGroup.js
new file mode 100644
index 00000000..7748a113
--- /dev/null
+++ b/remove/lib/deleteResourceGroup.js
@@ -0,0 +1,9 @@
+'use strict';
+
+module.exports = {
+  deleteResourceGroup () {
+    return this.provider.LoginWithServicePrincipal()
+      .then(() => this.provider.DeleteDeployment())
+      .then(() => this.provider.DeleteResourceGroup());
+  }
+};
diff --git a/shared/bindings.json b/shared/bindings.json
new file mode 100644
index 00000000..57b78476
--- /dev/null
+++ b/shared/bindings.json
@@ -0,0 +1,1741 @@
+{
+    "$schema": "<TBD>",
+    "contentVersion": "2016-03-04-alpha",
+    "variables": {
+        "storageConnStringLabel": "$variables_storageConnStringLabel",
+        "appSettingsHelp": "$variables_appSettingsHelp",
+        "selectConnection": "$variables_selectConnection",
+        "parameterName": "$variables_parameterName",
+        "paramNameLabel": "$variables_paramNameLabel",
+        "paramNameInputHelp": "$variables_paramNameInputHelp",
+        "paramNameOutputHelp": "$variables_paramNameOutputHelp",
+        "apiHubTableDataSetLabel": "$variables_apiHubTableDataSetLabel",
+        "apiHubTableDataSetHelp": "$variables_apiHubTableDataSetHelp",
+        "apiHubTableNameLabel": "$variables_apiHubTableNameLabel",
+        "apiHubTableHelp": "$variables_apiHubTableHelp",
+        "apiHubTableEntityLabel": "$variables_apiHubTableEntityLabel",
+        "apiHubTableEntityHelp": "$variables_apiHubTableEntityHelp",
+        "apiHubTableConnectionLabel": "$variables_apiHubTableConnectionLabel",
+        "apiHubTableConnectionHelp": "$variables_apiHubTableConnectionHelp"
+    },
+    "bindings": [
+        {
+            "type": "timerTrigger",
+            "displayName": "$timerTrigger_displayName",
+            "direction": "trigger",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\timerTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "myTimer",
+                    "required": true,
+                    "label": "$timerTrigger_name_label",
+                    "help": "$timerTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "schedule",
+                    "value": "string",
+                    "defaultValue": "0 * * * * *",
+                    "required": true,
+                    "label": "$timerTrigger_schedule_label",
+                    "help": "$timerTrigger_schedule_help",
+                    "validators": [
+                        {
+                            "expression": "^(\\*|((([1-5]\\d)|\\d)(\\-(([1-5]\\d)|\\d)(\\/\\d+)?)?)(,((([1-5]\\d)|\\d)(\\-(([1-5]\\d)|\\d)(\\/\\d+)?)?))*)(\\/\\d+)? (\\*|((([1-5]\\d)|\\d)(\\-(([1-5]\\d)|\\d)(\\/\\d+)?)?)(,((([1-5]\\d)|\\d)(\\-(([1-5]\\d)|\\d)(\\/\\d+)?)?))*)(\\/\\d+)? (\\*|(((1\\d)|(2[0-3])|\\d)(\\-((1\\d)|(2[0-3])|\\d)(\\/\\d+)?)?)(,(((1\\d)|(2[0-3])|\\d)(\\-((1\\d)|(2[0-3])|\\d)(\\/\\d+)?)?))*)(\\/\\d+)? (\\*|((([1-2]\\d)|(3[0-1])|[1-9])(\\-(([1-2]\\d)|(3[0-1])|[1-9])(\\/\\d+)?)?)(,((([1-2]\\d)|(3[0-1])|[1-9])(\\-(([1-2]\\d)|(3[0-1])|[1-9])(\\/\\d+)?)?))*)(\\/\\d+)? (\\*|(([A-Za-z]+|(1[0-2])|[1-9])(\\-([A-Za-z]+|(1[0-2])|[1-9])(\\/\\d+)?)?)(,(([A-Za-z]+|(1[0-2])|[1-9])(\\-([A-Za-z]+|(1[0-2])|[1-9])(\\/\\d+)?)?))*)(\\/\\d+)? (\\*|(([A-Za-z]+|[0-6])(\\-([A-Za-z]+|[0-6])(\\/\\d+)?)?)(,(([A-Za-z]+|[0-6])(\\-([A-Za-z]+|[0-6])(\\/\\d+)?)?))*)(\\/\\d+)?$",
+                            "errorText": "$timerTrigger_schedule_errorText"
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "type": "eventHubTrigger",
+            "displayName": "$eventHubTrigger_displayName",
+            "direction": "trigger",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\eventHubTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "myEventHubMessage",
+                    "required": true,
+                    "label": "$eventHubTrigger_name_label",
+                    "help": "$eventHubTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "myeventhub",
+                    "required": true,
+                    "label": "$eventHubOut_path_label",
+                    "help": "$eventHubTrigger_path_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-z0-9]$|^[a-z0-9][a-z0-9-_.]{0,48}[a-z0-9]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$eventHubTrigger_path_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "consumerGroup",
+                    "value": "string",
+                    "defaultValue": "$Default",
+                    "required": false,
+                    "label": "$eventHubTrigger_consumerGroup_label",
+                    "help": "$eventHubTrigger_consumerGroup_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-z0-9]$|^[a-z0-9][a-z0-9-_.]{0,48}[a-z0-9]$)|^\\$Default$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$eventHubTrigger_consumerGroup_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "EventHub",
+                    "required": true,
+                    "label": "$eventHubTrigger_connection_label",
+                    "help": "$eventHubTrigger_connection_help",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "eventHub",
+            "displayName": "$eventHubOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "actions": [
+                {
+                    "template": "EventHubTrigger",
+                    "binding": "eventHubTrigger",
+                    "settings": [
+                        "path",
+                        "connection"
+                    ]
+                }
+            ],
+            "documentation": "$content=Documentation\\eventHubOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputEventHubMessage",
+                    "required": true,
+                    "label": "$eventHubOut_name_label",
+                    "help": "$eventHubOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "outeventhub",
+                    "required": true,
+                    "label": "$eventHubOut_path_label",
+                    "help": "$eventHubOut_path_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-z0-9]$|^[a-z0-9][a-z0-9-_.]{0,48}[a-z0-9]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$eventHubOut_path_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "EventHub",
+                    "required": true,
+                    "label": "$eventHubOut_connection_label",
+                    "help": "$eventHubOut_connection_help",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "queue",
+            "displayName": "$queueOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": true,
+            "actions": [
+                {
+                    "template": "QueueTrigger",
+                    "binding": "queueTrigger",
+                    "settings": [
+                        "queueName",
+                        "connection"
+                    ]
+                }
+            ],
+            "documentation": "$content=Documentation\\queueOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputQueueItem",
+                    "required": true,
+                    "label": "$queueOut_name_label",
+                    "help": "$queueOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "queueName",
+                    "value": "string",
+                    "defaultValue": "outqueue",
+                    "required": true,
+                    "label": "$queueOut_queueName_label",
+                    "help": "$queueOut_queueName_help",
+                    "validators": [
+                        {
+                            "expression": "^[0-9a-z][a-z0-9-]{1,61}[0-9a-z]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$queueOut_queueName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "Storage",
+                    "required": true,
+                    "label": "[variables('storageConnStringLabel')]",
+                    "help": "[variables('appSettingsHelp')]",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "queueTrigger",
+            "displayName": "$queueTrigger_displayName",
+            "direction": "trigger",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\queueTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "myQueueItem",
+                    "required": true,
+                    "label": "$queueTrigger_name_label",
+                    "help": "$queueTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "queueName",
+                    "value": "string",
+                    "defaultValue": "myqueue",
+                    "required": true,
+                    "label": "$queueTrigger_queueName_label",
+                    "help": "$queueTrigger_queueName_help",
+                    "validators": [
+                        {
+                            "expression": "^[0-9a-z][a-z0-9-]{1,61}[0-9a-z]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$queueTrigger_queueName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "Storage",
+                    "required": true,
+                    "label": "[variables('storageConnStringLabel')]",
+                    "help": "[variables('appSettingsHelp')]",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "blob",
+            "displayName": "$blobOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": true,
+            "actions": [
+                {
+                    "template": "BlobTrigger",
+                    "binding": "blobTrigger",
+                    "settings": [
+                        "path",
+                        "connection"
+                    ]
+                }
+            ],
+            "documentation": "$content=Documentation\\blobOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputBlob",
+                    "required": true,
+                    "label": "$blobOut_name_label",
+                    "help": "$blobOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "outcontainer/{rand-guid}",
+                    "required": true,
+                    "label": "$blobOut_path_label",
+                    "help": "$blobOut_path_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-z0-9{](?:[a-z0-9{}]|(?:\\-(?!\\-))){1,61}[a-z0-9{}][\\/](\\S){0,1023}[^\\/]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$blobOut_path_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "Storage",
+                    "required": true,
+                    "label": "[variables('storageConnStringLabel')]",
+                    "help": "[variables('appSettingsHelp')]",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "blob",
+            "displayName": "$blobIn_displayName",
+            "direction": "in",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\blobIn.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "inputBlob",
+                    "required": true,
+                    "label": "$blobIn_name_label",
+                    "help": "$blobIn_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "incontainer/{name}",
+                    "required": true,
+                    "label": "$blobIn_path_label",
+                    "help": "$blobIn_path_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-z0-9{](?:[a-z0-9{}]|(?:\\-(?!\\-))){1,61}[a-z0-9{}][\\/](\\S){0,1023}[^\\/]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$blobIn_patherrorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "Storage",
+                    "required": true,
+                    "label": "[variables('storageConnStringLabel')]",
+                    "help": "[variables('appSettingsHelp')]",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "blobTrigger",
+            "displayName": "$blobTrigger_displayName",
+            "direction": "trigger",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\blobTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "myBlob",
+                    "required": true,
+                    "label": "$blobTrigger_name_label",
+                    "help": "$blobTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "mycontainer",
+                    "required": true,
+                    "label": "$blobTrigger_path_label",
+                    "help": "$blobTrigger_path_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-z0-9{](?:[a-z0-9{}]|(?:\\-(?!\\-))){1,61}[a-z0-9{}]$)|(^[a-z0-9{](?:[a-z0-9{}]|(?:\\-(?!\\-))){1,61}[a-z0-9{}][\\/](\\S){0,1023}[^\\/]$)|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$blobTrigger_path_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "Storage",
+                    "required": true,
+                    "label": "[variables('storageConnStringLabel')]",
+                    "help": "[variables('appSettingsHelp')]",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "apiHubFile",
+            "displayName": "$apiHubFileIn_displayName",
+            "direction": "in",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\apiHubFileIn.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "inputFile",
+                    "required": true,
+                    "label": "$apiHubFileIn_name_label",
+                    "help": "$apiHubFileIn_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "path/{file}",
+                    "required": true,
+                    "label": "$apiHubFileIn_path_label",
+                    "help": "$apiHubFileIn_path_help",
+                    "validators": []
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "ApiHub",
+                    "required": true,
+                    "label": "$apiHubFileIn_connection_label",
+                    "help": "$apiHubFileIn_connection_help",
+                    "metadata": {
+                        "capability": "blob"
+                    }
+                }
+            ]
+        },
+        {
+            "type": "apiHubFile",
+            "displayName": "$apiHubFileIn_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\apiHubFileOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputFile",
+                    "required": true,
+                    "label": "$apiHubFileIn_name_label",
+                    "help": "$apiHubFileIn_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "path/{file}",
+                    "required": true,
+                    "label": "$apiHubFileIn_path_label",
+                    "help": "$apiHubFileIn_path_help",
+                    "validators": []
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "ApiHub",
+                    "required": true,
+                    "label": "$apiHubFileIn_connection_label",
+                    "help": "$apiHubFileIn_connection_help",
+                    "metadata": {
+                        "capability": "blob"
+                    }
+                }
+            ]
+        },
+        {
+            "type": "apiHubFileTrigger",
+            "displayName": "$apiHubFileTrigger_displayName",
+            "direction": "trigger",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\apiHubFileTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "inputFile",
+                    "required": true,
+                    "label": "$apiHubFileTrigger_name_label",
+                    "help": "$apiHubFileTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "path",
+                    "value": "string",
+                    "defaultValue": "path/{file}",
+                    "required": true,
+                    "label": "$apiHubFileTrigger_path_label",
+                    "help": "$apiHubFileTrigger_path_help",
+                    "validators": []
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "ApiHub",
+                    "required": true,
+                    "label": "$apiHubFileTrigger_connection_label",
+                    "help": "$apiHubFileTrigger_connection_help",
+                    "metadata": {
+                        "capability": "blob",
+                        "excluded": [
+                            "googledrive",
+                            "azureblob"
+                        ]
+                    }
+                }
+            ]
+        },
+        {
+            "type": "apiHubTable",
+            "displayName": "$apiHubTableIn_displayName",
+            "direction": "in",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\apiHubTable.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "inputTable",
+                    "required": true,
+                    "label": "[variables('paramNameLabel')]",
+                    "help": "[variables('paramNameInputHelp')]",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "dataSetName",
+                    "value": "string",
+                    "defaultValue": "default",
+                    "required": false,
+                    "label": "[variables('apiHubTableDataSetLabel')]",
+                    "help": "[variables('apiHubTableDataSetHelp')]",
+                    "validators": []
+                },
+                {
+                    "name": "tableName",
+                    "value": "string",
+                    "defaultValue": null,
+                    "required": false,
+                    "label": "[variables('apiHubTableNameLabel')]",
+                    "help": "[variables('apiHubTableHelp')]",
+                    "validators": []
+                },
+                {
+                    "name": "entityId",
+                    "value": "string",
+                    "defaultValue": null,
+                    "required": false,
+                    "label": "[variables('apiHubTableEntityLabel')]",
+                    "help": "[variables('apiHubTableEntityHelp')]",
+                    "validators": []
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "ApiHub",
+                    "required": true,
+                    "label": "[variables('apiHubTableConnectionLabel')]",
+                    "help": "[variables('apiHubTableConnectionHelp')]",
+                    "metadata": {
+                        "capability": "tabular"
+                    }
+                }
+            ]
+        },
+        {
+            "type": "apiHubTable",
+            "displayName": "$apiHubTableOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\apiHubTableOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputTable",
+                    "required": true,
+                    "label": "[variables('paramNameLabel')]",
+                    "help": "[variables('paramNameOutputHelp')]",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "dataSetName",
+                    "value": "string",
+                    "defaultValue": "default",
+                    "required": false,
+                    "label": "[variables('apiHubTableDataSetLabel')]",
+                    "help": "[variables('apiHubTableDataSetHelp')]",
+                    "validators": []
+                },
+                {
+                    "name": "tableName",
+                    "value": "string",
+                    "defaultValue": null,
+                    "required": false,
+                    "label": "[variables('apiHubTableNameLabel')]",
+                    "help": "[variables('apiHubTableHelp')]",
+                    "validators": []
+                },
+                {
+                    "name": "entityId",
+                    "value": "string",
+                    "defaultValue": null,
+                    "required": false,
+                    "label": "[variables('apiHubTableEntityLabel')]",
+                    "help": "[variables('apiHubTableEntityHelp')]",
+                    "validators": []
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "ApiHub",
+                    "required": true,
+                    "label": "[variables('apiHubTableConnectionLabel')]",
+                    "help": "[variables('apiHubTableConnectionHelp')]",
+                    "metadata": {
+                        "capability": "tabular"
+                    }
+                }
+            ]
+        },
+        {
+            "type": "httpTrigger",
+            "displayName": "$httpTrigger_displayName",
+            "direction": "trigger",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\httpTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "req",
+                    "required": true,
+                    "label": "$httpTrigger_name_label",
+                    "help": "$httpTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "route",
+                    "value": "string",
+                    "required": false,
+                    "label": "$httpTrigger_route_label",
+                    "help": "$httpTrigger_route_help",
+                    "validators": []
+                },
+                {
+                    "name": "webHookType",
+                    "value": "enum",
+                    "enum": [
+                        {
+                            "value": "github",
+                            "display": "GitHub"
+                        },
+                        {
+                            "value": "genericJson",
+                            "display": "Generic JSON"
+                        },
+                        {
+                            "value":"slack",
+                            "display":"Slack"
+                        }
+                    ],
+                    "label": "$httpTrigger_webHookType_label",
+                    "help": "$httpTrigger_webHookType_help"
+                },
+                {
+                    "name": "authLevel",
+                    "value": "enum",
+                    "enum": [
+                        {
+                            "value": "function",
+                            "display": "Function"
+                        },
+                        {
+                            "value": "anonymous",
+                            "display": "Anonymous"
+                        },
+                        {
+                            "value": "admin",
+                            "display": "Admin"
+                        }
+                    ],
+                    "label": "$httpTrigger_authLevel_label",
+                    "help": "$httpTrigger_authLevel_help"
+                },
+                {
+                    "name": "methods",
+                    "value": "checkBoxList",
+                    "defaultValue": [
+                        "get",
+                        "post",
+                        "delete",
+                        "head",
+                        "patch",
+                        "put",
+                        "options",
+                        "trace"
+                    ],
+                    "enum": [
+                        {
+                            "value": "get",
+                            "display": "GET"
+                        },
+                        {
+                            "value": "post",
+                            "display": "POST"
+                        },
+                        {
+                            "value": "delete",
+                            "display": "DELETE"
+                        },
+                        {
+                            "value": "head",
+                            "display": "HEAD"
+                        },
+                        {
+                            "value": "patch",
+                            "display": "PATCH"
+                        },
+                        {
+                            "value": "put",
+                            "display": "PUT"
+                        },
+                        {
+                            "value": "options",
+                            "display": "OPTIONS"
+                        },
+                        {
+                            "value": "trace",
+                            "display": "TRACE"
+                        }
+                    ],
+                    "label": "$httpTrigger_methods_label",
+                    "help": "$httpTrigger_methods_help"
+                }
+            ],
+            "rules": [
+                {
+                    "name": "mode",
+                    "type": "exclusivity",
+                    "values": [
+                        {
+                            "value": "authLevel",
+                            "display": "Standard",
+                            "hiddenSettings": [
+                                "webHookType"
+                            ],
+                            "shownSettings": [
+                                "authLevel"
+                            ]
+                        },
+                        {
+                            "value": "webHookType",
+                            "display": "Webhook",
+                            "hiddenSettings": [
+                                "authLevel"
+                            ],
+                            "shownSettings": [
+                                "webHookType"
+                            ]
+                        }
+                    ],
+                    "label": "$httpTrigger_mode_label",
+                    "help": "$httpTrigger_mode_help"
+                },
+                {
+                    "name": "methodRule",
+                    "type": "exclusivity",
+                    "values": [
+                        {
+                            "value": "allMethods",
+                            "display": "All methods",
+                            "hiddenSettings": [
+                                "methods"
+                            ],
+                            "shownSettings": []
+                        },
+                        {
+                            "value": "methods",
+                            "display": "Selected methods",
+                            "hiddenSettings": [],
+                            "shownSettings": [
+                                "methods"
+                            ]
+                        }
+                    ],
+                    "label": "$httpTrigger_methodRule_label",
+                    "help": "$httpTrigger_methodRule_help"
+                }
+            ]
+        },
+        {
+            "type": "http",
+            "displayName": "$httpOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\httpOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "res",
+                    "required": true,
+                    "label": "$httpOut_name_label",
+                    "help": "$httpOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "type": "serviceBusTrigger",
+            "displayName": "$serviceBusTrigger_displayName",
+            "direction": "trigger",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\serviceBusTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "mySbMsg",
+                    "required": true,
+                    "label": "$serviceBusTrigger_name_label",
+                    "help": "$serviceBusTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "queueName",
+                    "value": "string",
+                    "defaultValue": "mysbqueue",
+                    "required": true,
+                    "label": "$serviceBusTrigger_queueName_label",
+                    "help": "$serviceBusTrigger_queueName_help",
+                    "validators": [
+                        {
+                            "expression": "^[0-9a-z][a-z0-9_.-]{1,48}[0-9a-z]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$serviceBusTrigger_queueName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "topicName",
+                    "value": "string",
+                    "defaultValue": "mysbtopic",
+                    "required": true,
+                    "label": "$serviceBusTrigger_topicName_label",
+                    "help": "$serviceBusTrigger_topicName_help",
+                    "validators": [
+                        {
+                            "expression": "^[0-9a-z][a-z0-9_.-]{1,48}[0-9a-z]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$serviceBusTrigger_topicName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "subscriptionName",
+                    "value": "string",
+                    "defaultValue": "mysubscription",
+                    "required": true,
+                    "label": "$serviceBusTrigger_subscriptionName_label",
+                    "help": "$serviceBusTrigger_subscriptionName_help",
+                    "validators": [
+                        {
+                            "expression": "^[0-9a-zA-Z][a-zA-Z0-9_.-]{1,48}[0-9a-zA-Z]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$serviceBusTrigger_subscriptionName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "EventHub",
+                    "required": true,
+                    "label": "$serviceBusTrigger_connection_label",
+                    "help": "$serviceBusTrigger_connection_help",
+                    "placeholder": "[variables('selectConnection')]"
+                },
+                {
+                    "name": "accessRights",
+                    "value": "enum",
+                    "enum": [
+                        {
+                            "value": "Manage",
+                            "display": "Manage"
+                        },
+                        {
+                            "value": "Listen",
+                            "display": "Listen"
+                        }
+                    ],
+                    "label": "$serviceBusTrigger_accessRights_label",
+                    "help": "$serviceBusTrigger_accessRights_help"
+                }
+            ],
+            "rules": [
+                {
+                    "name": "messageType",
+                    "type": "exclusivity",
+                    "values": [
+                        {
+                            "value": "queueName",
+                            "display": "$serviceBusTrigger_messageType_queueName",
+                            "hiddenSettings": [
+                                "topicName",
+                                "subscriptionName"
+                            ],
+                            "shownSettings": [
+                                "queueName"
+                            ]
+                        },
+                        {
+                            "value": "topicName",
+                            "display": "$serviceBusTrigger_messageType_topicName",
+                            "hiddenSettings": [
+                                "queueName"
+                            ],
+                            "shownSettings": [
+                                "topicName",
+                                "subscriptionName"
+                            ]
+                        }
+                    ],
+                    "label": "$serviceBusTrigger_messageType_label",
+                    "help": "$serviceBusTrigger_messageType_help"
+                }
+            ]
+        },
+        {
+            "type": "serviceBus",
+            "displayName": "$serviceBusOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\serviceBusOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputSbMsg",
+                    "required": true,
+                    "label": "$serviceBusOut_name_label",
+                    "help": "$serviceBusOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "queueName",
+                    "value": "string",
+                    "defaultValue": "outqueue",
+                    "required": true,
+                    "label": "$serviceBusOut_queueName_label",
+                    "help": "$serviceBusOut_queueName_help",
+                    "validators": [
+                        {
+                            "expression": "^[0-9a-z][a-z0-9_.-]{1,48}[0-9a-z]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$serviceBusOut_queueName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "topicName",
+                    "value": "string",
+                    "defaultValue": "outtopic",
+                    "required": true,
+                    "label": "$serviceBusOut_topicName_label",
+                    "help": "$serviceBusOut_topicName_help",
+                    "validators": [
+                        {
+                            "expression": "^[0-9a-z][a-z0-9_.-]{1,48}[0-9a-z]$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$serviceBusOut_topicName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "EventHub",
+                    "required": true,
+                    "label": "$serviceBusOut_connection_label",
+                    "help": "$serviceBusOut_connection_help",
+                    "placeholder": "[variables('selectConnection')]"
+                },
+                {
+                    "name": "accessRights_",
+                    "value": "enum",
+                    "enum": [
+                        {
+                            "value": "Manage",
+                            "display": "Manage"
+                        },
+                        {
+                            "value": "Send",
+                            "display": "Send"
+                        }
+                    ],
+                    "label": "$serviceBusOut_accessRights_label",
+                    "help": "$serviceBusOut_accessRights_help"
+                }
+            ],
+            "rules": [
+                {
+                    "name": "messageType",
+                    "type": "exclusivity",
+                    "values": [
+                        {
+                            "value": "queueName",
+                            "display": "$serviceBusOut_messageType_queueName",
+                            "hiddenSettings": [
+                                "topicName"
+                            ],
+                            "shownSettings": [
+                                "queueName"
+                            ]
+                        },
+                        {
+                            "value": "topicName",
+                            "display": "$serviceBusOut_messageType_topicName",
+                            "hiddenSettings": [
+                                "queueName"
+                            ],
+                            "shownSettings": [
+                                "topicName"
+                            ]
+                        }
+                    ],
+                    "label": "$serviceBusOut_messageType_label",
+                    "help": "$serviceBusOut_messageType_help"
+                }
+            ]
+        },
+        {
+            "type": "manualTrigger",
+            "displayName": "Manual",
+            "direction": "trigger",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\manualTrigger.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "input",
+                    "required": true,
+                    "label": "$manualTrigger_name_label",
+                    "help": "$manualTrigger_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "type": "table",
+            "displayName": "$tableout_displayName",
+            "direction": "out",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\tableOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputTable",
+                    "required": true,
+                    "label": "$tableout_name_label",
+                    "help": "$tableout_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "tableName",
+                    "value": "string",
+                    "defaultValue": "outTable",
+                    "required": true,
+                    "label": "$table_tableName_label",
+                    "help": "$table_tableName_help",
+                    "validators": [
+                        {
+                            "expression": "^[A-Za-z][A-Za-z0-9]{2,62}$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$table_tableName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "Storage",
+                    "required": true,
+                    "label": "[variables('storageConnStringLabel')]",
+                    "help": "[variables('appSettingsHelp')]",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "table",
+            "displayName": "$tableIn_displayName",
+            "direction": "in",
+            "enabledInTryMode": true,
+            "documentation": "$content=Documentation\\tableIn.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "inputTable",
+                    "required": true,
+                    "label": "$tableIn_name_label",
+                    "help": "$tableIn_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "tableName",
+                    "value": "string",
+                    "defaultValue": "inTable",
+                    "required": true,
+                    "label": "$tableIn_tableName_label",
+                    "help": "$tableIn_tableName_help",
+                    "validators": [
+                        {
+                            "expression": "^[A-Za-z][A-Za-z0-9]{2,62}$|^[{][a-zA-Z0-9]{1,126}[}]$|^[%][a-zA-Z0-9]{1,126}[%]$",
+                            "errorText": "$tableIn_tableName_errorText"
+                        }
+                    ]
+                },
+                {
+                    "name": "partitionKey",
+                    "value": "string",
+                    "required": false,
+                    "label": "$tableIn_partitionKey_label",
+                    "help": "$tableIn_partitionKey_help"
+                },
+                {
+                    "name": "rowKey",
+                    "value": "string",
+                    "required": false,
+                    "label": "$tableIn_rowKey_label",
+                    "help": "$tableIn_rowKey_help"
+                },
+                {
+                    "name": "take",
+                    "value": "int",
+                    "defaultValue": 50,
+                    "required": false,
+                    "label": "$tableIn_take_label",
+                    "help": "$tableIn_take_help"
+                },
+                {
+                    "name": "filter",
+                    "value": "string",
+                    "required": false,
+                    "label": "$tableIn_filter_label",
+                    "help": "$tableIn_filter_help"
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "Storage",
+                    "required": true,
+                    "label": "[variables('storageConnStringLabel')]",
+                    "help": "[variables('appSettingsHelp')]",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "documentDB",
+            "displayName": "$documentDB_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\documentDBOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputDocument",
+                    "required": true,
+                    "label": "$documentDBOut_name_label",
+                    "help": "$documentDBOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "databaseName",
+                    "value": "string",
+                    "defaultValue": "outDatabase",
+                    "required": true,
+                    "label": "$documentDBOut_databaseName_label",
+                    "help": "$documentDBOut_databaseName_help"
+                },
+                {
+                    "name": "collectionName",
+                    "value": "string",
+                    "defaultValue": "MyCollection",
+                    "required": true,
+                    "label": "$documentDBOut_collectionName_label",
+                    "help": "$documentDBOut_collectionName_help"
+                },
+                {
+                    "name": "createIfNotExists",
+                    "value": "boolean",
+                    "defaultValue": false,
+                    "required": true,
+                    "label": "$documentDBOut_createIfNotExists_label",
+                    "help": "$documentDBOut_createIfNotExists_help"
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "DocumentDB",
+                    "required": true,
+                    "label": "$documentDBOut_connection_label",
+                    "help": "$documentDBOut_connection_help",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "documentDB",
+            "displayName": "$documentDBIn_displayName",
+            "direction": "in",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\documentDBIn.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "inputDocument",
+                    "required": true,
+                    "label": "$documentDBIn_name_label",
+                    "help": "$documentDBIn_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "databaseName",
+                    "value": "string",
+                    "defaultValue": "inDatabase",
+                    "required": true,
+                    "label": "$documentDBIn_databaseName_label",
+                    "help": "$documentDBIn_databaseName_help"
+                },
+                {
+                    "name": "collectionName",
+                    "value": "string",
+                    "defaultValue": "MyCollection",
+                    "required": true,
+                    "label": "$documentDBIn_collectionName_label",
+                    "help": "$documentDBIn_collectionName_help"
+                },
+                {
+                    "name": "id",
+                    "value": "string",
+                    "defaultValue": "{documentId}",
+                    "required": true,
+                    "label": "$documentDBIn_id_label",
+                    "help": "$documentDBIn_id_help"
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "DocumentDB",
+                    "required": true,
+                    "label": "$documentDBIn_connection_label",
+                    "help": "$documentDBIn_connection_help",
+                    "placeholder": "[variables('selectConnection')]"
+                }
+            ]
+        },
+        {
+            "type": "mobileTable",
+            "displayName": "$mobileTableOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\mobileTableOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "outputRecord",
+                    "required": true,
+                    "label": "$mobileTableOut_name_label",
+                    "help": "$mobileTableOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "tableName",
+                    "value": "string",
+                    "defaultValue": "outTable",
+                    "required": true,
+                    "label": "$mobileTableOut_tableName_label",
+                    "help": "$mobileTableOut_tableName_help"
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "defaultValue": "My_MobileApp_Uri",
+                    "required": true,
+                    "label": "$mobileTableOut_connection_label",
+                    "help": "$mobileTableOut_connection_help"
+                },
+                {
+                    "name": "apiKey",
+                    "value": "string",
+                    "required": false,
+                    "label": "$mobileTableOut_apiKey_label",
+                    "help": "$mobileTableOut_apiKey_help"
+                }
+            ]
+        },
+        {
+            "type": "mobileTable",
+            "displayName": "$mobileTableIn_displayName",
+            "direction": "in",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\mobileTableIn.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "inputRecord",
+                    "required": true,
+                    "label": "$mobileTableIn_name_label",
+                    "help": "$mobileTableIn_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "tableName",
+                    "value": "string",
+                    "defaultValue": "inTable",
+                    "required": true,
+                    "label": "$mobileTableIn_tableName_label",
+                    "help": "$mobileTableIn_tableName_help"
+                },
+                {
+                    "name": "id",
+                    "value": "string",
+                    "defaultValue": "{itemId}",
+                    "required": true,
+                    "label": "$mobileTableIn_id_label",
+                    "help": "$mobileTableIn_id_help"
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "defaultValue": "My_MobileApp_Uri",
+                    "required": true,
+                    "label": "$mobileTableIn_connection_label",
+                    "help": "$mobileTableIn_connection_help"
+                },
+                {
+                    "name": "apiKey",
+                    "value": "string",
+                    "required": false,
+                    "label": "$mobileTableIn_apiKey_label",
+                    "help": "$mobileTableIn_apiKey_help"
+                }
+            ]
+        },
+        {
+            "type": "notificationHub",
+            "displayName": "$notificationHubOut_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\notificationHubOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "notification",
+                    "required": true,
+                    "label": "$notificationHubOut_name_label",
+                    "help": "$notificationHubOut_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "hubName",
+                    "value": "string",
+                    "required": true,
+                    "label": "$notificationHubOut_hubName_label",
+                    "help": "$notificationHubOut_hubName_help"
+                },
+                {
+                    "name": "connection",
+                    "value": "string",
+                    "resource": "ServiceBus",
+                    "required": true,
+                    "label": "$notificationHubOut_connection_label",
+                    "help": "$notificationHubOut_connection_help",
+                    "placeholder": "[variables('selectConnection')]"
+                },
+                {
+                    "name": "tagExpression",
+                    "value": "string",
+                    "required": false,
+                    "label": "$notificationHubOut_tagExpression_label",
+                    "help": "$notificationHubOut_tagExpression_help"
+                },
+                {
+                    "name": "enableTestSend",
+                    "value": "boolean",
+                    "required": false,
+                    "label": "$notificationHubOut_enableTestSend_label",
+                    "help": "$notificationHubOut_enableTestSend_help"
+                },
+                {
+                    "name": "platform",
+                    "value": "enum",
+                    "enum": [
+                        {
+                            "value": "",
+                            "display": "Template"
+                        },
+                        {
+                            "value": "apns",
+                            "display": "Apple (APNS)"
+                        },
+                        {
+                            "value": "adm",
+                            "display": "Amazon (ADM)"
+                        },
+                        {
+                            "value": "gcm",
+                            "display": "Google (GCM)"
+                        },
+                        {
+                            "value": "wns",
+                            "display": "Windows (WNS)"
+                        },
+                        {
+                            "value": "mpns",
+                            "display": "Windows Phone (MPNS)"
+                        }
+                    ],
+                    "label": "$notificationHubOut_platform_label",
+                    "help": "$notificationHubOut_platform_help"
+                }
+            ]
+        },
+        {
+            "type": "sendGrid",
+            "displayName": "$sendGrid_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\sendGridOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "message",
+                    "required": true,
+                    "label": "$sendGrid_name_label",
+                    "help": "$sendGrid_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "apiKey",
+                    "value": "string",
+                    "defaultValue": "SendGridApiKey",
+                    "required": true,
+                    "label": "$sendGrid_apiKey_label",
+                    "help": "$sendGrid_apiKey_help"
+                },
+                {
+                    "name": "to",
+                    "value": "string",
+                    "defaultValue": "",
+                    "required": false,
+                    "label": "$sendGrid_to_label",
+                    "help": "$sendGrid_to_help"
+                },
+                {
+                    "name": "from",
+                    "value": "string",
+                    "defaultValue": "",
+                    "required": false,
+                    "label": "$sendGrid_from_label",
+                    "help": "$sendGrid_from_help"
+                },
+                {
+                    "name": "subject",
+                    "value": "string",
+                    "defaultValue": "",
+                    "required": false,
+                    "label": "$sendGrid_subject_label",
+                    "help": "$sendGrid_subject_help"
+                },
+                {
+                    "name": "text",
+                    "value": "string",
+                    "defaultValue": "",
+                    "required": false,
+                    "label": "$sendGrid_text_label",
+                    "help": "$sendGrid_text_help"
+                }
+            ]
+        },
+        {
+            "type": "twilioSms",
+            "displayName": "$twilioSms_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\twilioSmsOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "message",
+                    "required": true,
+                    "label": "$twilioSms_name_label",
+                    "help": "$twilioSms_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "accountSid",
+                    "value": "string",
+                    "defaultValue": "TwilioAccountSid",
+                    "required": true,
+                    "label": "$twilioSms_accountsid_label",
+                    "help": "$twilioSms_accountsid_help"
+                },
+                {
+                    "name": "authToken",
+                    "value": "string",
+                    "defaultValue": "TwilioAuthToken",
+                    "required": true,
+                    "label": "$twilioSms_authtoken_label",
+                    "help": "$twilioSms_authtoken_help"
+                },
+                {
+                    "name": "to",
+                    "value": "string",
+                    "defaultValue": "",
+                    "required": false,
+                    "label": "$twilioSms_to_label",
+                    "help": "$twilioSms_to_help"
+                },
+                {
+                    "name": "from",
+                    "value": "string",
+                    "defaultValue": "",
+                    "required": false,
+                    "label": "$twilioSms_from_label",
+                    "help": "$twilioSms_from_help"
+                },
+                {
+                    "name": "body",
+                    "value": "string",
+                    "defaultValue": "",
+                    "required": false,
+                    "label": "$twilioSms_body_label",
+                    "help": "$twilioSms_body_help"
+                }
+            ]
+        },
+        {
+            "type": "bot",
+            "displayName": "$bot_out_displayName",
+            "direction": "out",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\botOut.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "$return",
+                    "required": true,
+                    "label": "$bot_out_name_label",
+                    "help": "$bot_out_name_help",
+                    "validators": [
+                        {
+                            "expression": "(^[a-zA-Z][a-zA-Z0-9]{0,127}$)|^\\$return$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "botId",
+                    "value": "string",
+                    "required": true,
+                    "label": "$bot_botId_label",
+                    "help": "$bot_botId_help"
+                },
+                {
+                    "name": "secret",
+                    "value": "string",
+                    "required": false,
+                    "label": "$bot_out_secret_label",
+                    "help": "$bot_out_secret_help"
+                }
+            ]
+        },
+        {
+            "type": "bot",
+            "displayName": "$bot_in_displayName",
+            "direction": "in",
+            "enabledInTryMode": false,
+            "documentation": "$content=Documentation\\botIn.md",
+            "settings": [
+                {
+                    "name": "name",
+                    "value": "string",
+                    "defaultValue": "bot",
+                    "required": true,
+                    "label": "$bot_in_name_label",
+                    "help": "$bot_in_name_help",
+                    "validators": [
+                        {
+                            "expression": "^[a-zA-Z][a-zA-Z0-9]{0,127}$",
+                            "errorText": "[variables('parameterName')]"
+                        }
+                    ]
+                },
+                {
+                    "name": "secret",
+                    "value": "string",
+                    "required": false,
+                    "label": "$bot_in_secret_label",
+                    "help": "$bot_in_secret_help"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/shared/getAdminKey.js b/shared/getAdminKey.js
new file mode 100644
index 00000000..74ca01d0
--- /dev/null
+++ b/shared/getAdminKey.js
@@ -0,0 +1,7 @@
+'use strict';
+
+module.exports = {
+  getAdminKey () {
+    return this.provider.getAdminKey();
+  }
+};
diff --git a/shared/loginToAzure.js b/shared/loginToAzure.js
new file mode 100644
index 00000000..c23cccc9
--- /dev/null
+++ b/shared/loginToAzure.js
@@ -0,0 +1,9 @@
+'use strict';
+
+module.exports = {
+  loginToAzure () {
+    this.serverless.cli.log('Logging in to Azure');
+
+return this.provider.LoginWithServicePrincipal();
+  }
+};
diff --git a/shared/parseBindings.js b/shared/parseBindings.js
new file mode 100644
index 00000000..f2e35337
--- /dev/null
+++ b/shared/parseBindings.js
@@ -0,0 +1,43 @@
+'use strict';
+
+const path = require('path');
+
+const bindingsJson = require(path.join(__dirname, 'bindings.json'));
+
+const constants = {
+  'bindings': 'bindings',
+  'settings': 'settings',
+  'name': 'name',
+  'displayName': 'displayName',
+  'type': 'type'
+};
+
+module.exports = {
+  'getBindingsMetaData': function (serverless) {
+    serverless.cli.log('Parsing Azure Functions Bindings.json...');
+    const bindingDisplayNames = [];
+    const bindingTypes = [];
+    const bindingSettings = [];
+    const bindingSettingsNames = [];
+
+    for (let bindingsIndex = 0; bindingsIndex < bindingsJson[constants.bindings].length; bindingsIndex++) {
+      const settingsNames = [];
+
+      bindingTypes.push(bindingsJson[constants.bindings][bindingsIndex][constants.type]);
+      bindingDisplayNames.push(bindingsJson[constants.bindings][bindingsIndex][constants.displayName].toLowerCase());
+      bindingSettings[bindingsIndex] = bindingsJson[constants.bindings][bindingsIndex][constants.settings];
+      for (let bindingSettingsIndex = 0; bindingSettingsIndex < bindingSettings[bindingsIndex].length; bindingSettingsIndex++) {
+        settingsNames.push(bindingSettings[bindingsIndex][bindingSettingsIndex][constants.name]);
+      }
+      bindingSettingsNames[bindingsIndex] = settingsNames;
+    }
+    const parsedBindings = {
+      'bindingDisplayNames': bindingDisplayNames,
+      'bindingTypes': bindingTypes,
+      'bindingSettings': bindingSettings,
+      'bindingSettingsNames': bindingSettingsNames
+    };
+
+return parsedBindings;
+  }
+};
diff --git a/shared/utils.js b/shared/utils.js
new file mode 100644
index 00000000..a87cc3ca
--- /dev/null
+++ b/shared/utils.js
@@ -0,0 +1,204 @@
+'use strict';
+
+const constants = {
+  'type': 'type',
+  'direction': 'direction',
+  'trigger': 'Trigger',
+  'inDirection': 'in',
+  'outDirection': 'out',
+  'settings': 'settings',
+  'name': 'name',
+  'value': 'value',
+  'resource': 'resource',
+  'required': 'required',
+  'storage': 'storage',
+  'connection': 'connection',
+  'enum': 'enum',
+  'defaultValue': 'defaultValue',
+  'webHookType': 'webHookType',
+  'httpTrigger': 'httpTrigger',
+  'queue': 'queue',
+  'queueName': 'queueName',
+  'displayName': 'displayName',
+  'xAzureSettings': 'x-azure-settings',
+  'entryPoint': 'entryPoint'
+};
+
+module.exports = {
+  'getFunctionMetaData': function (functionName, parsedBindings, serverless) {
+    const bindings = [];
+    let bindingSettingsNames = [];
+    let bindingSettings = [];
+    let bindingUserSettings = {};
+    let bindingType;
+    const functionsJson = {'disabled': false, 'bindings': []};
+    const functionObject = serverless.service.getFunction(functionName);
+    const handler = functionObject.handler;
+    const events = functionObject.events;
+    const params = {};
+
+    const bindingTypes = parsedBindings.bindingTypes;
+    const bindingDisplayNames = parsedBindings.bindingDisplayNames;
+
+    for (let eventsIndex = 0; eventsIndex < events.length; eventsIndex++) {
+      bindingType = Object.keys(functionObject.events[eventsIndex])[0];
+
+      if (eventsIndex === 0) {
+        bindingType += constants.trigger;
+      }
+
+      const index = bindingTypes.indexOf(bindingType);
+
+      if (index < 0) {
+        throw new Error(`Binding  ${bindingType} not supported`);
+      }
+
+      serverless.cli.log(`Building binding for function: ${functionName} event: ${bindingType}`);
+
+      bindingUserSettings = {};
+      const azureSettings = events[eventsIndex][constants.xAzureSettings];
+      let bindingTypeIndex = bindingTypes.indexOf(bindingType);
+      const bindingUserSettingsMetaData = this.getBindingUserSettingsMetaData(azureSettings, bindingType, bindingTypeIndex, bindingDisplayNames);
+
+      bindingTypeIndex = bindingUserSettingsMetaData.index;
+      bindingUserSettings = bindingUserSettingsMetaData.userSettings;
+
+      if (bindingType.includes(constants.queue) && functionObject.events[eventsIndex].queue) {
+        bindingUserSettings[constants.queueName] = functionObject.events[eventsIndex].queue;
+      }
+
+      if (bindingTypeIndex < 0) {
+        throw new Error('Binding not supported');
+      }
+
+      bindingSettings = parsedBindings.bindingSettings[bindingTypeIndex];
+      bindingSettingsNames = parsedBindings.bindingSettingsNames[bindingTypeIndex];
+
+      if (azureSettings) {
+        for (let azureSettingKeyIndex = 0; azureSettingKeyIndex < Object.keys(azureSettings).length; azureSettingKeyIndex++) {
+          const key = Object.keys(azureSettings)[azureSettingKeyIndex];
+
+          if (bindingSettingsNames.indexOf(key) >= 0) {
+            bindingUserSettings[key] = azureSettings[key];
+          }
+        }
+      }
+
+      bindings.push(this.getBinding(bindingType, bindingSettings, bindingUserSettings, serverless));
+    }
+
+    if (bindingType === constants.httpTrigger && bindings.length === 1) {
+      bindings.push(this.getHttpOutBinding(bindingUserSettings));
+    }
+
+    functionsJson.bindings = bindings;
+    params.functionsJson = functionsJson;
+
+    const entryPointAndHandlerPath = this.getEntryPointAndHandlerPath(handler);
+    const metaData = {
+      'entryPoint': entryPointAndHandlerPath[constants.entryPoint],
+      'handlerPath': entryPointAndHandlerPath.handlerPath,
+      'params': params
+    };
+
+return metaData;
+  },
+
+  'getBindingUserSettingsMetaData': function (azureSettings, bindingType, bindingTypeIndex, bindingDisplayNames) {
+    let bindingDisplayNamesIndex = bindingTypeIndex;
+    const bindingUserSettings = {};
+
+    if (azureSettings) {
+      const directionIndex = Object.keys(azureSettings).indexOf(constants.direction);
+
+      if (directionIndex >= 0) {
+        const key = Object.keys(azureSettings)[directionIndex];
+        const displayName = `$${bindingType}${azureSettings[key]}_displayName`;
+
+        bindingDisplayNamesIndex = bindingDisplayNames.indexOf(displayName.toLowerCase());
+        bindingUserSettings[constants.direction] = azureSettings[key];
+      }
+    }
+    const bindingUserSettingsMetaData = {
+      'index': bindingDisplayNamesIndex,
+      'userSettings': bindingUserSettings
+    };
+
+return bindingUserSettingsMetaData;
+  },
+
+  'getEntryPointAndHandlerPath': function (handler) {
+    let handlerPath = 'handler.js';
+    let entryPoint = handler;
+    const handlerSplit = handler.split('.');
+
+    if (handlerSplit.length > 1) {
+      entryPoint = handlerSplit[handlerSplit.length - 1];
+      handlerPath = `${handler.substring(0, handler.lastIndexOf('.'))}.js`;
+    }
+    const metaData = {
+      'entryPoint': entryPoint,
+      'handlerPath': handlerPath
+    };
+
+return metaData;
+  },
+
+  'getHttpOutBinding': function (bindingUserSettings) {
+    const binding = {};
+
+    binding[constants.type] = 'http';
+    binding[constants.direction] = constants.outDirection;
+    binding[constants.name] = '$return';
+    if (bindingUserSettings[constants.webHookType]) {
+      binding[constants.name] = 'res';
+    }
+
+return binding;
+  },
+
+  'getBinding': function (bindingType, bindingSettings, bindingUserSettings, serverless) {
+    const binding = {};
+
+    binding[constants.type] = bindingType;
+    if (bindingUserSettings && bindingUserSettings[constants.direction]) {
+      binding[constants.direction] = bindingUserSettings[constants.direction];
+    } else if (bindingType.includes(constants.trigger)) {
+      binding[constants.direction] = constants.inDirection;
+    } else {
+      binding[constants.direction] = constants.outDirection;
+    }
+
+    for (let bindingSettingsIndex = 0; bindingSettingsIndex < bindingSettings.length; bindingSettingsIndex++) {
+      const name = bindingSettings[bindingSettingsIndex][constants.name];
+
+      if (bindingUserSettings && bindingUserSettings[name]) {
+        binding[name] = bindingUserSettings[name];
+        continue;
+      }
+      const value = bindingSettings[bindingSettingsIndex][constants.value];
+      const required = bindingSettings[bindingSettingsIndex][constants.required];
+      const resource = bindingSettings[bindingSettingsIndex][constants.resource];
+
+      if (required) {
+        const defaultValue = bindingSettings[bindingSettingsIndex][constants.defaultValue];
+
+        if (defaultValue) {
+          binding[name] = defaultValue;
+        } else if (name === constants.connection && resource.toLowerCase() === constants.storage) {
+          binding[name] = 'AzureWebJobsStorage';
+        } else {
+          throw new Error(`Required property ${name} is missing for binding:${bindingType}`);
+        }
+      }
+
+      if (value === constants.enum && name !== constants.webHookType) {
+        const enumValues = bindingSettings[bindingSettingsIndex][constants.enum];
+
+        binding[name] = enumValues[0][constants.value];
+      }
+    }
+
+return binding;
+  }
+};

From 1d41a9e0a1bd2c48faeb61ce3b8fc09a41406f74 Mon Sep 17 00:00:00 2001
From: pragnagopa <pgopa@microsoft.com>
Date: Fri, 17 Feb 2017 09:52:28 -0800
Subject: [PATCH 2/2] Work around for kudu sync triggers issue

---
 deploy/lib/createFunction.js  |  3 ++-
 deploy/lib/createFunctions.js |  3 ++-
 provider/azureProvider.js     | 31 +++++++++++++++++++++++++++++++
 3 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/deploy/lib/createFunction.js b/deploy/lib/createFunction.js
index a0c22700..5ddd9d64 100644
--- a/deploy/lib/createFunction.js
+++ b/deploy/lib/createFunction.js
@@ -8,6 +8,7 @@ module.exports = {
     const metaData = utils.getFunctionMetaData(functionName, this.provider.getParsedBindings(), this.serverless);
 
 return this.provider.createZipObject(functionName, metaData.entryPoint, metaData.handlerPath, metaData.params)
-      .then(() => this.provider.createAndUploadZipFunctions());
+      .then(() => this.provider.createAndUploadZipFunctions())
+      .then(() => this.provider.syncTriggers());
   }
 };
diff --git a/deploy/lib/createFunctions.js b/deploy/lib/createFunctions.js
index 9247ce0d..e1960681 100644
--- a/deploy/lib/createFunctions.js
+++ b/deploy/lib/createFunctions.js
@@ -14,6 +14,7 @@ module.exports = {
     });
 
     return BbPromise.all(createFunctionPromises)
-            .then(() => this.provider.createAndUploadZipFunctions());
+            .then(() => this.provider.createAndUploadZipFunctions())
+            .then(() => this.provider.syncTriggers());
   }
 };
diff --git a/provider/azureProvider.js b/provider/azureProvider.js
index b5ed7543..8c6b9243 100644
--- a/provider/azureProvider.js
+++ b/provider/azureProvider.js
@@ -473,6 +473,33 @@ return new BbPromise((resolve, reject) => {
 
   }
 
+  syncTriggers () {
+    let options = {};
+    const requestUrl = ` https://management.azure.com/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.Web/sites/${functionAppName}/functions/synctriggers?api-version=2015-08-01`;
+    options = {
+       'host': 'management.azure.com',
+       'method': 'post',
+       'body': {},
+       'url': requestUrl,
+       'json': true,
+       'headers': {
+         'Authorization': constants.bearer + principalCredentials.tokenCache._entries[0].accessToken,
+         'Accept': 'application/json,*/*'
+       }
+     };
+
+return new BbPromise((resolve, reject) => {
+        request(options, (err, res, body) => {
+          if (err) {
+            reject(err);
+          }
+          this.serverless.cli.log(`Syncing Triggers....Response statuscode: ${res.statusCode}`);
+          resolve(res);
+        });
+      });
+
+  }
+
   cleanUpFunctionsBeforeDeploy (serverlessFunctions) {
     const deleteFunctionPromises = [];
 
@@ -516,6 +543,10 @@ return new BbPromise((resolve, reject) => {
       const folderForJSFunction = path.join(functionsFolder, functionName);
       const handlerPath = path.join(this.serverless.config.servicePath, filePath);
 
+      if (!fs.existsSync(functionsFolder)) {
+        fs.mkdirSync(functionsFolder);
+      }
+
       if (!fs.existsSync(folderForJSFunction)) {
         fs.mkdirSync(folderForJSFunction);
       }