Skip to content

Commit

Permalink
Merge pull request #9 from john0isaac/feat-azd-compatible
Browse files Browse the repository at this point in the history
Feat azd compatible
  • Loading branch information
pamelafox committed Jan 2, 2024
2 parents 2b2ce01 + b6a0993 commit 1756ff5
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 48 deletions.
6 changes: 0 additions & 6 deletions .azure/config

This file was deleted.

1 change: 0 additions & 1 deletion .azure/config.json

This file was deleted.

6 changes: 0 additions & 6 deletions .azure/translation-telephone/.env

This file was deleted.

7 changes: 0 additions & 7 deletions .azure/translation-telephone/config.json

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ env
.coverage
node_modules
.venv
.azure
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,36 @@ The app is currently hosted on Microsoft Azure. Specifically:
* Azure Database for PostGreSQL flexible server
* Azure Cognitive Services (for Translation)

To deploy your own instance, follow the [tutorial for Flask app + PostGreSQL deployment](https://docs.microsoft.com/en-us/azure/app-service/tutorial-python-postgresql-app) but using this app instead of the sample app.
To deploy your own instance, follow these steps:

Make sure you specify the following environment variables in the App Service configuration:
1. Sign up for a [free Azure account](https://azure.microsoft.com/free/?WT.mc_id=python-79461-pamelafox)
2. Install the [Azure Dev CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd?WT.mc_id=python-79461-pamelafox). (If you open this repository in Codespaces or with the VS Code Dev Containers extension, that part will be done for you.)
3. Initialize a new `azd` environment:

* `DBHOST`, `DBNAME`, `DBPASS`, `DBUSER`: The above linked tutorial shows how to set these.
* `FLASK_APP`: Set to 'src'
* `AZURE_TRANSLATE_API_KEY`: Get this by registering for Azure Cognitive Services.
```shell
azd init
```

You will also need to migrate the database by using the App Service SSH and running `flask db upgrade`.
It will prompt you to provide a name (like "flask-app") that will later be used in the name of the deployed resources.

4. Provision and deploy all the resources:

```shell
azd up
```

It will prompt you to login, pick a subscription, and provide a location (like "eastus"). Then it will provision the resources in your account and deploy the latest code. If you get an error with deployment, changing the location (like to "centralus") can help, as there may be availability constraints for some of the resources.

5. When azd has finished deploying, you'll see an endpoint URI in the command output. Visit that URI and you should see the website and be able to translate messages.

6. For the website to work fully (i.e. save translations to the database), you must migrate the database. Navigate to the App Service in the Azure Portal, select SSH, and run this command once you're in the SSH terminal:

```shell
flask db upgrade
```

6. When you've made any changes to the app code, you can just run:

```shell
azd deploy
```
2 changes: 1 addition & 1 deletion azure.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json

name: flask-db-quiz-example
name: translation-telephone

services:
web:
Expand Down
15 changes: 15 additions & 0 deletions infra/app/security.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
param keyVaultName string
param rgName string

param name string
param cognitiveServiceName string

// Store Cognitive Service Key in Key Vault
module cognitiveServiceSecret '../core/security/keyvault-secret.bicep' = {
name: 'cognitiveServiceKey'
params: {
name: name
keyVaultName : keyVaultName
secretValue: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', cognitiveServiceName), '2023-05-01').key1
}
}
41 changes: 41 additions & 0 deletions infra/core/ai/cognitiveservices.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@description('The name of the Cognitive Service.')
param name string

@description('The location into which your Azure resources should be deployed.')
param location string = resourceGroup().location

@description('The tags to apply to each resource.')
param tags object = {}

@description('The kind of Cognitive Service to create. See: https://learn.microsoft.com/en-us/azure/cognitive-services/create-account-bicep for available kinds.')
@allowed([ 'CognitiveServices', 'ComputerVision', 'CustomVision.Prediction', 'CustomVision.Training', 'Face', 'FormRecognizer', 'SpeechServices', 'LUIS', 'QnAMaker', 'TextAnalytics', 'TextTranslation', 'AnomalyDetector', 'ContentModerator', 'Personalizer', 'OpenAI' ])
param kind string

@description('The name of the SKU. Be aware that not all SKUs may be available for your Subscription. See: https://learn.microsoft.com/en-us/rest/api/cognitiveservices/accountmanagement/resource-skus')
@allowed([ 'F0', 'S0', 'S1', 'S2', 'S3', 'S4' ])
param sku string

param publicNetworkAccess string = 'Enabled'

resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' = {
name: name
location: location
tags: tags
sku: {
name: sku
}
kind: kind
properties: {
publicNetworkAccess: publicNetworkAccess

}
}

@description('Resource Name')
output name string = cognitiveService.name

@description('Resource Id')
output id string = cognitiveService.id

@description('Endpoint')
output endpoint string = cognitiveService.properties.endpoint
68 changes: 52 additions & 16 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,36 @@ param name string
@description('Primary location for all resources')
param location string

@secure()
@description('PostGreSQL Server administrator password')
param postgresServerPassword string

@description('Id of the user or app to assign application roles')
param principalId string = ''

@secure()
@description('PostGreSQL Server administrator password')
param postgresAdminPassword string

var resourceToken = toLower(uniqueString(subscription().id, name, location))
var tags = { 'azd-env-name': name }
var prefix = '${name}-${resourceToken}'

var postgresServerName = '${prefix}-postgres'
var postgresServerAdmin = 'flaskadmin'
var postgresDatabaseName = 'transtel'
var rgName = '${prefix}-rg'

resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: '${prefix}-rg'
name: rgName
location: location
tags: tags
}

module cognitiveService 'core/ai/cognitiveservices.bicep' = {
name: 'cognitiveservice'
scope: resourceGroup
params: {
name: '${prefix}-cognitiveservice'
location: location
sku: 'S1'
kind: 'TextTranslation'
publicNetworkAccess: 'Enabled'
}
}

// Store secrets in a keyvault
module keyVault './core/security/keyvault.bicep' = {
name: 'keyvault'
Expand All @@ -42,16 +51,40 @@ module keyVault './core/security/keyvault.bicep' = {
}
}

module keyVaultSecret './core/security/keyvault-secret.bicep' = {
name: 'keyvault-secret'
module postgreSQLDBSecret './core/security/keyvault-secret.bicep' = {
name: 'keyvaultsecret-postgresql'
scope: resourceGroup
params: {
keyVaultName: keyVault.outputs.name
name: 'postgresServerPassword'
secretValue: postgresServerPassword
name: 'postgresAdminPassword'
secretValue: postgresAdminPassword
}
}

module cognitiveServiceSecret './app/security.bicep' = {
name: 'keyvaultsecret-cognitiveservice'
scope: resourceGroup
params: {
rgName: rgName
keyVaultName: keyVault.outputs.name
name: 'cognitiveServiceKey'
cognitiveServiceName: cognitiveService.outputs.name
}
}

module webaccess './core/security/keyvault-access.bicep' = {
name: 'web-keyvault-access'
scope: resourceGroup
params: {
keyVaultName: keyVault.outputs.name
principalId: web.outputs.identityPrincipalId
}
}

var postgresServerName = '${prefix}-postgres'
var postgresServerAdmin = 'admin${uniqueString(resourceGroup.id)}'
var postgresDatabaseName = 'transtel'


module postgresServer 'core/database/postgresql/flexibleserver.bicep' = {
name: 'postgresql'
Expand All @@ -68,8 +101,8 @@ module postgresServer 'core/database/postgresql/flexibleserver.bicep' = {
storageSizeGB: 32
}
version: '13'
administratorLogin: 'flaskadmin'
administratorLoginPassword: postgresServerPassword
administratorLogin: postgresServerAdmin
administratorLoginPassword: postgresAdminPassword
databaseNames: [postgresDatabaseName]
allowAzureIPsFirewall: true
}
Expand All @@ -91,9 +124,11 @@ module web 'core/host/appservice.bicep' = {
DBHOST: '${postgresServerName}.postgres.database.azure.com'
DBNAME: postgresDatabaseName
DBUSER: postgresServerAdmin
DBPASS: postgresServerPassword
DBPASS: '@Microsoft.KeyVault(VaultName=${keyVault.outputs.name};SecretName=postgresAdminPassword)'
FLASK_APP: 'src'
AZURE_TRANSLATE_API_KEY: '@Microsoft.KeyVault(VaultName=${keyVault.outputs.name};SecretName=cognitiveServiceKey)'
}
keyVaultName: keyVault.outputs.name
}
}

Expand All @@ -114,3 +149,4 @@ module appServicePlan 'core/host/appserviceplan.bicep' = {

output WEB_URI string = web.outputs.uri
output AZURE_LOCATION string = location
output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name
4 changes: 2 additions & 2 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"principalId": {
"value": "${AZURE_PRINCIPAL_ID}"
},
"postgresServerPassword": {
"value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} postgresServerPassword)"
"postgresAdminPassword": {
"value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} postgresAdminPassword)"
}
}
}
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ Flask
requests
psycopg2
Flask-SQLAlchemy
Flask-Migrate
Flask-Migrate
6 changes: 4 additions & 2 deletions src/translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

def translate_with_azure(text, from_lang, to_lang):
url = "https://api.cognitive.microsofttranslator.com/translate"
location = "westus2"
# REGION_NAME is read-only env var from App Service Environment
# Reference: https://learn.microsoft.com/azure/app-service/reference-app-settings#app-environment
location = os.environ.get("REGION_NAME", "NoRegionFound")
params = {"api-version": "3.0", "from": from_lang, "to": to_lang}
headers = {
"Ocp-Apim-Subscription-Key": os.environ.get("AZURE_TRANSLATE_API_KEY", "NoKeyFound"),
Expand All @@ -18,6 +20,6 @@ def translate_with_azure(text, from_lang, to_lang):

request = requests.post(url, params=params, headers=headers, json=body)
response = request.json()
if (type(response) is dict) and (err := response.get("error", None)):
if isinstance(response, dict) and (err := response.get("error", None)):
return None, err["message"], "AZURE"
return response[0]["translations"][0]["text"], None, "AZURE"

0 comments on commit 1756ff5

Please sign in to comment.