Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
318 stars 53 forks source link

Azure Portal does not display Azure Function & Static Web App configuration #1089

Closed johnnyreilly closed 1 year ago

johnnyreilly commented 1 year ago

Describe the bug

When deploying an Azure Static Web App that uses the Azure Function API built in, the Azure Portal either displays the configuration for the Azure Static Web App or the configuration for the Azure Function App. Never both - and you need both when diagnosing issues.

To Reproduce

Deploy an Azure Static Web App with a Function App. We're doing this using the following Bicep template

@description('Tags that our resources need')
param tags object

@description('Location for the resources')
param location string

@description('Type of managed service identity. SystemAssigned, UserAssigned. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#managedserviceidentity')
param identityType string = 'SystemAssigned'

@description('The list of user assigned identities associated with the resource.')
param userAssignedIdentities object = {}

@description('The SKU for the static site. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#skudescription')
param sku object = {
  name: 'Standard'
  tier: 'Standard'
}

@description('Lock the config file for this static web app. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#staticsite')
param allowConfigFileUpdates bool = true

@description('Where the repository lives eg https://dev.azure.com/investec/investec-cloud-experience/_git/tea-and-biscuits')
param repositoryUrl string

@description('The branch being deployed eg main')
param repositoryBranch string

@description('The name of the static site eg stapp-tab-prod-001')
param staticSiteName string

@secure()
@description('Configuration for your static site')
param appSettings object = {}

@secure()
@description('Configuration for your static site function app')
param functionAppAppSettings object = {}

@description('Build properties for the static site.')
param buildProperties object = {}

@allowed([
  'Disabled'
  'Disabling'
  'Enabled'
  'Enabling'
])
@description('State indicating the status of the enterprise grade CDN serving traffic to the static web app.')
param enterpriseGradeCdnStatus string = 'Disabled'

@allowed([
  'Disabled'
  'Enabled'
])
@description('State indicating whether staging environments are allowed or not allowed for a static web app.')
param stagingEnvironmentPolicy string = 'Enabled'

@description('Template Options for the static site. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#staticsitetemplateoptions')
param templateProperties object = {}

@description('Custom domain name for the static side eg testing-swa.sbx.investec.io')
param customDomainName string = ''

resource staticSite 'Microsoft.Web/staticSites@2022-03-01' = { // https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep
  name: staticSiteName
  location: location
  tags: tags
  identity: {
    type: identityType
    userAssignedIdentities: empty(userAssignedIdentities) ? null : userAssignedIdentities
  }
  sku: sku
  properties: {
    allowConfigFileUpdates: allowConfigFileUpdates
    provider: 'DevOps' // see: https://github.com/Azure/static-web-apps/issues/516
    repositoryUrl: repositoryUrl
    branch: repositoryBranch
    buildProperties: empty(buildProperties) ? null : buildProperties
    enterpriseGradeCdnStatus: enterpriseGradeCdnStatus
    stagingEnvironmentPolicy: stagingEnvironmentPolicy
    templateProperties: empty(templateProperties) ? null : templateProperties
  }
}

resource customDomain 'Microsoft.Web/staticSites/customDomains@2022-03-01' = if(!empty(customDomainName)) {
  parent: staticSite
  name: !empty(customDomainName) ? customDomainName : 'blank' // https://github.com/Azure/bicep/issues/1754
  properties: {}
}

// CONFIGURATION FOR STATIC WEB APP
resource staticSiteAppsettings 'Microsoft.Web/staticSites/config@2021-03-01' = {
  parent: staticSite
  name: 'appsettings'
  kind: 'config'
  properties: appSettings
}

// CONFIGURATION FOR FUNCTION APP
resource staticWebAppFunctionAppSettings 'Microsoft.Web/staticSites/config@2021-03-01' = if(!empty(functionAppAppSettings)) {
  name: 'functionappsettings'
  kind: 'config'
  parent: staticSite
  properties: functionAppAppSettings
}

output defaultHostName string = staticSite.properties.defaultHostname // eg gentle-bush-0db02ce03.azurestaticapps.net
output siteName string = staticSite.name
output siteResourceId string = staticSite.id
output siteSystemAssignedIdentityId string = (staticSite.identity.type == 'SystemAssigned') ? staticSite.identity.principalId : ''

The above template might be invoked like this:

module staticWebApp 'static-sites.bicep' = {
  name: '${deploymentPrefix}-how-you-doin-${branchHash}'
  params: {
    appSettings: {
      AAD_CLIENT_ID: aadClientIdRef
      AAD_CLIENT_SECRET: aadClientSecretRef
    }
    functionAppAppSettings: {
      keyVaultName: keyVaultName
      APPINSIGHTS_INSTRUMENTATIONKEY: appInsights.properties.InstrumentationKey
      APPLICATIONINSIGHTS_CONNECTION_STRING: appInsights.properties.ConnectionString
    }
    location: location
    repositoryBranch: repositoryBranch
    repositoryUrl: repositoryUrl
    staticSiteName: staticSiteName
    tags: tagsWithHiddenLinks
    customDomainName: (repositoryBranch == 'main') ? customDomainName : ''
  }
}

Expected behavior A clear and concise description of what you expected to happen.

Screenshots

screenshot of the Azure Portal

The above is a screenshot of the Azure Portal displaying only the static web app configuration and not the function app configuration. I have also seen it the other way around as well:

image

Device info (if applicable): n/a Additional context Add any other context about the problem here.

johnnyreilly commented 1 year ago

I'm not certain, but I think this might be a more fundamental issue. Based on how my app is behaving, it seems likely that when it comes to Static Web Apps and Function Apps, it's only possible to configure one.

For now, we need to ship an app, so I'm going to rearchitect away from using in-built Azure Functions in favour of something else (probably bring your own functions). But I'll keep a branch around of the old approach if the team wants to investigate at any point.

thomasgauvin commented 1 year ago

@johnnyreilly Static Web Apps does not have separate 'static web apps' and 'function apps' app settings. There are no 'static web app' app settings, since your Static Web App is static content and does not have any 'runtime variables'. Instead, the 'app settings' are those of the function app, so you should not be creating the SWA with separate function app & swa app settings. Based on your Bicep file, it seems you are overwriting these with 'Microsoft.Web/staticSites/config@2021-03-01'.

My hypothesis is that merging your staticSiteAppsettings and staticWebAppFunctionAppSettings would resolve your issues.

johnnyreilly commented 1 year ago

I attempted to merge the two app settings. Deployment of the infrastructure succeeded - and I can see the config in the Azure Portal. This was using this static-sites.bicep:

@description('Tags that our resources need')
param tags object

@description('Location for the resources')
param location string

@description('Type of managed service identity. SystemAssigned, UserAssigned. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#managedserviceidentity')
param identityType string = 'SystemAssigned'

@description('The list of user assigned identities associated with the resource.')
param userAssignedIdentities object = {}

@description('The SKU for the static site. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#skudescription')
param sku object = {
  name: 'Standard'
  tier: 'Standard'
}

@description('Lock the config file for this static web app. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#staticsite')
param allowConfigFileUpdates bool = true

@description('Where the repository lives eg https://dev.azure.com/investec/investec-cloud-experience/_git/tea-and-biscuits')
param repositoryUrl string

@description('The branch being deployed eg main')
param repositoryBranch string

@description('The name of the static site eg stapp-tab-prod-001')
param staticSiteName string

@secure()
@description('Configuration for your static site')
param appSettings object = {}

@secure()
@description('Configuration for your static site function app')
param functionAppAppSettings object = {}

@description('Build properties for the static site.')
param buildProperties object = {}

@allowed([
  'Disabled'
  'Disabling'
  'Enabled'
  'Enabling'
])
@description('State indicating the status of the enterprise grade CDN serving traffic to the static web app.')
param enterpriseGradeCdnStatus string = 'Disabled'

@allowed([
  'Disabled'
  'Enabled'
])
@description('State indicating whether staging environments are allowed or not allowed for a static web app.')
param stagingEnvironmentPolicy string = 'Enabled'

@description('Template Options for the static site. https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep#staticsitetemplateoptions')
param templateProperties object = {}

@description('Custom domain name for the static side eg testing-swa.sbx.investec.io')
param customDomainName string = ''

resource staticSite 'Microsoft.Web/staticSites@2022-03-01' = { // https://docs.microsoft.com/en-us/azure/templates/microsoft.web/staticsites?tabs=bicep
  name: staticSiteName
  location: location
  tags: tags
  identity: {
    type: identityType
    userAssignedIdentities: empty(userAssignedIdentities) ? null : userAssignedIdentities
  }
  sku: sku
  properties: {
    allowConfigFileUpdates: allowConfigFileUpdates
    provider: 'DevOps' // see: https://github.com/Azure/static-web-apps/issues/516
    repositoryUrl: repositoryUrl
    branch: repositoryBranch
    buildProperties: empty(buildProperties) ? null : buildProperties
    enterpriseGradeCdnStatus: enterpriseGradeCdnStatus
    stagingEnvironmentPolicy: stagingEnvironmentPolicy
    templateProperties: empty(templateProperties) ? null : templateProperties
  }
}

resource customDomain 'Microsoft.Web/staticSites/customDomains@2022-03-01' = if(!empty(customDomainName)) {
  parent: staticSite
  name: !empty(customDomainName) ? customDomainName : 'blank' // https://github.com/Azure/bicep/issues/1754
  properties: {}
}

resource staticSiteAppsettings 'Microsoft.Web/staticSites/config@2022-03-01' = {
  parent: staticSite
  name: 'appsettings'
  kind: 'config'
  properties: appSettings
}

resource staticWebAppFunctionAppSettings 'Microsoft.Web/staticSites/config@2022-03-01' = if(!empty(functionAppAppSettings)) {
  name: 'functionappsettings'
  kind: 'config'
  parent: staticSite
  properties: functionAppAppSettings
}

output defaultHostName string = staticSite.properties.defaultHostname // eg gentle-bush-0db02ce03.azurestaticapps.net
output siteName string = staticSite.name
output siteResourceId string = staticSite.id
output siteSystemAssignedIdentityId string = (staticSite.identity.type == 'SystemAssigned') ? staticSite.identity.principalId : ''

Which was invoked by this bicep (note we only pass appSettings; we no longer pass functionAppAppSettings):

module staticWebApp 'static-sites.bicep' = {
  name: '${deploymentPrefix}-how-you-doin-${branchHash}'
  params: {
    appSettings: {
      AAD_CLIENT_ID: aadClientIdRef
      AAD_CLIENT_SECRET: aadClientSecretRef

      keyVaultName: keyVaultName
      APPINSIGHTS_INSTRUMENTATIONKEY: appInsights.properties.InstrumentationKey
      APPLICATIONINSIGHTS_CONNECTION_STRING: appInsights.properties.ConnectionString
      telemetryU: telemetryU
      telemetryP: telemetryP
      telemetryDbKey: telemetryDbAccountDb.listKeys().primaryMasterKey

      telemetryURef: telemetryURef
      telemetryPRef: telemetryPRef
      telemetryDbKeyRef: telemetryDbKeyRef

      telemetryDbDatabaseName: telemetryDbDatabaseName
      telemetryDbContainerName: telemetryDbContainerName
      telemetryDbEndpoint: telemetryDbAccountDb.properties.documentEndpoint
    }
    location: location
    repositoryBranch: repositoryBranch
    repositoryUrl: repositoryUrl
    staticSiteName: staticSiteName
    tags: tagsWithHiddenLinks
    customDomainName: (repositoryBranch == 'main') ? customDomainName : ''
  }
}

However, new problems presented. When running the deployment of the app:

          - task: AzureStaticWebApp@0
            name: DeployStaticWebApp
            displayName: Deploy Static Web App
            inputs:
              app_location: "./src/how-you-doin"
              output_location: "dist"
              api_location: "./src/how-you-doin/api"
              verbose: true
              azure_static_web_apps_api_token: $(apiKey)

The following "Failed to deploy the Azure Functions" failure resulted:

image
---End of Oryx build logs---
Oryx build has completed. Took 16.9032263 seconds. Exit Code: 0
Function Runtime Information. OS: linux, Functions Runtime: ~4, node version: 18
Finished building function app with Oryx
Zipping Api Artifacts
Api Zip will be created from directory: /da68094a-ad1d-4172-a9a3-2af5a5f717d6-swa-oryx/api
Api Content Hash: 101f7f753b8bebcd752dfe1e8d06fa72
Done Zipping Api Artifacts
Zipping App Artifacts
App Zip will be created from directory: /da68094a-ad1d-4172-a9a3-2af5a5f717d6-swa-oryx/app/dist
Done Zipping App Artifacts
Uploading build artifacts.
Finished Upload. Polling on deployment.
Status: InProgress. Time: 0.0456268(s)
Status: InProgress. Time: 15.7960357(s)
Status: InProgress. Time: 31.4996043(s)
Status: InProgress. Time: 46.5379767(s)
Status: InProgress. Time: 61.5658698(s)
Status: Failed. Time: 76.6065698(s)
Deployment Failed :(
Deployment Failure Reason: Failed to deploy the Azure Functions.

For further information, please visit the Azure Static Web Apps documentation at https://docs.microsoft.com/en-us/azure/static-web-apps/
If you believe this behavior is unexpected, please raise a GitHub issue at https://github.com/azure/static-web-apps/issues/
Exiting
##[error]Error: The process '/usr/bin/bash' failed with exit code 1
Finishing: Deploy Static Web App
johnnyreilly commented 1 year ago

Is this linked to #819?

johnnyreilly commented 1 year ago

So I think this was two things:

  1. Using 2 configurations when it turns out SWAs only support 1
  2. Once that was resolved we bumped on the outage

I think this might be resolved! Closing with optimism.

johnnyreilly commented 1 year ago

I've updated a historic blog post to give better advice around how to configure static web apps: https://johnnyreilly.com/application-insights-bicep-azure-static-web-apps#updated-9-march-2023

lipalath-ms commented 1 year ago

@johnnyreilly Could you please confirm if this is what you were facing. When I deployed static web app with some config settings, this is what I see:

image

Here is my bicep:

@description('Enter an abbreviation for the solution.')
@minLength(2)
@maxLength(3)
param solutionAbbreviation string = 'xxx'

@description('Enter an abbreviation for the environment.')
@minLength(2)
@maxLength(6)
param environmentAbbreviation string

@description('Resource location.')
param location string

@description('Name of the public source branch where webapp repo exists.')
param branch string

@description('Repository URL.')
param repositoryUrl string

resource staticWebApp 'Microsoft.Web/staticSites@2022-03-01' = {
  name: '${solutionAbbreviation}-ui-${environmentAbbreviation}'
  location: location
  sku: {
    name: 'Free'
    tier: 'Free'
  }
  properties: {
    allowConfigFileUpdates: true
    branch: branch
    buildProperties: {
      appLocation: 'UI/web-app'
      skipGithubActionWorkflowGeneration: true
    }
    enterpriseGradeCdnStatus: 'Disabled'
    provider: 'DevOps'
    repositoryUrl: repositoryUrl
    stagingEnvironmentPolicy: 'Disabled'
  }
}

resource staticWebAppAppSettings 'Microsoft.Web/staticSites/config@2022-03-01' = {
  parent: staticWebApp
  kind: 'config'
  name: 'appsettings'
  properties: {
    REACT_APP_AAD_UI_APP_CLIENT_ID: 'xxx'
    REACT_APP_AAD_API_APP_CLIENT_ID: 'xxx'
    REACT_APP_AAD_APP_TENANT_ID: 'xxx'
    REACT_APP_AAD_APP_SERVICE_BASE_URI: 'https://xxx-webapi.azurewebsites.net'
  }
}

// Check on if this API Key is acceptable for deployment purposes of front end
output deployment_token string = listSecrets(staticWebApp.id, staticWebApp.apiVersion).properties.apiKey

I'm unable to view these in the config:

    REACT_APP_AAD_UI_APP_CLIENT_ID: 'xxx'
    REACT_APP_AAD_API_APP_CLIENT_ID: 'xxx'
    REACT_APP_AAD_APP_TENANT_ID: 'xxx'
    REACT_APP_AAD_APP_SERVICE_BASE_URI: 'https://xxx-webapi.azurewebsites.net'

Could you please let me know how you resolved this?

johnnyreilly commented 1 year ago

I think your issue is different to mine; I always saw the configuration from at least one config; you don't appear to see it at all.

Feel free to copy my code: https://github.com/johnnyreilly/blog.johnnyreilly.com/tree/main/infra

lipalath-ms commented 1 year ago

Thanks! Do you know what role or least permission is required to access config settings of static web app? I don't see 'Contributor' role for this static web app resource. I opened an issue regarding this: https://github.com/Azure/static-web-apps/issues/1121. Please let me know.