Skip to content

Commit

Permalink
Merge pull request #40 from tonybaloney/django_5_managedid_storage
Browse files Browse the repository at this point in the history
Managed Identity Storage for media and static content
  • Loading branch information
tonybaloney committed Apr 22, 2024
2 parents 625c0a4 + 932487e commit f04641c
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 44 deletions.
Empty file.
17 changes: 0 additions & 17 deletions demo-application/project/backend/storage.py

This file was deleted.

37 changes: 23 additions & 14 deletions demo-application/project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"""
import os
from pathlib import Path
from azure.identity import DefaultAzureCredential


# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand Down Expand Up @@ -133,19 +135,34 @@

USE_TZ = True



DEFAULT_FILE_STORAGE = "project.backend.storage.AzureMediaStorage"
STATICFILES_STORAGE = "project.backend.storage.AzureStaticStorage"

AZURE_STORAGE_KEY = os.environ.get("AZURE_STORAGEACCOUNT_KEY", False)
AZURE_ACCOUNT_NAME = os.environ.get("AZURE_STORAGEACCOUNT_NAME", False)
AZURE_MEDIA_CONTAINER = os.environ.get("AZURE_MEDIA_CONTAINER", "media")
AZURE_STATIC_CONTAINER = os.environ.get("AZURE_STATIC_CONTAINER", "static")

# AZURE_CUSTOM_DOMAIN = f'{AZURE_ACCOUNT_NAME}.azureedge.net' # CDN URL
AZURE_CUSTOM_DOMAIN = f"{AZURE_ACCOUNT_NAME}.blob.core.windows.net" # Files URL

STORAGES = {
"default": {
"BACKEND": "storages.backends.azure_storage.AzureStorage",
"OPTIONS": {
"token_credential": DefaultAzureCredential(),
"account_name": AZURE_ACCOUNT_NAME,
"azure_container": AZURE_MEDIA_CONTAINER,
"expiration_secs": 3600,
},
},
"staticfiles": {
"BACKEND": "storages.backends.azure_storage.AzureStorage",
"OPTIONS": {
"token_credential": DefaultAzureCredential(),
"account_name": AZURE_ACCOUNT_NAME,
"azure_container": AZURE_STATIC_CONTAINER,
"expiration_secs": 3600,
},
},
}

STATIC_URL = f"https://{AZURE_CUSTOM_DOMAIN}/{AZURE_STATIC_CONTAINER}/"
MEDIA_URL = f"https://{AZURE_CUSTOM_DOMAIN}/{AZURE_MEDIA_CONTAINER}/"

Expand All @@ -155,14 +172,6 @@


INSTRUMENTATION_KEY = os.getenv("APPINSIGHTS_CONNECTION_STRING", None)
if INSTRUMENTATION_KEY:
OPENCENSUS = {
"TRACE": {
"SAMPLER": "opencensus.trace.samplers.ProbabilitySampler(rate=1)",
"EXPORTER": f"""opencensus.ext.azure.trace_exporter.AzureExporter(connection_string='{INSTRUMENTATION_KEY}')""",
}
}

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
Expand Down
9 changes: 5 additions & 4 deletions demo-application/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
wheel
django>=4.0.0
django>=5.0.0
azure-identity
django-crispy-forms>=2.0.0,<3.0.0
crispy-bootstrap4==2022.1
psycopg2-binary>=2.9.5
django-storages[azure]==1.13.2
opentelemetry-instrumentation-django==0.36b0
azure-monitor-opentelemetry-exporter==1.0.0b12
django-storages[azure]
opentelemetry-instrumentation-django
azure-monitor-opentelemetry-exporter
uvicorn
1 change: 1 addition & 0 deletions demo-application/startup.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
python manage.py collectstatic --noinput
python manage.py migrate
python -m gunicorn --workers 2 --threads 4 --timeout 60 --access-logfile \
'-' --error-logfile '-' --bind=0.0.0.0:8000 \
Expand Down
2 changes: 2 additions & 0 deletions demo-application/startup_asgi.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
python manage.py collectstatic --noinput
python manage.py migrate
gunicorn --workers 2 --threads 4 --timeout 60 --access-logfile \
'-' --error-logfile '-' --bind=0.0.0.0:8000 -k uvicorn.workers.UvicornWorker \
--chdir=/home/site/wwwroot project.asgi
11 changes: 11 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ module resources 'resources.bicep' = {
}
}

module storageContribRoleUser 'role.bicep' = {
scope: resourceGroup
name: 'App-Storage-Blob-Data-Contributor'
params: {
principalId: resources.outputs.identityPrincipalId
roleDefinitionId: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
principalType: 'ServicePrincipal'
}
}


output APPLICATIONINSIGHTS_CONNECTION_STRING string = resources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING
output AZURE_LOAD_TEST_NAME string = resources.outputs.AZURE_LOAD_TEST_NAME
output AZURE_LOAD_TEST_HOST string = resources.outputs.AZURE_LOAD_TEST_HOST
Expand Down
22 changes: 13 additions & 9 deletions infra/resources.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ param tags object
var abbrs = loadJsonContent('abbreviations.json')
var databaseSubnetName = 'database-subnet'
var webappSubnetName = 'webapp-subnet'

var mediaContainerName = 'media'
var staticContainerName = 'static'

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = {
name: '${abbrs.networkVirtualNetworks}${resourceToken}'
location: location
Expand Down Expand Up @@ -106,10 +110,9 @@ resource web 'Microsoft.Web/sites@2022-03-01' = {
DATABASE_USERNAME: postgresdb.properties.administratorLogin
DATABASE_PASSWORD: databasePassword
DATABASE_ADDRESS: '${abbrs.dBforPostgreSQLServers}${resourceToken}.postgres.database.azure.com'
AZURE_STORAGEACCOUNT_KEY: storage.listKeys().keys[0].value
AZURE_STORAGEACCOUNT_NAME: storage.name
AZURE_MEDIA_CONTAINER: 'media'
AZURE_STATIC_CONTAINER: 'static'
AZURE_MEDIA_CONTAINER: mediaContainerName
AZURE_STATIC_CONTAINER: staticContainerName
SCM_DO_BUILD_DURING_DEPLOYMENT: 'true'
APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsightsResources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING
SECRET_KEY: secretKey
Expand Down Expand Up @@ -249,8 +252,8 @@ resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' = {
publicNetworkAccess: 'Enabled'
allowCrossTenantReplication: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: true
allowSharedKeyAccess: true
allowBlobPublicAccess: false
allowSharedKeyAccess: false
networkAcls: {
bypass: 'AzureServices'
virtualNetworkRules: []
Expand Down Expand Up @@ -282,18 +285,18 @@ resource blobservices 'Microsoft.Storage/storageAccounts/blobServices@2021-09-01
}

resource static 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01' = {
name: 'static'
name: staticContainerName
parent: blobservices
properties: {
publicAccess: 'Blob'
publicAccess: 'None'
}
}

resource media 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01' = {
name: 'media'
name: mediaContainerName
parent: blobservices
properties: {
publicAccess: 'Blob'
publicAccess: 'None'
}
}

Expand All @@ -308,6 +311,7 @@ resource loadtest 'Microsoft.LoadTestService/loadTests@2022-12-01' = {
}
}

output identityPrincipalId string = web.identity.principalId
output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsightsResources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING
output WEB_URI string = 'https://${web.properties.defaultHostName}'
output AZURE_LOAD_TEST_NAME string = loadtest.name
Expand Down
21 changes: 21 additions & 0 deletions infra/role.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
metadata description = 'Creates a role assignment for a service principal.'
param principalId string

@allowed([
'Device'
'ForeignGroup'
'Group'
'ServicePrincipal'
'User'
])
param principalType string = 'ServicePrincipal'
param roleDefinitionId string

resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId)
properties: {
principalId: principalId
principalType: principalType
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
}
}

0 comments on commit f04641c

Please sign in to comment.