forked from jetvova/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
159 lines (134 loc) · 5.65 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2016-2019, Pulumi Corporation. All rights reserved.
import * as azure from "@pulumi/azure";
import * as pulumi from "@pulumi/pulumi";
import * as random from "@pulumi/random";
// Create a resource group
const resourceGroup = new azure.core.ResourceGroup("resourceGroup");
// Create a storage account for Blobs
const storageAccount = new azure.storage.Account("storage", {
resourceGroupName: resourceGroup.name,
accountReplicationType: "LRS",
accountTier: "Standard",
});
// The container to put our files into
const storageContainer = new azure.storage.Container("files", {
storageAccountName: storageAccount.name,
containerAccessType: "private",
});
// Azure SQL Server that we want to access from the application
const administratorLoginPassword = new random.RandomPassword("password", { length: 16, special: true }).result;
const sqlServer = new azure.sql.SqlServer("sqlserver", {
resourceGroupName: resourceGroup.name,
// The login and password are required but won't be used in our application
administratorLogin: "manualadmin",
administratorLoginPassword,
version: "12.0",
});
// Azure SQL Database that we want to access from the application
const database = new azure.sql.Database("sqldb", {
resourceGroupName: resourceGroup.name,
serverName: sqlServer.name,
requestedServiceObjectiveName: "S0",
});
// The connection string that has no credentials in it: authertication will come through MSI
const connectionString = pulumi.interpolate`Server=tcp:${sqlServer.name}.database.windows.net;Database=${database.name};`;
// A file in Blob Storage that we want to access from the application
const textBlob = new azure.storage.Blob("text", {
storageAccountName: storageAccount.name,
storageContainerName: storageContainer.name,
type: "Block",
source: "./README.md",
});
// A plan to host the App Service
const appServicePlan = new azure.appservice.Plan("asp", {
resourceGroupName: resourceGroup.name,
kind: "App",
sku: {
tier: "Basic",
size: "B1",
},
});
// ASP.NET deployment package
const blob = new azure.storage.Blob("zip", {
storageAccountName: storageAccount.name,
storageContainerName: storageContainer.name,
type: "Block",
source: new pulumi.asset.FileArchive("./webapp/bin/Debug/netcoreapp2.2/publish"),
});
const clientConfig = azure.core.getClientConfig({ async: true });
const tenantId = clientConfig.then(config => config.tenantId);
const currentPrincipal = clientConfig.then(config => config.objectId);
// Key Vault to store secrets (e.g. Blob URL with SAS)
const vault = new azure.keyvault.KeyVault("vault", {
resourceGroupName: resourceGroup.name,
skuName: "standard",
tenantId: tenantId,
accessPolicies: [{
tenantId,
// The current principal has to be granted permissions to Key Vault so that it can actually add and then remove
// secrets to/from the Key Vault. Otherwise, 'pulumi up' and 'pulumi destroy' operations will fail.
objectId: currentPrincipal,
secretPermissions: ["delete", "get", "list", "set"],
}],
});
// Put the URL of the zip Blob to KV
const secret = new azure.keyvault.Secret("deployment-zip", {
keyVaultId: vault.id,
value: azure.storage.signedBlobReadUrl(blob, storageAccount),
});
const secretUri = pulumi.interpolate`${vault.vaultUri}secrets/${secret.name}/${secret.version}`;
// The application hosted in App Service
const app = new azure.appservice.AppService("app", {
resourceGroupName: resourceGroup.name,
appServicePlanId: appServicePlan.id,
// A system-assigned managed service identity to be used for authentication and authorization to the SQL Database and the Blob Storage
identity: {
type: "SystemAssigned",
},
appSettings: {
// Website is deployed from a URL read from the Key Vault
"WEBSITE_RUN_FROM_ZIP": pulumi.interpolate`@Microsoft.KeyVault(SecretUri=${secretUri})`,
// Note that we simply provide the URL without SAS or keys
"StorageBlobUrl": textBlob.url,
},
// A SQL connection string, still without secrets in it
connectionStrings: [{
name: "db",
value: connectionString,
type: "SQLAzure",
}],
});
// Work around a preview issue https://github.com/pulumi/pulumi-azure/issues/192
const principalId = app.identity.apply(id => id.principalId || "11111111-1111-1111-1111-111111111111");
// Grant App Service access to KV secrets
const policy = new azure.keyvault.AccessPolicy("app-policy", {
keyVaultId: vault.id,
tenantId: tenantId,
objectId: principalId,
secretPermissions: ["get"],
});
// Make the App Service the admin of the SQL Server (double check if you want a more fine-grained security model in your real app)
const sqlAdmin = new azure.sql.ActiveDirectoryAdministrator("adadmin", {
resourceGroupName: resourceGroup.name,
tenantId: tenantId,
objectId: principalId,
login: "adadmin",
serverName: sqlServer.name,
});
// Grant access from App Service to the container in the storage
const blobPermission = new azure.role.Assignment("readblob", {
principalId,
scope: pulumi.interpolate`${storageAccount.id}/blobServices/default/containers/${storageContainer.name}`,
roleDefinitionName: "Storage Blob Data Reader",
});
// Add SQL firewall exceptions
const firewallRules = app.outboundIpAddresses.apply(
ips => ips.split(",").map(
ip => new azure.sql.FirewallRule(`FR${ip}`, {
resourceGroupName: resourceGroup.name,
startIpAddress: ip,
endIpAddress: ip,
serverName: sqlServer.name,
}),
));
export const endpoint = pulumi.interpolate `https://${app.defaultSiteHostname}`;