pulumi-django-azure 1.0.8__tar.gz → 1.0.10__tar.gz

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.

@@ -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.10
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,8 +47,21 @@ 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
 
53
+ ## Project requirements
54
+ Your Django project should contain a folder `cicd` with these files:
55
+ * pre_build.sh: commands to be executed before building the application, for example NPM install, CSS build commands,...
56
+ * post_build.sh: commands to be executed after building the application, e.g. cleaning up.
57
+ Note that this runs in the identity of the build container, so you should not run database or storage manipulations here.
58
+ * startup.sh: commands to run the actual application. I recommend to put at least:
59
+ ```bash
60
+ python manage.py migrate
61
+ python manage.py collectstatic --noinput
62
+ gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
63
+ ```
64
+ Be sure to change `yourapplication` in the above.
52
65
  ## Installation
53
66
  This package is published on PyPi, so you can just add pulumi-django-azure to your requirements file.
54
67
 
@@ -176,6 +189,55 @@ pgAdmin will be created with a default login:
176
189
 
177
190
  Best practice is to log in right away, create a user for yourself and delete this default user.
178
191
 
192
+ ## Azure OAuth2 / Django Social Auth
193
+ 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:
194
+ ```
195
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.key' secret_ID
196
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.secret' secret_value
197
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.tenant_id' directory_tenant_id
198
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.client_id' application_id
199
+ ```
200
+
201
+ Then in your Django deployment, pass to the `add_django_website` command:
202
+ ```
203
+ secrets={
204
+ "mywebsite_social_auth_azure": "AZURE_OAUTH",
205
+ },
206
+ ```
207
+
208
+ The value will be automatically stored in the vault where the application has access to.
209
+ The environment variable will be suffixed with `_SECRET_NAME`.
210
+
211
+ Then, in your application, retrieve this data from the vault, e.g.:
212
+ ```python
213
+ from azure.keyvault.secrets import SecretClient
214
+ from azure.identity import DefaultAzureCredential
215
+
216
+ # Azure credentials
217
+ azure_credential = DefaultAzureCredential()
218
+
219
+ # Azure Key Vault
220
+ AZURE_KEY_VAULT = env("AZURE_KEY_VAULT")
221
+ AZURE_KEY_VAULT_URI = f"https://{AZURE_KEY_VAULT}.vault.azure.net"
222
+ azure_key_vault_client = SecretClient(vault_url=AZURE_KEY_VAULT_URI, credential=azure_credential)
223
+
224
+ # Social Auth settings
225
+ oauth_secret = azure_key_vault_client.get_secret(env("AZURE_OAUTH_SECRET_NAME"))
226
+ oauth_secret = json.loads(oauth_secret.value)
227
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = oauth_secret["client_id"]
228
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = oauth_secret["secret"]
229
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID = oauth_secret["tenant_id"]
230
+ SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ["username", "first_name", "last_name", "email"]
231
+ SOCIAL_AUTH_POSTGRES_JSONFIELD = True
232
+
233
+ AUTHENTICATION_BACKENDS = (
234
+ "social_core.backends.azuread_tenant.AzureADTenantOAuth2",
235
+ "django.contrib.auth.backends.ModelBackend",
236
+ )
237
+ ```
238
+
239
+ And of course add the login button somewhere, following Django Social Auth instructions.
240
+
179
241
  ## Automate deployments
180
242
  When using a service like GitLab, you can configure a Webhook to fire upon a push to your branch.
181
243
 
@@ -8,8 +8,21 @@ To have a proper and secure environment, we need these components:
8
8
  * PostgreSQL server
9
9
  * Azure Communication Services to send e-mails
10
10
  * Webapp with multiple custom host names and managed SSL for the website itself
11
+ * Azure Key Vault per application
11
12
  * Webapp running pgAdmin
12
13
 
14
+ ## Project requirements
15
+ Your Django project should contain a folder `cicd` with these files:
16
+ * pre_build.sh: commands to be executed before building the application, for example NPM install, CSS build commands,...
17
+ * post_build.sh: commands to be executed after building the application, e.g. cleaning up.
18
+ Note that this runs in the identity of the build container, so you should not run database or storage manipulations here.
19
+ * startup.sh: commands to run the actual application. I recommend to put at least:
20
+ ```bash
21
+ python manage.py migrate
22
+ python manage.py collectstatic --noinput
23
+ gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
24
+ ```
25
+ Be sure to change `yourapplication` in the above.
13
26
  ## Installation
14
27
  This package is published on PyPi, so you can just add pulumi-django-azure to your requirements file.
15
28
 
@@ -137,6 +150,55 @@ pgAdmin will be created with a default login:
137
150
 
138
151
  Best practice is to log in right away, create a user for yourself and delete this default user.
139
152
 
153
+ ## Azure OAuth2 / Django Social Auth
154
+ 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:
155
+ ```
156
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.key' secret_ID
157
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.secret' secret_value
158
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.tenant_id' directory_tenant_id
159
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.client_id' application_id
160
+ ```
161
+
162
+ Then in your Django deployment, pass to the `add_django_website` command:
163
+ ```
164
+ secrets={
165
+ "mywebsite_social_auth_azure": "AZURE_OAUTH",
166
+ },
167
+ ```
168
+
169
+ The value will be automatically stored in the vault where the application has access to.
170
+ The environment variable will be suffixed with `_SECRET_NAME`.
171
+
172
+ Then, in your application, retrieve this data from the vault, e.g.:
173
+ ```python
174
+ from azure.keyvault.secrets import SecretClient
175
+ from azure.identity import DefaultAzureCredential
176
+
177
+ # Azure credentials
178
+ azure_credential = DefaultAzureCredential()
179
+
180
+ # Azure Key Vault
181
+ AZURE_KEY_VAULT = env("AZURE_KEY_VAULT")
182
+ AZURE_KEY_VAULT_URI = f"https://{AZURE_KEY_VAULT}.vault.azure.net"
183
+ azure_key_vault_client = SecretClient(vault_url=AZURE_KEY_VAULT_URI, credential=azure_credential)
184
+
185
+ # Social Auth settings
186
+ oauth_secret = azure_key_vault_client.get_secret(env("AZURE_OAUTH_SECRET_NAME"))
187
+ oauth_secret = json.loads(oauth_secret.value)
188
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = oauth_secret["client_id"]
189
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = oauth_secret["secret"]
190
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID = oauth_secret["tenant_id"]
191
+ SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ["username", "first_name", "last_name", "email"]
192
+ SOCIAL_AUTH_POSTGRES_JSONFIELD = True
193
+
194
+ AUTHENTICATION_BACKENDS = (
195
+ "social_core.backends.azuread_tenant.AzureADTenantOAuth2",
196
+ "django.contrib.auth.backends.ModelBackend",
197
+ )
198
+ ```
199
+
200
+ And of course add the login button somewhere, following Django Social Auth instructions.
201
+
140
202
  ## Automate deployments
141
203
  When using a service like GitLab, you can configure a Webhook to fire upon a push to your branch.
142
204
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pulumi-django-azure"
7
- version = "1.0.8"
7
+ version = "1.0.10"
8
8
  description = "Simply deployment of Django on Azure with Pulumi"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Maarten Ureel", email = "maarten@youreal.eu" }]
@@ -27,7 +27,7 @@ Homepage = "https://gitlab.com/MaartenUreel/pulumi-django-azure"
27
27
 
28
28
  [tool.poetry]
29
29
  name = "pulumi-django-azure"
30
- version = "1.0.8"
30
+ version = "1.0.10"
31
31
  description = "Simply deployment of Django on Azure with Pulumi"
32
32
  authors = ["Maarten Ureel <maarten@youreal.eu>"]
33
33
 
@@ -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
 
@@ -510,6 +577,7 @@ class DjangoDeployment(pulumi.ComponentResource):
510
577
  ),
511
578
  https_only=True,
512
579
  site_config=azure.web.SiteConfigArgs(
580
+ app_command_line="cicd/startup.sh",
513
581
  always_on=True,
514
582
  health_check_path=self.HEALTH_CHECK_PATH,
515
583
  ftps_state=azure.web.FtpsState.DISABLED,
@@ -528,6 +596,8 @@ class DjangoDeployment(pulumi.ComponentResource):
528
596
  azure.web.NameValuePairArgs(name="DJANGO_SETTINGS_MODULE", value=django_settings_module),
529
597
  azure.web.NameValuePairArgs(name="DJANGO_SECRET_KEY", value=secret_key.result),
530
598
  azure.web.NameValuePairArgs(name="DJANGO_ALLOWED_HOSTS", value=",".join(website_hosts)),
599
+ # Vault settings
600
+ azure.web.NameValuePairArgs(name="AZURE_KEY_VAULT", value=vault.name),
531
601
  # Storage settings
532
602
  azure.web.NameValuePairArgs(name="AZURE_STORAGE_ACCOUNT_NAME", value=self._storage_account.name),
533
603
  azure.web.NameValuePairArgs(name="AZURE_STORAGE_CONTAINER_STATICFILES", value=static_container.name),
@@ -572,6 +642,24 @@ class DjangoDeployment(pulumi.ComponentResource):
572
642
  f"{name}_deploy_ssh_key_url", app.name.apply(lambda name: f"https://{name}.scm.azurewebsites.net/api/sshkey?ensurePublicKey=1")
573
643
  )
574
644
 
645
+ # Find the role for Key Vault Secrets User
646
+ vault_access_role = vault.id.apply(
647
+ lambda scope: azure.authorization.get_role_definition(
648
+ role_definition_id="4633458b-17de-408a-b874-0445c86b69e6",
649
+ scope=scope,
650
+ )
651
+ )
652
+
653
+ # Grant the app access to the vault
654
+ azure.authorization.RoleAssignment(
655
+ f"ra-{name}-vault-user",
656
+ principal_id=principal_id,
657
+ principal_type=azure.authorization.PrincipalType.SERVICE_PRINCIPAL,
658
+ # Key Vault Secrets User
659
+ role_definition_id=vault_access_role.id,
660
+ scope=vault.id,
661
+ )
662
+
575
663
  # Find the role for Storage Blob Data Contributor
576
664
  storage_role = self._storage_account.id.apply(
577
665
  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.10
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,8 +47,21 @@ 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
 
53
+ ## Project requirements
54
+ Your Django project should contain a folder `cicd` with these files:
55
+ * pre_build.sh: commands to be executed before building the application, for example NPM install, CSS build commands,...
56
+ * post_build.sh: commands to be executed after building the application, e.g. cleaning up.
57
+ Note that this runs in the identity of the build container, so you should not run database or storage manipulations here.
58
+ * startup.sh: commands to run the actual application. I recommend to put at least:
59
+ ```bash
60
+ python manage.py migrate
61
+ python manage.py collectstatic --noinput
62
+ gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
63
+ ```
64
+ Be sure to change `yourapplication` in the above.
52
65
  ## Installation
53
66
  This package is published on PyPi, so you can just add pulumi-django-azure to your requirements file.
54
67
 
@@ -176,6 +189,55 @@ pgAdmin will be created with a default login:
176
189
 
177
190
  Best practice is to log in right away, create a user for yourself and delete this default user.
178
191
 
192
+ ## Azure OAuth2 / Django Social Auth
193
+ 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:
194
+ ```
195
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.key' secret_ID
196
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.secret' secret_value
197
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.tenant_id' directory_tenant_id
198
+ pulumi config set --secret --path 'mywebsite_social_auth_azure.client_id' application_id
199
+ ```
200
+
201
+ Then in your Django deployment, pass to the `add_django_website` command:
202
+ ```
203
+ secrets={
204
+ "mywebsite_social_auth_azure": "AZURE_OAUTH",
205
+ },
206
+ ```
207
+
208
+ The value will be automatically stored in the vault where the application has access to.
209
+ The environment variable will be suffixed with `_SECRET_NAME`.
210
+
211
+ Then, in your application, retrieve this data from the vault, e.g.:
212
+ ```python
213
+ from azure.keyvault.secrets import SecretClient
214
+ from azure.identity import DefaultAzureCredential
215
+
216
+ # Azure credentials
217
+ azure_credential = DefaultAzureCredential()
218
+
219
+ # Azure Key Vault
220
+ AZURE_KEY_VAULT = env("AZURE_KEY_VAULT")
221
+ AZURE_KEY_VAULT_URI = f"https://{AZURE_KEY_VAULT}.vault.azure.net"
222
+ azure_key_vault_client = SecretClient(vault_url=AZURE_KEY_VAULT_URI, credential=azure_credential)
223
+
224
+ # Social Auth settings
225
+ oauth_secret = azure_key_vault_client.get_secret(env("AZURE_OAUTH_SECRET_NAME"))
226
+ oauth_secret = json.loads(oauth_secret.value)
227
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = oauth_secret["client_id"]
228
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = oauth_secret["secret"]
229
+ SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID = oauth_secret["tenant_id"]
230
+ SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ["username", "first_name", "last_name", "email"]
231
+ SOCIAL_AUTH_POSTGRES_JSONFIELD = True
232
+
233
+ AUTHENTICATION_BACKENDS = (
234
+ "social_core.backends.azuread_tenant.AzureADTenantOAuth2",
235
+ "django.contrib.auth.backends.ModelBackend",
236
+ )
237
+ ```
238
+
239
+ And of course add the login button somewhere, following Django Social Auth instructions.
240
+
179
241
  ## Automate deployments
180
242
  When using a service like GitLab, you can configure a Webhook to fire upon a push to your branch.
181
243