pulumi-django-azure 1.0.0__py3-none-any.whl → 1.0.1__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-1.0.0.dist-info → pulumi_django_azure-1.0.1.dist-info}/LICENSE +1 -1
- {pulumi_django_azure-1.0.0.dist-info → pulumi_django_azure-1.0.1.dist-info}/METADATA +63 -1
- pulumi_django_azure-1.0.1.dist-info/RECORD +5 -0
- pulumi_django_azure-1.0.1.dist-info/top_level.txt +1 -0
- pulumi-django-azure/__init__.py +0 -0
- pulumi-django-azure/django_deployment.py +0 -485
- pulumi_django_azure-1.0.0.dist-info/RECORD +0 -7
- pulumi_django_azure-1.0.0.dist-info/top_level.txt +0 -1
- {pulumi_django_azure-1.0.0.dist-info → pulumi_django_azure-1.0.1.dist-info}/WHEEL +0 -0
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pulumi-django-azure
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
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
|
|
@@ -24,6 +24,7 @@ License: MIT License
|
|
|
24
24
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
25
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
26
|
SOFTWARE.
|
|
27
|
+
|
|
27
28
|
Project-URL: Homepage, https://gitlab.com/MaartenUreel/pulumi-django-azure
|
|
28
29
|
Keywords: django,pulumi,azure
|
|
29
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -47,6 +48,67 @@ To have a proper and secure environment, we need these components:
|
|
|
47
48
|
* Webapp with multiple custom host names and managed SSL for the website itself
|
|
48
49
|
* Webapp running pgAdmin
|
|
49
50
|
|
|
51
|
+
## Installation
|
|
52
|
+
This package is not yet published on PyPi. Until then, you can install using:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
poetry add git+ssh://git@gitlab.com:MaartenUreel/pulumi-django-azure.git
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A simple project could look like this:
|
|
59
|
+
```python
|
|
60
|
+
import pulumi
|
|
61
|
+
import pulumi_azure_native as azure
|
|
62
|
+
from pulumi_django_azure import DjangoDeployment
|
|
63
|
+
|
|
64
|
+
stack = pulumi.get_stack()
|
|
65
|
+
config = pulumi.Config()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Create resource group
|
|
69
|
+
rg = azure.resources.ResourceGroup(f"rg-{stack}")
|
|
70
|
+
|
|
71
|
+
# Create VNet
|
|
72
|
+
vnet = azure.network.VirtualNetwork(
|
|
73
|
+
f"vnet-{stack}",
|
|
74
|
+
resource_group_name=rg.name,
|
|
75
|
+
address_space=azure.network.AddressSpaceArgs(
|
|
76
|
+
address_prefixes=["10.0.0.0/16"],
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Deploy the website and all its components
|
|
81
|
+
django = DjangoDeployment(
|
|
82
|
+
stack,
|
|
83
|
+
tenant_id="abc123...",
|
|
84
|
+
resource_group_name=rg.name,
|
|
85
|
+
vnet=vnet,
|
|
86
|
+
pgsql_ip_prefix="10.0.10.0/24",
|
|
87
|
+
appservice_ip_prefix="10.0.20.0/24",
|
|
88
|
+
app_service_sku=azure.web.SkuDescriptionArgs(
|
|
89
|
+
name="B2",
|
|
90
|
+
tier="Basic",
|
|
91
|
+
),
|
|
92
|
+
storage_account_name="mystorageaccount",
|
|
93
|
+
cdn_host="cdn.example.com",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
django.add_django_website(
|
|
97
|
+
name="web",
|
|
98
|
+
db_name="mywebsite",
|
|
99
|
+
repository_url="git@gitlab.com:project/website.git",
|
|
100
|
+
repository_branch="main",
|
|
101
|
+
website_hosts=["example.com", "www.example.com"],
|
|
102
|
+
django_settings_module="mywebsite.settings.production",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
django.add_database_administrator(
|
|
106
|
+
object_id="a1b2c3....",
|
|
107
|
+
user_name="user@example.com",
|
|
108
|
+
tenant_id="a1b2c3....",
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
50
112
|
## Deployment steps
|
|
51
113
|
|
|
52
114
|
1. Deploy without custom hosts (for CDN and websites)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
pulumi_django_azure-1.0.1.dist-info/LICENSE,sha256=NX2LN3U319Zaac8b7ZgfNOco_nTBbN531X_M_13niSg,1087
|
|
2
|
+
pulumi_django_azure-1.0.1.dist-info/METADATA,sha256=VdDeV0iWCPEQjIZBCiFyGsDIvab3LxXJ1c0AsuL7_ys,7755
|
|
3
|
+
pulumi_django_azure-1.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
4
|
+
pulumi_django_azure-1.0.1.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
5
|
+
pulumi_django_azure-1.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
pulumi-django-azure/__init__.py
DELETED
|
File without changes
|
|
@@ -1,485 +0,0 @@
|
|
|
1
|
-
from typing import Optional, Sequence
|
|
2
|
-
|
|
3
|
-
import pulumi
|
|
4
|
-
import pulumi_azure_native as azure
|
|
5
|
-
import pulumi_random
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class DjangoDeployment(pulumi.ComponentResource):
|
|
9
|
-
HEALTH_CHECK_PATH = "/health-check"
|
|
10
|
-
|
|
11
|
-
def __init__(
|
|
12
|
-
self,
|
|
13
|
-
name,
|
|
14
|
-
resource_group_name: pulumi.Input[str],
|
|
15
|
-
vnet: azure.network.VirtualNetwork,
|
|
16
|
-
pgsql_ip_prefix: str,
|
|
17
|
-
appservice_ip_prefix: str,
|
|
18
|
-
app_service_sku: azure.web.SkuDescriptionArgs,
|
|
19
|
-
storage_account_name: str,
|
|
20
|
-
cdn_host: Optional[str],
|
|
21
|
-
opts=None,
|
|
22
|
-
):
|
|
23
|
-
super().__init__("pkg:index:DjangoDeployment", name, None, opts)
|
|
24
|
-
|
|
25
|
-
# child_opts = pulumi.ResourceOptions(parent=self)
|
|
26
|
-
|
|
27
|
-
self._name = name
|
|
28
|
-
self._rg = resource_group_name
|
|
29
|
-
self._vnet = vnet
|
|
30
|
-
|
|
31
|
-
# Storage resources
|
|
32
|
-
self._create_storage(account_name=storage_account_name)
|
|
33
|
-
self._cdn_host = self._create_cdn(custom_host=cdn_host)
|
|
34
|
-
|
|
35
|
-
# PostgreSQL resources
|
|
36
|
-
self._create_database(ip_prefix=pgsql_ip_prefix)
|
|
37
|
-
|
|
38
|
-
# Subnet for the apps
|
|
39
|
-
self._app_subnet = self._create_subnet(
|
|
40
|
-
name="app-service",
|
|
41
|
-
prefix=appservice_ip_prefix,
|
|
42
|
-
delegation_service="Microsoft.Web/serverFarms",
|
|
43
|
-
service_endpoints=["Microsoft.Storage"],
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
# Create App Service plan that will host all websites
|
|
47
|
-
self._app_service_plan = self._create_app_service_plan(sku=app_service_sku)
|
|
48
|
-
|
|
49
|
-
# Create a pgAdmin app
|
|
50
|
-
self._create_pgadmin_app()
|
|
51
|
-
|
|
52
|
-
def _create_storage(self, account_name: str):
|
|
53
|
-
# Create blob storage
|
|
54
|
-
self._storage_account = azure.storage.StorageAccount(
|
|
55
|
-
f"sa-{self._name}",
|
|
56
|
-
resource_group_name=self._rg,
|
|
57
|
-
account_name=account_name,
|
|
58
|
-
sku=azure.storage.SkuArgs(
|
|
59
|
-
name=azure.storage.SkuName.STANDARD_LRS,
|
|
60
|
-
),
|
|
61
|
-
kind=azure.storage.Kind.STORAGE_V2,
|
|
62
|
-
access_tier=azure.storage.AccessTier.HOT,
|
|
63
|
-
allow_blob_public_access=True,
|
|
64
|
-
public_network_access=azure.storage.PublicNetworkAccess.ENABLED,
|
|
65
|
-
enable_https_traffic_only=True,
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
def _create_cdn(self, custom_host: Optional[str]) -> pulumi.Output[str]:
|
|
69
|
-
"""
|
|
70
|
-
Create a CDN endpoint. If a host name is given, it will be used as the custom domain.
|
|
71
|
-
Otherwise, the default CDN host name will be returned.
|
|
72
|
-
|
|
73
|
-
:param custom_host: The custom domain (optional)
|
|
74
|
-
:return: The CDN host name
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
# Put CDN in front
|
|
78
|
-
cdn = azure.cdn.Profile(
|
|
79
|
-
f"cdn-{self._name}",
|
|
80
|
-
resource_group_name=self._rg,
|
|
81
|
-
location="global",
|
|
82
|
-
sku=azure.cdn.SkuArgs(
|
|
83
|
-
name=azure.cdn.SkuName.STANDARD_MICROSOFT,
|
|
84
|
-
),
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
endpoint_origin = self._storage_account.primary_endpoints.apply(
|
|
88
|
-
lambda primary_endpoints: primary_endpoints.blob.replace("https://", "").replace("/", "")
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
self._cdn_endpoint = azure.cdn.Endpoint(
|
|
92
|
-
f"cdn-endpoint-{self._name}",
|
|
93
|
-
resource_group_name=self._rg,
|
|
94
|
-
location="global",
|
|
95
|
-
is_compression_enabled=True,
|
|
96
|
-
content_types_to_compress=[
|
|
97
|
-
"application/javascript",
|
|
98
|
-
"application/json",
|
|
99
|
-
"application/x-javascript",
|
|
100
|
-
"application/xml",
|
|
101
|
-
"text/css",
|
|
102
|
-
"text/html",
|
|
103
|
-
"text/javascript",
|
|
104
|
-
"text/plain",
|
|
105
|
-
],
|
|
106
|
-
is_http_allowed=False,
|
|
107
|
-
is_https_allowed=True,
|
|
108
|
-
profile_name=cdn.name,
|
|
109
|
-
origin_host_header=endpoint_origin,
|
|
110
|
-
origins=[azure.cdn.DeepCreatedOriginArgs(name="origin-storage", host_name=endpoint_origin)],
|
|
111
|
-
query_string_caching_behavior=azure.cdn.QueryStringCachingBehavior.IGNORE_QUERY_STRING,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
pulumi.export("cdn_cname", self._cdn_endpoint.host_name)
|
|
115
|
-
|
|
116
|
-
# Add custom domain if given
|
|
117
|
-
if custom_host:
|
|
118
|
-
azure.cdn.CustomDomain(
|
|
119
|
-
f"cdn-custom-domain-{self._name}",
|
|
120
|
-
resource_group_name=self._rg,
|
|
121
|
-
profile_name=cdn.name,
|
|
122
|
-
endpoint_name=self._cdn_endpoint.name,
|
|
123
|
-
host_name=custom_host,
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
return custom_host
|
|
127
|
-
else:
|
|
128
|
-
# Return the default CDN host name
|
|
129
|
-
return self._cdn_endpoint.host_name
|
|
130
|
-
|
|
131
|
-
def _create_database(self, ip_prefix: str):
|
|
132
|
-
# Create subnet for PostgreSQL
|
|
133
|
-
subnet = self._create_subnet(
|
|
134
|
-
name="pgsql",
|
|
135
|
-
prefix=ip_prefix,
|
|
136
|
-
delegation_service="Microsoft.DBforPostgreSQL/flexibleServers",
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
# Create private DNS zone
|
|
140
|
-
dns = azure.network.PrivateZone(
|
|
141
|
-
f"dns-pgsql-{self._name}",
|
|
142
|
-
resource_group_name=self._rg,
|
|
143
|
-
location="global",
|
|
144
|
-
# The zone name must end with this to work with Azure DB
|
|
145
|
-
private_zone_name=f"{self._name}.postgres.database.azure.com",
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
# Link the private DNS zone to the VNet in order to make resolving work
|
|
149
|
-
azure.network.VirtualNetworkLink(
|
|
150
|
-
f"vnet-link-pgsql-{self._name}",
|
|
151
|
-
resource_group_name=self._rg,
|
|
152
|
-
location="global",
|
|
153
|
-
private_zone_name=dns.name,
|
|
154
|
-
virtual_network=azure.network.SubResourceArgs(id=self._vnet.id),
|
|
155
|
-
registration_enabled=False,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# Create PostgreSQL server
|
|
159
|
-
self._pgsql = azure.dbforpostgresql.Server(
|
|
160
|
-
f"pgsql-{self._name}",
|
|
161
|
-
resource_group_name=self._rg,
|
|
162
|
-
sku=azure.dbforpostgresql.SkuArgs(
|
|
163
|
-
name="Standard_B2s",
|
|
164
|
-
tier=azure.dbforpostgresql.SkuTier.BURSTABLE,
|
|
165
|
-
),
|
|
166
|
-
version="16",
|
|
167
|
-
auth_config=azure.dbforpostgresql.AuthConfigArgs(
|
|
168
|
-
password_auth=azure.dbforpostgresql.PasswordAuthEnum.DISABLED,
|
|
169
|
-
active_directory_auth=azure.dbforpostgresql.ActiveDirectoryAuthEnum.ENABLED,
|
|
170
|
-
tenant_id=config.require("tenant_id"),
|
|
171
|
-
),
|
|
172
|
-
storage=azure.dbforpostgresql.StorageArgs(storage_size_gb=32),
|
|
173
|
-
network=azure.dbforpostgresql.NetworkArgs(
|
|
174
|
-
delegated_subnet_resource_id=subnet.id,
|
|
175
|
-
private_dns_zone_arm_resource_id=dns.id,
|
|
176
|
-
),
|
|
177
|
-
backup=azure.dbforpostgresql.BackupArgs(
|
|
178
|
-
backup_retention_days=7,
|
|
179
|
-
geo_redundant_backup=azure.dbforpostgresql.GeoRedundantBackupEnum.DISABLED,
|
|
180
|
-
),
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
pulumi.export("pgsql_host", self._pgsql.fully_qualified_domain_name)
|
|
184
|
-
|
|
185
|
-
def _create_subnet(
|
|
186
|
-
self, name, prefix, delegation_service: Optional[str] = None, service_endpoints: Sequence[str] = []
|
|
187
|
-
) -> azure.network.Subnet:
|
|
188
|
-
"""
|
|
189
|
-
Generic method to create a subnet with a delegation.
|
|
190
|
-
|
|
191
|
-
:param name: The name of the subnet
|
|
192
|
-
:param prefix: The IP prefix
|
|
193
|
-
:param delegation_service: The service to delegate to
|
|
194
|
-
:param service_endpoints: The service endpoints to enable
|
|
195
|
-
:return: The subnet
|
|
196
|
-
"""
|
|
197
|
-
|
|
198
|
-
if delegation_service:
|
|
199
|
-
delegation_service = azure.network.DelegationArgs(
|
|
200
|
-
name=f"delegation-{name}-{self._name}",
|
|
201
|
-
service_name=delegation_service,
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
service_endpoints = [azure.network.ServiceEndpointPropertiesFormatArgs(service=s) for s in service_endpoints]
|
|
205
|
-
|
|
206
|
-
return azure.network.Subnet(
|
|
207
|
-
f"subnet-{name}-{self._name}",
|
|
208
|
-
resource_group_name=self._rg,
|
|
209
|
-
virtual_network_name=self._vnet.name,
|
|
210
|
-
address_prefix=prefix,
|
|
211
|
-
delegations=[delegation_service],
|
|
212
|
-
service_endpoints=service_endpoints,
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
def _create_app_service_plan(self, sku: azure.web.SkuDescriptionArgs) -> azure.web.AppServicePlan:
|
|
216
|
-
# Create App Service plan
|
|
217
|
-
return azure.web.AppServicePlan(
|
|
218
|
-
f"asp-{self._name}",
|
|
219
|
-
resource_group_name=self._rg,
|
|
220
|
-
kind="Linux",
|
|
221
|
-
reserved=True,
|
|
222
|
-
sku=sku,
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
def _create_pgadmin_app(self):
|
|
226
|
-
# The app itself
|
|
227
|
-
app = azure.web.WebApp(
|
|
228
|
-
f"app-pgadmin-{self._name}",
|
|
229
|
-
resource_group_name=self._rg,
|
|
230
|
-
server_farm_id=self._app_service_plan.id,
|
|
231
|
-
virtual_network_subnet_id=self._app_subnet.id,
|
|
232
|
-
identity=azure.web.ManagedServiceIdentityArgs(
|
|
233
|
-
type=azure.web.ManagedServiceIdentityType.SYSTEM_ASSIGNED,
|
|
234
|
-
),
|
|
235
|
-
https_only=True,
|
|
236
|
-
site_config=azure.web.SiteConfigArgs(
|
|
237
|
-
ftps_state=azure.web.FtpsState.DISABLED,
|
|
238
|
-
linux_fx_version="DOCKER|dpage/pgadmin4",
|
|
239
|
-
health_check_path="/misc/ping",
|
|
240
|
-
app_settings=[
|
|
241
|
-
azure.web.NameValuePairArgs(name="DOCKER_REGISTRY_SERVER_URL", value="https://index.docker.io/v1"),
|
|
242
|
-
azure.web.NameValuePairArgs(name="DOCKER_ENABLE_CI", value="true"),
|
|
243
|
-
# azure.web.NameValuePairArgs(name="WEBSITE_HTTPLOGGING_RETENTION_DAYS", value="7"),
|
|
244
|
-
# pgAdmin settings
|
|
245
|
-
azure.web.NameValuePairArgs(name="PGADMIN_DISABLE_POSTFIX", value="true"),
|
|
246
|
-
azure.web.NameValuePairArgs(name="PGADMIN_AUTHENTICATION_SOURCES", value="['oauth2, 'internal']"),
|
|
247
|
-
azure.web.NameValuePairArgs(name="PGADMIN_OAUTH2_NAME", value="azure"),
|
|
248
|
-
azure.web.NameValuePairArgs(name="PGADMIN_DEFAULT_EMAIL", value="dbadmin@dbadmin.net"),
|
|
249
|
-
azure.web.NameValuePairArgs(name="PGADMIN_DEFAULT_PASSWORD", value="dbadmin"),
|
|
250
|
-
],
|
|
251
|
-
),
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
# Create a storage container for persistent data (SMB share)
|
|
255
|
-
share = azure.storage.FileShare(
|
|
256
|
-
f"share-pgadmin-{self._name}",
|
|
257
|
-
resource_group_name=self._rg,
|
|
258
|
-
account_name=self._storage_account.name,
|
|
259
|
-
share_name="pgadmin",
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
# Mount the storage container
|
|
263
|
-
azure.web.WebAppAzureStorageAccounts(
|
|
264
|
-
f"app-pgadmin-mount-{self._name}",
|
|
265
|
-
resource_group_name=self._rg,
|
|
266
|
-
name=app.name,
|
|
267
|
-
properties={
|
|
268
|
-
"pgadmin-data": azure.web.AzureStorageInfoValueArgs(
|
|
269
|
-
account_name=self._storage_account.name,
|
|
270
|
-
access_key=self._get_storage_account_access_keys(self._storage_account)[0].value,
|
|
271
|
-
mount_path="/var/lib/pgadmin",
|
|
272
|
-
share_name=share.name,
|
|
273
|
-
type=azure.web.AzureStorageType.AZURE_FILES,
|
|
274
|
-
)
|
|
275
|
-
},
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
pulumi.export("pgadmin_url", app.default_host_name.apply(lambda host: f"https://{host}"))
|
|
279
|
-
|
|
280
|
-
def _add_webapp_host(self, app: azure.web.WebApp, host: str, suffix: str):
|
|
281
|
-
"""
|
|
282
|
-
Because of a circular dependency, we need to create the certificate and the binding in two steps.
|
|
283
|
-
First we create a binding without a certificate,
|
|
284
|
-
then we create the certificate and then we update the binding.
|
|
285
|
-
|
|
286
|
-
The certificate needs the binding, and the binding needs the thumbprint of the certificate.
|
|
287
|
-
|
|
288
|
-
See also: https://github.com/pulumi/pulumi-azure-native/issues/578
|
|
289
|
-
|
|
290
|
-
:param app: The web app
|
|
291
|
-
:param host: The host name
|
|
292
|
-
:param suffix: A suffix to make the resource name unique
|
|
293
|
-
"""
|
|
294
|
-
|
|
295
|
-
safe_host = host.replace(".", "-")
|
|
296
|
-
|
|
297
|
-
try:
|
|
298
|
-
# Retrieve the existing binding - this will throw an exception if it doesn't exist
|
|
299
|
-
azure.web.get_web_app_host_name_binding(
|
|
300
|
-
resource_group_name=app.resource_group,
|
|
301
|
-
name=app.name,
|
|
302
|
-
host_name=host,
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
# Create a managed certificate
|
|
306
|
-
# This will work because the binding exists actually
|
|
307
|
-
certificate = azure.web.Certificate(
|
|
308
|
-
f"cert-{suffix}-{safe_host}",
|
|
309
|
-
resource_group_name=self._rg,
|
|
310
|
-
server_farm_id=app.server_farm_id,
|
|
311
|
-
canonical_name=host,
|
|
312
|
-
host_names=[host],
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
# Create a new binding, replacing the old one,
|
|
316
|
-
# with the certificate
|
|
317
|
-
azure.web.WebAppHostNameBinding(
|
|
318
|
-
f"host-binding-{suffix}-{safe_host}",
|
|
319
|
-
resource_group_name=self._rg,
|
|
320
|
-
name=app.name,
|
|
321
|
-
host_name=host,
|
|
322
|
-
ssl_state=azure.web.SslState.SNI_ENABLED,
|
|
323
|
-
thumbprint=certificate.thumbprint,
|
|
324
|
-
)
|
|
325
|
-
except Exception:
|
|
326
|
-
# Create a binding without a certificate
|
|
327
|
-
azure.web.WebAppHostNameBinding(
|
|
328
|
-
f"host-binding-{suffix}-{safe_host}",
|
|
329
|
-
resource_group_name=self._rg,
|
|
330
|
-
name=app.name,
|
|
331
|
-
host_name=host,
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
def _get_storage_account_access_keys(
|
|
335
|
-
self, storage_account: azure.storage.StorageAccount
|
|
336
|
-
) -> Sequence[azure.storage.outputs.StorageAccountKeyResponse]:
|
|
337
|
-
"""
|
|
338
|
-
Helper function to get the access keys for a storage account.
|
|
339
|
-
|
|
340
|
-
:param storage_account: The storage account
|
|
341
|
-
:return: The access keys
|
|
342
|
-
"""
|
|
343
|
-
|
|
344
|
-
keys = azure.storage.list_storage_account_keys(
|
|
345
|
-
resource_group_name=self._rg,
|
|
346
|
-
account_name=storage_account.name,
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
return keys.keys
|
|
350
|
-
|
|
351
|
-
def add_database_administrator(self, object_id: str, user_name: str, tenant_id: str):
|
|
352
|
-
azure.dbforpostgresql.Administrator(
|
|
353
|
-
# Must be random but a GUID
|
|
354
|
-
f"pgsql-admin-{user_name.replace('@', '_')}-{self._name}",
|
|
355
|
-
resource_group_name=self._rg,
|
|
356
|
-
server_name=self._pgsql.name,
|
|
357
|
-
object_id=object_id,
|
|
358
|
-
principal_name=user_name,
|
|
359
|
-
principal_type=azure.dbforpostgresql.PrincipalType.USER,
|
|
360
|
-
tenant_id=tenant_id,
|
|
361
|
-
)
|
|
362
|
-
|
|
363
|
-
def add_django_website(
|
|
364
|
-
self,
|
|
365
|
-
name: str,
|
|
366
|
-
db_name: str,
|
|
367
|
-
repository_url: str,
|
|
368
|
-
repository_branch: str,
|
|
369
|
-
website_hosts: list[str],
|
|
370
|
-
django_settings_module: str,
|
|
371
|
-
):
|
|
372
|
-
# Create a database
|
|
373
|
-
db = azure.dbforpostgresql.Database(
|
|
374
|
-
f"db-{name}",
|
|
375
|
-
database_name=db_name,
|
|
376
|
-
resource_group_name=self._rg,
|
|
377
|
-
server_name=self._pgsql.name,
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
# Container for media files
|
|
381
|
-
media_container = azure.storage.BlobContainer(
|
|
382
|
-
f"storage-container-{name}-media-{self._name}",
|
|
383
|
-
resource_group_name=self._rg,
|
|
384
|
-
account_name=self._storage_account.name,
|
|
385
|
-
public_access=azure.storage.PublicAccess.BLOB,
|
|
386
|
-
container_name=f"{name}-media",
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
# Container for static files
|
|
390
|
-
static_container = azure.storage.BlobContainer(
|
|
391
|
-
f"storage-container-{name}-static-{self._name}",
|
|
392
|
-
resource_group_name=self._rg,
|
|
393
|
-
account_name=self._storage_account.name,
|
|
394
|
-
public_access=azure.storage.PublicAccess.BLOB,
|
|
395
|
-
container_name=f"{name}-static",
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
# Create a Django Secret Key (random)
|
|
399
|
-
secret_key = pulumi_random.RandomString(f"django-secret-{name}-{self._name}", length=50)
|
|
400
|
-
|
|
401
|
-
app = azure.web.WebApp(
|
|
402
|
-
f"app-{name}-{self._name}",
|
|
403
|
-
resource_group_name=self._rg,
|
|
404
|
-
server_farm_id=self._app_service_plan.id,
|
|
405
|
-
virtual_network_subnet_id=self._app_subnet.id,
|
|
406
|
-
identity=azure.web.ManagedServiceIdentityArgs(
|
|
407
|
-
type=azure.web.ManagedServiceIdentityType.SYSTEM_ASSIGNED,
|
|
408
|
-
),
|
|
409
|
-
https_only=True,
|
|
410
|
-
site_config=azure.web.SiteConfigArgs(
|
|
411
|
-
always_on=True,
|
|
412
|
-
health_check_path=self.HEALTH_CHECK_PATH,
|
|
413
|
-
ftps_state=azure.web.FtpsState.DISABLED,
|
|
414
|
-
python_version="3.12",
|
|
415
|
-
# scm_type=azure.web.ScmType.EXTERNAL_GIT,
|
|
416
|
-
linux_fx_version="PYTHON|3.12",
|
|
417
|
-
app_settings=[
|
|
418
|
-
# Build settings
|
|
419
|
-
azure.web.NameValuePairArgs(name="SCM_DO_BUILD_DURING_DEPLOYMENT", value="true"),
|
|
420
|
-
azure.web.NameValuePairArgs(name="PRE_BUILD_COMMAND", value="cicd/pre_build.sh"),
|
|
421
|
-
azure.web.NameValuePairArgs(name="POST_BUILD_COMMAND", value="cicd/post_build.sh"),
|
|
422
|
-
azure.web.NameValuePairArgs(name="HEALTH_CHECK_PATH", value=self.HEALTH_CHECK_PATH),
|
|
423
|
-
# Django settings
|
|
424
|
-
azure.web.NameValuePairArgs(name="DEBUG", value="true"),
|
|
425
|
-
azure.web.NameValuePairArgs(name="DJANGO_SETTINGS_MODULE", value=django_settings_module),
|
|
426
|
-
azure.web.NameValuePairArgs(name="DJANGO_SECRET_KEY", value=secret_key.result),
|
|
427
|
-
azure.web.NameValuePairArgs(name="DJANGO_ALLOWED_HOSTS", value=",".join(website_hosts)),
|
|
428
|
-
# Storage settings
|
|
429
|
-
azure.web.NameValuePairArgs(name="AZURE_STORAGE_ACCOUNT_NAME", value=self._storage_account.name),
|
|
430
|
-
azure.web.NameValuePairArgs(name="AZURE_STORAGE_CONTAINER_STATICFILES", value=static_container.name),
|
|
431
|
-
azure.web.NameValuePairArgs(name="AZURE_STORAGE_CONTAINER_MEDIA", value=media_container.name),
|
|
432
|
-
# CDN
|
|
433
|
-
azure.web.NameValuePairArgs(name="CDN_HOST", value=self._cdn_host),
|
|
434
|
-
# Database settings
|
|
435
|
-
azure.web.NameValuePairArgs(name="DB_HOST", value=self._pgsql.fully_qualified_domain_name),
|
|
436
|
-
azure.web.NameValuePairArgs(name="DB_NAME", value=db.name),
|
|
437
|
-
azure.web.NameValuePairArgs(name="DB_USER", value=f"{name}_managed_identity"),
|
|
438
|
-
],
|
|
439
|
-
),
|
|
440
|
-
)
|
|
441
|
-
|
|
442
|
-
# We need this to create the database role and grant permissions
|
|
443
|
-
principal_id = app.identity.apply(lambda identity: identity.principal_id)
|
|
444
|
-
pulumi.export(f"{name}_site_principal_id", principal_id)
|
|
445
|
-
pulumi.export(f"{name}_site_db_user", f"{name}_managed_identity")
|
|
446
|
-
|
|
447
|
-
# We need this to verify custom domains
|
|
448
|
-
pulumi.export(f"{name}_site_domain_verification_id", app.custom_domain_verification_id)
|
|
449
|
-
pulumi.export(f"{name}_site_domain_cname", app.default_host_name)
|
|
450
|
-
|
|
451
|
-
for host in website_hosts:
|
|
452
|
-
self._add_webapp_host(app=app, host=host, suffix=f"{name}-{self._name}")
|
|
453
|
-
|
|
454
|
-
# To enable deployment from GitLab
|
|
455
|
-
azure.web.WebAppSourceControl(
|
|
456
|
-
f"app-{name}-sourcecontrol-{self._name}",
|
|
457
|
-
resource_group_name=self._rg,
|
|
458
|
-
name=app.name,
|
|
459
|
-
repo_url=repository_url,
|
|
460
|
-
branch=repository_branch,
|
|
461
|
-
is_git_hub_action=False,
|
|
462
|
-
is_manual_integration=True,
|
|
463
|
-
is_mercurial=False,
|
|
464
|
-
)
|
|
465
|
-
|
|
466
|
-
# Where we can retrieve the SSH key
|
|
467
|
-
pulumi.export(
|
|
468
|
-
f"{name}_deploy_ssh_key_url", app.name.apply(lambda name: f"https://{name}.scm.azurewebsites.net/api/sshkey?ensurePublicKey=1")
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
# Find the role for Storage Blob Data Contributor
|
|
472
|
-
storage_role = self._storage_account.id.apply(
|
|
473
|
-
lambda scope: azure.authorization.get_role_definition(
|
|
474
|
-
role_definition_id="ba92f5b4-2d11-453d-a403-e96b0029c9fe",
|
|
475
|
-
scope=scope,
|
|
476
|
-
)
|
|
477
|
-
)
|
|
478
|
-
|
|
479
|
-
azure.authorization.RoleAssignment(
|
|
480
|
-
f"ra-{name}-storage",
|
|
481
|
-
principal_id=principal_id,
|
|
482
|
-
principal_type=azure.authorization.PrincipalType.SERVICE_PRINCIPAL,
|
|
483
|
-
role_definition_id=storage_role.id,
|
|
484
|
-
scope=self._storage_account.id,
|
|
485
|
-
)
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
pulumi-django-azure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
pulumi-django-azure/django_deployment.py,sha256=GaQCEEKN8VSxFl7dmGN7dzCST7RPMkeamPJH3im2Be4,20162
|
|
3
|
-
pulumi_django_azure-1.0.0.dist-info/LICENSE,sha256=z4HD1y8njTvCggIu-d4Nt_Ha_lkNXUqJt1TeFhmQL-Y,1086
|
|
4
|
-
pulumi_django_azure-1.0.0.dist-info/METADATA,sha256=Rt0oQyqTtmou0hL3_zPALHh6enZWERrkz8uklKLPMIA,6206
|
|
5
|
-
pulumi_django_azure-1.0.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
6
|
-
pulumi_django_azure-1.0.0.dist-info/top_level.txt,sha256=1VBZjtWWLt9__5oAjjq4zj96rfdE79Kr1ve8F38aVNc,20
|
|
7
|
-
pulumi_django_azure-1.0.0.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pulumi-django-azure
|
|
File without changes
|