I have deployed a FastAPI to azure web apps. I have used the following yaml:
webapps-actions
name: Build and deploy Python app to Azure Web App - fast-api-port
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python version
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Create and start virtual environment
run: |
python -m venv venv
source venv/bin/activate
- name: Install dependencies
run: pip install -r requirements.txt
# Optional: Add step to run tests here (PyTest, Django test suites, etc.)
- name: Zip artifact for deployment
run: zip release.zip ./* -r
- name: Upload artifact for deployment jobs
uses: actions/upload-artifact@v4
with:
name: python-app
path: |
release.zip
!venv/
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
permissions:
id-token: write #This is required for requesting the JWT
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: python-app
- name: Unzip artifact for deployment
run: unzip release.zip
- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_xx }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_xx }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_xx }}
- name: 'Deploy to Azure Web App'
uses: azure/webapps-deploy@v3
id: deploy-to-webapp
with:
app-name: 'fast-api-port'
slot-name: 'Production'
I can see the files now exists on the server:
However when I navigate to the url of the web app I get following:
How do I start the uvicorn server in the deployment? Somehow I need the fastapi to get started.
I added following in Configuration:
Code:
from fastapi import FastAPI,Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
import uvicorn
from fastapi import FastAPI, Security
import os
from typing import Dict
from settings import Settings
from pydantic import AnyHttpUrl,BaseModel
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from fastapi_azure_auth.user import User
settings = Settings()
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
"""
Load OpenID config on startup.
"""
await azure_scheme.openid_config.load_config()
yield
app = FastAPI(
swagger_ui_oauth2_redirect_url='/oauth2-redirect',
swagger_ui_init_oauth={
'usePkceWithAuthorizationCodeGrant': True,
'clientId': settings.OPENAPI_CLIENT_ID,
'scopes': settings.SCOPE_NAME,
},
)
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
CORSMiddleware,
allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
)
azure_scheme = SingleTenantAzureAuthorizationCodeBearer(
app_client_id=settings.APP_CLIENT_ID,
tenant_id=settings.TENANT_ID,
scopes=settings.SCOPES,
)
class User(BaseModel):
name: str
roles: list[str] = []
@app.get("/", dependencies=[Security(azure_scheme)])
async def root():
print("Yo bro")
return {"whoIsTheBest": "DNA Team is"}
@app.get("/test", dependencies=[Security(azure_scheme)])
async def root():
print("Yo test")
return {"whoIsTheBest": "DNA Team is!"}
@app.get("/me", dependencies=[Security(azure_scheme)])
async def me(request: Request):
print("Me")
return User(roles=request.state.user.roles,name=request.state.user.name)
if __name__ == '__main__':
uvicorn.run('main:app', reload=True)
I tried your code and successfully deployed it Azure Web app without any issues.
I got the same error after deploying the application, so to resolve the issue I've configured the below startup command in my Azure Web app configuration section.
gunicorn --worker-class uvicorn.workers.UvicornWorker --timeout 600 --access-logfile '-' --error-logfile '-' main:app
requirements.txt
file.requirements.txt:
fastapi-azure-auth
pydantic-settings
uvicorn
pydantic
fastapi
main .py:
import uvicorn
from fastapi import FastAPI, Security
from fastapi.middleware.cors import CORSMiddleware
from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
from pydantic import AnyHttpUrl
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
BACKEND_CORS_ORIGINS: list[str | AnyHttpUrl] = ['http://localhost:8000']
OPENAPI_CLIENT_ID: str = "<openapi-client-id>"
APP_CLIENT_ID: str = "<App-client-id>"
TENANT_ID: str = "<Tenant-id>"
SCOPE_DESCRIPTION: str = "user_impersonation"
@property
def SCOPE_NAME(self) -> str:
return f'api://{self.APP_CLIENT_ID}/{self.SCOPE_DESCRIPTION}'
@property
def SCOPES(self) -> dict:
return {
self.SCOPE_NAME: self.SCOPE_DESCRIPTION,
}
@property
def OPENAPI_AUTHORIZATION_URL(self) -> str:
return f"https://login.microsoftonline.com/{self.TENANT_ID}/oauth2/v2.0/authorize"
@property
def OPENAPI_TOKEN_URL(self) -> str:
return f"https://login.microsoftonline.com/{self.TENANT_ID}/oauth2/v2.0/token"
class Config:
env_file = '.env'
env_file_encoding = 'utf-8'
case_sensitive = True
settings = Settings()
app = FastAPI(
swagger_ui_oauth2_redirect_url='/oauth2-redirect',
swagger_ui_init_oauth={
'usePkceWithAuthorizationCodeGrant': True,
'clientId': settings.OPENAPI_CLIENT_ID,
'scopes': settings.SCOPE_NAME,
},
)
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
CORSMiddleware,
allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
)
azure_scheme = SingleTenantAzureAuthorizationCodeBearer(
app_client_id=settings.APP_CLIENT_ID,
tenant_id=settings.TENANT_ID,
scopes=settings.SCOPES,
)
@app.on_event('startup')
async def load_config() -> None:
"""
Load OpenID config on startup.
"""
await azure_scheme.openid_config.load_config()
@app.get("/", dependencies=[Security(azure_scheme)])
async def root():
return {"message": "Hello World"}
if __name__ == '__main__':
uvicorn.run('main:app', reload=True)
workflow file:
name: Build and deploy Python app to Azure Web App - fastapiappad
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python version
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Create and start virtual environment
run: |
python -m venv venv
source venv/bin/activate
- name: Install dependencies
run: pip install -r requirements.txt
- name: Zip artifact for deployment
run: zip release.zip ./* -r
- name: Upload artifact for deployment jobs
uses: actions/upload-artifact@v4
with:
name: python-app
path: |
release.zip
!venv/
deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
permissions:
id-token: write #This is required for requesting the JWT
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: python-app
- name: Unzip artifact for deployment
run: unzip release.zip
- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_567E8DE5C0184859AA5CE7A1734B4FC2 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_68D8756C56714C6D8C105DF2BC96EBDE }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_D8A75A645E934359908917C8364D5670 }}
- name: 'Deploy to Azure Web App'
uses: azure/webapps-deploy@v3
id: deploy-to-webapp
with:
app-name: 'fastapiappad'
slot-name: 'Production'
I've successfully deployed the application to Azure Web App via GitHub actions.
Output: