Skip to content

Commit 06a0adf

Browse files
Merge pull request #1169 from NHSDigital/DTOSS-12458-investigate-container-startup-probes-and-credential-chain
[DTOSS-12458] - feat(manage_breast_screening): add managed identity support for Azure Blob Storage and PostgreSQL
2 parents 3e1dc2b + d28d9fa commit 06a0adf

5 files changed

Lines changed: 28 additions & 9 deletions

File tree

docs/infrastructure/environment-variables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Description: Private key used to sign JWT and sent to API OAuth2 provider. Used
3030

3131
To rotate: [Generate a new keypair](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/application-restricted-restful-apis-signed-jwt-authentication#step-2-generate-a-key-pair) and contact the NHS Notify onboarding team so they can update the public key.
3232

33-
## AZURE_CLIENT_ID
33+
## AZURE_DB_CLIENT_ID
3434

3535
Client ID of the database managed identity, when using.
3636

infrastructure/modules/container-apps/variables.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ locals {
255255
}
256256

257257
azure_db_env = {
258-
AZURE_CLIENT_ID = var.deploy_database_as_container ? null : module.db_connect_identity[0].client_id
258+
AZURE_DB_CLIENT_ID = var.deploy_database_as_container ? null : module.db_connect_identity[0].client_id
259259
DATABASE_HOST = var.deploy_database_as_container ? null : module.postgres[0].host
260260
DATABASE_NAME = var.deploy_database_as_container ? null : module.postgres[0].database_names[0]
261261
DATABASE_USER = var.deploy_database_as_container ? null : module.db_connect_identity[0].name
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from os import environ
2+
3+
from azure.identity import DefaultAzureCredential
4+
from storages.backends.azure_storage import AzureStorage
5+
6+
7+
class ManagedIdentityAzureStorage(AzureStorage):
8+
"""
9+
AzureStorage subclass that instantiates DefaultAzureCredential in __init__
10+
rather than at settings import time, avoiding credential chain errors during
11+
Django startup before the managed identity is available.
12+
"""
13+
14+
def __init__(self, **kwargs):
15+
kwargs.setdefault(
16+
"token_credential",
17+
DefaultAzureCredential(managed_identity_client_id=environ.get("BLOB_MI_CLIENT_ID")),
18+
)
19+
super().__init__(**kwargs)

manage_breast_screening/config/postgresql/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from os import environ
2+
13
from azure.identity import DefaultAzureCredential
24
from django.db.backends.postgresql import base
35

@@ -24,7 +26,9 @@ class DatabaseWrapper(base.DatabaseWrapper):
2426

2527
def __init__(self, *args, **kwargs):
2628
super().__init__(*args, **kwargs)
27-
self.azure_credential = DefaultAzureCredential()
29+
self.azure_credential = DefaultAzureCredential(
30+
managed_identity_client_id=environ.get("AZURE_DB_CLIENT_ID")
31+
)
2832

2933
def _get_azure_connection_password(self) -> str:
3034
# This makes use of in-memory token caching

manage_breast_screening/config/settings.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from os import environ
1515
from pathlib import Path
1616

17-
from azure.identity import DefaultAzureCredential
1817
from dotenv import load_dotenv
1918
from jinja2 import ChainableUndefined
2019

@@ -210,13 +209,10 @@ def list_env(key):
210209
},
211210
}
212211
else:
213-
# In production, use DefaultAzureCredential for authentication
212+
# In production, authenticate to Azure Blob Storage using managed identity.
214213
dicom_storage_options = {
215-
"BACKEND": "storages.backends.azure_storage.AzureStorage",
214+
"BACKEND": "manage_breast_screening.config.azure_blob_storage.ManagedIdentityAzureStorage",
216215
"OPTIONS": {
217-
"token_credential": DefaultAzureCredential(
218-
managed_identity_client_id=environ.get("BLOB_MI_CLIENT_ID")
219-
),
220216
"account_name": environ.get("STORAGE_ACCOUNT_NAME"),
221217
"azure_container": "dicom",
222218
},

0 commit comments

Comments
 (0)