@gradientedge/cdk-utils-azure 1.0.0
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.
- package/LICENSE +21 -0
- package/dist/src/common/constants.d.ts +83 -0
- package/dist/src/common/constants.js +87 -0
- package/dist/src/common/construct.d.ts +80 -0
- package/dist/src/common/construct.js +128 -0
- package/dist/src/common/index.d.ts +6 -0
- package/dist/src/common/index.js +6 -0
- package/dist/src/common/resource-name-formatter.d.ts +18 -0
- package/dist/src/common/resource-name-formatter.js +34 -0
- package/dist/src/common/stack.d.ts +46 -0
- package/dist/src/common/stack.js +120 -0
- package/dist/src/common/tagging.d.ts +29 -0
- package/dist/src/common/tagging.js +78 -0
- package/dist/src/common/types.d.ts +57 -0
- package/dist/src/common/types.js +1 -0
- package/dist/src/construct/event-handler/index.d.ts +2 -0
- package/dist/src/construct/event-handler/index.js +2 -0
- package/dist/src/construct/event-handler/main.d.ts +61 -0
- package/dist/src/construct/event-handler/main.js +180 -0
- package/dist/src/construct/event-handler/types.d.ts +35 -0
- package/dist/src/construct/event-handler/types.js +1 -0
- package/dist/src/construct/function-app/index.d.ts +2 -0
- package/dist/src/construct/function-app/index.js +2 -0
- package/dist/src/construct/function-app/main.d.ts +128 -0
- package/dist/src/construct/function-app/main.js +374 -0
- package/dist/src/construct/function-app/types.d.ts +33 -0
- package/dist/src/construct/function-app/types.js +1 -0
- package/dist/src/construct/index.d.ts +6 -0
- package/dist/src/construct/index.js +6 -0
- package/dist/src/construct/rest-api/index.d.ts +2 -0
- package/dist/src/construct/rest-api/index.js +2 -0
- package/dist/src/construct/rest-api/main.d.ts +64 -0
- package/dist/src/construct/rest-api/main.js +216 -0
- package/dist/src/construct/rest-api/types.d.ts +25 -0
- package/dist/src/construct/rest-api/types.js +1 -0
- package/dist/src/construct/rest-api-function/index.d.ts +2 -0
- package/dist/src/construct/rest-api-function/index.js +2 -0
- package/dist/src/construct/rest-api-function/main.d.ts +66 -0
- package/dist/src/construct/rest-api-function/main.js +302 -0
- package/dist/src/construct/rest-api-function/types.d.ts +29 -0
- package/dist/src/construct/rest-api-function/types.js +1 -0
- package/dist/src/construct/rest-api-with-cache/index.d.ts +2 -0
- package/dist/src/construct/rest-api-with-cache/index.js +2 -0
- package/dist/src/construct/rest-api-with-cache/main.d.ts +41 -0
- package/dist/src/construct/rest-api-with-cache/main.js +85 -0
- package/dist/src/construct/rest-api-with-cache/types.d.ts +13 -0
- package/dist/src/construct/rest-api-with-cache/types.js +1 -0
- package/dist/src/construct/site-with-webapp/index.d.ts +2 -0
- package/dist/src/construct/site-with-webapp/index.js +2 -0
- package/dist/src/construct/site-with-webapp/main.d.ts +60 -0
- package/dist/src/construct/site-with-webapp/main.js +176 -0
- package/dist/src/construct/site-with-webapp/types.d.ts +30 -0
- package/dist/src/construct/site-with-webapp/types.js +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/services/api-management/index.d.ts +2 -0
- package/dist/src/services/api-management/index.js +2 -0
- package/dist/src/services/api-management/main.d.ts +143 -0
- package/dist/src/services/api-management/main.js +244 -0
- package/dist/src/services/api-management/types.d.ts +72 -0
- package/dist/src/services/api-management/types.js +1 -0
- package/dist/src/services/app-configuration/index.d.ts +2 -0
- package/dist/src/services/app-configuration/index.js +2 -0
- package/dist/src/services/app-configuration/main.d.ts +41 -0
- package/dist/src/services/app-configuration/main.js +71 -0
- package/dist/src/services/app-configuration/types.d.ts +3 -0
- package/dist/src/services/app-configuration/types.js +1 -0
- package/dist/src/services/app-service/index.d.ts +2 -0
- package/dist/src/services/app-service/index.js +2 -0
- package/dist/src/services/app-service/main.d.ts +40 -0
- package/dist/src/services/app-service/main.js +90 -0
- package/dist/src/services/app-service/types.d.ts +6 -0
- package/dist/src/services/app-service/types.js +1 -0
- package/dist/src/services/application-insights/index.d.ts +2 -0
- package/dist/src/services/application-insights/index.js +2 -0
- package/dist/src/services/application-insights/main.d.ts +40 -0
- package/dist/src/services/application-insights/main.js +68 -0
- package/dist/src/services/application-insights/types.d.ts +6 -0
- package/dist/src/services/application-insights/types.js +1 -0
- package/dist/src/services/authorisation/constants.d.ts +13 -0
- package/dist/src/services/authorisation/constants.js +14 -0
- package/dist/src/services/authorisation/index.d.ts +3 -0
- package/dist/src/services/authorisation/index.js +3 -0
- package/dist/src/services/authorisation/main.d.ts +84 -0
- package/dist/src/services/authorisation/main.js +120 -0
- package/dist/src/services/authorisation/types.d.ts +3 -0
- package/dist/src/services/authorisation/types.js +1 -0
- package/dist/src/services/cosmosdb/constants.d.ts +8 -0
- package/dist/src/services/cosmosdb/constants.js +10 -0
- package/dist/src/services/cosmosdb/index.d.ts +3 -0
- package/dist/src/services/cosmosdb/index.js +3 -0
- package/dist/src/services/cosmosdb/main.d.ts +87 -0
- package/dist/src/services/cosmosdb/main.js +162 -0
- package/dist/src/services/cosmosdb/types.d.ts +9 -0
- package/dist/src/services/cosmosdb/types.js +1 -0
- package/dist/src/services/dns/index.d.ts +2 -0
- package/dist/src/services/dns/index.js +2 -0
- package/dist/src/services/dns/main.d.ts +58 -0
- package/dist/src/services/dns/main.js +107 -0
- package/dist/src/services/dns/types.d.ts +9 -0
- package/dist/src/services/dns/types.js +1 -0
- package/dist/src/services/eventgrid/index.d.ts +2 -0
- package/dist/src/services/eventgrid/index.js +2 -0
- package/dist/src/services/eventgrid/main.d.ts +69 -0
- package/dist/src/services/eventgrid/main.js +136 -0
- package/dist/src/services/eventgrid/types.d.ts +11 -0
- package/dist/src/services/eventgrid/types.js +1 -0
- package/dist/src/services/function/index.d.ts +2 -0
- package/dist/src/services/function/index.js +2 -0
- package/dist/src/services/function/main.d.ts +60 -0
- package/dist/src/services/function/main.js +223 -0
- package/dist/src/services/function/types.d.ts +20 -0
- package/dist/src/services/function/types.js +1 -0
- package/dist/src/services/index.d.ts +18 -0
- package/dist/src/services/index.js +18 -0
- package/dist/src/services/key-vault/index.d.ts +2 -0
- package/dist/src/services/key-vault/index.js +2 -0
- package/dist/src/services/key-vault/main.d.ts +49 -0
- package/dist/src/services/key-vault/main.js +83 -0
- package/dist/src/services/key-vault/types.d.ts +5 -0
- package/dist/src/services/key-vault/types.js +1 -0
- package/dist/src/services/monitor/index.d.ts +2 -0
- package/dist/src/services/monitor/index.js +2 -0
- package/dist/src/services/monitor/main.d.ts +31 -0
- package/dist/src/services/monitor/main.js +36 -0
- package/dist/src/services/monitor/types.d.ts +3 -0
- package/dist/src/services/monitor/types.js +1 -0
- package/dist/src/services/operational-insights/index.d.ts +2 -0
- package/dist/src/services/operational-insights/index.js +2 -0
- package/dist/src/services/operational-insights/main.d.ts +40 -0
- package/dist/src/services/operational-insights/main.js +64 -0
- package/dist/src/services/operational-insights/types.d.ts +5 -0
- package/dist/src/services/operational-insights/types.js +1 -0
- package/dist/src/services/portal/error.d.ts +5 -0
- package/dist/src/services/portal/error.js +10 -0
- package/dist/src/services/portal/index.d.ts +4 -0
- package/dist/src/services/portal/index.js +4 -0
- package/dist/src/services/portal/main.d.ts +33 -0
- package/dist/src/services/portal/main.js +51 -0
- package/dist/src/services/portal/renderer.d.ts +11 -0
- package/dist/src/services/portal/renderer.js +156 -0
- package/dist/src/services/portal/types.d.ts +40 -0
- package/dist/src/services/portal/types.js +1 -0
- package/dist/src/services/redis/index.d.ts +2 -0
- package/dist/src/services/redis/index.js +2 -0
- package/dist/src/services/redis/main.d.ts +31 -0
- package/dist/src/services/redis/main.js +52 -0
- package/dist/src/services/redis/types.d.ts +3 -0
- package/dist/src/services/redis/types.js +1 -0
- package/dist/src/services/resource-group/index.d.ts +2 -0
- package/dist/src/services/resource-group/index.js +2 -0
- package/dist/src/services/resource-group/main.d.ts +38 -0
- package/dist/src/services/resource-group/main.js +53 -0
- package/dist/src/services/resource-group/types.d.ts +3 -0
- package/dist/src/services/resource-group/types.js +1 -0
- package/dist/src/services/security-center/index.d.ts +2 -0
- package/dist/src/services/security-center/index.js +2 -0
- package/dist/src/services/security-center/main.d.ts +31 -0
- package/dist/src/services/security-center/main.js +33 -0
- package/dist/src/services/security-center/types.d.ts +3 -0
- package/dist/src/services/security-center/types.js +1 -0
- package/dist/src/services/servicebus/index.d.ts +2 -0
- package/dist/src/services/servicebus/index.js +2 -0
- package/dist/src/services/servicebus/main.d.ts +67 -0
- package/dist/src/services/servicebus/main.js +127 -0
- package/dist/src/services/servicebus/types.d.ts +11 -0
- package/dist/src/services/servicebus/types.js +1 -0
- package/dist/src/services/storage/index.d.ts +2 -0
- package/dist/src/services/storage/index.js +2 -0
- package/dist/src/services/storage/main.d.ts +88 -0
- package/dist/src/services/storage/main.js +173 -0
- package/dist/src/services/storage/types.d.ts +20 -0
- package/dist/src/services/storage/types.js +1 -0
- package/dist/src/types/index.d.ts +4 -0
- package/dist/src/types/index.js +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { HostnameType, LoggerType } from '@pulumi/azure-native/apimanagement/index.js';
|
|
2
|
+
import { getComponentOutput } from '@pulumi/azure-native/applicationinsights/index.js';
|
|
3
|
+
import { getVaultOutput } from '@pulumi/azure-native/keyvault/index.js';
|
|
4
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
5
|
+
import { CommonAzureConstruct } from '../../common/index.js';
|
|
6
|
+
import { RoleDefinitionId } from '../../services/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* @classdesc Provides a construct to create and deploy an Azure API Management service with diagnostics and logging
|
|
9
|
+
* @example
|
|
10
|
+
* import { AzureRestApi, AzureRestApiProps } from '@gradientedge/cdk-utils'
|
|
11
|
+
*
|
|
12
|
+
* class CustomConstruct extends AzureRestApi {
|
|
13
|
+
* constructor(id: string, props: AzureRestApiProps) {
|
|
14
|
+
* super(id, props)
|
|
15
|
+
* this.props = props
|
|
16
|
+
* this.id = id
|
|
17
|
+
* this.initResources()
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
export class AzureRestApi extends CommonAzureConstruct {
|
|
22
|
+
props;
|
|
23
|
+
api;
|
|
24
|
+
applicationInsights;
|
|
25
|
+
constructor(id, props) {
|
|
26
|
+
super(id, props);
|
|
27
|
+
this.props = props;
|
|
28
|
+
this.id = id;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @summary Initialise and provision resources
|
|
32
|
+
*/
|
|
33
|
+
initResources() {
|
|
34
|
+
this.createResourceGroup();
|
|
35
|
+
this.resolveApiKeyVault();
|
|
36
|
+
this.resolveCommonLogAnalyticsWorkspace();
|
|
37
|
+
this.resolveApplicationInsights();
|
|
38
|
+
this.createApiManagement();
|
|
39
|
+
this.createNamespaceSecretRole();
|
|
40
|
+
this.createNamespaceSecret();
|
|
41
|
+
this.createSubscriptionKeySecret();
|
|
42
|
+
this.createApiManagementLogger();
|
|
43
|
+
this.createApiDiagnostic();
|
|
44
|
+
this.createDiagnosticLog();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @summary Method to resolve the API authentication Key Vault
|
|
48
|
+
*/
|
|
49
|
+
resolveApiKeyVault() {
|
|
50
|
+
this.api.authKeyVault = getVaultOutput({
|
|
51
|
+
vaultName: this.props.apiAuthKeyVault.name,
|
|
52
|
+
resourceGroupName: this.props.apiAuthKeyVault.resourceGroupName,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* @summary Method to resolve the Application Insights instance
|
|
57
|
+
*/
|
|
58
|
+
resolveApplicationInsights() {
|
|
59
|
+
if (!this.props.commonApplicationInsights || !this.props.commonApplicationInsights.resourceName)
|
|
60
|
+
return;
|
|
61
|
+
this.applicationInsights = getComponentOutput({
|
|
62
|
+
resourceName: this.props.commonApplicationInsights.resourceName,
|
|
63
|
+
resourceGroupName: this.props.commonApplicationInsights.resourceGroupName,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* @summary Method to create or resolve an existing API Management service
|
|
68
|
+
*/
|
|
69
|
+
createApiManagement() {
|
|
70
|
+
if (this.props.apiManagement.useExistingApiManagement) {
|
|
71
|
+
if (this.props.apiManagement.apiStackName) {
|
|
72
|
+
const apiStack = new pulumi.StackReference(this.props.apiManagement.apiStackName);
|
|
73
|
+
this.api.id = apiStack.getOutput('apiId');
|
|
74
|
+
this.api.name = apiStack.getOutput('apiName');
|
|
75
|
+
this.api.resourceGroupName = apiStack.getOutput('apiResourceGroupName');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
let hostnameConfigurations;
|
|
80
|
+
if (this.props.apiManagement.certificateKeyVaultId) {
|
|
81
|
+
hostnameConfigurations = [
|
|
82
|
+
{
|
|
83
|
+
hostName: `api-${this.props.locationConfig?.[this.props.location].name}.${this.props.domainName}`,
|
|
84
|
+
keyVaultId: this.props.apiManagement.certificateKeyVaultId,
|
|
85
|
+
type: HostnameType.Management,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
this.api.apim = this.apiManagementManager.createApiManagementService(this.id, this, {
|
|
90
|
+
...this.props.apiManagement,
|
|
91
|
+
serviceName: this.props.stackName,
|
|
92
|
+
location: this.resourceGroup.location,
|
|
93
|
+
resourceGroupName: this.resourceGroup.name,
|
|
94
|
+
hostnameConfigurations,
|
|
95
|
+
}, undefined, undefined, { protect: true });
|
|
96
|
+
this.api.id = this.api.apim.id;
|
|
97
|
+
this.api.name = this.api.apim.name;
|
|
98
|
+
this.api.resourceGroupName = this.resourceGroup.name;
|
|
99
|
+
if (this.props.apiManagement.certificateKeyVaultId) {
|
|
100
|
+
this.authorisationManager.createRoleAssignment(`${this.id}-kv-role`, this, {
|
|
101
|
+
principalId: this.api.apim.identity.apply(identity => identity?.principalId ?? ''),
|
|
102
|
+
roleDefinitionId: RoleDefinitionId.KEY_VAULT_CERTIFICATE_USER,
|
|
103
|
+
scope: this.props.apiManagement.certificateKeyVaultId,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
this.registerOutputs({
|
|
108
|
+
apiId: this.api.id,
|
|
109
|
+
apiName: this.api.name,
|
|
110
|
+
apiResourceGroupName: this.api.resourceGroupName,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* @summary Method to create the Key Vault role assignment for the API Management identity
|
|
115
|
+
*/
|
|
116
|
+
createNamespaceSecretRole() {
|
|
117
|
+
if (this.props.apiManagement.useExistingApiManagement)
|
|
118
|
+
return;
|
|
119
|
+
this.api.namedValueRoleAssignment = this.authorisationManager.createRoleAssignment(`${this.id}-key-vault-role-api-namespace`, this, {
|
|
120
|
+
principalId: this.api.apim.identity.apply(identity => identity?.principalId ?? ''),
|
|
121
|
+
roleDefinitionId: `/subscriptions/${this.props.subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6`,
|
|
122
|
+
scope: this.api.authKeyVault.id,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @summary Method to create the namespace secret in Key Vault for Application Insights
|
|
127
|
+
*/
|
|
128
|
+
createNamespaceSecret() {
|
|
129
|
+
if (this.props.apiManagement.useExistingApiManagement)
|
|
130
|
+
return;
|
|
131
|
+
this.api.namedValueSecret = this.keyVaultManager.createKeyVaultSecret(`${this.id}-key-vault-api-namespace-secret`, this, {
|
|
132
|
+
vaultName: this.api.authKeyVault.name,
|
|
133
|
+
secretName: `${this.applicationInsights.name}-${this.props.stackName}-key`,
|
|
134
|
+
resourceGroupName: this.resourceGroup.name,
|
|
135
|
+
properties: {
|
|
136
|
+
value: this.applicationInsights.instrumentationKey,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @summary Method to create the API Management subscription and store the key in Key Vault
|
|
142
|
+
*/
|
|
143
|
+
createSubscriptionKeySecret() {
|
|
144
|
+
if (this.props.apiManagement.useExistingApiManagement)
|
|
145
|
+
return;
|
|
146
|
+
const apiManagementSubscription = this.apiManagementManager.createSubscription(this.id, this, {
|
|
147
|
+
serviceName: this.api.apim.name,
|
|
148
|
+
resourceGroupName: this.resourceGroup.name,
|
|
149
|
+
displayName: 'all-apis',
|
|
150
|
+
state: 'active',
|
|
151
|
+
allowTracing: false,
|
|
152
|
+
scope: '', // todo
|
|
153
|
+
});
|
|
154
|
+
this.keyVaultManager.createKeyVaultSecret(`${this.id}-key-vault-api-subscription-key-secret`, this, {
|
|
155
|
+
vaultName: this.api.authKeyVault.name,
|
|
156
|
+
secretName: `${this.props.stackName}-subscription-key`,
|
|
157
|
+
resourceGroupName: this.resourceGroup.name,
|
|
158
|
+
properties: {
|
|
159
|
+
value: apiManagementSubscription.primaryKey.apply(key => key ?? ''),
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* @summary Method to create the API Management logger with Application Insights integration
|
|
165
|
+
*/
|
|
166
|
+
createApiManagementLogger() {
|
|
167
|
+
if (this.props.apiManagement.useExistingApiManagement)
|
|
168
|
+
return;
|
|
169
|
+
const apiAppNamedValue = this.apiManagementManager.createNamedValue(`${this.id}-am-nv`, this, {
|
|
170
|
+
displayName: this.applicationInsights.name,
|
|
171
|
+
resourceGroupName: this.resourceGroup.name,
|
|
172
|
+
serviceName: this.api.apim.name,
|
|
173
|
+
namedValueId: `${this.applicationInsights.name}-key`,
|
|
174
|
+
secret: true,
|
|
175
|
+
keyVault: {
|
|
176
|
+
secretIdentifier: this.api.namedValueSecret.id,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
this.api.logger = this.apiManagementManager.createLogger(`${this.id}-am-logger`, this, {
|
|
180
|
+
resourceGroupName: this.resourceGroup.name,
|
|
181
|
+
serviceName: this.api.apim.name,
|
|
182
|
+
resourceId: this.applicationInsights.id,
|
|
183
|
+
loggerType: LoggerType.ApplicationInsights,
|
|
184
|
+
credentials: {
|
|
185
|
+
instrumentationKey: `{{${apiAppNamedValue.displayName}}}`,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* @summary Method to create the API diagnostic settings for API Management
|
|
191
|
+
*/
|
|
192
|
+
createApiDiagnostic() {
|
|
193
|
+
if (this.props.apiManagement.useExistingApiManagement)
|
|
194
|
+
return;
|
|
195
|
+
this.apiManagementManager.createApiDiagnostic(`${this.id}-all-apis`, this, {
|
|
196
|
+
...this.props.apiManagementDiagnostic,
|
|
197
|
+
apiId: this.api.apim.id,
|
|
198
|
+
resourceGroupName: this.resourceGroup.name,
|
|
199
|
+
serviceName: this.api.apim.name,
|
|
200
|
+
loggerId: this.api.logger.id,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* @summary Method to create the Monitor diagnostic log settings for API Management
|
|
205
|
+
*/
|
|
206
|
+
createDiagnosticLog() {
|
|
207
|
+
if (this.props.apiManagement.useExistingApiManagement)
|
|
208
|
+
return;
|
|
209
|
+
this.monitorManager.createMonitorDiagnosticSettings(`${this.id}-apim-diagnostic`, this, {
|
|
210
|
+
...this.props.apiManagementDiagnosticSettings,
|
|
211
|
+
name: `${this.props.stackName}-api-management`,
|
|
212
|
+
resourceUri: this.api.apim.id,
|
|
213
|
+
workspaceId: this.commonLogAnalyticsWorkspace.id,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ApiManagementService, Logger } from '@pulumi/azure-native/apimanagement/index.js';
|
|
2
|
+
import { RoleAssignment } from '@pulumi/azure-native/authorization/index.js';
|
|
3
|
+
import { GetVaultResult, Secret } from '@pulumi/azure-native/keyvault/index.js';
|
|
4
|
+
import { Input, Output } from '@pulumi/pulumi';
|
|
5
|
+
import { ApiDiagnosticProps, ApiManagementProps, CommonAzureStackProps, MonitorDiagnosticSettingProps } from '../../index.js';
|
|
6
|
+
export interface ApiAuthKeyVault {
|
|
7
|
+
name: string;
|
|
8
|
+
resourceGroupName: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AzureRestApiProps extends CommonAzureStackProps {
|
|
11
|
+
apiAuthKeyVault: ApiAuthKeyVault;
|
|
12
|
+
apiManagement: ApiManagementProps;
|
|
13
|
+
apiManagementDiagnostic: ApiDiagnosticProps;
|
|
14
|
+
apiManagementDiagnosticSettings: MonitorDiagnosticSettingProps;
|
|
15
|
+
}
|
|
16
|
+
export interface AzureApi {
|
|
17
|
+
id: Input<string>;
|
|
18
|
+
name: Input<string>;
|
|
19
|
+
resourceGroupName: Input<string>;
|
|
20
|
+
authKeyVault: Output<GetVaultResult>;
|
|
21
|
+
apim: ApiManagementService;
|
|
22
|
+
namedValueSecret: Secret;
|
|
23
|
+
namedValueRoleAssignment: RoleAssignment;
|
|
24
|
+
logger: Logger;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ApiManagementApiOperationProps } from '../../services/index.js';
|
|
2
|
+
import { AzureFunctionApp } from '../function-app/index.js';
|
|
3
|
+
import { AzureApiFunction, AzureRestApiFunctionProps } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* @classdesc Provides a construct to create and deploy an Azure Function App with API Management integration
|
|
6
|
+
* @example
|
|
7
|
+
* import { AzureRestApiFunction, AzureRestApiFunctionProps } from '@gradientedge/cdk-utils'
|
|
8
|
+
*
|
|
9
|
+
* class CustomConstruct extends AzureRestApiFunction {
|
|
10
|
+
* constructor(id: string, props: AzureRestApiFunctionProps) {
|
|
11
|
+
* super(id, props)
|
|
12
|
+
* this.props = props
|
|
13
|
+
* this.id = id
|
|
14
|
+
* this.initResources()
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
export declare class AzureRestApiFunction extends AzureFunctionApp {
|
|
19
|
+
props: AzureRestApiFunctionProps;
|
|
20
|
+
api: AzureApiFunction;
|
|
21
|
+
constructor(id: string, props: AzureRestApiFunctionProps);
|
|
22
|
+
/**
|
|
23
|
+
* @summary Initialise and provision resources
|
|
24
|
+
*/
|
|
25
|
+
initResources(): void;
|
|
26
|
+
/**
|
|
27
|
+
* @summary Method to resolve the API authentication Key Vault
|
|
28
|
+
*/
|
|
29
|
+
protected resolveApiKeyVault(): void;
|
|
30
|
+
/**
|
|
31
|
+
* @summary Method to create the namespace secret in Key Vault for the function app host key
|
|
32
|
+
*/
|
|
33
|
+
protected createNamespaceSecret(): void;
|
|
34
|
+
/**
|
|
35
|
+
* @summary Method to create or resolve an existing API Management service
|
|
36
|
+
*/
|
|
37
|
+
protected createApiManagement(): void;
|
|
38
|
+
/**
|
|
39
|
+
* @summary Method to create the API Management named value and backend for the function app
|
|
40
|
+
*/
|
|
41
|
+
protected createApiManagementNamespace(): void;
|
|
42
|
+
/**
|
|
43
|
+
* @summary Method to create the API Management API and operation routes
|
|
44
|
+
*/
|
|
45
|
+
protected createApiManagementRoutes(): void;
|
|
46
|
+
/**
|
|
47
|
+
* @summary Method to create an API Management API operation
|
|
48
|
+
*/
|
|
49
|
+
protected createApiOperation(operation: ApiManagementApiOperationProps): void;
|
|
50
|
+
/**
|
|
51
|
+
* @summary Method to create a cache policy for an API Management API operation
|
|
52
|
+
*/
|
|
53
|
+
protected createApiOperationCachePolicy(operation: ApiManagementApiOperationProps): void;
|
|
54
|
+
/**
|
|
55
|
+
* @summary Method to create the CORS policy for API Management
|
|
56
|
+
*/
|
|
57
|
+
protected createCorsPolicy(): void;
|
|
58
|
+
/**
|
|
59
|
+
* @summary Method to create the API-level policy for API Management with backend routing and tracing
|
|
60
|
+
*/
|
|
61
|
+
protected createApiPolicy(): void;
|
|
62
|
+
/**
|
|
63
|
+
* @summary Method to get the dashboard template variables including API Management name
|
|
64
|
+
*/
|
|
65
|
+
protected dashboardVariables(): Record<string, any>;
|
|
66
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { HostnameType, NamedValue } from '@pulumi/azure-native/apimanagement/index.js';
|
|
2
|
+
import { getVaultOutput } from '@pulumi/azure-native/keyvault/index.js';
|
|
3
|
+
import { listWebAppHostKeysOutput } from '@pulumi/azure-native/web/index.js';
|
|
4
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
5
|
+
import _ from 'lodash';
|
|
6
|
+
import { RoleDefinitionId } from '../../services/index.js';
|
|
7
|
+
import { AzureFunctionApp } from '../function-app/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* @classdesc Provides a construct to create and deploy an Azure Function App with API Management integration
|
|
10
|
+
* @example
|
|
11
|
+
* import { AzureRestApiFunction, AzureRestApiFunctionProps } from '@gradientedge/cdk-utils'
|
|
12
|
+
*
|
|
13
|
+
* class CustomConstruct extends AzureRestApiFunction {
|
|
14
|
+
* constructor(id: string, props: AzureRestApiFunctionProps) {
|
|
15
|
+
* super(id, props)
|
|
16
|
+
* this.props = props
|
|
17
|
+
* this.id = id
|
|
18
|
+
* this.initResources()
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
export class AzureRestApiFunction extends AzureFunctionApp {
|
|
23
|
+
props;
|
|
24
|
+
api;
|
|
25
|
+
constructor(id, props) {
|
|
26
|
+
super(id, props);
|
|
27
|
+
this.props = props;
|
|
28
|
+
this.id = id;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @summary Initialise and provision resources
|
|
32
|
+
*/
|
|
33
|
+
initResources() {
|
|
34
|
+
this.createResourceGroup();
|
|
35
|
+
this.resolveCommonLogAnalyticsWorkspace();
|
|
36
|
+
this.resolveApplicationInsights();
|
|
37
|
+
this.createAppServicePlan();
|
|
38
|
+
this.createdParsedAppConfigurations();
|
|
39
|
+
this.createAppConfiguration();
|
|
40
|
+
this.createAppConfigurations();
|
|
41
|
+
this.createStorageAccount();
|
|
42
|
+
this.createStorageDeploymentContainer();
|
|
43
|
+
this.createStorageContainer();
|
|
44
|
+
this.createDataStorageAccount();
|
|
45
|
+
this.createDataStorageContainer();
|
|
46
|
+
this.generateStorageContainerSas();
|
|
47
|
+
this.createFunctionHosts();
|
|
48
|
+
this.createCodePackage();
|
|
49
|
+
this.createFunctionAppSiteConfig();
|
|
50
|
+
this.createFunctionApp();
|
|
51
|
+
this.createRoleAssignments();
|
|
52
|
+
this.resolveApiKeyVault();
|
|
53
|
+
this.createNamespaceSecret();
|
|
54
|
+
this.createApiManagement();
|
|
55
|
+
this.createApiManagementNamespace();
|
|
56
|
+
this.createApiManagementRoutes();
|
|
57
|
+
this.createCorsPolicy();
|
|
58
|
+
this.createFunctionDashboard();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* @summary Method to resolve the API authentication Key Vault
|
|
62
|
+
*/
|
|
63
|
+
resolveApiKeyVault() {
|
|
64
|
+
this.api.authKeyVault = getVaultOutput({
|
|
65
|
+
vaultName: this.props.apiAuthKeyVault.name,
|
|
66
|
+
resourceGroupName: this.props.apiAuthKeyVault.resourceGroupName,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* @summary Method to create the namespace secret in Key Vault for the function app host key
|
|
71
|
+
*/
|
|
72
|
+
createNamespaceSecret() {
|
|
73
|
+
if (!this.props.apiManagement.useExistingApiManagement)
|
|
74
|
+
return;
|
|
75
|
+
const functionDefaultKey = listWebAppHostKeysOutput({
|
|
76
|
+
name: this.app.name,
|
|
77
|
+
resourceGroupName: this.resourceGroup.name,
|
|
78
|
+
});
|
|
79
|
+
this.api.namedValueSecret = this.keyVaultManager.createKeyVaultSecret(`${this.id}-key-vault-api-namespace-secret`, this, {
|
|
80
|
+
vaultName: this.api.authKeyVault.name,
|
|
81
|
+
secretName: pulumi.interpolate `${this.app.name}key`,
|
|
82
|
+
resourceGroupName: this.resourceGroup.name,
|
|
83
|
+
properties: {
|
|
84
|
+
value: functionDefaultKey.functionKeys?.apply(keys => keys?.['default'] ?? ''),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @summary Method to create or resolve an existing API Management service
|
|
90
|
+
*/
|
|
91
|
+
createApiManagement() {
|
|
92
|
+
if (this.props.apiManagement.useExistingApiManagement) {
|
|
93
|
+
if (this.props.apiManagement.apiStackName) {
|
|
94
|
+
const apiStack = new pulumi.StackReference(this.props.apiManagement.apiStackName);
|
|
95
|
+
this.api.id = apiStack.getOutput('apiId');
|
|
96
|
+
this.api.name = apiStack.getOutput('apiName');
|
|
97
|
+
this.api.resourceGroupName = apiStack.getOutput('apiResourceGroupName');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
let hostnameConfigurations;
|
|
102
|
+
if (this.props.apiManagement.certificateKeyVaultId) {
|
|
103
|
+
hostnameConfigurations = [
|
|
104
|
+
{
|
|
105
|
+
hostName: `api-${this.props.locationConfig?.[this.props.location].name}.${this.props.domainName}`,
|
|
106
|
+
keyVaultId: this.props.apiManagement.certificateKeyVaultId,
|
|
107
|
+
type: HostnameType.Management,
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
this.api.apim = this.apiManagementManager.createApiManagementService(this.id, this, {
|
|
112
|
+
...this.props.apiManagement,
|
|
113
|
+
serviceName: this.props.stackName,
|
|
114
|
+
location: this.resourceGroup.location,
|
|
115
|
+
resourceGroupName: this.resourceGroup.name,
|
|
116
|
+
hostnameConfigurations,
|
|
117
|
+
}, undefined, undefined, { protect: true });
|
|
118
|
+
this.api.id = this.api.apim.id;
|
|
119
|
+
this.api.name = this.api.apim.name;
|
|
120
|
+
this.api.resourceGroupName = this.resourceGroup.name;
|
|
121
|
+
if (this.props.apiManagement.certificateKeyVaultId) {
|
|
122
|
+
this.authorisationManager.createRoleAssignment(`${this.id}-kv-role`, this, {
|
|
123
|
+
principalId: this.api.apim.identity.apply(identity => identity?.principalId ?? ''),
|
|
124
|
+
roleDefinitionId: RoleDefinitionId.KEY_VAULT_CERTIFICATE_USER,
|
|
125
|
+
scope: this.props.apiManagement.certificateKeyVaultId,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @summary Method to create the API Management named value and backend for the function app
|
|
132
|
+
*/
|
|
133
|
+
createApiManagementNamespace() {
|
|
134
|
+
this.api.namedValue = new NamedValue(`${this.id}-am-nv`, {
|
|
135
|
+
displayName: this.app.name,
|
|
136
|
+
keyVault: {
|
|
137
|
+
secretIdentifier: this.api.namedValueSecret.id,
|
|
138
|
+
},
|
|
139
|
+
resourceGroupName: this.api.resourceGroupName,
|
|
140
|
+
secret: true,
|
|
141
|
+
serviceName: this.api.name,
|
|
142
|
+
});
|
|
143
|
+
this.api.backend = this.apiManagementManager.createBackend(this.id, this, {
|
|
144
|
+
...this.props.apiManagementBackend,
|
|
145
|
+
title: this.props.stackName,
|
|
146
|
+
resourceGroupName: this.api.resourceGroupName,
|
|
147
|
+
serviceName: this.api.name,
|
|
148
|
+
url: pulumi.interpolate `https://${this.app.name}.azurewebsites.net/${this.props.apiManagementBackend.backendUrlPath}`,
|
|
149
|
+
resourceId: pulumi.interpolate `https://management.azure.com/subscriptions/${this.props.subscriptionId}/resourceGroups/${this.resourceGroup.name}/providers/Microsoft.Web/sites/${this.app.name}`,
|
|
150
|
+
credentials: {
|
|
151
|
+
header: {
|
|
152
|
+
'x-functions-key': [`{{${this.api.namedValue.name}}}`],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* @summary Method to create the API Management API and operation routes
|
|
159
|
+
*/
|
|
160
|
+
createApiManagementRoutes() {
|
|
161
|
+
this.api.managementApi = this.apiManagementManager.createApi(`${this.id}-apim-api`, this, {
|
|
162
|
+
...this.props.apiManagementApi,
|
|
163
|
+
displayName: this.props.apiManagementApi.displayName ?? this.props.stackName,
|
|
164
|
+
serviceName: this.api.name,
|
|
165
|
+
resourceGroupName: this.api.resourceGroupName,
|
|
166
|
+
isCurrent: this.props.apiManagementApi.isCurrent ?? true,
|
|
167
|
+
protocols: this.props.apiManagementApi.protocols ?? ['https'],
|
|
168
|
+
});
|
|
169
|
+
_.forEach(this.props.apiManagementApi.operations, operation => {
|
|
170
|
+
this.createApiOperation(operation);
|
|
171
|
+
this.createApiOperationCachePolicy(operation);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* @summary Method to create an API Management API operation
|
|
176
|
+
*/
|
|
177
|
+
createApiOperation(operation) {
|
|
178
|
+
this.api.apiOperations[operation.displayName.toString()] = this.apiManagementManager.createOperation(`${this.id}-apim-api-apim-api-operation-${operation.displayName}-${operation.method}`, this, {
|
|
179
|
+
operationId: `${operation.displayName}-${operation.method}`,
|
|
180
|
+
method: operation.method.toString().toUpperCase(),
|
|
181
|
+
serviceName: this.api.name,
|
|
182
|
+
resourceGroupName: this.api.resourceGroupName,
|
|
183
|
+
apiId: this.api.id,
|
|
184
|
+
displayName: operation.displayName,
|
|
185
|
+
urlTemplate: operation.urlTemplate,
|
|
186
|
+
templateParameters: operation.templateParameters,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* @summary Method to create a cache policy for an API Management API operation
|
|
191
|
+
*/
|
|
192
|
+
createApiOperationCachePolicy(operation) {
|
|
193
|
+
if (!operation.caching || !operation.caching.enableCacheSet)
|
|
194
|
+
return;
|
|
195
|
+
this.apiManagementManager.createOperationPolicy(`${this.id}-apim-api-operation-policy-${operation.displayName}-${operation.method}`, this, {
|
|
196
|
+
apiId: this.api.id,
|
|
197
|
+
resourceGroupName: this.api.resourceGroupName,
|
|
198
|
+
serviceName: this.api.name,
|
|
199
|
+
operationId: `${operation.displayName}-${operation.method}`,
|
|
200
|
+
value: `
|
|
201
|
+
<policies>
|
|
202
|
+
<policies>
|
|
203
|
+
<inbound>
|
|
204
|
+
<base />
|
|
205
|
+
${this.props.apiManagementApi.cacheSetInboundPolicy}
|
|
206
|
+
</inbound>
|
|
207
|
+
<backend>
|
|
208
|
+
<base />
|
|
209
|
+
</backend>
|
|
210
|
+
<outbound>
|
|
211
|
+
<base />
|
|
212
|
+
${this.props.apiManagementApi.cacheSetOutboundPolicy}
|
|
213
|
+
</outbound>
|
|
214
|
+
<on-error>
|
|
215
|
+
<base />
|
|
216
|
+
</on-error>
|
|
217
|
+
</policies>`.replace(/\n[ \t]*\n/g, '\n'), // move to utils
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* @summary Method to create the CORS policy for API Management
|
|
222
|
+
*/
|
|
223
|
+
createCorsPolicy() {
|
|
224
|
+
if (!this.props.apiManagementCors?.enableCors)
|
|
225
|
+
return;
|
|
226
|
+
const allowedOrigins = [];
|
|
227
|
+
if (this.props.apiManagementCors.allowedOrigins) {
|
|
228
|
+
_.forEach(this.props.apiManagementCors.allowedOrigins, (origin) => {
|
|
229
|
+
allowedOrigins.push(`<origin>${origin}</origin>`);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else if (this.props.apiManagementCors.originSubdomain) {
|
|
233
|
+
_.forEach(this.props.locales, (locale) => {
|
|
234
|
+
allowedOrigins.push(`<origin>https://${this.props.apiManagementCors?.originSubdomain}-${locale}.${this.props.domainName}</origin>`);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const allowedHeaders = [];
|
|
238
|
+
_.forEach(this.props.apiManagementCors.allowedHeaders, (header) => {
|
|
239
|
+
allowedHeaders.push(`<header>${header}</header>`);
|
|
240
|
+
});
|
|
241
|
+
const allowedMethods = [];
|
|
242
|
+
_.forEach(this.props.apiManagementCors.allowedMethods, (method) => {
|
|
243
|
+
allowedMethods.push(`<method>${method}</method>`);
|
|
244
|
+
});
|
|
245
|
+
this.api.corsPolicyXmlContent = `
|
|
246
|
+
<cors allow-credentials="${this.props.apiManagementCors.allowCredentials}">
|
|
247
|
+
<allowed-origins>
|
|
248
|
+
${allowedOrigins.toString().replaceAll(',', '')}
|
|
249
|
+
</allowed-origins>
|
|
250
|
+
<allowed-methods>
|
|
251
|
+
${allowedMethods.toString().replaceAll(',', '')}
|
|
252
|
+
</allowed-methods>
|
|
253
|
+
<allowed-headers>
|
|
254
|
+
${allowedHeaders.toString().replaceAll(',', '')}
|
|
255
|
+
</allowed-headers>
|
|
256
|
+
</cors>`.replace(/\n[ \t]*\n/g, '\n'); // move to utils
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* @summary Method to create the API-level policy for API Management with backend routing and tracing
|
|
260
|
+
*/
|
|
261
|
+
createApiPolicy() {
|
|
262
|
+
const policyXmlContent = pulumi.interpolate `
|
|
263
|
+
<policies>
|
|
264
|
+
<inbound>
|
|
265
|
+
<base />
|
|
266
|
+
${this.api.corsPolicyXmlContent ?? ''}
|
|
267
|
+
<set-backend-service backend-id="${this.api.backend.name}" />
|
|
268
|
+
<set-header name="traceparent" exists-action="override">
|
|
269
|
+
<value>@(context.Request.Headers.GetValueOrDefault("traceparent", ""))</value>
|
|
270
|
+
</set-header>
|
|
271
|
+
</inbound>
|
|
272
|
+
<backend>
|
|
273
|
+
<base />
|
|
274
|
+
</backend>
|
|
275
|
+
<outbound>
|
|
276
|
+
<base />
|
|
277
|
+
<set-header name="traceparent" exists-action="override">
|
|
278
|
+
<value>@(context.Request.Headers.GetValueOrDefault("traceparent", ""))</value>
|
|
279
|
+
</set-header>
|
|
280
|
+
</outbound>
|
|
281
|
+
<on-error>
|
|
282
|
+
<base />
|
|
283
|
+
</on-error>
|
|
284
|
+
</policies>`;
|
|
285
|
+
this.apiManagementManager.createPolicy(`${this.id}-apim-api-policy`, this, {
|
|
286
|
+
serviceName: this.api.name,
|
|
287
|
+
apiId: this.api.id,
|
|
288
|
+
resourceGroupName: this.api.resourceGroupName,
|
|
289
|
+
value: policyXmlContent.apply(xml => xml.replace(/\n[ \t]*\n/g, '\n')),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* @summary Method to get the dashboard template variables including API Management name
|
|
294
|
+
*/
|
|
295
|
+
dashboardVariables() {
|
|
296
|
+
const variables = super.dashboardVariables();
|
|
297
|
+
return {
|
|
298
|
+
...variables,
|
|
299
|
+
apimName: this.api.name,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Api, ApiOperation, Backend, NamedValue } from '@pulumi/azure-native/apimanagement/index.js';
|
|
2
|
+
import { ApiManagementApiProps, ApiManagementBackendProps, ApiManagementProps, ApplicationInsightsProps, AzureApi, AzureFunctionAppProps, AzureRestApiProps } from '../../index.js';
|
|
3
|
+
export interface ApiManagementRestApiProps extends ApiManagementProps {
|
|
4
|
+
useExistingApiManagement: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface ApiManagementCors {
|
|
7
|
+
enableCors: boolean;
|
|
8
|
+
allowCredentials: boolean;
|
|
9
|
+
allowedMethods: string[];
|
|
10
|
+
allowedHeaders: string[];
|
|
11
|
+
allowedOrigins?: string[];
|
|
12
|
+
originSubdomain?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AzureRestApiFunctionProps extends AzureRestApiProps, AzureFunctionAppProps {
|
|
15
|
+
apiManagementBackend: ApiManagementBackendProps;
|
|
16
|
+
apiManagementApi: ApiManagementApiProps;
|
|
17
|
+
apiManagementApplicationInsights?: ApplicationInsightsProps;
|
|
18
|
+
apiManagement: ApiManagementRestApiProps;
|
|
19
|
+
apiManagementCors?: ApiManagementCors;
|
|
20
|
+
}
|
|
21
|
+
export interface AzureApiFunction extends AzureApi {
|
|
22
|
+
corsPolicyXmlContent?: string;
|
|
23
|
+
apiOperations: {
|
|
24
|
+
[operation: string]: ApiOperation;
|
|
25
|
+
};
|
|
26
|
+
managementApi: Api;
|
|
27
|
+
backend: Backend;
|
|
28
|
+
namedValue: NamedValue;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|