diff --git a/.vscode/launch.json b/.vscode/launch.json index 64c43fdd..19edfeea 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "program": "${workspaceFolder}/system/dist/index.js", "args": [ "serve", - "${workspaceFolder}/test.yaml" + "${workspaceFolder}/system/package/test.yaml" ], "envFile": "${workspaceFolder}/.env", "env": { diff --git a/ingredient/ingredient-apim-api/README.md b/ingredient/ingredient-apim-api/README.md index cff90693..201b68be 100644 --- a/ingredient/ingredient-apim-api/README.md +++ b/ingredient/ingredient-apim-api/README.md @@ -55,7 +55,7 @@ options: **apis** ```yaml -apis: #follows this azure spec for *ApiVersionSetContract* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L1460 +apis: #follows this azure spec for *ApiVersionSetContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3215 - name: #unique id for the API (version set) - required versions: - versionSchema #See next section for version schema @@ -64,7 +64,7 @@ apis: #follows this azure spec for *ApiVersionSetContract* : https://github.com/ **api[n].versions** ```yaml -versions: #follows this azure spec for *ApiCreateOrUpdateParameter* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L1310 +versions: #follows this azure spec for *ApiCreateOrUpdateParameter* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L180 - name: #typically you want set the id to - to keep the id consistant for the version set it belongs to - required version: #version string that will be used as part of the above ApiVersionSetContract.versioningSchema products: #optional list of product names to assign API to @@ -77,15 +77,15 @@ versions: #follows this azure spec for *ApiCreateOrUpdateParameter* : https://gi **api.versions[n].policies** ```yaml -policies: #scheme follows this azure spec for *PolicyContract* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L797 +policies: #scheme follows this azure spec for *PolicyContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3097 - operation: #optional, and if not set this will be the default policy set for the entire api. Otherwise, name of an operation within this api to apply the policy to. ``` **api.versions[n].diagnostics** ```yaml -diagnostics: #scheme follows this azure spec for *PolicyContract* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L727 - - name: #required name of the diagnostic +diagnostics: #follows this azure spec for *DiagnosticSettingsResource* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/monitor/arm-monitor/src/models/index.ts#L991 + - name: #required name of diagnostics settings ``` ## Utility Functions diff --git a/ingredient/ingredient-apim-api/package.json b/ingredient/ingredient-apim-api/package.json index 4f4e4c15..b557dbbd 100644 --- a/ingredient/ingredient-apim-api/package.json +++ b/ingredient/ingredient-apim-api/package.json @@ -34,7 +34,7 @@ "@types/node": "^10.12.18" }, "dependencies": { - "@azure/arm-apimanagement": "^6.0.0", + "@azure/arm-apimanagement": "^9.0.0", "got": "^11.7.0" }, "publishConfig": { diff --git a/ingredient/ingredient-apim-api/src/functions.ts b/ingredient/ingredient-apim-api/src/functions.ts index 6bbeb9a4..8dd3f26b 100644 --- a/ingredient/ingredient-apim-api/src/functions.ts +++ b/ingredient/ingredient-apim-api/src/functions.ts @@ -1,11 +1,12 @@ +import { ClientSecretCredential } from '@azure/identity'; import { BaseUtility } from '@azbake/core' import { ApiManagementClient } from "@azure/arm-apimanagement" -import { ApiGetResponse, BackendGetResponse } from '@azure/arm-apimanagement/esm/models'; +import { ApiGetResponse, BackendGetResponse } from '@azure/arm-apimanagement/src/models'; export class ApimApiUtils extends BaseUtility { public async get_api(resourceGroup: string, apimName: string, apiId: string): Promise { - const token: any = this.context.AuthToken + const token = new ClientSecretCredential(this.context.AuthToken.domain, this.context.AuthToken.clientId, this.context.AuthToken.secret); let client = new ApiManagementClient(token, this.context.Environment.authentication.subscriptionId); let api = await client.api.get(resourceGroup, apimName, apiId); @@ -16,7 +17,7 @@ export class ApimApiUtils extends BaseUtility { } public async get_backend(resourceGroup: string, apimName: string, backendId: string): Promise { - const token: any = this.context.AuthToken + const token = new ClientSecretCredential(this.context.AuthToken.domain, this.context.AuthToken.clientId, this.context.AuthToken.secret); let client = new ApiManagementClient(token, this.context.Environment.authentication.subscriptionId); let backend = await client.backend.get(resourceGroup, apimName, backendId); diff --git a/ingredient/ingredient-apim-api/src/plugin.ts b/ingredient/ingredient-apim-api/src/plugin.ts index ea86c76d..e1a68213 100644 --- a/ingredient/ingredient-apim-api/src/plugin.ts +++ b/ingredient/ingredient-apim-api/src/plugin.ts @@ -1,7 +1,9 @@ +import { ClientSecretCredential } from '@azure/identity'; import { BaseIngredient, IngredientManager, BakeVariable } from "@azbake/core" -import { ApiManagementClient } from "@azure/arm-apimanagement" -import { DiagnosticCreateOrUpdateOptionalParams, ApiCreateOrUpdateParameter, ApiContract, PolicyContract, ApiPolicyCreateOrUpdateOptionalParams, ProductContract, ApiVersionSetContract, ApiVersionSetCreateOrUpdateOptionalParams, ApiVersionSetContractDetails, DiagnosticContract } from "@azure/arm-apimanagement/esm/models"; +import { ApiManagementClient, ProductContract } from "@azure/arm-apimanagement" +import { DiagnosticCreateOrUpdateOptionalParams, ApiCreateOrUpdateParameter, ApiContract, PolicyContract, ApiPolicyCreateOrUpdateOptionalParams, ApiVersionSetContract, ApiVersionSetCreateOrUpdateOptionalParams, ApiVersionSetContractDetails, DiagnosticContract } from "@azure/arm-apimanagement/src/models"; import { RestError } from "@azure/ms-rest-js" +import { PagedAsyncIterableIterator } from '@azure/core-paging'; import * as fs from 'fs'; import stockDiagnostics from "./stockDiagnostics.json" @@ -85,7 +87,7 @@ export class ApimApiPlugin extends BaseIngredient { this._logger.log('APIM API Plugin: Binding APIM to resource: ' + this.resource_group + '\\' + this.resource_name); - const token: any = this._ctx.AuthToken + const token = new ClientSecretCredential(this._ctx.AuthToken.domain, this._ctx.AuthToken.clientId, this._ctx.AuthToken.secret); this.apim_client = new ApiManagementClient(token, this._ctx.Environment.authentication.subscriptionId) @@ -103,14 +105,7 @@ export class ApimApiPlugin extends BaseIngredient { this.ca_file = fs.readFileSync(pemFile) } - let apis : Array = new Array() - let svcResponse = await this.apim_client.api.listByService(this.resource_group, this.resource_name) - apis = apis.concat(svcResponse) - while(svcResponse.nextLink) { - svcResponse = await this.apim_client.api.listByServiceNext(svcResponse.nextLink) - apis = apis.concat(svcResponse) - } - this.apim_apis = apis + this.apim_apis = await this.GetArrayFromPagedIterator(this.apim_client.api.listByService(this.resource_group, this.resource_name)); let optionParam =this._ctx.Ingredient.properties.parameters.get('options') || undefined if (optionParam){ @@ -194,7 +189,7 @@ export class ApimApiPlugin extends BaseIngredient { api.apiVersion = api.version api.apiVersionSetId = apiVersion.id api.apiVersionSet = apiVersion - let result = await this.apim_client.api.createOrUpdate(this.resource_group, this.resource_name, api.name, api, {ifMatch : '*'}) + let result = await this.apim_client.api.beginCreateOrUpdateAndWait(this.resource_group, this.resource_name, api.name, api, {ifMatch : '*'}) this._logger.log("APIM API Plugin: API " + result.displayName + " published") apiRevisionId = result.apiRevision || "" @@ -332,7 +327,7 @@ export class ApimApiPlugin extends BaseIngredient { let applyDiagnostic = true; try { - var existingDiagnostics = await this.apim_client.apiDiagnostic.listByService(this.resource_group, this.resource_name, apiId);; + var existingDiagnostics = await this.GetArrayFromPagedIterator(this.apim_client.apiDiagnostic.listByService(this.resource_group, this.resource_name, apiId)); for (let i = 0; i < existingDiagnostics.length; i++) { let existingDiagnosticsLoggerId = existingDiagnostics[i].loggerId || "" @@ -352,21 +347,17 @@ export class ApimApiPlugin extends BaseIngredient { if (applyDiagnostic) { let logErrMessage = "APIM API Plugin: Could not apply diagnostics " + diagnostics.name + " to API " + apiId; - try { - let apiResponse = await this.apim_client.apiDiagnostic.createOrUpdate( + + await this.apim_client.apiDiagnostic + .createOrUpdate( this.resource_group, this.resource_name, apiId, diagnostics.name, diagnostics, - {ifMatch:'*'}) - - if (apiResponse._response.status != 200 && apiResponse._response.status != 201){ - this._logger.error(logErrMessage) - } - } catch (error) { - this._logger.error(logErrMessage + ": '" + error + "'") - } + {ifMatch:'*'} + ) + .catch((error) => this._logger.error(logErrMessage + '\n' + error)) } } @@ -380,16 +371,29 @@ export class ApimApiPlugin extends BaseIngredient { let policyData = await this.ResolvePolicy(policy) if (operation == "base") { - let response = await this.apim_client.apiPolicy.createOrUpdate(this.resource_group, this.resource_name, apiId, policyData, {ifMatch: '*'}) - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM API Plugin: Could not apply API Policy for API " + apiId) - } + await this.apim_client.apiPolicy + .createOrUpdate( + this.resource_group, + this.resource_name, + apiId, + "policy", + policyData, + {ifMatch: '*'} + ) + .catch((error) => this._logger.error("APIM API Plugin: Could not apply API Policy for API " + apiId + '\n' + error)) } else { - let response = await this.apim_client.apiOperationPolicy.createOrUpdate(this.resource_group, this.resource_name, apiId, operation, policyData,{ifMatch: '*'}) - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM API Plugin: Could not apply API Policy for API " + apiId + " operation: " + operation) - } + await this.apim_client.apiOperationPolicy + .createOrUpdate( + this.resource_group, + this.resource_name, + apiId, + operation, + "policy", + policyData, + {ifMatch: '*'} + ) + .catch((error) => this._logger.error("APIM API Plugin: Could not apply API Policy for API " + apiId + " operation: " + operation + '\n' + error)) } } @@ -398,25 +402,24 @@ export class ApimApiPlugin extends BaseIngredient { if (this.apim_client == undefined) return //Clean existing products if different than the one you are assigning to - var existingProducts = await this.apim_client.apiProduct.listByApis(this.resource_group, this.resource_name, apiId); + var existingProducts = await this.GetArrayFromPagedIterator(this.apim_client.apiProduct.listByApis(this.resource_group, this.resource_name, apiId)); for (let i = 0; i < existingProducts.length; i++) { let oldProductId = existingProducts[i].name || "" if(oldProductId != productId) { - await this.apim_client.productApi.deleteMethod(this.resource_group, this.resource_name, oldProductId, apiId) - .then((result) => { this._logger.log('APIM API Plugin: Deleting API: ' + apiId + " from product " + oldProductId)}) - .catch((failure) => {this._logger.error("APIM API Plugin: Could not delete API " + apiId + "from product " + oldProductId) }) + await this.apim_client.productApi.delete(this.resource_group, this.resource_name, oldProductId, apiId) + .then(() => { this._logger.log('APIM API Plugin: Deleting API: ' + apiId + " from product " + oldProductId)}) + .catch((failure) => {this._logger.error("APIM API Plugin: Could not delete API " + apiId + "from product " + oldProductId + '\n' + failure) }) } } this._logger.log('APIM API Plugin: Assigning APIs: ' + apiId + " to product " + productId) - let apiResponse = await this.apim_client.productApi.createOrUpdate(this.resource_group, this.resource_name, productId, apiId) - if (apiResponse._response.status != 200 && apiResponse._response.status != 201){ - this._logger.error("APIM API Plugin: Could not bind API " + apiId + "to product " + productId) - } + await this.apim_client.productApi + .createOrUpdate(this.resource_group, this.resource_name, productId, apiId) + .catch((error) => this._logger.error("APIM API Plugin: Could not bind API " + apiId + "to product " + productId + '\n' + error)) } private GetApi(id: string): ApiContract | null { @@ -446,7 +449,7 @@ export class ApimApiPlugin extends BaseIngredient { let blockTime = (this.apim_options || {}).apiWaitTime - if (policy.value.startsWith("file:///")) { + if (policy.value && policy.value.startsWith("file:///")) { let content = fs.readFileSync(policy.value.replace("file:///", "")).toString('utf-8') policy.format = "xml"; @@ -477,5 +480,18 @@ export class ApimApiPlugin extends BaseIngredient { setTimeout(resolve,ms) }) } -} -//https://docs.microsoft.com/en-us/javascript/api/azure-arm-apimanagement/propertycontract?view=azure-node-latest \ No newline at end of file + + private async GetArrayFromPagedIterator(pagedIterator: PagedAsyncIterableIterator) : Promise + { + let retArray : Array = new Array() + + const pages = pagedIterator.byPage(); + for await (const page of pages) { + for (const item of page) { + retArray.push(item); + } + } + + return retArray; + } +} \ No newline at end of file diff --git a/ingredient/ingredient-apim/README.md b/ingredient/ingredient-apim/README.md index 69e123dc..9353bc83 100644 --- a/ingredient/ingredient-apim/README.md +++ b/ingredient/ingredient-apim/README.md @@ -45,7 +45,7 @@ Here is the documentation for all the supported paremeters for this ingredient. **apimService** ```yaml -apimService: #follows this azure spec for *ApiManagementServiceResource* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L2451 +apimService: #follows this azure spec for *ApiManagementServiceResource* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L4025 - name: #required name of APIM service ``` @@ -59,28 +59,28 @@ diagnostics: #follows this azure spec for *DiagnosticSettingsResource* : https:/ **namedValues** ```yaml -namedValues: #follows this azure spec for *PropertyContract * : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3769 +namedValues: #follows this azure spec for *NamedValueCreateContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#LL3568C1-L3568C1 - name: #required name of property (named value) ``` **groups** ```yaml -groups: #follows this azure spec for *GroupCreateParameters * : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3088 +groups: #follows this azure spec for *GroupCreateParameters* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L1882 - name: #required name of the group ``` **users** ```yaml -users: #follows this azure spec for *UserCreateParameters * : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L4387 +users: #follows this azure spec for *UserCreateParameters* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L2874 - name: #required name of the user ``` **subscriptions** ```yaml -subscriptions: #follows this azure spec for *SubscriptionCreateParameters * : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L4107 +subscriptions: #follows this azure spec for *SubscriptionCreateParameters* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L2746 - name: #required name of the subscription user: #optional user lookup, can also use ownerId on SubscriptionCreateParameters if the path is known ``` @@ -96,7 +96,7 @@ apis: **products** ```yaml -products: #follows this azure spec for *ProductContract* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L826 +products: #follows this azure spec for *ProductContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3111 - name: #required name of the product apis: #optional array of api names to add to product - api1 @@ -107,8 +107,8 @@ products: #follows this azure spec for *ProductContract* : https://github.com/Az **loggers** -```yaml #follows this azure spec for *LoggerContract* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3261 -loggers: +```yaml +loggers: #follows this azure spec for *LoggerContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3537 - name: #required name of the logger cleanKeys: true # clean the old subscription keys ``` @@ -116,28 +116,28 @@ loggers: **authServers** ```yaml -authServers: #follows this azure spec for *AuthorizationServerContract* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L1641 +authServers: #follows this azure spec for *AuthorizationServerContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3229 - name: #required name of the auth server ``` **identityProviders** ```yaml -identityProviders: #follows this azure spec for *IdentityProviderContract* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3193 +identityProviders: #follows this azure spec for *IdentityProviderContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3489 - identityProviderContractType: microsoft #this is part of the IdentityProviderContract contract and is required ``` **autoScaleSettings** ```yaml -autoScaleSettings: #follows this azure spec for *AutoscaleSettingResource * : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/monitor/arm-monitor/src/models/index.ts#L347 +autoScaleSettings: #follows this azure spec for *AutoscaleSettingResource* : https://github.com/Azure/azure-sdk-for-js/blob/20fe312b1122b21811f9364e3d95fe77202e6466/sdk/monitor/arm-monitor/src/models/index.ts#L347 - name: ``` **backends** ```yaml -backends: #follows this azure spec for *BackendContract* : https://github.com/Azure/azure-sdk-for-js/blob/0f358041f43f3414be37c9bd44492acb6f461f61/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L1932 +backends: #follows this azure spec for *BackendContract* : https://github.com/Azure/azure-sdk-for-js/blob/01898c51c663be4c53e02034a0468cf550ce5279/sdk/apimanagement/arm-apimanagement/src/models/index.ts#L3301 - name: ``` diff --git a/ingredient/ingredient-apim/package.json b/ingredient/ingredient-apim/package.json index 226d66ac..f5c3331f 100644 --- a/ingredient/ingredient-apim/package.json +++ b/ingredient/ingredient-apim/package.json @@ -26,7 +26,7 @@ "@types/node": "^10.12.18" }, "dependencies": { - "@azure/arm-apimanagement": "^6.0.0", + "@azure/arm-apimanagement": "^9.0.0", "@azure/arm-monitor": "^5.4.0", "@azure/arm-network": "^14.0.0", "got": "^11.7.0" diff --git a/ingredient/ingredient-apim/src/functions.ts b/ingredient/ingredient-apim/src/functions.ts index 1a2e79da..29d92671 100644 --- a/ingredient/ingredient-apim/src/functions.ts +++ b/ingredient/ingredient-apim/src/functions.ts @@ -1,7 +1,8 @@ +import { ClientSecretCredential } from '@azure/identity'; import { BaseUtility, IngredientManager } from '@azbake/core' import { ApiManagementClient } from "@azure/arm-apimanagement" import { NetworkManagementClient } from '@azure/arm-network'; -import { SubscriptionGetResponse, LoggerGetResponse, NamedValueGetResponse } from '@azure/arm-apimanagement/esm/models'; +import { SubscriptionGetResponse, LoggerGetResponse, NamedValueGetResponse } from '@azure/arm-apimanagement/src/models'; import { SubnetsGetResponse } from '@azure/arm-network/esm/models'; export class ApimUtils extends BaseUtility { @@ -43,7 +44,7 @@ export class ApimUtils extends BaseUtility { } public async get_logger(resourceGroup: string, apimName: string, loggerId: string): Promise { - const token: any = this.context.AuthToken + const token = new ClientSecretCredential(this.context.AuthToken.domain, this.context.AuthToken.clientId, this.context.AuthToken.secret); let client = new ApiManagementClient(token, this.context.Environment.authentication.subscriptionId) let logger = await client.logger.get(resourceGroup, apimName, loggerId) @@ -54,7 +55,7 @@ export class ApimUtils extends BaseUtility { } public async get_namedValue(resourceGroup: string, apimName: string, namedValueId: string): Promise { - const token: any = this.context.AuthToken + const token = new ClientSecretCredential(this.context.AuthToken.domain, this.context.AuthToken.clientId, this.context.AuthToken.secret); let client = new ApiManagementClient(token, this.context.Environment.authentication.subscriptionId); let namedValue = await client.namedValue.get(resourceGroup, apimName, namedValueId); @@ -65,7 +66,7 @@ export class ApimUtils extends BaseUtility { } public async get_subscription(resourceGroup: string, resource: string, subscriptionId: string) : Promise { - const token: any = this.context.AuthToken + const token = new ClientSecretCredential(this.context.AuthToken.domain, this.context.AuthToken.clientId, this.context.AuthToken.secret); let apim_client = new ApiManagementClient(token, this.context.Environment.authentication.subscriptionId) let subscription = await apim_client.subscription.get(resourceGroup, resource, subscriptionId) @@ -76,7 +77,7 @@ export class ApimUtils extends BaseUtility { } public async get_subscription_key(resourceGroup: string, resource: string, subscriptionId: string) : Promise { - const token: any = this.context.AuthToken + const token = new ClientSecretCredential(this.context.AuthToken.domain, this.context.AuthToken.clientId, this.context.AuthToken.secret); let apim_client = new ApiManagementClient(token, this.context.Environment.authentication.subscriptionId) let secrets = await apim_client.subscription.listSecrets(resourceGroup, resource, subscriptionId); @@ -87,7 +88,7 @@ export class ApimUtils extends BaseUtility { } public async get_subscription_keySecondary(resourceGroup: string, resource: string, subscriptionId: string) : Promise { - const token: any = this.context.AuthToken + const token = new ClientSecretCredential(this.context.AuthToken.domain, this.context.AuthToken.clientId, this.context.AuthToken.secret); let apim_client = new ApiManagementClient(token, this.context.Environment.authentication.subscriptionId) let secrets = await apim_client.subscription.listSecrets(resourceGroup, resource, subscriptionId); diff --git a/ingredient/ingredient-apim/src/plugin.ts b/ingredient/ingredient-apim/src/plugin.ts index ba25e0d9..5a6ded66 100644 --- a/ingredient/ingredient-apim/src/plugin.ts +++ b/ingredient/ingredient-apim/src/plugin.ts @@ -1,12 +1,16 @@ import { BaseIngredient, IngredientManager, BakeVariable } from "@azbake/core" -import { ApiManagementClient, NamedValue } from "@azure/arm-apimanagement" +import { ApiManagementClient, NamedValue, NamedValueContract } from "@azure/arm-apimanagement" import { MonitorManagementClient } from "@azure/arm-monitor" -import { BackendContract, BackendCreateOrUpdateOptionalParams, ApiDeleteMethodOptionalParams, ProductDeleteMethodOptionalParams, ApiManagementServiceResource, IdentityProviderContract, +import { BackendContract, BackendCreateOrUpdateOptionalParams, ApiDeleteOptionalParams, ProductDeleteOptionalParams, ApiManagementServiceResource, IdentityProviderCreateOrUpdateOptionalParams, LoggerCreateOrUpdateOptionalParams, AuthorizationServerCreateOrUpdateOptionalParams, UserCreateOrUpdateOptionalParams, GroupCreateOrUpdateOptionalParams, LoggerContract, GroupCreateParameters, UserCreateParameters, AuthorizationServerContract, PolicyContract, SubscriptionCreateParameters, ProductContract, - ProductCreateOrUpdateOptionalParams, ProductPolicyCreateOrUpdateOptionalParams, SubscriptionCreateOrUpdateOptionalParams, NamedValueCreateOrUpdateOptionalParams, NamedValueContract, NamedValueCreateContract, IdentityProviderCreateContract } from "@azure/arm-apimanagement/esm/models"; + ProductCreateOrUpdateOptionalParams, ProductPolicyCreateOrUpdateOptionalParams, SubscriptionCreateOrUpdateOptionalParams, NamedValueCreateOrUpdateOptionalParams, NamedValueCreateContract, IdentityProviderCreateContract } from "@azure/arm-apimanagement/src/models"; import { DiagnosticSettingsResource, AutoscaleSettingResource, AutoscaleSettingsCreateOrUpdateResponse } from "@azure/arm-monitor/esm/models"; +import { PagedAsyncIterableIterator } from '@azure/core-paging'; +import { HttpResponse } from "@azure/ms-rest-js" +import { ClientSecretCredential } from '@azure/identity'; import * as fs from 'fs'; + interface IApim extends ApiManagementServiceResource{ name: string } @@ -140,7 +144,8 @@ export class ApimPlugin extends BaseIngredient { } this._logger.log('APIM Plugin: Binding APIM to resource: ' + this.resource_group + '\\' + this.resource_name); - const token: any = this._ctx.AuthToken + + const token = new ClientSecretCredential(this._ctx.AuthToken.domain, this._ctx.AuthToken.clientId, this._ctx.AuthToken.secret); this.apim_client = new ApiManagementClient(token, this._ctx.Environment.authentication.subscriptionId) @@ -169,18 +174,16 @@ export class ApimPlugin extends BaseIngredient { let apimData = await this.ResolveApim(apim); - var response = await this.apim_client.apiManagementService.createOrUpdate - ( + this.apim = await this.apim_client.apiManagementService + .beginCreateOrUpdateAndWait( this.resource_group, this.resource_name, apimData ) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update APIM service " + this.resource_name) - } - - this.apim = response; + .catch((error) => { + this._logger.error("APIM Plugin: Could not create/update APIM service " + this.resource_name + '\n' + error) + throw error + }); } private async BuildDiagnostics(): Promise{ @@ -210,16 +213,14 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM diagnostics: ' + apimDiagnostics.name) - var response = await monitorClient.diagnosticSettings.createOrUpdate - ( + await monitorClient.diagnosticSettings + .createOrUpdate( resourceUri, diagnosticsData, apimDiagnostics.name ) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update APIM diagnostics " + apimDiagnostics.name) - } + .then((response) => this.LogResponseIfError(response._response, "APIM Plugin: Could not create/update APIM diagnostics " + apimDiagnostics.name)) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update APIM diagnostics " + apimDiagnostics.name + '\n' + error)) } private async BuildNamedValues(): Promise { @@ -247,18 +248,15 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM named value: ' + namedValue.name) - var response = await this.apim_client.namedValue.createOrUpdate - ( + await this.apim_client.namedValue + .beginCreateOrUpdateAndWait( this.resource_group, this.resource_name, namedValue.name, namedValueData, {ifMatch:'*'} ) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update named value " + namedValue.name) - } + .catch((error) => this._logger.error("APIM Plugin: Could not create/update named value " + namedValue.name + '\n' + error)) } private async BuildGroups(): Promise { @@ -284,16 +282,15 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM group: ' + group.displayName) - let response = await this.apim_client.group.createOrUpdate( - this.resource_group, - this.resource_name, - group.name, - group, - {ifMatch:'*'}) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update group " + group.name) - } + await this.apim_client.group + .createOrUpdate( + this.resource_group, + this.resource_name, + group.name, + group, + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update group " + group.name + '\n' + error)) } private async BuildUsers(): Promise { @@ -319,27 +316,24 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM user: ' + user.name) - let response = await this.apim_client.user.createOrUpdate( - this.resource_group, - this.resource_name, - user.name, - user, - {ifMatch:'*'}) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update user " + user.name) - } + await this.apim_client.user + .createOrUpdate( + this.resource_group, + this.resource_name, + user.name, + user, + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update user " + user.name + '\n' + error)) if (user.groups) { this._logger.log('APIM Plugin: Assigning group `' + user.groups.toString() + "` to user `" + user.name + "`") for(let i=0; i < user.groups.length; ++i){ let group = user.groups[i] - let apiResponse = await this.apim_client.groupUser.create(this.resource_group, this.resource_name, group, user.name) - - if (apiResponse._response.status != 200 && apiResponse._response.status != 201){ - this._logger.error("APIM Plugin: Could not bind group " + group + "to user " + user.name) - } + await this.apim_client.groupUser + .create(this.resource_group, this.resource_name, group, user.name) + .catch((error) => this._logger.error("APIM Plugin: Could not bind group " + group + "to user " + user.name + '\n' + error)) } } } @@ -384,15 +378,14 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM Subscription: ' + sub.name) - let response = await this.apim_client.subscription.createOrUpdate( - this.resource_group, - this.resource_name, - sub.name, - sub, {ifMatch:'*'}) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update subscription: " + sub.name) - } + await this.apim_client.subscription + .createOrUpdate( + this.resource_group, + this.resource_name, + sub.name, + sub, {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update subscription: " + sub.name + '\n' + error)) } private async BuildAPIs(): Promise { @@ -445,16 +438,15 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Deleting APIM API: ' + api.name) - let response = await this.apim_client.api.deleteMethod( - this.resource_group, - this.resource_name, - api.name, - '*', - {ifMatch:'*', deleteRevisions:true}) - - if (response._response.status != 200 && response._response.status != 201 && response._response.status != 204) { - this._logger.error("APIM Plugin: Could not delete API " + api.name) - } + await this.apim_client.api + .delete( + this.resource_group, + this.resource_name, + api.name, + '*', + {ifMatch:'*', deleteRevisions:true} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not delete API " + api.name + '\n' + error)) } private async DeleteProduct(product: IApimProduct): Promise { @@ -462,16 +454,15 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Deleting APIM product: ' + product.name) - let response = await this.apim_client.product.deleteMethod( - this.resource_group, - this.resource_name, - product.name, - '*', - {ifMatch:'*', deleteSubscriptions:true}) - - if (response._response.status != 200 && response._response.status != 201 && response._response.status != 204) { - this._logger.error("APIM Plugin: Could not delete product " + product.name) - } + await this.apim_client.product + .delete( + this.resource_group, + this.resource_name, + product.name, + '*', + {ifMatch:'*', deleteSubscriptions:true} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not delete product " + product.name + '\n' + error)) } private async BuildProduct(product: IApimProduct): Promise { @@ -479,27 +470,24 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM product: ' + product.name) - let response = await this.apim_client.product.createOrUpdate( - this.resource_group, - this.resource_name, - product.name, - product, - {ifMatch:'*'}) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update product " + product.name) - } + await this.apim_client.product + .createOrUpdate( + this.resource_group, + this.resource_name, + product.name, // TODO: check all id/names? + product, + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update product " + product.name + '\n' + error)) if (product.apis) { this._logger.log('APIM Plugin: Assigning APIs `' + product.apis.toString() + "` to product `" + product.name + "`") for(let i=0; i < product.apis.length; ++i){ let api = product.apis[i] - let apiResponse = await this.apim_client.productApi.createOrUpdate(this.resource_group, this.resource_name, product.name, api) - - if (apiResponse._response.status != 200 && apiResponse._response.status != 201){ - this._logger.error("APIM Plugin: Could not bind API " + api + "to product " + product.name) - } + await this.apim_client.productApi + .createOrUpdate(this.resource_group, this.resource_name, product.name, api) + .catch((error) => this._logger.error("APIM Plugin: Could not bind API " + api + "to product " + product.name + '\n' + error)) } } @@ -508,11 +496,9 @@ export class ApimPlugin extends BaseIngredient { for(let i=0; i < product.groups.length; ++i){ let group = product.groups[i] - let apiResponse = await this.apim_client.productGroup.createOrUpdate(this.resource_group, this.resource_name, product.name, group) - - if (apiResponse._response.status != 200 && apiResponse._response.status != 201){ - this._logger.error("APIM Plugin: Could not bind group " + group + "to product " + product.name) - } + await this.apim_client.productGroup + .createOrUpdate(this.resource_group, this.resource_name, product.name, group) + .catch((error) => this._logger.error("APIM Plugin: Could not bind group " + group + "to product " + product.name + '\n' + error)) } } @@ -520,16 +506,17 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM product policy: ' + product.name) let policyData = await this.ResolvePolicy(product.policy) - let policyResponse = await this.apim_client.productPolicy.createOrUpdate( + + await this.apim_client.productPolicy + .createOrUpdate( this.resource_group, this.resource_name, product.name, + "policy", policyData, - {ifMatch:'*'}) - - if (policyResponse._response.status != 200 && policyResponse._response.status != 201){ - this._logger.error("APIM Plugin: Could not apply policies to product " + product.name) - } + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not apply policies to product " + product.name + '\n' + error)) } } @@ -558,15 +545,20 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM logger: ' + logger.name) - var response = await this.apim_client.logger.createOrUpdate( - this.resource_group, - this.resource_name, - logger.name, - loggerData, - {ifMatch:'*'}) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error(`APIM Plugin: Could not create/update logger for '+ logger.name`) + var response = await this.apim_client.logger + .createOrUpdate( + this.resource_group, + this.resource_name, + logger.name, + loggerData, + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update logger for " + logger.name + '\n' + error)) + + if(!response || !response.credentials || !loggerData.credentials) + { + this._logger.error("APIM Plugin: Logger Cleanup - failed to retrieve logger data info.") + return; } var currentLoggerCreds; @@ -583,7 +575,7 @@ export class ApimPlugin extends BaseIngredient { //Clean logger keys if (logger.cleanKeys == undefined || logger.cleanKeys) { - let result = await this.apim_client.namedValue.listByService(this.resource_group, this.resource_name) || "" + let result = await this.GetArrayFromPagedIterator(this.apim_client.namedValue.listByService(this.resource_group, this.resource_name)) let propEtag = "" for (let i = 0; i < result.length; i++) { let id = result[i].name || "" @@ -593,14 +585,21 @@ export class ApimPlugin extends BaseIngredient { // only cleanup logger keys with the same value (aikey or eventhub namespace). Otherwise you will delete keys for other loggers if (loggerValue == secretResult.value) { - await this.apim_client.namedValue.getEntityTag(this.resource_group, this.resource_name, id).then((result) => { propEtag = result.eTag }) - await this.apim_client.namedValue.deleteMethod(this.resource_group, this.resource_name, id, propEtag) - .then((result) => { - this._logger.log(`APIM Plugin: Logger Cleanup - Removed old key - ${displayName}: ${result._response.status == 200}`) + await this.apim_client.namedValue.getEntityTag(this.resource_group, this.resource_name, id).then((result) => { + if (result.eTag) { + propEtag = result.eTag + } + }) + + if (propEtag != "") { + await this.apim_client.namedValue.delete(this.resource_group, this.resource_name, id, propEtag) + .then(() => { + this._logger.log(`APIM Plugin: Logger Cleanup - Removed old key - ${displayName}`) }) .catch((failure) => { - this._logger.error(`APIM Plugin: Logger Cleanup - failed to remove Logger key: ${displayName}`) + this._logger.error(`APIM Plugin: Logger Cleanup - failed to remove Logger key: ${displayName}` + '\n' + failure) }) + } } } } @@ -637,16 +636,15 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM auth server: ' + authServer.name) - let response = await this.apim_client.authorizationServer.createOrUpdate( - this.resource_group, - this.resource_name, - authServer.name, - authServerData, - {ifMatch:'*'}) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update auth server " + authServer.name) - } + await this.apim_client.authorizationServer + .createOrUpdate( + this.resource_group, + this.resource_name, + authServer.name, + authServerData, + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update auth server " + authServer.name + '\n' + error)) } private async BuildIdentityProviders(): Promise { @@ -671,25 +669,24 @@ export class ApimPlugin extends BaseIngredient { if (this.apim_client == undefined) return - if(!identityProvider.identityProviderCreateContractType){ + if(!identityProvider.name){ this._logger.error("APIM Plugin: identityProviderContractType is required") return } let identityProviderData = await this.ResolveIdentityProvider(identityProvider); - this._logger.log('APIM Plugin: Add/Update APIM identity provider: ' + identityProvider.identityProviderCreateContractType) - - let response = await this.apim_client.identityProvider.createOrUpdate( - this.resource_group, - this.resource_name, - identityProvider.identityProviderCreateContractType, - identityProviderData, - {ifMatch:'*'}) + this._logger.log('APIM Plugin: Add/Update APIM identity provider: ' + identityProvider.name) - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update identity provider " + identityProvider.identityProviderCreateContractType) - } + await this.apim_client.identityProvider + .createOrUpdate( + this.resource_group, + this.resource_name, + identityProvider.name, + identityProviderData, + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update identity provider " + identityProvider.name + '\n' + error)) } private async BuildAutoscaleSettings() : Promise { @@ -760,16 +757,15 @@ export class ApimPlugin extends BaseIngredient { this._logger.log('APIM Plugin: Add/Update APIM backend: ' + backendData.name) - let response = await this.apim_client.backend.createOrUpdate( - this.resource_group, - this.resource_name, - backendData.name, - backendData, - {ifMatch:'*'}) - - if (response._response.status != 200 && response._response.status != 201) { - this._logger.error("APIM Plugin: Could not create/update backend " + backendData.name) - } + await this.apim_client.backend + .createOrUpdate( + this.resource_group, + this.resource_name, + backendData.name, + backendData, + {ifMatch:'*'} + ) + .catch((error) => this._logger.error("APIM Plugin: Could not create/update backend " + backendData.name + '\n' + error)) } private async ResolveApim(apim: IApim) : Promise { @@ -988,7 +984,7 @@ export class ApimPlugin extends BaseIngredient { return policy } - if (policy.value.startsWith("file:///")) { + if (policy.value && policy.value.startsWith("file:///")) { let content = fs.readFileSync(policy.value.replace("file:///", "")).toString('utf-8') policy.format = "xml"; policy.value = content; @@ -1038,4 +1034,25 @@ export class ApimPlugin extends BaseIngredient { return userId.id } + + private async GetArrayFromPagedIterator(pagedIterator: PagedAsyncIterableIterator) : Promise + { + let retArray : Array = new Array() + + const pages = pagedIterator.byPage(); + for await (const page of pages) { + for (const item of page) { + retArray.push(item); + } + } + + return retArray; + } + + private LogResponseIfError(response: HttpResponse, errorMessage: string) + { + if (response.status >= 400) { + this._logger.error(errorMessage); + } + } } \ No newline at end of file diff --git a/test.yaml b/system/package/acs/test.yaml similarity index 96% rename from test.yaml rename to system/package/acs/test.yaml index 4dc7b74a..06f590f2 100644 --- a/test.yaml +++ b/system/package/acs/test.yaml @@ -21,4 +21,4 @@ recipe: parameters: primaryConnectionString: "[acsutils.get_primary_connectionstring( acsutils.create_resource_name(), await coreutils.resource_group())]" dependsOn: - - acs-create + - acs-create \ No newline at end of file