pulumi-django-azure 1.0.7__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.
- pulumi_django_azure/django_deployment.py +90 -3
- {pulumi_django_azure-1.0.7.dist-info → pulumi_django_azure-1.0.9.dist-info}/METADATA +51 -1
- pulumi_django_azure-1.0.9.dist-info/RECORD +7 -0
- pulumi_django_azure-1.0.7.dist-info/RECORD +0 -7
- {pulumi_django_azure-1.0.7.dist-info → pulumi_django_azure-1.0.9.dist-info}/LICENSE +0 -0
- {pulumi_django_azure-1.0.7.dist-info → pulumi_django_azure-1.0.9.dist-info}/WHEEL +0 -0
- {pulumi_django_azure-1.0.7.dist-info → pulumi_django_azure-1.0.9.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
487
|
-
environment_variables["
|
|
545
|
+
# Add the service endpoint as environment variable
|
|
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.
|
|
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=jqDh1K1OnSvfxiSW0ZTJNGfwZ620C8y5KvPSF4vrCDg,26607
|
|
3
|
-
pulumi_django_azure-1.0.7.dist-info/LICENSE,sha256=NX2LN3U319Zaac8b7ZgfNOco_nTBbN531X_M_13niSg,1087
|
|
4
|
-
pulumi_django_azure-1.0.7.dist-info/METADATA,sha256=TYfdQP5kCTUpLibHrNu2rcEOwCBSlZ2BrLVRsI35eQo,8113
|
|
5
|
-
pulumi_django_azure-1.0.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
6
|
-
pulumi_django_azure-1.0.7.dist-info/top_level.txt,sha256=MNPRJhq-_G8EMCHRkjdcb_xrqzOkmKogXUGV7Ysz3g0,20
|
|
7
|
-
pulumi_django_azure-1.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|