Skip to content

Commit

Permalink
Add one-click deployment button and Bicep/ARM templates (#3)
Browse files Browse the repository at this point in the history
* First step

* Update URL

* Add Azure Gov button

* Update documentation

* Update README

* Add license
  • Loading branch information
iMicknl authored Jun 20, 2022
1 parent 663f502 commit 42d4330
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 14 deletions.
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"extensions": [
"ms-azuretools.vscode-azurefunctions",
"ms-azuretools.vscode-docker",
"ms-python.python"
"ms-python.python",
"msazurermtools.azurerm-vscode-tools",
"ms-azuretools.vscode-bicep"
],

// Use 'postCreateCommand' to run commands after the container is created.
Expand Down
44 changes: 31 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
![Deploy to Azure](https://aka.ms/deploytoazurebutton)
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)][deployment-url]
[![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)][deployment-url-gov]

[deployment-url]: https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FiMicknl%2Fazure-functions-libpostal%2Fmain%2Fdeploy%2Faz_function_deployment.json

[deployment-url-gov]: https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FiMicknl%2Fazure-functions-libpostal%2Fmain%2Fdeploy%2Faz_function_deployment.json


# azure-functions-libpostal
An Azure Function project which utilizes [libpostal](https://github.com/openvenues/libpostal), a C library for parsing and normalizing street addresses. This can be useful for matching address strings with a database, and it can be used with Azure Cognitive Search to split an address over multiple index fields.

Due to C binding required for Libpostal, we need to [create an Azure Function on Linux using a custom container](https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-function-linux-custom-image?tabs=in-process%2Cbash%2Cazure-cli&pivots=programming-language-python). This will require a Premium plan or Dedicated (App Service) plan.

## How to deploy

You can easily deploy this Azure Function in your own Azure environment, by clicking the Deploy to Azure button.

[![Deploy to Azure](https://aka.ms/deploytoazurebutton)][deployment-url] [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)][deployment-url-gov]

This will deploy an Azure Function app with a dedicated App Service plan (B3 sku) using the published [Docker container on GitHub Container Registry](https://github.com/iMicknl/azure-functions-libpostal/pkgs/container/azure-functions-libpostal). The project can easily be forked to make code changes and/or infrastructure changes.


## Endpoints
### /api/ParseAddress
### /api/ParseAddress (GET/POST)
**Input**:
```json
{
"address": "Evert van de Beekstraat 354, 1118 CZ Schiphol, Nederland",
}
```

or

```https://[APP_NAME].azurewebsites.net/api/ParseAddress?address=Evert%20van%20de%20Beekstraat%20354,%201118%20CZ%20Schiphol,%20Nederland```


**Output**:
```json
{
Expand All @@ -26,7 +45,7 @@ Due to C binding required for Libpostal, we need to [create an Azure Function on
}
```

### /api/ParseAddressCognitiveSearch
### /api/ParseAddressCognitiveSearch (POST)

**Input**:
```json
Expand Down Expand Up @@ -60,7 +79,7 @@ Due to C binding required for Libpostal, we need to [create an Azure Function on
```

**Output**:
```
```json
{
"values": [
{
Expand Down Expand Up @@ -149,7 +168,7 @@ Due to C binding required for Libpostal, we need to [create an Azure Function on
}
```

## Getting Started with Azure Function
## Contribute to this repository

#### Project Structure
The main project folder (<project_root>) can contain the following files:
Expand All @@ -168,15 +187,14 @@ Each function has its own code file and binding configuration file ([**function.

#### Developing your Python function using VS Code

This project includes a devcontainer that can be used on GitHub Codespaces or Visual Studio Code with Docker. During first build, it wil compile the Libpostal C module and download all training data.

If you have not already, please checkout our [quickstart](https://aka.ms/azure-functions/python/quickstart) to get you started with Azure Functions developments in Python.
This project includes a devcontainer that can be used on GitHub Codespaces or Visual Studio Code with Docker. During first build, it wil compile the Libpostal C module and download all training data. If you have not already, please checkout our [quickstart](https://aka.ms/azure-functions/python/quickstart) to get you started with Azure Functions developments in Python.

#### Publishing your function app to Azure

```sh
docker build --tag imicknl/azurefunctionsimage:v1.0.0 .
docker run -p 8080:80 -it imicknl/azurefunctionsimage:v1.0.0
```

For more information on deployment options for Azure Functions, please visit this [guide](https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-python#publish-the-project-to-azure).


## License

MIT

119 changes: 119 additions & 0 deletions deploy/az_function_deployment.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
@description('Project name.')
param appName string

@description('Specifies region of all resources. Keep the default if you want to use the same region as the resource group.')
param location string = resourceGroup().location

// storage accounts must be between 3 and 24 characters in length and use numbers and lower-case letters only
var storageAccountName = '${substring(appName,0,10)}${uniqueString(resourceGroup().id)}'
var hostingPlanName = '${appName}${uniqueString(resourceGroup().id)}'
var appInsightsName = '${appName}${uniqueString(resourceGroup().id)}'
var functionAppName = appName

resource appServicePlan 'Microsoft.Web/serverFarms@2020-06-01' = {
name: hostingPlanName
location: location
sku: {
name: 'B3'
}
kind: 'linux'
properties: {
reserved: true
}
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
name: storageAccountName
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
properties: {
supportsHttpsTrafficOnly: true
encryption: {
services: {
file: {
keyType: 'Account'
enabled: true
}
blob: {
keyType: 'Account'
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
accessTier: 'Hot'
}
}


resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
publicNetworkAccessForIngestion: 'Enabled'
publicNetworkAccessForQuery: 'Enabled'
}
tags: {
// circular dependency means we can't reference functionApp directly /subscriptions/<subscriptionId>/resourceGroups/<rg-name>/providers/Microsoft.Web/sites/<appName>"
'hidden-link:/subscriptions/${subscription().id}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Web/sites/${functionAppName}': 'Resource'
}
}


resource azureFunction 'Microsoft.Web/sites@2020-12-01' = {
name: functionAppName
location: location
kind: 'functionapp'
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: 'DOCKER|ghcr.io/imicknl/azure-functions-libpostal:latest'
alwaysOn: true
ftpsState: 'Disabled'
appSettings: [
{
name: 'AzureWebJobsStorage'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value}'
}
{
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value}'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.properties.InstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: 'InstrumentationKey=${appInsights.properties.InstrumentationKey}'
}
{
name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE'
value: 'false'
}
{
name: 'DOCKER_REGISTRY_SERVER_URL'
value: 'ghcr.io'
}
{
name: 'DOCKER_REGISTRY_SERVER_USERNAME'
value: ''
}
{
name: 'DOCKER_REGISTRY_SERVER_PASSWORD'
value: ''
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
]
}
}
}
147 changes: 147 additions & 0 deletions deploy/az_function_deployment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.7.4.23292",
"templateHash": "11022243258630732139"
}
},
"parameters": {
"appName": {
"type": "string",
"metadata": {
"description": "Project name."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Specifies region of all resources. Keep the default if you want to use the same region as the resource group."
}
}
},
"variables": {
"storageAccountName": "[format('{0}{1}', substring(parameters('appName'), 0, 10), uniqueString(resourceGroup().id))]",
"hostingPlanName": "[format('{0}{1}', parameters('appName'), uniqueString(resourceGroup().id))]",
"appInsightsName": "[format('{0}{1}', parameters('appName'), uniqueString(resourceGroup().id))]",
"functionAppName": "[parameters('appName')]"
},
"resources": [
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2020-06-01",
"name": "[variables('hostingPlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "B2"
},
"kind": "linux",
"properties": {
"reserved": true
}
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS"
},
"properties": {
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"accessTier": "Hot"
}
},
{
"type": "Microsoft.Insights/components",
"apiVersion": "2020-02-02",
"name": "[variables('appInsightsName')]",
"location": "[parameters('location')]",
"kind": "web",
"properties": {
"Application_Type": "web",
"publicNetworkAccessForIngestion": "Enabled",
"publicNetworkAccessForQuery": "Enabled"
},
"tags": {
"[format('hidden-link:/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Web/sites/{2}', subscription().id, resourceGroup().name, variables('functionAppName'))]": "Resource"
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2020-12-01",
"name": "[variables('functionAppName')]",
"location": "[parameters('location')]",
"kind": "functionapp",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"httpsOnly": true,
"siteConfig": {
"linuxFxVersion": "DOCKER|ghcr.io/imicknl/azure-functions-libpostal:latest",
"alwaysOn": true,
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(resourceId('Microsoft.Insights/components', variables('appInsightsName'))).InstrumentationKey]"
},
{
"name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
"value": "[format('InstrumentationKey={0}', reference(resourceId('Microsoft.Insights/components', variables('appInsightsName'))).InstrumentationKey)]"
},
{
"name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
"value": "false"
},
{
"name": "DOCKER_REGISTRY_SERVER_URL",
"value": "ghcr.io"
},
{
"name": "DOCKER_REGISTRY_SERVER_USERNAME",
"value": ""
},
{
"name": "DOCKER_REGISTRY_SERVER_PASSWORD",
"value": ""
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~4"
}
]
}
},
"dependsOn": [
"[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]",
"[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
]
}
]
}

0 comments on commit 42d4330

Please sign in to comment.