{"id":23662844,"url":"https://github.com/hvalfangst/azure-static-react-website-triggering-functions","last_synced_at":"2026-04-11T04:34:36.696Z","repository":{"id":264708317,"uuid":"892319795","full_name":"hvalfangst/azure-static-react-website-triggering-functions","owner":"hvalfangst","description":"Static web app written in React which uploads CSV files to Azure storage blobs via HTTP-triggered Azure function, which is then processed by a blob-triggered function and its results stored in yet another blob.","archived":false,"fork":false,"pushed_at":"2025-02-11T15:20:42.000Z","size":610,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-30T01:03:49.613Z","etag":null,"topics":["az-204","az-cli","azure","azure-functions","azure-storage-account","azure-storage-blob","blob-trigger","http-trigger","javascript","python","react","static-website"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hvalfangst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2024-11-21T22:25:57.000Z","updated_at":"2025-02-11T15:20:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"957bd2b5-f494-4932-b0fd-7d6d0f23a2bc","html_url":"https://github.com/hvalfangst/azure-static-react-website-triggering-functions","commit_stats":null,"previous_names":["hvalfangst/azure-blob-triggered-event-grid-published-to-service-bus","hvalfangst/azure-static-react-website-triggering-functions"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hvalfangst/azure-static-react-website-triggering-functions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hvalfangst%2Fazure-static-react-website-triggering-functions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hvalfangst%2Fazure-static-react-website-triggering-functions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hvalfangst%2Fazure-static-react-website-triggering-functions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hvalfangst%2Fazure-static-react-website-triggering-functions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hvalfangst","download_url":"https://codeload.github.com/hvalfangst/azure-static-react-website-triggering-functions/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hvalfangst%2Fazure-static-react-website-triggering-functions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31669116,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-10T17:19:37.612Z","status":"online","status_checked_at":"2026-04-11T02:00:05.776Z","response_time":54,"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":["az-204","az-cli","azure","azure-functions","azure-storage-account","azure-storage-blob","blob-trigger","http-trigger","javascript","python","react","static-website"],"created_at":"2024-12-29T05:14:59.598Z","updated_at":"2026-04-11T04:34:36.673Z","avatar_url":"https://github.com/hvalfangst.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Static web app invoking Azure functions\n\nThe aim of this repository is to demonstrate how to deploy a [static website](client/src/App.js) written in React to a [Storage Blob](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website). The\nhosted SPA allows users to upload CSV files containing demographic and financial data about individuals. The files are uploaded to a storage blob by calling an HTTP-triggered Azure Function with the appropriate output bindings. \nOnce the CSV has been uploaded to the storage blob, another, blob-triggered Azure Function calculates correlations between various variables, such as experience, state, gender, and income. \nThe computed statistics are then stored in a new blob container, which is used to serve the results to the user.\nThese two functions are defined in the python script [function_app.py](hvalfangst_function/function_app.py) - which is the main entrypoint of our Azure Function App instance.\n\nThe SPA is protected with Oauth2.0 authorization code flow with PKCE and OIDC. The user is redirected to the Azure AD login page, where they must authenticate before being redirected back to the SPA.\n\n\nThe associated Azure infrastructure is deployed with a script (more on that below).\n\nA branch-triggered pipeline has been set up to deploy our code to the respective Azure resources using a GitHub Actions Workflows [script](.github/workflows/deploy_to_azure.yml). \nThe two functions are deployed using the Function App's associated **publish profile**, whereas the static web app is deployed using a **Service Principal** configured with a **Federated Credential**. \nNote that the static website is hosted directly from a storage blob, as our associated storage container has been configured to serve static websites in our resource provisioning script. \nThus, deploying the website is simply a matter of uploading the static files to the designated blob container.\n\n\n## Requirements\n\n- **Platform**: x86-64, Linux/WSL\n- **Programming Languages**: [React](https://reactjs.org/docs/getting-started.html), [Python 3](https://www.python.org/downloads/)\n- **Cloud Account**: [Azure](https://azure.microsoft.com/en-us/pricing/purchase-options/azure-account)\n- **Resource provisioning**: [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/)\n\n\n## Allocate resources\n\nThe shell script [allocate_resources](infra/allocate_resources.sh) creates Azure resources using the Azure CLI in conjunction with a\n[Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep) template [file](infra/main.bicep). \n\nIt will create the following hierarchy of resources:\n\n```mermaid\ngraph TD\n    A[Subscription]\n    A --\u003e B[Resource Group]\n    B --\u003e C[Storage Account]\n    C --\u003e D[Blob Container]\n    D --\u003e|Static Website Hosting| H[index.html]\n    B --\u003e E[App Service Plan]\n    E --\u003e|Hosts| G[Function App]\n    G --\u003e|Uses| F[Application Insights]\n\n    A --\u003e|Contains| B\n    B --\u003e|Contains| C\n    C --\u003e|Contains| D\n    B --\u003e|Contains| E\n    B --\u003e|Contains| F\n```\n\n## Registrations\nIn addition to the resources listed above, the script will also create a **service principal** and two Microsoft Entra ID **app registrations.** \n\n### Service Principal for GitHub Actions\nThe service principal has been assigned contributor role to our resource group, which is sufficient in order to deploy the static web app to the storage blob.\nIt has been assigned a federated credential configured to work with this repository as it is utilized in our CI/CD [GitHub Actions Workflow script](.github/workflows/deploy_to_azure.yml).\n\n### App Registration for Azure Function App\n\nExposes the scopes **Csv.Writer** and **Csv.Reader** under URI **api://hvalfangst-function-app**\n\n![img.png](images/expose_api.png)\n\n### App Registration for SPA\n\nHas a redirect URI configured to the static web app's URL and the permissions **Csv.Writer** and the OIDC ones.\n\n![img_1.png](images/api_permissions.png)\n\n\n## GitHub secrets\nFour secrets are required in order for the GitHub Actions Workflow script to deploy the code to the Azure resources. \nAs may be observed in the [script](.github/workflows/deploy_to_azure.yml), these are:\n\n- **AZURE_GITHUB_SP_CLIENT_ID**: Used to authenticate the service principal in order to deploy the static web app\n- **AZURE_SUBSCRIPTION_ID**: Used to authenticate the service principal in order to deploy the static web app\n- **AZURE_TENANT_ID**: Used to authenticate the service principal and for the OIDC flow in the React SPA\n- **PUBLISH_PROFILE**: Used to deploy our two functions to the Azure Function App\n- **STATIC_WEB_APP_CLIENT_ID**: Used in the React SPA for OIDC authentication\n\n### Subscription and Tenant ID\nThe **subscription ID** and **tenant ID** is found by running the following Azure CLI command:\n\n```bash\naz account show --query id\naz account show --query tenantId\n```\n\n### Publish Profile\nThe publish profile may be obtained by navigating to the Azure Portal, selecting the Azure Function App, and clicking on **Get publish profile**.\n\n### Azure GitHub Service Principal\nThe service principal used for GitHub Actions is created as part of our resource provisioning script and\nas thus should be displayed in the terminal output as such:\n\n![sp_terminal.png](images/sp_terminal.png)\n\n### Static Web App Client ID\nSimilarly, the **client ID** for the static web app is created as part of our resource provisioning script and outputted to the terminal as such:\n\n![spa_terminal.png](images/spa_terminal.png)\n\n## Usage\nAfter provisioning resources, setting up secrets, and pushing the code to the repository, one\nmay access the static web app by navigating to the following URL:\n\n\nhttps://hvalfangststorageaccount.z6.web.core.windows.net, which results in the following.\n\n![csv_uploader.png](images/csv_uploader.png)\n\nClick on **Sign In** to initiate the OIDC flow - which redirects to the Azure AD permission consent screen.\n\n![oidc.png](images/oidc_sign_in.png)\n\nClik on **Accept** and check off the **Consent on behalf of your organization** box to be redirected back to the SPA, where you will be greeted with the following.\n\n![authenticated_user.png](images/authenticated_user.png)\n\nProceed to click on **Upload** to choose a file to upload. Pick the CSV file named [input](input.csv) which has been provided for this purpose.\n\n![choose_file.png](images/choose_file.png)\n\n![file_chosen.png](images/file_chosen.png)\n\nThe file name will be displayed in the input field. Click on **Upload** to attempt to upload the file to the storage blob.\n\nIf the upload was successful, the following message will be displayed.\n\n![upload_successful.png](images/upload_successful.png)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhvalfangst%2Fazure-static-react-website-triggering-functions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhvalfangst%2Fazure-static-react-website-triggering-functions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhvalfangst%2Fazure-static-react-website-triggering-functions/lists"}