{"id":13706983,"url":"https://github.com/dataheld/azureappservice","last_synced_at":"2025-11-06T06:30:27.347Z","repository":{"id":73224502,"uuid":"299075519","full_name":"dataheld/azureappservice","owner":"dataheld","description":"Deploy shiny applications and other R workloads to Azure App Service","archived":false,"fork":false,"pushed_at":"2023-06-13T08:31:40.000Z","size":147,"stargazers_count":1,"open_issues_count":30,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-11-13T15:49:20.670Z","etag":null,"topics":["azure","azure-webapp","caas","rstats","rstats-package","shiny"],"latest_commit_sha":null,"homepage":"https://dataheld.github.io/azureappservice/","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dataheld.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}},"created_at":"2020-09-27T16:44:49.000Z","updated_at":"2023-06-11T18:48:18.000Z","dependencies_parsed_at":"2024-01-14T20:34:20.559Z","dependency_job_id":null,"html_url":"https://github.com/dataheld/azureappservice","commit_stats":null,"previous_names":["dataheld/azureappservice"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fazureappservice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fazureappservice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fazureappservice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fazureappservice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dataheld","download_url":"https://codeload.github.com/dataheld/azureappservice/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239484219,"owners_count":19646429,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["azure","azure-webapp","caas","rstats","rstats-package","shiny"],"created_at":"2024-08-02T22:01:14.171Z","updated_at":"2025-11-06T06:30:27.284Z","avatar_url":"https://github.com/dataheld.png","language":"Dockerfile","readme":"# azureappservice\n\nazureappservice lets you manage [Azure App Service (AAS)](https://azure.microsoft.com/en-us/services/app-service/), a Platform-as-a-Service (PaaS) and Container-as-a-Service (CaaS) from Microsoft.\n\nYou can use it to deploy a containerised shiny app to the cloud.\n\n::: {.alert .alert-warning}\nCloud services such as AAS can rack up unexpected bills,\nif you don't\n[manage costs carefully](https://learn.microsoft.com/en-us/azure/cost-management-billing/).\nIf you are not familiar with cloud services,\nor would like a solution with a simpler way to control costs,\nconsider Posit's [shinyapps.io](https://www.shinyapps.io).\n:::\n\n## Prerequisites\n\n- A **dockerised shiny app**.\n    You must ship your shiny app inside a docker container,\n    where the app has all the dependencies it needs to run.\n    You can use [muggle](https://maxheld.de/muggle/) (recommended),\n    [rocker/shiny](https://hub.docker.com/r/rocker/shiny)\n    or roll your own.\n- An **azure account** with an active subscription.\n\n## Usage\n\nYou can deploy a shiny app to AAS from CI (GitHub Actions),\nor using your local computer (Shell).\n\nDeploying from CI is recommended to let you reap the benefits of fully\nautomated continuous integration and continuous delivery (CI/CD).\nIf you deploy from CI, every `git push` to the appropriate branch\nwill be deployed to production.\n\nYou can use your local shell as backstop or\nfor a quicker turnaround during debugging.\n\n### Log In to Azure {.tabset}\n\nYou first need to authenticate into Azure to be able to make changes to Azure resources.\n\n#### Local (Shell)\n\n[Sign in interactively](https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli)\nwith Azure CLI.\n\n```sh\naz login\n```\n\n#### CI (GitHub Actions)\n\nUse the [Azure Login GitHub Action](https://github.com/marketplace/actions/azure-login).\nFor maximum security and convenience,\nuse OpenID Connect (OIDC) based Federated Identity Credentials\n(or Workflow Identify Federation, WIF).\n\nTo authenticate via WIF, you need to complete two steps on the Azure side:\n\n1. [Create an *app registration* for GitHub](https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation-create-trust-user-assigned-managed-identity?pivots=identity-wif-mi-methods-azp).\n    You only need *one* app registration for all your deployments from GitHub Actions to Azure.\n    (Though there might be a limit of 20 federated credentials for each app registration).\n1. For each combination of GitHub organisation/repository (`octouser/octoproject`) and environment (say, `production`) create one [federated credential](https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation-create-trust-user-assigned-managed-identity?pivots=identity-wif-mi-methods-azp).\n\n    The values for organization/repository and environment\n    must match those in the GitHub repo from which you want to deploy.\n    You can add several of these federated credentials;\n    without storing additional secrets on GitHub.\n    All federated credentials on the same app registration\n    use the same secrets as created in the above.\n\n::: {.alert .alert-info}\nAt this point in the setup,\nthe app you registered in the above has *no* assigned roles\nand cannot do anything on Azure.\nAs a result,\nyou may get this error message if you run the Azure Login GitHub Action:\n\n\u003e ```sh\n\u003e ERROR: (SubscriptionNotFound) The subscription '***' could not be found.`\n\u003e Code: SubscriptionNotFound\n\u003e ```\n\nThis will be fixed below,\nwhere you'll give the app registration the necessary privileges.\n\nIf you want to run the Azure Login GitHub Action as is,\npass the `allow-no-subscriptions: true` argument.\n:::\n\n### Create an Azure Container Registry (Once)\n\nThe easiest and safest way for AAS to retrieve container images is from\nAzure's own container registry (ACR).\nIf you don't already have an instance,\n[set up a container registry](https://learn.microsoft.com/en-us/azure/container-registry/).\n\n::: {.alert .alert-info}\nThere is nothing specifically shiny-related about the required ACR instance,\nand any configuration will do.\nConsequently, no ARM template is included here.\nIf your organisation already has an ACR instance,\nplease use that\nor defer to your organisations needs and policies in configuring a new one.\n:::\n\n### Login to ACR {.tabset}\n\nLog in to ACR, leveraging the above login to Azure.\nThis step is necessary to allow `docker` to speak to your ACR instance.\n\n#### Local (Shell)\n\nNo extra step necessary.\n\n#### CI (GitHub Actions)\n\nIf you're using OIDC (recommended) as per the above,\ngive your app `AcrPush` privileges on your registry using Azure's access control.\n\n```sh\naz acr login --name \u003cregistry-name\u003e\n```\n\n### Push `runner` Image to ACR (Every Commit)\n\nPush the image which contains the shiny app (`runner`) to ACR.\n\n```sh\ndocker push \u003cregistry-name\u003e.azurecr.io/\u003cproject-name\u003e/runner:production\n```\n\n### Create an Azure App Service Plan (Once)\n\nThe [Azure App Service plan (ASP)](https://learn.microsoft.com/en-us/azure/app-service/)\nis the compute resource running the container with your shiny app.\nLoosely,\nit is the *host virtual machine (VM)* on which `docker run` is executed.\n\nCreate an ASP with Linux as the host operating system.\n\n::: {.alert .alert-info}\nLike the ACR instance above,\nShiny places no special demands on the ASP.\nThe best SKU and other settings depend on your organisations policies\nand individual use case.\n:::\n\nContra other Container-as-a-Service (CaaS) offerings,\nAzure allows you to run entirely separate (dockerised) web apps\ninside *one* ASP, sharing compute resources.\nFor example, you could run several unrelated shiny apps in the same ASP.\n\n::: {.alert .alert-info}\nHosting several shiny apps on the same ASP can be a cost-effective way\nto host shiny,\nespecially when you expect uncorrelated and limited traffic on each of the apps.\n\nIf you deploy several shiny apps to the same ASP,\nthe apps are isolated in their own docker containers.\nFor example, if the R session of one shiny app is busy,\nthe other apps on the same ASP should still be responsive.\n\nHowever,\nthese apps and their containers still share the same physical resources,\nand high resource use of one app may spill over into another.\nMemory usage in particular can be a bottleneck on the lower-powered ASP SKUs;\nif one shiny app exhausts the ASP's memory,\nother apps may become unavailable.\n:::\n\n### Create a Web App (Once per App) {.tabset}\n\nYou're now ready to create the (dockerised) web app\nwhich will drive your shiny app.\n\nThe easiest way to make sure all the settings are correct is to use the\nAzure Resource Manager (ARM) bicep template included with this package (`inst/arm`).\n\n1. Copy `template.bicep` to the repository with your shiny app.\n    To avoid `R CMD check` warning messages,\n    place it inside `inst/arm`.\n2. Copy `template.parameters.json` and replace the values.\n\n#### Local Shell\n\n```sh\naz deployment group create \\\n  --resource-group marketing \\\n  --template-file inst/arm/template.bicep \\\n  --parameters inst/arm/template.parameters.json\n```\n\nFor extra security,\nyou will have to enter your Azure subscription at the prompt.\n\n#### CI (GitHub Actions)\n\nUse the [arm-deploy](https://github.com/Azure/arm-deploy) GitHub Action\nto deploy.\nSee `.github/workflows/cicd.yaml` for an example.\n\nThere's no need to recreate the (identical) app in CI on every commit,\nbut it's also harmless to do so.\nRecreating the app on every commit ensures that the settings are under version\ncontrol (infrastructure-as-code).\n\n::: {.alert .alert-warning}\nIf you want deploy the ARM template from GitHub Actions (recommended),\nthe app registration created in the above for GitHub Actions\nmust receive `Contributor` privileges on the resource group\nor ASP to which you want to deploy your app.\nThis extends a fairly elevaned privilege to grant GitHub Actions on Azure.\nMake sure to follow\n[best practices for information security](https://docs.github.com/en/code-security)\nto safeguard your GitHub account.\n:::\n\n::: {.alert .alert-info}\nHosting several shiny apps on the same ASP can be a cost-effective way\nto host shiny,\nespecially when you expect uncorrelated and limited traffic on each of the apps.\n\nIf you deploy several shiny apps to the same ASP,\nthe apps are isolated in their own docker containers.\nFor example, if the R session of one shiny app is busy,\nthe other apps on the same ASP should still be responsive.\n\nHowever,\nthese apps and their containers still share the same physical resources,\nand high resource use of one app may spill over into another.\nMemory usage in particular can be a bottleneck on the lower-powered ASP SKUs;\nif one shiny app exhausts the ASP's memory,\nother apps may become unavailable.\n:::\n\n### Update the Web App (Every Commit) {.tabset}\n\nTo put the latest version of your dockerised shiny app in production,\nsimply restart the web app;\nit will then pull the current image under the appropriate tag.\n\n#### Local Shell\n\n```sh\naz webapp restart --name MyWebapp --resource-group MyResourceGroup\n```\n\nFor example,\n\n```sh\naz webapp restart --name dataheld-azureappservice --resource-group marketing\n```\n\n#### CI (GitHub Actions)\n\nGive the above created app registration for GitHub Actions write\nprivileges on the web app using the web apps access control (IAM) settings.\nChoose `Contributor` as a role.\n\nYou can then run the same command as in your shell;\nAzure CLI is already installed on most GitHub runners.\n\nFor example:\n\n```yaml\n- name: \"Restart App\"\n        run: |\n          az webapp restart \\\n            --name dataheld-azureappservice \\\n            --resource-group marketing\n```\n\n### Visit the Live Shiny App {.tabset}\n\n#### Local (Shell)\n\nFor example:\n\n```sh\naz webapp browse --name dataheld-azureappservice --resource-group marketing\n```\n\n#### CI (GitHub Actions)\n\nLook for the `production` environment in your GitHub repo\nto find the URL under which the web app is live.\n\n## Installation\n\nInstall the development version from GitHub with:\n\n```r\nremotes::install_github(\"dataheld/azureappservice\")\n```\n\nYou need not take on azureappservice as a runtime dependency (in your `DESCRIPTION`s `Imports` field), because the package is typically only needed during deployment.\nConsider adding it as an optional `Suggests` dependency, or add it separately to the compute environment from which you deploy.\n","funding_links":[],"categories":["Dockerfile"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdataheld%2Fazureappservice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdataheld%2Fazureappservice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdataheld%2Fazureappservice/lists"}