pulumi-django-azure 1.0.8__py3-none-any.whl → 1.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pulumi-django-azure might be problematic. Click here for more details.

@@ -35,13 +35,13 @@ class DjangoDeployment(pulumi.ComponentResource):
35
35
  :param app_service_sku: The SKU for the app service plan.
36
36
  :param storage_account_name: The name of the storage account. Should be unique across Azure.
37
37
  :param cdn_host: A custom CDN host name (optional)
38
- :param comms_data_location: The data location for the Communication Services (optional if you don't need it)
39
38
  :param opts: The resource options
40
39
  """
41
40
 
42
41
  super().__init__("pkg:index:DjangoDeployment", name, None, opts)
43
42
 
44
43
  # child_opts = pulumi.ResourceOptions(parent=self)
44
+ self._config = pulumi.Config()
45
45
 
46
46
  self._name = name
47
47
  self._tenant_id = tenant_id
@@ -391,6 +391,59 @@ class DjangoDeployment(pulumi.ComponentResource):
391
391
 
392
392
  return comm_service
393
393
 
394
+ def _add_webapp_vault(self, administrators: list[str], suffix: str) -> azure.keyvault.Vault:
395
+ # Create a keyvault
396
+ vault = azure.keyvault.Vault(
397
+ f"vault-{suffix}",
398
+ resource_group_name=self._rg,
399
+ vault_name=f"vault-{suffix}",
400
+ properties=azure.keyvault.VaultPropertiesArgs(
401
+ tenant_id=self._tenant_id,
402
+ sku=azure.keyvault.SkuArgs(
403
+ name=azure.keyvault.SkuName.STANDARD,
404
+ family=azure.keyvault.SkuFamily.A,
405
+ ),
406
+ enable_rbac_authorization=True,
407
+ ),
408
+ )
409
+
410
+ # Find the Key Vault Administrator role
411
+ administrator_role = vault.id.apply(
412
+ lambda scope: azure.authorization.get_role_definition(
413
+ role_definition_id="00482a5a-887f-4fb3-b363-3b7fe8e74483",
414
+ scope=scope,
415
+ )
416
+ )
417
+
418
+ # Add vault administrators
419
+ for a in administrators:
420
+ azure.authorization.RoleAssignment(
421
+ f"ra-{suffix}-vault-admin-{a}",
422
+ principal_id=a,
423
+ principal_type=azure.authorization.PrincipalType.USER,
424
+ role_definition_id=administrator_role.id,
425
+ scope=vault.id,
426
+ )
427
+
428
+ return vault
429
+
430
+ def _add_webapp_secret(self, vault: azure.keyvault.Vault, secret_name: str, config_secret_name: str, suffix: str):
431
+ secret = self._config.require_secret(config_secret_name)
432
+
433
+ # Normalize the secret name
434
+ secret_name = secret_name.replace("_", "-").lower()
435
+
436
+ # Create a secret in the vault
437
+ return azure.keyvault.Secret(
438
+ f"secret-{suffix}-{secret_name}",
439
+ resource_group_name=self._rg,
440
+ vault_name=vault.name,
441
+ secret_name=secret_name,
442
+ properties=azure.keyvault.SecretPropertiesArgs(
443
+ value=secret,
444
+ ),
445
+ )
446
+
394
447
  def _get_storage_account_access_keys(
395
448
  self, storage_account: azure.storage.StorageAccount
396
449
  ) -> Sequence[azure.storage.outputs.StorageAccountKeyResponse]:
@@ -437,8 +490,10 @@ class DjangoDeployment(pulumi.ComponentResource):
437
490
  website_hosts: list[str],
438
491
  django_settings_module: str,
439
492
  environment_variables: dict[str, str] = {},
493
+ secrets: dict[str, str] = {},
440
494
  comms_data_location: Optional[str] = None,
441
495
  comms_domains: Optional[list[str]] = [],
496
+ vault_administrators: Optional[list[str]] = [],
442
497
  ) -> azure.web.WebApp:
443
498
  """
444
499
  Create a Django website with it's own database and storage containers.
@@ -450,8 +505,12 @@ class DjangoDeployment(pulumi.ComponentResource):
450
505
  :param website_hosts: The list of custom host names for the website.
451
506
  :param django_settings_module: The Django settings module to load.
452
507
  :param environment_variables: A dictionary of environment variables to set.
508
+ :param secrets: A dictionary of secrets to store in the Key Vault and assign as environment variables.
509
+ The key is the name of the Pulumi secret, the value is the name of the environment variable
510
+ and the name of the secret in the Key Vault.
453
511
  :param comms_data_location: The data location for the Communication Services (optional if you don't need it).
454
512
  :param comms_domains: The list of custom domains for the E-mail Communication Services (optional).
513
+ :param vault_administrator: The principal ID of the vault administrator (optional).
455
514
  """
456
515
 
457
516
  # Create a database
@@ -483,11 +542,19 @@ class DjangoDeployment(pulumi.ComponentResource):
483
542
  # Communication Services (optional)
484
543
  if comms_data_location:
485
544
  comms = self._add_webapp_comms(comms_data_location, comms_domains, f"{name}-{self._name}")
486
- # Add the domains as environment variable
545
+ # Add the service endpoint as environment variable
487
546
  environment_variables["AZURE_COMMUNICATION_SERVICE_ENDPOINT"] = comms.host_name.apply(lambda host: f"https://{host}")
488
547
  else:
489
548
  comms = None
490
549
 
550
+ # Key Vault
551
+ vault = self._add_webapp_vault(vault_administrators, f"{name}-{self._name}")
552
+
553
+ # Add secrets
554
+ for config_name, env_name in secrets.items():
555
+ s = self._add_webapp_secret(vault, env_name, config_name, f"{name}-{self._name}")
556
+ environment_variables[f"{env_name}_SECRET_NAME"] = s.name
557
+
491
558
  # Create a Django Secret Key (random)
492
559
  secret_key = pulumi_random.RandomString(f"django-secret-{name}-{self._name}", length=50)
493
560
 
@@ -528,6 +595,8 @@ class DjangoDeployment(pulumi.ComponentResource):
528
595
  azure.web.NameValuePairArgs(name="DJANGO_SETTINGS_MODULE", value=django_settings_module),
529
596
  azure.web.NameValuePairArgs(name="DJANGO_SECRET_KEY", value=secret_key.result),
530
597
  azure.web.NameValuePairArgs(name="DJANGO_ALLOWED_HOSTS", value=",".join(website_hosts)),
598
+ # Vault settings
599
+ azure.web.NameValuePairArgs(name="AZURE_KEY_VAULT", value=vault.name),
531
600
  # Storage settings
532
601
  azure.web.NameValuePairArgs(name="AZURE_STORAGE_ACCOUNT_NAME", value=self._storage_account.name),
533
602
  azure.web.NameValuePairArgs(name="AZURE_STORAGE_CONTAINER_STATICFILES", value=static_container.name),
@@ -572,6 +641,24 @@ class DjangoDeployment(pulumi.ComponentResource):
572
641
  f"{name}_deploy_ssh_key_url", app.name.apply(lambda name: f"https://{name}.scm.azurewebsites.net/api/sshkey?ensurePublicKey=1")
573
642
  )
574
643
 
644
+ # Find the role for Key Vault Secrets User
645
+ vault_access_role = vault.id.apply(
646
+ lambda scope: azure.authorization.get_role_definition(
647
+ role_definition_id="4633458b-17de-408a-b874-0445c86b69e6",
648
+ scope=scope,
649
+ )
650
+ )
651
+
652
+ # Grant the app access to the vault
653
+ azure.authorization.RoleAssignment(
654
+ f"ra-{name}-vault-user",
655
+ principal_id=principal_id,
656
+ principal_type=azure.authorization.PrincipalType.SERVICE_PRINCIPAL,
657
+ # Key Vault Secrets User
658
+ role_definition_id=vault_access_role.id,
659
+ scope=vault.id,
660
+ )
661
+
575
662
  # Find the role for Storage Blob Data Contributor
576
663
  storage_role = self._storage_account.id.apply(
577
664
  lambda scope: azure.authorization.get_role_definition(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pulumi-django-azure
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Summary: Simply deployment of Django on Azure with Pulumi
5
5
  Author-email: Maarten Ureel <maarten@youreal.eu>
6
6
  License: MIT License
@@ -47,6 +47,7 @@ To have a proper and secure environment, we need these components:
47
47
  * PostgreSQL server
48
48
  * Azure Communication Services to send e-mails
49
49
  * Webapp with multiple custom host names and managed SSL for the website itself
50
+ * Azure Key Vault per application
50
51
  * Webapp running pgAdmin
51
52
 
52
53
  ## Installation
@@ -176,6 +177,55 @@ pgAdmin will be created with a default login:
176
177
 
177
178
  Best practice is to log in right away, create a user for yourself and delete this default user.
178
179
 
180
+ ## Azure OAuth2 / Django Social Auth
181
+ If you want to set up login with Azure, which would make sense since you are in the ecosystem, you need to create an App Registration in Entra ID, create a secret and then register these settings in your stack:
182
+ ```
183
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.key' secret_ID
184
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.secret' secret_value
185
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.tenant_id' directory_tenant_id
186
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.client_id' application_id
187
+ ```
188
+
189
+ Then in your Django deployment, pass to the `add_django_website` command:
190
+ ```
191
+ secrets={
192
+ "mywebsite_social_auth_azure": "AZURE_OAUTH",
193
+ },
194
+ ```
195
+
196
+ The value will be automatically stored in the vault where the application has access to.
197
+ The environment variable will be suffixed with `_SECRET_NAME`.
198
+
199
+ Then, in your application, retrieve this data from the vault, e.g.:
200
+ ```python
201
+ from azure.keyvault.secrets import SecretClient
202
+ from azure.identity import DefaultAzureCredential
203
+
204
+ # Azure credentials
205
+ azure_credential = DefaultAzureCredential()
206
+
207
+ # Azure Key Vault
208
+ AZURE_KEY_VAULT = env("AZURE_KEY_VAULT")
209
+ AZURE_KEY_VAULT_URI = f"https://{AZURE_KEY_VAULT}.vault.azure.net"
210
+ azure_key_vault_client = SecretClient(vault_url=AZURE_KEY_VAULT_URI, credential=azure_credential)
211
+
212
+ # Social Auth settings
213
+ oauth_secret = azure_key_vault_client.get_secret(env("AZURE_OAUTH_SECRET_NAME"))
214
+ oauth_secret = json.loads(oauth_secret.value)
215
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = oauth_secret["client_id"]
216
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = oauth_secret["secret"]
217
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID = oauth_secret["tenant_id"]
218
+ SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ["username", "first_name", "last_name", "email"]
219
+ SOCIAL_AUTH_POSTGRES_JSONFIELD = True
220
+
221
+ AUTHENTICATION_BACKENDS = (
222
+ "social_core.backends.azuread_tenant.AzureADTenantOAuth2",
223
+ "django.contrib.auth.backends.ModelBackend",
224
+ )
225
+ ```
226
+
227
+ And of course add the login button somewhere, following Django Social Auth instructions.
228
+
179
229
  ## Automate deployments
180
230
  When using a service like GitLab, you can configure a Webhook to fire upon a push to your branch.
181
231
 
@@ -0,0 +1,7 @@
1
+ pulumi_django_azure/__init__.py,sha256=tXTvPfr8-Nll5cjMyY9yj_0z_PQ0XAcxihzHRCES-hU,63
2
+ pulumi_django_azure/django_deployment.py,sha256=gqdrUq7dLO7ds4OmZt5X-UKEO328ZlxsFTqA97UiSFo,30222
3
+ pulumi_django_azure-1.0.9.dist-info/LICENSE,sha256=NX2LN3U319Zaac8b7ZgfNOco_nTBbN531X_M_13niSg,1087
4
+ pulumi_django_azure-1.0.9.dist-info/METADATA,sha256=3-xSB9Kfhrwn2yWW7KOsVQsJuNMyl2fDU3c02SttWcY,10283
5
+ pulumi_django_azure-1.0.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ pulumi_django_azure-1.0.9.dist-info/top_level.txt,sha256=MNPRJhq-_G8EMCHRkjdcb_xrqzOkmKogXUGV7Ysz3g0,20
7
+ pulumi_django_azure-1.0.9.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- pulumi_django_azure/__init__.py,sha256=tXTvPfr8-Nll5cjMyY9yj_0z_PQ0XAcxihzHRCES-hU,63
2
- pulumi_django_azure/django_deployment.py,sha256=nNf6dt9Zp8IXMf9n63lxpkRNfogyb1VXx3aPm2Vzln4,26659
3
- pulumi_django_azure-1.0.8.dist-info/LICENSE,sha256=NX2LN3U319Zaac8b7ZgfNOco_nTBbN531X_M_13niSg,1087
4
- pulumi_django_azure-1.0.8.dist-info/METADATA,sha256=Y6pp3unBOMUFApTjglKm6fgbXcHk0Uz1gC-xN_Srl9o,8113
5
- pulumi_django_azure-1.0.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- pulumi_django_azure-1.0.8.dist-info/top_level.txt,sha256=MNPRJhq-_G8EMCHRkjdcb_xrqzOkmKogXUGV7Ysz3g0,20
7
- pulumi_django_azure-1.0.8.dist-info/RECORD,,