Fix React-Redux routing issues with Windows App Services

Create a web.config file with the following structure and make sure it gets deployed in your wwwroot.

<?xml version="1.0"?>
<configuration>
    <location path="index.html">
        <system.webServer>
            <staticContent>
                <clientCache cacheControlMode="DisableCache" />
            </staticContent>
        </system.webServer>
    </location>
    <system.webServer>
    <staticContent>
        <!-- Serve json files -->
        <remove fileExtension=".json" />
        <mimeMap fileExtension=".json" mimeType="application/json" />

        <!-- Serve fonts, too -->
        <remove fileExtension=".woff" />
        <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
        <remove fileExtension=".woff2" />
        <mimeMap fileExtension=".woff2" mimeType="application/font-woff" />        
    </staticContent>
        <rewrite>
            <rules>
                <rule name="React Routes" stopProcessing="true">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

An alternative web.config template is here.

Don’t show .html extension for static *.html sites

<configuration>
  <system.webServer>
      <rewrite>
            <rules>
                <rule name="RewriteURL" stopProcessing="true">
                    <match url="^(.*)$" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="{R:1}.html" />
                </rule> 
           </rules>
       </rewrite>
    </system.webServer>    
 </configuration>

– via Stack Overflow

Configure Web Apps using rich/nested objects

Aka how do you go from defining a rich configuration section in your appsettings.Development.json file to configuring it in the web app’s Configuration blade.

If you’re using Windows web apps, then you just need to use the “:” (colon) character to define nested key structures. If you’re on Linux, then you need to use “__” (double underscore). Arrays are supported in both cases, but you’ll need to manually specify the indexes in the key name i.e. MyConfiguration__ClientEvents__0 or MyConfiguration:ClientEvents:0.

For example:

{
    "MyConfiguration": {
        "ClientId": "marco",
        "ClientSecret": "polo",        
        "ClientEvents": [
            "SimulationStarted",
            "SimulationEnded"
        ],
    }
}

Will be configured in a Linux web app’s Configuration blade as:

[
  {
    "name": "MyConfiguration__ClientId",
    "value": "marco",
    "slotSetting": false
  },
  {
    "name": "MyConfiguration__ClientSecret",
    "value": "polo",
    "slotSetting": false
  },
  {
    "name": "MyConfiguration__ClientEvents__0",
    "value": "SimulationStarted",
    "slotSetting": false
  },
  {
    "name": "MyConfiguration__ClientEvents__1",
    "value": "SimulationEnded",
    "slotSetting": false
  }
]

See also the wiki on injecting configuration objects in your services/controllers to see how you can access these values in your code.

– via StackOverflow

Unable to save app service configuration (or anything, really), due to “Deny Public Network Access” policy

Even though you’re using a VNET and have enabled private endpoints.

In my case, I could see that "publicNetworkAccess": null in the service’s JSON view, which means the public network access is dynamically set based on the networking settings. Guessing the policy doesn’t check for this dynamic setting, I’ve set manually publicNetworkAccess as Disabled. Saving the app service configuration worked afterwards 😬.

az webapp config set --name <function_name> --resource-group <resource_group> --generic-configurations '{"publicNetworkAccess":"Disabled"}'

Deploy a SvelteKit app to a Linux plan App Service

Gosh, this was harder than it needed to be. The idea is that, for Node apps, Azure expects a package.json to exist in wwwroot, otherwise things will not work. Ideally with a "type": "module". Maybe also with a "start": "node /home/site/wwwroot/" script.

Here’s what I did:

  1. Created a Node-based web app.
  2. Updated my package.json to include a "start": "node /home/site/wwwroot/" script. Alternatively, in the Azure Portal, set the Configuration -> General Settings -> Startup command to node /home/site/wwwroot/
  3. Updated my GitHub action to include said package.json in my build directory. It would have probably been better to create a new one from scratch, but by the time I got to this step I just wanted things to work.
# Kinda like this:

  build_and_deploy:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout repository
      uses: actions/checkout@v3

    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '20'
    
    - name: Install deps & Build
      run: |
        npm install
        npm run build
        
        cp ./package.json ./build/        
      working-directory: myapp-web

    - name: Deploy to Azure App Service
      uses: azure/webapps-deploy@v2
      with:
        app-name: '<myapp-name>'
        publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }} 
        package: './myapp-web/build'
  1. For some reason, the app will start real slow. So activate Configuration -> General Settings -> Always On.
  2. Debug by checking https://<myapp-name>.scm.azurewebsites.net/ – specifically, Site wwwroot and https://<myapp-name>.scm.azurewebsites.net/api/vfs/LogFiles/"

Some references:

Swap deployment slots while deploying with GitHub Actions


- name: Azure Login (webapp)
  uses: azure/login@v1.1
  with:
    creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Deploy to web app
  uses: azure/webapps-deploy@v2
  with:
    app-name: 'xxx'
    slot-name: 'xxx'
    images: 'xxx'

- name: Health check
  uses: jtalk/url-health-check-action@v1.2
  with:
    url: https://xxx/health
    max-attempts: 2
    retry-delay: 5s

- name: Azure Swap Slots
  uses: azure/CLI@v1
  with:
    inlineScript: |
            az webapp deployment slot swap --slot xxx --name xxx --resource-group xxx

– via Reddit