diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 6bc006d1..bd20e051 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -7,4 +7,5 @@ The dev container configuration lets you open the repository in a [GitHub codesp - Python - Running `pip install -r requirements.txt` from the project at container start. - PostgreSQL +- Redis - [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/overview) (so you can run `azd` commands directly). diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bba23fd0..881c06aa 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,7 @@ "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "features": { "ghcr.io/azure/azure-dev/azd:latest": {} - }, + }, "customizations": { "vscode": { // Add the IDs of extensions you want installed when the container is created. @@ -14,7 +14,8 @@ "ms-python.python", "ms-python.vscode-pylance", "mtxr.sqltools", - "mtxr.sqltools-driver-pg" + "mtxr.sqltools-driver-pg", + "GitHub.copilot" ], "settings": { "sqltools.connections": [ diff --git a/.github/workflows/python-test.yaml b/.github/workflows/python-test.yaml index e438a10a..41e454af 100644 --- a/.github/workflows/python-test.yaml +++ b/.github/workflows/python-test.yaml @@ -51,3 +51,4 @@ jobs: DBHOST: localhost DBUSER: postgres DBPASS: postgres + SECRET_KEY: flask-insecure-key-${{ github.run_id }}-${{ github.run_attempt }} diff --git a/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml b/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml new file mode 100644 index 00000000..0a048cea --- /dev/null +++ b/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml @@ -0,0 +1,81 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy +# More GitHub Actions for Azure: https://github.com/Azure/actions +# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions + +name: Build and deploy Python app to Azure Web App - web3-hotel-krivamar + +on: + push: + branches: + - starter-no-infra + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read #This is required for actions/checkout + + 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 + contents: read #This is required for actions/checkout + + 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_EB9DA3824EFE47CD85819EB7185C6CEE }} + tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_585414977F2D4BC3A05AE4A7A1384849 }} + subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_BF0EEB52860944129C22EDD6B5D8E130 }} + + - name: 'Deploy to Azure Web App' + uses: azure/webapps-deploy@v3 + id: deploy-to-webapp + with: + app-name: 'web3-hotel-krivamar' + slot-name: 'Production' + \ No newline at end of file diff --git a/.vscode/README.md b/.vscode/README.md deleted file mode 100644 index 35976b45..00000000 --- a/.vscode/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# .vscode directory - -This `.vscode` directory contains configuration that lets you launch and debug in Visual Studio Code and isn't used by the sample application. diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index b1d7f90c..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Run server", - "type": "python", - "request": "launch", - "module": "flask", - "env": { - "FLASK_APP": "app.py", - "FLASK_DEBUG": "1" - }, - "args": [ - "run", - "--no-debugger", - "--no-reload" - ], - "jinja": true, - "justMyCode": true - } - ] -} diff --git a/app.py b/app.py index 8fc6a89b..bf07d074 100644 --- a/app.py +++ b/app.py @@ -118,6 +118,6 @@ def star_rating(id): def favicon(): return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon') - -if __name__ == '__main__': - app.run() +# add port 8000 +#if __name__ == '__main__': +# app.run(host='0.0.0.0', port=8000) diff --git a/azure.yaml b/azure.yaml deleted file mode 100644 index 033038f2..00000000 --- a/azure.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json -# azure.yaml is an azd configuration file and isn't used by the sample application. - -name: python-app-service-postgresql-redis-infra -metadata: - template: python-app-service-postgresql-redis-infra@0.0.1-beta -services: - web: - project: . - language: py - host: appservice -hooks: - postprovision: - posix: - shell: sh - run: printf '\nApp Service app has the following connection settings:\n' && printf "$CONNECTION_SETTINGS" | jq -r '.[]' | sed 's/\(.*\)/\t- \1/' && printf "\nSee the settings in the portal:\033[1;36m $WEB_APP_CONFIG\n" - interactive: true - continueOnError: true - windows: - shell: pwsh - run: Write-Host "`n`nApp Service app has the following connection settings:`n" $CONNECTION_SETTINGS | ConvertFrom-Json | ForEach-Object { Write-Host "\t- $_" } - interactive: true - continueOnError: true - postdeploy: - posix: - shell: sh - run: printf "Open SSH session to App Service container at:\033[1;36m $WEB_APP_SSH\033[0m\nStream App Service logs at:\033[1;36m $WEB_APP_LOG_STREAM\n" - interactive: true - continueOnError: true - windows: - shell: pwsh - run: Write-Host "`n`nOpen SSH session to App Service container at:`n" $WEB_APP_SSH; Write-Host "Stream App Service logs at:`n" $WEB_APP_LOG_STREAM - interactive: true - continueOnError: true \ No newline at end of file diff --git a/azureproject/production.py b/azureproject/production.py index 388f2a87..cf67e72c 100644 --- a/azureproject/production.py +++ b/azureproject/production.py @@ -1,8 +1,8 @@ import os -DATABASE_URI = 'postgresql+psycopg2://{dbuser}:{dbpass}@{dbhost}/{dbname}'.format( - dbuser=os.getenv('AZURE_POSTGRESQL_USER'), - dbpass=os.getenv('AZURE_POSTGRESQL_PASSWORD'), - dbhost=os.getenv('AZURE_POSTGRESQL_HOST'), - dbname=os.getenv('AZURE_POSTGRESQL_NAME') -) \ No newline at end of file + DATABASE_URI = 'postgresql+psycopg2://{dbuser}:{dbpass}@{dbhost}/{dbname}'.format( + dbuser=os.getenv('AZURE_POSTGRESQL_USER'), + dbpass=os.getenv('AZURE_POSTGRESQL_PASSWORD'), + dbhost=os.getenv('AZURE_POSTGRESQL_HOST'), + dbname=os.getenv('AZURE_POSTGRESQL_NAME') + ) \ No newline at end of file diff --git a/infra/README.md b/infra/README.md deleted file mode 100644 index b6a6c2e0..00000000 --- a/infra/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# infra directory - -This `infra` directory contains azd files used for `azd provision` and isn't used by the sample application. diff --git a/infra/appinsights.bicep b/infra/appinsights.bicep deleted file mode 100644 index 83cfe17a..00000000 --- a/infra/appinsights.bicep +++ /dev/null @@ -1,1244 +0,0 @@ -param prefix string -param location string -param tags object -param workspaceId string - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { - name: '${prefix}-appinsights' - location: location - tags: tags - kind: 'web' - properties: { - Application_Type: 'web' - WorkspaceResourceId: workspaceId - } -} - -// 2020-09-01-preview because that is the latest valid version -resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { - name: '${prefix}-dashboard' - location: location - tags: tags - properties: { - lenses: [ - { - order: 0 - parts: [ - { - position: { - x: 0 - y: 0 - colSpan: 2 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'id' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' - asset: { - idInputName: 'id' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'overview' - } - } - { - position: { - x: 2 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'ProactiveDetection' - } - } - { - position: { - x: 3 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:20:33.345Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 5 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-08T18:47:35.237Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'ConfigurationId' - value: '78ce933e-e864-4b05-a27b-71fd55a6afad' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 0 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Usage' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 3 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:22:35.782Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Reliability' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 7 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:42:40.072Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'failures' - } - } - { - position: { - x: 8 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Responsiveness\r\n' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 11 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:43:37.804Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'performance' - } - } - { - position: { - x: 12 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Browser' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 15 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'MetricsExplorerJsonDefinitionId' - value: 'BrowserPerformanceTimelineMetrics' - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - createdTime: '2018-05-08T12:16:27.534Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'CurrentFilter' - value: { - eventTypes: [ - 4 - 1 - 3 - 5 - 2 - 6 - 13 - ] - typeFacets: {} - isPermissive: false - } - } - { - name: 'id' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'browser' - } - } - { - position: { - x: 0 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'sessions/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Sessions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'users/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Users' - color: '#7E58FF' - } - } - ] - title: 'Unique sessions and users' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'segmentationUsers' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Failed requests' - color: '#EC008C' - } - } - ] - title: 'Failed requests' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'failures' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/duration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server response time' - color: '#00BCF2' - } - } - ] - title: 'Server response time' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'performance' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/networkDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Page load network connect time' - color: '#7E58FF' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/processingDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Client processing time' - color: '#44F1C8' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/sendDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Send request time' - color: '#EB9371' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/receiveDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Receiving response time' - color: '#0672F1' - } - } - ] - title: 'Average page load time breakdown' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/availabilityPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability' - color: '#47BDF5' - } - } - ] - title: 'Average availability' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'availability' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/server' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server exceptions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'dependencies/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Dependency failures' - color: '#7E58FF' - } - } - ] - title: 'Server exceptions and Dependency failures' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processorCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Processor time' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process CPU' - color: '#7E58FF' - } - } - ] - title: 'Average processor and process CPU utilization' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/browser' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Browser exceptions' - color: '#47BDF5' - } - } - ] - title: 'Browser exceptions' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/count' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability test results count' - color: '#47BDF5' - } - } - ] - title: 'Availability test results count' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processIOBytesPerSecond' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process IO rate' - color: '#47BDF5' - } - } - ] - title: 'Average process I/O rate' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/memoryAvailableBytes' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Available memory' - color: '#47BDF5' - } - } - ] - title: 'Average available memory' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - ] - } - ] - } -} - -output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.properties.ConnectionString diff --git a/infra/main.bicep b/infra/main.bicep deleted file mode 100644 index 376f2467..00000000 --- a/infra/main.bicep +++ /dev/null @@ -1,51 +0,0 @@ -targetScope = 'subscription' - -@minLength(1) -@maxLength(64) -@description('Name which is used to generate a short unique hash for each resource') -param name string - -@minLength(1) -@description('Primary location for all resources') -param location string - -@secure() -@description('PostGreSQL Server administrator password') -param databasePassword string - -@secure() -@description('Django SECRET_KEY for securing signed data') -param secretKey string - -param principalId string = '' - -var resourceToken = toLower(uniqueString(subscription().id, name, location)) -var tags = { 'azd-env-name': name } - -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: '${name}-rg' - location: location - tags: tags -} - -module resources 'resources.bicep' = { - name: 'resources' - scope: resourceGroup - params: { - name: name - location: location - resourceToken: resourceToken - tags: tags - databasePassword: databasePassword - principalId: principalId - secretKey: secretKey - } -} - -output AZURE_LOCATION string = location -output APPLICATIONINSIGHTS_CONNECTION_STRING string = resources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING -output WEB_URI string = resources.outputs.WEB_URI -output CONNECTION_SETTINGS array = resources.outputs.CONNECTION_SETTINGS -output WEB_APP_LOG_STREAM string = resources.outputs.WEB_APP_LOG_STREAM -output WEB_APP_SSH string = resources.outputs.WEB_APP_SSH -output WEB_APP_CONFIG string = resources.outputs.WEB_APP_CONFIG diff --git a/infra/main.parameters.json b/infra/main.parameters.json deleted file mode 100644 index a6f1d93b..00000000 --- a/infra/main.parameters.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "name": { - "value": "${AZURE_ENV_NAME}" - }, - "location": { - "value": "${AZURE_LOCATION}" - }, - "databasePassword": { - "value": "$(secretOrRandomPassword)" - }, - "principalId": { - "value": "${AZURE_PRINCIPAL_ID}" - }, - "secretKey": { - "value": "$(secretOrRandomPassword)" - } - } - } diff --git a/infra/resources.bicep b/infra/resources.bicep deleted file mode 100644 index f5fba55f..00000000 --- a/infra/resources.bicep +++ /dev/null @@ -1,547 +0,0 @@ -param name string -param location string -param resourceToken string -param principalId string -param tags object -@secure() -param databasePassword string -@secure() -param secretKey string -var appName = '${name}-${resourceToken}' - -var pgServerName = '${appName}-server' - -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { - name: '${appName}-vnet' - location: location - tags: tags - properties: { - addressSpace: { - addressPrefixes: [ - '10.0.0.0/16' - ] - } - subnets: [ - { - name: 'database-subnet' - properties: { - addressPrefix: '10.0.0.0/24' - delegations: [ - { - name: '${appName}-subnet-delegation' - properties: { - serviceName: 'Microsoft.DBforPostgreSQL/flexibleServers' - } - } - ] - privateEndpointNetworkPolicies: 'Enabled' - privateLinkServiceNetworkPolicies: 'Enabled' - } - } - { - name: 'webapp-subnet' - properties: { - addressPrefix: '10.0.1.0/24' - delegations: [ - { - name: 'dlg-appServices' - properties: { - serviceName: 'Microsoft.Web/serverFarms' - } - } - ] - } - } - { - name: 'cache-subnet' - properties:{ - addressPrefix: '10.0.2.0/24' - privateEndpointNetworkPolicies: 'Disabled' - } - } - { - name: 'vault-subnet' - properties: { - addressPrefix: '10.0.3.0/24' - privateEndpointNetworkPolicies: 'Disabled' - } - } - ] - } - resource subnetForDb 'subnets' existing = { - name: 'database-subnet' - } - resource subnetForVault 'subnets' existing = { - name: 'vault-subnet' - } - resource subnetForApp 'subnets' existing = { - name: 'webapp-subnet' - } - resource subnetForCache 'subnets' existing = { - name: 'cache-subnet' - } -} - -// Resources needed to secure Key Vault behind a private endpoint -resource privateDnsZoneKeyVault 'Microsoft.Network/privateDnsZones@2020-06-01' = { - name: 'privatelink.vaultcore.azure.net' - location: 'global' - resource vnetLink 'virtualNetworkLinks@2020-06-01' = { - location: 'global' - name: '${appName}-vaultlink' - properties: { - virtualNetwork: { - id: virtualNetwork.id - } - registrationEnabled: false - } - } -} -resource vaultPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { - name: '${appName}-vault-privateEndpoint' - location: location - properties: { - subnet: { - id: virtualNetwork::subnetForVault.id - } - privateLinkServiceConnections: [ - { - name: '${appName}-vault-privateEndpoint' - properties: { - privateLinkServiceId: keyVault.id - groupIds: ['vault'] - } - } - ] - } - resource privateDnsZoneGroup 'privateDnsZoneGroups@2024-01-01' = { - name: 'default' - properties: { - privateDnsZoneConfigs: [ - { - name: 'vault-config' - properties: { - privateDnsZoneId: privateDnsZoneKeyVault.id - } - } - ] - } - } -} - -resource privateDnsZoneDB 'Microsoft.Network/privateDnsZones@2024-06-01' = { - name: '${pgServerName}.private.postgres.database.azure.com' - location: 'global' - tags: tags - dependsOn: [ - virtualNetwork - ] - resource privateDnsZoneLinkDB 'virtualNetworkLinks@2024-06-01' = { - name: '${appName}-dblink' - location: 'global' - properties: { - virtualNetwork: { - id: virtualNetwork.id - } - registrationEnabled: false - } - } -} - -// Resources needed to secure Redis Cache behind a private endpoint -resource cachePrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-03-01' = { - name: '${appName}-cache-privateEndpoint' - location: location - properties: { - subnet: { - id: virtualNetwork::subnetForCache.id - } - privateLinkServiceConnections: [ - { - name: '${appName}-cache-privateEndpoint' - properties: { - privateLinkServiceId: redisCache.id - groupIds: ['redisCache'] - } - } - ] - } - resource privateDnsZoneGroup 'privateDnsZoneGroups' = { - name: 'default' - properties: { - privateDnsZoneConfigs: [ - { - name: 'cache-config' - properties: { - privateDnsZoneId: privateDnsZoneCache.id - } - } - ] - } - } -} -resource privateDnsZoneCache 'Microsoft.Network/privateDnsZones@2024-06-01' = { - name: 'privatelink.redis.cache.windows.net' - location: 'global' - dependsOn: [ - virtualNetwork - ] - resource privateDnsZoneLinkCache 'virtualNetworkLinks@2020-06-01' = { - name: '${appName}-cachelink' - location: 'global' - properties: { - virtualNetwork: { - id: virtualNetwork.id - } - registrationEnabled: false - } - } -} - -// The Key Vault is used to manage SQL database and redis secrets. -// Current user has the admin permissions to configure key vault secrets, but by default doesn't have the permissions to read them. -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { - name: '${take(replace(appName, '-', ''), 17)}-vault' - location: location - properties: { - enableRbacAuthorization: true - tenantId: subscription().tenantId - sku: { family: 'A', name: 'standard' } - // Only allow requests from the private endpoint in the VNET. - publicNetworkAccess: 'Disabled' // To see the secret in the portal, change to 'Enabled' - networkAcls: { - defaultAction: 'Deny' // To see the secret in the portal, change to 'Allow' - bypass: 'None' - } - } -} - -// Grant the current user with key vault secret user role permissions over the key vault. This lets you inspect the secrets, such as in the portal -// If you remove this section, you can't read the key vault secrets, but the app still has access with its managed identity. -resource keyVaultSecretUserRoleRoleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = { - scope: subscription() - name: '4633458b-17de-408a-b874-0445c86b69e6' // The built-in Key Vault Secret User role -} -resource keyVaultSecretUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = { - scope: keyVault - name: guid(resourceGroup().id, principalId, keyVaultSecretUserRoleRoleDefinition.id) - properties: { - roleDefinitionId: keyVaultSecretUserRoleRoleDefinition.id - principalId: principalId - principalType: 'User' - } -} - -resource dbserver 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' = { - location: location - tags: tags - name: pgServerName - sku: { - name: 'Standard_B1ms' - tier: 'Burstable' - } - properties: { - version: '12' - administratorLogin: 'postgresadmin' - administratorLoginPassword: databasePassword - storage: { - storageSizeGB: 128 - } - backup: { - backupRetentionDays: 7 - geoRedundantBackup: 'Disabled' - } - network: { - delegatedSubnetResourceId: virtualNetwork::subnetForDb.id - privateDnsZoneArmResourceId: privateDnsZoneDB.id - } - highAvailability: { - mode: 'Disabled' - } - maintenanceWindow: { - customWindow: 'Disabled' - dayOfWeek: 0 - startHour: 0 - startMinute: 0 - } - } - - resource db 'databases@2024-08-01' = { - name: '${appName}-database' - } - dependsOn: [ - privateDnsZoneDB::privateDnsZoneLinkDB - ] -} - -// The Redis cache is configured to the minimum pricing tier -resource redisCache 'Microsoft.Cache/redis@2024-11-01' = { - name: '${appName}-cache' - location: location - properties: { - sku: { - name: 'Basic' - family: 'C' - capacity: 0 - } - redisConfiguration: {} - enableNonSslPort: false - redisVersion: '6' - publicNetworkAccess: 'Disabled' - } -} - -// The App Service plan is configured to the B1 pricing tier -resource appServicePlan 'Microsoft.Web/serverfarms@2024-04-01' = { - name: '${appName}-plan' - location: location - kind: 'linux' - properties: { - reserved: true - } - sku: { - name: 'B1' - } -} - -resource web 'Microsoft.Web/sites@2024-04-01' = { - name: appName - location: location - tags: union(tags, { 'azd-service-name': 'web' }) // Needed by AZD - properties: { - siteConfig: { - linuxFxVersion: 'PYTHON|3.12' // Set to Python 3.12 - ftpsState: 'Disabled' - appCommandLine: 'startup.sh' - minTlsVersion: '1.2' - } - serverFarmId: appServicePlan.id - httpsOnly: true - } - identity: { - type: 'SystemAssigned' - } - - // For app setting configuration see the appsettings resource - - // Disable basic authentication for FTP and SCM - resource ftp 'basicPublishingCredentialsPolicies@2023-12-01' = { - name: 'ftp' - properties: { - allow: false - } - } - resource scm 'basicPublishingCredentialsPolicies@2023-12-01' = { - name: 'scm' - properties: { - allow: false - } - } - - // Enable App Service native logs - resource logs 'config' = { - name: 'logs' - properties: { - applicationLogs: { - fileSystem: { - level: 'Verbose' - } - } - detailedErrorMessages: { - enabled: true - } - failedRequestsTracing: { - enabled: true - } - httpLogs: { - fileSystem: { - enabled: true - retentionInDays: 1 - retentionInMb: 35 - } - } - } - } - - // Enable VNET integration - resource webappVnetConfig 'networkConfig' = { - name: 'virtualNetwork' - properties: { - subnetResourceId: virtualNetwork::subnetForApp.id - } - } - - dependsOn: [ virtualNetwork ] -} - -// Service Connector from the app to the key vault, which generates the connection settings for the App Service app -// The application code doesn't make any direct connections to the key vault, but the setup expedites the managed identity access -// so that the cache connector can be configured with key vault references. -resource vaultConnector 'Microsoft.ServiceLinker/linkers@2024-04-01' = { - scope: web - name: 'vaultConnector' - properties: { - clientType: 'python' - targetService: { - type: 'AzureResource' - id: keyVault.id - } - authInfo: { - authType: 'systemAssignedIdentity' // Use a system-assigned managed identity. No password is used. - } - vNetSolution: { - type: 'privateLink' - } - } - dependsOn: [ - vaultPrivateEndpoint - ] -} - -// Connector to the PostgreSQL database, which generates the connection string for the App Service app -resource dbConnector 'Microsoft.ServiceLinker/linkers@2024-04-01' = { - scope: web - name: 'defaultConnector' - properties: { - targetService: { - type: 'AzureResource' - id: dbserver::db.id - } - authInfo: { - authType: 'secret' - name: 'postgresadmin' - secretInfo: { - secretType: 'rawValue' - value: databasePassword - } - } - secretStore: { - keyVaultId: keyVault.id // Configure secrets as key vault references. No secret is exposed in App Service. - } - clientType: 'django' - } -} - -// Service Connector from the app to the cache, which generates an app setting for the App Service app -resource cacheConnector 'Microsoft.ServiceLinker/linkers@2024-04-01' = { - scope: web - name: 'RedisConnector' - properties: { - clientType: 'python' - targetService: { - type: 'AzureResource' - id: resourceId('Microsoft.Cache/Redis/Databases', redisCache.name, '0') - } - authInfo: { - authType: 'accessKey' - } - secretStore: { - keyVaultId: keyVault.id // Configure secrets as key vault references. No secret is exposed in App Service. - } - vNetSolution: { - type: 'privateLink' - - } - } - dependsOn: [ - cachePrivateEndpoint - ] -} - -resource webdiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: 'AllLogs' - scope: web - properties: { - workspaceId: logAnalyticsWorkspace.id - logs: [ - { - category: 'AppServiceHTTPLogs' - enabled: true - } - { - category: 'AppServiceConsoleLogs' - enabled: true - } - { - category: 'AppServiceAppLogs' - enabled: true - } - { - category: 'AppServiceAuditLogs' - enabled: true - } - { - category: 'AppServiceIPSecAuditLogs' - enabled: true - } - { - category: 'AppServicePlatformLogs' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { - name: '${appName}-workspace' - location: location - tags: tags - properties: any({ - retentionInDays: 30 - features: { - searchVersion: 1 - } - sku: { - name: 'PerGB2018' - } - }) -} - -module applicationInsightsResources 'appinsights.bicep' = { - name: 'applicationinsights-resources' - params: { - prefix: appName - location: location - tags: tags - workspaceId: logAnalyticsWorkspace.id - } -} - -func checkAndFormatSecrets(config object) string => config.configType == 'KeyVaultSecret' ? '@Microsoft.KeyVault(SecretUri=${config.value})' : config.value - -// Add the app settings, by merging them with the ones created by the service connectors -var aggregatedAppSettings = union( - reduce(vaultConnector.listConfigurations().configurations, {}, (cur, next) => union(cur, { '${next.name}': checkAndFormatSecrets(next) })), - reduce(dbConnector.listConfigurations().configurations, {}, (cur, next) => union(cur, { '${next.name}': checkAndFormatSecrets(next) })), - reduce(cacheConnector.listConfigurations().configurations, {}, (cur, next) => union(cur, { '${next.name}': checkAndFormatSecrets(next) })), - { - SCM_DO_BUILD_DURING_DEPLOYMENT: 'true' - FLASK_DEBUG: 'False' - SECRET_KEY: secretKey - // Add other app settings here, for example: - // 'FOO': 'BAR' - } -) -resource appsettings 'Microsoft.Web/sites/config@2024-04-01' = { - name: 'appsettings' - parent: web - properties: aggregatedAppSettings -} -// Why is this needed? -// The service connectors automatically add necessary respective app settings to the App Service app. However, if you configure a separate -// set of app settings in a config/appsettings resource, expecting a cummulative effect, the app settings actually overwrite the ones -// created by the service connectors, and the service connectors don't recreate the app settings after the first run. This configuration -// is a workaround to ensure that the app settings are aggregated correctly and consistent across multiple deployments. - -output WEB_URI string = 'https://${web.properties.defaultHostName}' -output CONNECTION_SETTINGS array = map(concat(dbConnector.listConfigurations().configurations, cacheConnector.listConfigurations().configurations, vaultConnector.listConfigurations().configurations), config => config.name) -output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsightsResources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING -output WEB_APP_LOG_STREAM string = format('https://portal.azure.com/#@/resource{0}/logStream', web.id) -output WEB_APP_SSH string = format('https://{0}.scm.azurewebsites.net/webssh/host', web.name) -output WEB_APP_CONFIG string = format('https://portal.azure.com/#@/resource{0}/environmentVariablesAppSettings', web.id) diff --git a/startup.sh b/startup.sh deleted file mode 100644 index df8c6998..00000000 --- a/startup.sh +++ /dev/null @@ -1,6 +0,0 @@ -# startup.sh is used by infra/resources.bicep to automate database migrations and isn't used by the sample application - -flask db upgrade -gunicorn --workers 2 --threads 4 --timeout 60 --access-logfile \ - '-' --error-logfile '-' --bind=0.0.0.0:8000 \ - --chdir=/home/site/wwwroot app:app