Skip to content

Commit 471af05

Browse files
Integrate Azure Key Vault for SQL connection string management in web app
1 parent 1ec302f commit 471af05

4 files changed

Lines changed: 43 additions & 56 deletions

File tree

samples/web-app-sql-database/python/bicep/main.bicep

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -473,11 +473,10 @@ resource configAppSettings 'Microsoft.Web/sites/config@2024-11-01' = {
473473
properties: {
474474
SCM_DO_BUILD_DURING_DEPLOYMENT: 'true'
475475
ENABLE_ORYX_BUILD: 'true'
476-
SQL_SERVER: sqlServer.properties.fullyQualifiedDomainName
477-
SQL_DATABASE: sqlDatabaseName
478-
SQL_USERNAME: sqlDatabaseUsername
479-
SQL_PASSWORD: sqlDatabasePassword
480-
SQL_CONNECTION_STRING: '@Microsoft.KeyVault(SecretUri=${sqlConnectionStringSecret.properties.secretUri})'
476+
//Pass Key Vault name and secret name as app settings.
477+
//The Python SDK will retrieve the actual connection string value from Key Vault
478+
KEY_VAULT_NAME: keyVaultName
479+
SECRET_NAME: sqlConnectionStringSecretName
481480
LOGIN_NAME: username
482481
}
483482
}

samples/web-app-sql-database/python/scripts/deploy.sh

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -378,24 +378,18 @@ else
378378
exit 1
379379
fi
380380

381-
# Get Secret URI
382-
SECRET_URI=$($AZ keyvault secret show \
383-
--vault-name "$KEY_VAULT_NAME" \
384-
--name "$SECRET_NAME" \
385-
--query "id" \
386-
--output tsv)
387-
388-
echo "Secret URI: $SECRET_URI"
389-
390381
# Set web app settings
382+
# Pass Key Vault name and secret name as app settings.
383+
# The Python SDK will retrieve the actual connection string value from Key Vault.
391384
echo "Setting web app settings for [$WEB_APP_NAME]..."
392385
$AZ webapp config appsettings set \
393386
--name "$WEB_APP_NAME" \
394387
--resource-group "$RESOURCE_GROUP_NAME" \
395388
--settings \
396389
SCM_DO_BUILD_DURING_DEPLOYMENT='true' \
397390
ENABLE_ORYX_BUILD='true' \
398-
SQL_CONNECTION_STRING="@Microsoft.KeyVault(SecretUri=${SECRET_URI})" \
391+
KEY_VAULT_NAME="$KEY_VAULT_NAME" \
392+
SECRET_NAME="$SECRET_NAME" \
399393
LOGIN_NAME="$LOGIN_NAME" \
400394
--only-show-errors 1>/dev/null
401395

samples/web-app-sql-database/python/src/database.py

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
Supports both traditional ODBC connections and passwordless Azure AD authentication
44
"""
55

6-
from multiprocessing import connection
6+
import logging
77
import os
88
import struct
9-
import pyodbc
10-
import logging
11-
from typing import Optional, List, Dict, Any
129
from contextlib import contextmanager
10+
from typing import Any, Dict, List, Optional
11+
12+
import pyodbc
1313
from azure.identity import DefaultAzureCredential
14+
from azure.keyvault.secrets import SecretClient
1415

1516
# Configure logging
1617
logger = logging.getLogger(__name__)
@@ -61,44 +62,36 @@ def __init__(
6162
@classmethod
6263
def from_env(cls) -> 'SqlHelper':
6364
"""
64-
Create a SqlHelper instance from environment variables.
65-
66-
You can either pass the following set of variables, if you plan to use DefaultAzureCredential:
67-
- AZURE_CLIENT_ID
68-
- AZURE_CLIENT_SECRET
69-
- AZURE_TENANT_ID
70-
71-
Instead, if you plan to use the connection string directly, you can set:
72-
- SQL_SERVER
73-
- SQL_DATABASE
74-
- SQL_USERNAME
75-
- SQL_PASSWORD
65+
Create a SqlHelper instance using KEY_VAULT_NAME and SECRET_NAME environment variables.
66+
The secret must contain a SQL connection string. Uses DefaultAzureCredential for authentication.
7667
"""
77-
connection_string = os.environ.get("SQL_CONNECTION_STRING")
78-
if connection_string:
79-
return cls.from_connection_string(connection_string)
68+
key_vault_name = os.environ.get("KEY_VAULT_NAME")
69+
secret_name = os.environ.get("SECRET_NAME")
8070

81-
client_id = os.environ.get("AZURE_CLIENT_ID")
82-
client_secret = os.environ.get("AZURE_CLIENT_SECRET")
83-
tenant_id = os.environ.get("AZURE_TENANT_ID")
84-
server = os.environ.get("SQL_SERVER")
85-
database = os.environ.get("SQL_DATABASE")
86-
username = os.environ.get("SQL_USERNAME")
87-
password = os.environ.get("SQL_PASSWORD")
88-
89-
if not any([client_id, client_secret, tenant_id, server, database, username, password]):
90-
raise ValueError("You properly need to define environment variables.")
91-
92-
logger.info("Environment variables loaded successfully")
93-
94-
return cls(
95-
server=server,
96-
database=database,
97-
username=username,
98-
password=password,
99-
use_azure_credential=all([client_id, client_secret, tenant_id])
100-
)
101-
71+
if not key_vault_name or not secret_name:
72+
raise ValueError("KEY_VAULT_NAME and SECRET_NAME environment variables are required")
73+
74+
return cls.from_key_vault(key_vault_name, secret_name)
75+
76+
@classmethod
77+
def from_key_vault(cls, vault_name: str, secret_name: str) -> 'SqlHelper':
78+
"""
79+
Create a SqlHelper instance by reading the connection string from Azure Key Vault.
80+
81+
"""
82+
vault_url = f"https://{vault_name}.vault.azure.net"
83+
credential = DefaultAzureCredential(exclude_interactive_browser_credential=False)
84+
client = SecretClient(vault_url=vault_url, credential=credential)
85+
86+
logger.info(f"Retrieving secret [{secret_name}] from Key Vault [{vault_name}]...")
87+
secret = client.get_secret(secret_name)
88+
89+
if not secret.value:
90+
raise ValueError(f"Secret [{secret_name}] in Key Vault [{vault_name}] has no value")
91+
92+
logger.info(f"Secret [{secret_name}] retrieved successfully from Key Vault [{vault_name}]")
93+
return cls.from_connection_string(secret.value)
94+
10295
@classmethod
10396
def from_connection_string(cls, connection_string: str) -> 'SqlHelper':
10497
"""
@@ -127,7 +120,7 @@ def from_connection_string(cls, connection_string: str) -> 'SqlHelper':
127120
)
128121

129122
logger.info("Connection string parsed successfully")
130-
logger.debug(f"Server: {server}, Database: {database}, Username: {username}")
123+
logger.info(f"Server: {server}, Database: {database}, Username: {username}")
131124

132125
return cls(
133126
server=server,

samples/web-app-sql-database/python/src/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ azure-identity==1.25.1
33
pyodbc==5.3.0
44
gunicorn==20.1.0
55
python-dotenv==1.1.1
6+
azure-keyvault-secrets

0 commit comments

Comments
 (0)