{"id":21145335,"url":"https://github.com/jsa2/azure-functions-linuxstarterkit","last_synced_at":"2026-03-19T19:03:08.089Z","repository":{"id":103264314,"uuid":"409885753","full_name":"jsa2/Azure-Functions-LinuxStarterKit","owner":"jsa2","description":null,"archived":false,"fork":false,"pushed_at":"2023-08-21T11:25:05.000Z","size":24,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"v2","last_synced_at":"2025-03-14T13:43:08.986Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Bicep","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jsa2.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-24T08:14:06.000Z","updated_at":"2023-08-21T11:24:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"76403f03-459f-403e-819b-73283d8d2a9b","html_url":"https://github.com/jsa2/Azure-Functions-LinuxStarterKit","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jsa2/Azure-Functions-LinuxStarterKit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa2%2FAzure-Functions-LinuxStarterKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa2%2FAzure-Functions-LinuxStarterKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa2%2FAzure-Functions-LinuxStarterKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa2%2FAzure-Functions-LinuxStarterKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsa2","download_url":"https://codeload.github.com/jsa2/Azure-Functions-LinuxStarterKit/tar.gz/refs/heads/v2","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa2%2FAzure-Functions-LinuxStarterKit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29848657,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T22:37:40.667Z","status":"online","status_checked_at":"2026-02-26T02:00:06.774Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-20T08:39:36.345Z","updated_at":"2026-02-26T03:59:29.221Z","avatar_url":"https://github.com/jsa2.png","language":"Bicep","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Azure Functions Starter Kit Node.js on Linux \r\n\r\n\r\n## Major update\r\n- Uses managed Identity instead connection string for [``AzureWebJobsStorage``](https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings#azurewebjobsstorage)\r\n  - Finding the correct settings was bit harder than I anticipated. Bunch of helpful resources were added to the end regarding this search\r\n  - Some discussions raise the lack of MSI support for the scaling storage account (scaling plans) [``WEBSITE_CONTENTAZUREFILECONNECTIONSTRING``](https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) but this storage account does not store Functions keys like ``AzureWebJobsStorage`` in\r\n- Sets up the Azure RBAC for storage based on supporting multiple function types (timer,trigger etc)\r\n- Enables logging for Function related storage accounts and the function itself (this is to monitor security events)\r\n\r\n\r\n\r\n\r\n**Accessing code and secrets after deployment**\r\n\r\nCode and Function secrets are only available in the blob storage and accessable only by the managed identity of the function (unless defined otherwise)\r\n\r\n[``fn.bicep``](/fn.bicep)\r\n```\r\n//Bicep\r\n{\r\n  name:'AzureWebJobsStorage__blobServiceUri'\r\n  value:'https://${storageAccountName}.blob.${environment().suffixes.storage}'\r\n}\r\n{\r\n  name:'WEBSITE_RUN_FROM_PACKAGE'\r\n  value:packageUri\r\n}\r\n{\r\n  name: 'AzureWebJobsStorage__${storageAccountName}'\r\n  value: storageAccount.name\r\n}\r\n```\r\n\r\n\r\n## Solution description\r\nThis solution enables a quick way to deploy and work Linux based Azure Functions locally and cloud:\r\n- Deploys starter-kit zip-deploy using Azure CLI\r\n- Enables managed identity locally via IP-restricted endpoint exposed on the cloud version of the function\r\n\r\n![img](https://securecloud188323504.files.wordpress.com/2021/09/image-47.png)\r\n\r\n## Disclaimer\r\nRead [License](#license)\r\n\r\n\r\n## Deployment video\r\n\r\n\u003ca href=\"https://videopress.com/embed/0Wn0owI2\" title=\"Link Title\"\u003e\u003cimg src=\"https://securecloud188323504.files.wordpress.com/2021/09/image-49.png?w=1024\" alt=\"Alternate Text\" /\u003e\u003c/a\u003e\r\n\r\n\r\n## Table of contents\r\n- [Azure Functions Starter Kit Node.js on Linux](#azure-functions-starter-kit-nodejs-on-linux)\r\n  - [Solution description](#solution-description)\r\n  - [Disclaimer](#disclaimer)\r\n  - [Deployment video](#deployment-video)\r\n  - [Table of contents](#table-of-contents)\r\n  - [Prerequisites](#prerequisites)\r\n  - [Installation](#installation)\r\n    - [CLI script](#cli-script)\r\n  - [After testing](#after-testing)\r\n  - [License](#license)\r\n\r\n\r\n## Prerequisites \r\n\r\nRequirement | description | Install \r\n-|-|-\r\n✅ Bash shell | Tested with WSL2 (Ubuntu) on Windows 10 \r\n✅ [p7zip](https://www.7-zip.org/) | p7zip is  used to create the zip deployment package for package deployment | ``sudo apt-get install p7zip-full``\r\n✅ AZCLI | Azure Services installation |``curl -sL https://aka.ms/InstallAzureCLIDeb \\| sudo bash; az extension add --name storage-preview``\r\n✅ Node.js runtime 14 | Used in Azure Function, and to create local function config |[install with NVM](https://github.com/nvm-sh/nvm#install--update-script)\r\n✅ Azure Function Core Tools and VScode with Azure Functions extension  | if you want to add new templates) to this function and debug locally |[Install the Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v3%2Clinux%2Ccsharp%2Cportal%2Cbash%2Ckeda#v2)\r\n✅ JQ | parsing the output from bicep in bash | ``sudo apt install jq`` \r\n\r\n\r\n\r\n\r\n## Installation\r\n\r\n1. Deploy via [CLI script](#cli-script)\r\n2. Run `` func host start --javascript `` to test you can use managed identity locally. Managed identity is used to get information about the Azure Resource Group it's deployed on via cloud\r\n![img](https://securecloud188323504.files.wordpress.com/2021/09/image-48.png?w=1024)\r\n\r\n\r\n### CLI script\r\nThe CLI script below will use current subscription context to setup the solution after user has performed \r\n\r\nEnsure you have selected a single subscription context\r\n``` AZ LOGIN; az account set --subscription {subscriptionID} ``` \r\n```shell\r\n# Remove existing deployment package\r\nrm deploy.zip\r\n\r\n# Install required npm packages\r\nnpm install\r\n\r\n# Generate random strings for naming\r\nrnd=$(env LC_CTYPE=C tr -cd 'a-f0-9' \u003c /dev/urandom | head -c 10)\r\nrnd2=$(env LC_CTYPE=C tr -cd 'a-f0-9' \u003c /dev/urandom | head -c 4)\r\n\r\n# Define Azure resources names and locations\r\nfnName=fnapp-generic-$rnd2\r\nrg=RG-FN-$fnName\r\nlocation=westeurope\r\nIPRestriction=$(curl -s ifconfig.me)\r\nstorageAcc=privstorage$rnd\r\nlaws=lawsingestehoney\r\nazEnv=honeypot\r\nme=$(az rest -u https://graph.microsoft.com/v1.0/me | jq .id -r)\r\nAZURE_STORAGE_AUTH_MODE=login\r\npackageStore='function-releases'\r\n\r\n# Create Resource Group with tags\r\naz group create -n $rg -l $location --tags=\"azEnv=$azEnv\"\r\n\r\n# Create Storage account\r\nsaId=$(az storage account create -n $storageAcc  -g $rg --kind storageV2 -l $location -t Account --sku Standard_LRS  -o tsv --query \"id\" --allow-blob-public-access \"false\")\r\n\r\n# Assign Storage Blob Data Owner role\r\naz role assignment create --assignee $me --role \"Storage Blob Data Owner\" --scope $saId \r\n\r\n# Define source for the package\r\nsrc=\"https://$storageAcc.blob.core.windows.net/$packageStore/zip/deploy.zip\"\r\n\r\n# Create ZIP package\r\n7z a -tzip deploy.zip . -r -mx0 -xr\\!*.git -xr\\!*.vscode -xr\\!local.settings.json\r\n\r\n# Create storage container\r\naz storage container create -n $packageStore --account-name $storageAcc --auth-mode login\r\n\r\n# Upload the ZIP package to the storage\r\naz storage blob directory upload -c $packageStore --account-name $storageAcc -s \"deploy.zip\" -d zip \r\n\r\n# Update storage account settings\r\naz storage account update --name $storageAcc --resource-group $rg --allow-shared-key-access false\r\n\r\n# Define role definition ID\r\nroleDefId=\"/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b\"\r\n\r\n# Deploy using bicep templates\r\noutputData=$(az deployment group create --resource-group $rg --template-file fn.bicep --parameters roleDefinitionResourceId=$roleDefId rand=$rnd appName=$fnName storageAccountName=$storageAcc logAnalyticsWorkspaceName=$laws packageUri=$src)\r\n\r\n# Extract storage account details\r\nsaId2=$(echo $outputData  | jq .properties.outputResources[6].id -r)\r\nsaConstring=$(az storage account show-connection-string -g $rg  --id $saId2 -o tsv --query \"connectionString\")\r\n\r\n# Update function app settings\r\naz functionapp config access-restriction add --name $fnName --resource-group $rg --ip-address $IPRestriction --priority 1\r\n\r\n# Retrieve function keys and invoke URL\r\nkeys=$(az functionapp keys list -g $rg -n $fnName -o tsv --query functionKeys) \r\nmsiFn=$(az functionapp function show -g $rg -n $fnName --function-name testmsi -o tsv --query invokeUrlTemplate)\r\n\r\n# Make a request to the function\r\necho \"looking for template $msiFn\"?code=\"$keys\u0026resource=https://management.azure.com\"\r\ncurl -s \"$msiFn\"?code=\"$keys\u0026resource=https://management.azure.com\" \r\n\r\n# Run the configuration script\r\nnode createConf.js $saConstring $keys $msiFn $scope\r\n\r\n```\r\n## After testing\r\n```\r\naz group delete \\\r\n--resource-group $rg --no-wait -y\r\n```\r\n\r\n\r\n## resources\r\n\r\nI draw some examples and inspiration from the following sources \r\n\r\nhttps://learn.microsoft.com/en-us/azure/azure-functions/functions-identity-based-connections-tutorial#use-managed-identity-for-azurewebjobsstorage\r\n\r\nhttps://learn.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=blob#connecting-to-host-storage-with-an-identity\r\n\r\nhttps://stackoverflow.com/questions/66480199/enable-diagnostic-settings-for-storage-account-using-armtemplate\r\n\r\nhttps://learn.microsoft.com/en-us/azure/azure-functions/functions-identity-based-connections-tutorial#use-managed-identity-for-azurewebjobsstorage\r\n\r\n-https://techcommunity.microsoft.com/t5/apps-on-azure-blog/use-managed-identity-instead-of-azurewebjobsstorage-to-connect-a/bc-p/3739810\r\n\r\nhttps://learn.microsoft.com/en-us/answers/questions/1162721/how-to-secure-access-to-host-storage-account-for-f\r\n\r\nhttps://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings\r\n\r\nhttps://github.com/Azure/bicep/discussions/8435#discussioncomment-3694406  \r\n\r\nhttps://blog.vincentboots.nl/secure-azurewebjobstorage-in-azure-functions-647e56d32727\r\n\r\n\r\n## License\r\nCopyright 2023 Joosua Santasalo\r\n\r\nPermission 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:\r\n\r\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n\r\nTHE 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.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsa2%2Fazure-functions-linuxstarterkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsa2%2Fazure-functions-linuxstarterkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsa2%2Fazure-functions-linuxstarterkit/lists"}