{"version":3,"sources":["../../../packages/core/azure/azure-manager.ts"],"names":[],"mappings":"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","file":"azure-manager.d.ts","sourcesContent":["import { from, Observable, Subject, throwError } from 'rxjs';\r\nimport { AjaxRequest, AjaxResponse } from 'rxjs/ajax';\r\nimport { filter, map, mergeMap, take, tap } from 'rxjs/operators';\r\nimport { CoreEnvironment } from '../data/core-environment';\r\nimport { Http } from '../data/http';\r\nimport { LogLevel } from '../diagnostics/log-level';\r\nimport { Logging } from '../diagnostics/logging';\r\nimport { Strings } from '../generated/strings';\r\nimport {\r\n    RpcAzureOperation,\r\n    RpcAzureOperationResult,\r\n    RpcAzureOperationType,\r\n    RpcAzureResponseKey\r\n} from '../rpc/azure/rpc-azure-model';\r\nimport { RpcAzureRequestClient } from '../rpc/azure/rpc-azure-request-client';\r\nimport { Rpc } from '../rpc/rpc';\r\nimport { ConnectionProperties } from '../security/connection';\r\nimport { ArmTelemetry, AzureConstants, AzureIntegrationType } from './azure-constants';\r\nimport { AzureLogging } from './azure-logging';\r\nimport { AadAppConfig } from './models/aadAppConfig-data';\r\nimport { DeploymentOperationRequestBody } from './resources/deployment-operation-request-body';\r\nimport { DeploymentOperationResponse } from './resources/deployment-operation-response';\r\nimport { LocationsOperationResult } from './resources/location-data';\r\nimport {\r\n    ResourceGroupOperationResult,\r\n    ResourceGroupRequestBody,\r\n    ResourceGroupsOperationResult\r\n} from './resources/resource-group-data';\r\nimport { SubscriptionsOperationResult } from './resources/subscription-data';\r\n\r\n/**\r\n * Azure Manager class. Handles detecting and configuring Azure on a set of servers.\r\n */\r\nexport class AzureManager {\r\n\r\n    private watcher: Subject<RpcAzureOperationResult>;\r\n    private azureLogging: AzureLogging;\r\n    private readonly AzureGlobal = 'azureglobal';\r\n    private readonly AzureChina = 'azurechina';\r\n    private readonly AzureUSGov = 'azureusgov';\r\n\r\n    /**\r\n     * Initializes a new instance of the Azure Manager class\r\n     * @param rpc The rpc to forward auth requests to a parent window\r\n     */\r\n    constructor(private rpc: Rpc, private http: Http) {\r\n        this.watcher = new Subject<RpcAzureOperationResult>();\r\n        this.azureLogging = new AzureLogging();\r\n    }\r\n\r\n    /**\r\n     * Initialize the azure manager\r\n     */\r\n    public initialize() {\r\n        // When in Shell (the top window) do not register. Only extension register.\r\n        if (MsftSme.isExtension()) {\r\n            this.rpc.register(RpcAzureResponseKey.command, this.onRpcResponse.bind(this));\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Get application from gateway\r\n     */\r\n    public getAppInfo(): Observable<any> {\r\n        return this.sendRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.GetAadStatus\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Open the register Aad dialog\r\n     */\r\n    public openRegisterAadDialog(): Observable<any> {\r\n        return this.sendRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.OpenAzureDialog\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Signs in user.\r\n     */\r\n    public signIn(): Observable<any> {\r\n        return this.sendTokenRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.SignIn\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Signs out user.\r\n     */\r\n    public signOut(): Observable<any> {\r\n        return this.sendRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.SignOut\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Switches user accounts.\r\n     */\r\n    public switchAccounts(): Observable<any> {\r\n        throw new Error('To be implemented');\r\n    }\r\n\r\n    /**\r\n     * Get user token from azure\r\n     */\r\n    public getToken(): Observable<any> {\r\n        return this.sendTokenRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.GetAadToken\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Get the Azure AD graph token from azure\r\n     */\r\n    public getGraphToken(): Observable<any> {\r\n        return this.sendTokenRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.GetAadGraphToken\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Get the Microsoft graph token from azure\r\n     * @param scopes The set of permissions for which the token has to be requested.\r\n     * https://docs.microsoft.com/en-us/graph/permissions-reference\r\n     * A null value will request default scopes - openid and profile.\r\n     */\r\n    public getMicrosoftGraphToken(scopes: string[] = null): Observable<any> {\r\n        return this.sendTokenRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.GetMicrosoftGraphToken,\r\n            tokenScopes: scopes\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Checks to see if user is signed in. Returns true if signed in, else false.\r\n     */\r\n    public checkUserSignedIn(): Observable<boolean> {\r\n        return this.sendTokenRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.CheckUserSignedIn\r\n        }).pipe(map(result => {\r\n            const isSignedIn = result && result.appInfo && result.appInfo.token;\r\n            return !!isSignedIn;\r\n        }));\r\n    }\r\n\r\n    /**\r\n     * Get Azure subscriptions.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public getSubscriptions(integrationType?: AzureIntegrationType): Observable<SubscriptionsOperationResult> {\r\n        let armEndpoint = null;\r\n        const url = AzureConstants.azureSubscriptionUrl;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                return this.getRequestWithArmToken(url.format(armEndpoint), integrationType);\r\n            }),\r\n            map(result => {\r\n                const response: SubscriptionsOperationResult = {\r\n                    httpStatusCode: result.status,\r\n                    value: result.response\r\n                };\r\n                return response;\r\n            })\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Check whether a resource group exists or not.\r\n     * @param subscriptionId The subscription id.\r\n     * @param resourceGroupName The resource group name.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public checkResourceGroupExists(\r\n        subscriptionId: string,\r\n        resourceGroupName: string,\r\n        integrationType?: AzureIntegrationType): Observable<{ status: number }> {\r\n        let armEndpoint = null;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                return this.getArmToken();\r\n            }),\r\n            mergeMap(token => {\r\n                const url = AzureConstants.resourceGroupUrl.format(armEndpoint, subscriptionId, resourceGroupName);\r\n                const ajaxRequest = this.createRequestWithHeaders(token, { integrationType: integrationType });\r\n                return this.http.head(url, ajaxRequest);\r\n            }),\r\n            map(result => {\r\n                return { status: result.status };\r\n            })\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Create or update (if it exists) resource group.\r\n     * @param subscriptionId The subscription id.\r\n     * @param resourceGroupName The resource group name.\r\n     * @param body request body. Optional. See definition of ResourceGroupRequestBody for what fields can be inside this body.\r\n     * If this is not provided, a default of { \\'location\\': \\'West US\\' } will be used.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public createResourceGroup(\r\n        subscriptionId: string,\r\n        resourceGroupName: string,\r\n        body?: ResourceGroupRequestBody,\r\n        integrationType?: AzureIntegrationType): Observable<ResourceGroupOperationResult> {\r\n        let armEndpoint = null;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                const url = AzureConstants.resourceGroupUrl.format(armEndpoint, subscriptionId, resourceGroupName);\r\n                if (!body) {\r\n                    body = <ResourceGroupRequestBody>{ location: 'West US' };\r\n                }\r\n                return this.putRequestWithArmToken(url, body, integrationType);\r\n            }),\r\n            map(result => {\r\n                const response: ResourceGroupOperationResult = {\r\n                    httpStatusCode: result.status,\r\n                    value: result.response\r\n                };\r\n                return response;\r\n            })\r\n        );\r\n    }\r\n\r\n    /**\r\n     * List all resource groups in a subscription.\r\n     * @param subscriptionId The subscription id.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public getResourceGroups(subscriptionId: string, integrationType?: AzureIntegrationType): Observable<ResourceGroupsOperationResult> {\r\n        let armEndpoint = null;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                const url = AzureConstants.listResourceGroups.format(armEndpoint, subscriptionId);\r\n                return this.getRequestWithArmToken(url, integrationType);\r\n            }),\r\n            map(result => {\r\n                const response: ResourceGroupsOperationResult = {\r\n                    httpStatusCode: result.status,\r\n                    value: result.response\r\n                };\r\n                return response;\r\n            })\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Delete resource group.\r\n     * @param subscriptionId The subscription id.\r\n     * @param resourceGroupName The resource group name.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public deleteResourceGroup(\r\n        subscriptionId: string,\r\n        resourceGroupName: string,\r\n        integrationType?: AzureIntegrationType): Observable<{ status: number }> {\r\n        let armEndpoint = null;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                const url = AzureConstants.resourceGroupUrl.format(armEndpoint, subscriptionId, resourceGroupName);\r\n                return this.deleteRequestWithArmToken(url, integrationType);\r\n            }),\r\n            map(result => {\r\n                // result.status will either be 200 or 202.\r\n                return { status: result.status };\r\n            })\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Get all available geo-locations.\r\n     * @param subscriptionId The subscription id.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public getAllAzureLocations(subscriptionId: string, integrationType?: AzureIntegrationType): Observable<LocationsOperationResult> {\r\n        let armEndpoint = null;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                const url = AzureConstants.getAllGeoLocations.format(armEndpoint, subscriptionId);\r\n                return this.getRequestWithArmToken(url, integrationType);\r\n            }),\r\n            map(result => {\r\n                const response: LocationsOperationResult = {\r\n                    httpStatusCode: result.status,\r\n                    value: result.response\r\n                };\r\n                return response;\r\n            })\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Deploy resources with Resource Manager templates and Resource Manager REST API.\r\n     * @param subscriptionId The subscription id.\r\n     * @param resourceGroupName The resource group name.\r\n     * @param deploymentName The deployment name.\r\n     * @param resourceTemplate The resource template content.\r\n     * @param templateParameters The resource template parameters.\r\n     * @param deploymentMode The mode that is used to deploy resources.This value can be either Incremental or Complete.\r\n     * In Incremental mode, resources are deployed without deleting existing resources that are not included in the template.\r\n       In Complete mode, resources are deployed and existing resources in the resource group that are not included in the\r\n       template are deleted.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public DeployAzureTemplate(\r\n        subscriptionId: string,\r\n        resourceGroupName: string,\r\n        deploymentName: string,\r\n        resourceTemplate: string,\r\n        templateParameters: string,\r\n        deploymentMode: string = 'Incremental',\r\n        integrationType?: AzureIntegrationType): Observable<DeploymentOperationResponse> {\r\n        let armEndpoint = null;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                const url = AzureConstants.ArmDeploymentUrl.format(armEndpoint, subscriptionId, resourceGroupName, deploymentName);\r\n                const body: DeploymentOperationRequestBody = {\r\n                    properties: {\r\n                        template: JSON.parse(resourceTemplate),\r\n                        parameters: JSON.parse(templateParameters),\r\n                        mode: deploymentMode\r\n                    }\r\n                };\r\n\r\n                return this.putRequestWithArmToken(url, body, integrationType);\r\n            }),\r\n            tap(result => this.logTelemetry(result)),\r\n            map(result => this.createDeploymentOperationResponse(result))\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Validate resources with Resource Manager templates and Resource Manager REST API.\r\n     * @param subscriptionId The subscription id.\r\n     * @param resourceGroupName The resource group name.\r\n     * @param deploymentName The deployment name.\r\n     * @param resourceTemplate The resource template content.\r\n     * @param templateParameters The resource template parameters.\r\n     * @param deploymentMode The mode that is used to deploy resources.This value can be either Incremental or Complete.\r\n     * In Incremental mode, resources are deployed without deleting existing resources that are not included in the template.\r\n       In Complete mode, resources are deployed and existing resources in the resource group that are not included in the\r\n       template are deleted.\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public validateAzureTemplate(\r\n        subscriptionId: string,\r\n        resourceGroupName: string,\r\n        deploymentName: string,\r\n        resourceTemplate: string,\r\n        templateParameters: string,\r\n        deploymentMode: string = 'Incremental',\r\n        integrationType?: AzureIntegrationType\r\n    ): Observable<DeploymentOperationResponse> {\r\n        let armEndpoint = null;\r\n        return this.getAppInfo().pipe(\r\n            mergeMap(({ appInfo }: { appInfo: AadAppConfig }) => {\r\n                if (appInfo) {\r\n                    armEndpoint = this.getArmEndpoint(appInfo.cloudName);\r\n                } else {\r\n                    return throwError(() => new Error('Gateway is not registered to Azure.'));\r\n                }\r\n\r\n                const url = AzureConstants.validateAzureTemplate.format(armEndpoint, subscriptionId, resourceGroupName, deploymentName);\r\n                const body: DeploymentOperationRequestBody = {\r\n                    properties: {\r\n                        template: JSON.parse(resourceTemplate),\r\n                        parameters: JSON.parse(templateParameters),\r\n                        mode: deploymentMode\r\n                    }\r\n                };\r\n\r\n                return this.postRequestWithArmToken(url, body, integrationType);\r\n            }),\r\n            map(result => this.createDeploymentOperationResponse(result))\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Creates azure request with headers\r\n     * @param token bearer token for ARM request\r\n     * @param options additional information that can be send in the headers of the request.\r\n     *  - additionalHeaders any headers besides 'Authorization'\r\n     *  - integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     *    Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     *    on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     *    explicitly set.\r\n     * 'x-ms-client-request-id', 'x-ms-return-client-request-id', and 'Accept-Language'.\r\n     * @return formatted AjaxRequest for an ARM call\r\n     */\r\n    public createRequestWithHeaders(\r\n        token: string,\r\n        options?: {\r\n            additionalHeaders?: any,\r\n            integrationType?: AzureIntegrationType,\r\n            connectionProperties?: ConnectionProperties\r\n        }): AjaxRequest {\r\n        const ajaxRequest: AjaxRequest = <AjaxRequest>{ headers: {} };\r\n\r\n        ajaxRequest.headers = (options && options.additionalHeaders) || {};\r\n        (<any>ajaxRequest).headers[AzureConstants.authorizationHeaderKey] = AzureConstants.bearer + token;\r\n\r\n        const integrationType = (options && options.integrationType) || ArmTelemetry.toIntegrationType(MsftSme.self().Init.moduleName);\r\n\r\n        // TODO: Remove work around. Only intended for the quality release for Jan 2020.\r\n        const connectionProperties = options && options.connectionProperties;\r\n        (<any>ajaxRequest).headers[AzureConstants.clientRequestHeaderKey] =\r\n            this.formatClientRequestId(\r\n                integrationType,\r\n                this.hexEncodeTelemetryData(connectionProperties));\r\n\r\n        (<any>ajaxRequest).headers[AzureConstants.returnClientRequestHeaderKey] = 'True';\r\n        (<any>ajaxRequest).headers[AzureConstants.acceptLanguageHeaderKey] = CoreEnvironment.localizationManager.getLocaleId().neutral;\r\n        return ajaxRequest;\r\n    }\r\n\r\n    /**\r\n     * Gets the arm token.\r\n     */\r\n    public getArmToken(): Observable<string> {\r\n        return this.sendTokenRequest({\r\n            requestId: MsftSme.newGuid(),\r\n            operation: RpcAzureOperationType.GetAadToken\r\n        })\r\n            .pipe(\r\n                map(result => {\r\n                    return result && result.appInfo && result.appInfo.token;\r\n                }));\r\n    }\r\n\r\n    // TODO: Remove work around. Only intended for the quality release for Jan 2020.\r\n    /**\r\n * Parses connection properties for ARM telemetry data and encode in hex\r\n * @param connectionProperties properties on the current active connection\r\n * @return hex encoded json object.\r\n */\r\n    public hexEncodeTelemetryData(connectionProperties?: ConnectionProperties): string {\r\n        if (connectionProperties\r\n            && connectionProperties['operatingSystem']\r\n            && connectionProperties['version']\r\n            && connectionProperties['computerModel']\r\n            && connectionProperties['computerManufacturer']\r\n            && connectionProperties['isS2dEnabled'] != null) {\r\n            const telemetryData = {\r\n                operatingSystem: connectionProperties['operatingSystem'],\r\n                version: connectionProperties['version'],\r\n                computerManufacturer: connectionProperties['computerManufacturer'],\r\n                computerModel: connectionProperties['computerModel'],\r\n                isS2dEnabled: connectionProperties['isS2dEnabled']\r\n            };\r\n            const stringifiedData = JSON.stringify(telemetryData);\r\n            try {\r\n                return MsftSme.toHex(stringifiedData);\r\n            } catch {\r\n                return null;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n\r\n    // TODO: Remove work around. Only intended for the quality release for Jan 2020.\r\n    private formatClientRequestId(integrationType: string, encodedData?: string): string {\r\n        let clientRequestId = AzureConstants.clientRequestIdQualifier.format(MsftSme.newGuid());\r\n\r\n        if (integrationType) {\r\n            clientRequestId += AzureConstants.integrationTypeDataParameter.format(integrationType);\r\n        }\r\n\r\n        if (encodedData != null) {\r\n            if (encodedData.length < AzureConstants.maxEncodedTelemetryDataLength) {\r\n                clientRequestId += AzureConstants.telemetryDataParameter.format(encodedData);\r\n            } else {\r\n                clientRequestId += AzureConstants.telemetryDataParameter.format(AzureConstants.telemetryDataInvalidValue);\r\n            }\r\n        }\r\n\r\n        // Ordering of parameters is important for parsing in telemetry pipeline.\r\n        clientRequestId += AzureConstants.sourceParameter;\r\n\r\n        return clientRequestId;\r\n    }\r\n\r\n    /**\r\n     * Send request to RPC\r\n     * @param request RpcAzureOperation\r\n     */\r\n    private sendRequest(request: RpcAzureOperation): Observable<any> {\r\n        Logging.log({\r\n            level: LogLevel.Debug,\r\n            message: 'Sending request to AzureManagerShellService. Request:{0}'.format(JSON.stringify(request)),\r\n            source: 'AzureManager'\r\n        });\r\n\r\n        return from(RpcAzureRequestClient.azureRequest(this.rpc, request))\r\n            .pipe(\r\n                mergeMap(() => this.watcher),\r\n                filter((result) => result.requestId === request.requestId),\r\n                take(1),\r\n                map((result) => result));\r\n    }\r\n\r\n    /**\r\n     * Send token request to RPC\r\n     * @param request RpcAzureOperation\r\n     */\r\n    private sendTokenRequest(request: RpcAzureOperation): Observable<any> {\r\n        Logging.log({\r\n            level: LogLevel.Debug,\r\n            message: 'Sending request to AzureManagerShellService. Request:{0}'.format(JSON.stringify(request)),\r\n            source: 'AzureManager'\r\n        });\r\n\r\n        return from(RpcAzureRequestClient.azureRequest(this.rpc, request))\r\n            .pipe(\r\n                mergeMap(() => this.watcher),\r\n                filter((result) => result.requestId === request.requestId),\r\n                take(1),\r\n                map((result) => {\r\n                    if (result.appInfo) {\r\n                        return result;\r\n                    } else if (result.error) {\r\n                        throw new Error(result.error);\r\n                    }\r\n\r\n                    throw new Error(MsftSme.getStrings<Strings>().MsftSmeShell.Core.Rpc.Azure.Unexpected.Error.message);\r\n                }));\r\n    }\r\n\r\n    /**\r\n     * Handles Rpc response.\r\n     */\r\n    private onRpcResponse(data: RpcAzureOperationResult): Promise<any> {\r\n        this.watcher.next(data);\r\n        return Promise.resolve();\r\n    }\r\n\r\n    /**\r\n     * Determine cloud specific ARM endpoint based on the cloud that gateway is registered to.\r\n     * @param cloudName cloudName from gateway's app registration.\r\n     * @returns Returns cloud specific ARM endpoint.\r\n     */\r\n    public getArmEndpoint(cloudNameParam: string): string {\r\n        const cloudName = MsftSme.replaceAll(cloudNameParam, ' ', '').toLowerCase();\r\n\r\n        if (cloudName === this.AzureGlobal) {\r\n            return AzureConstants.azureGlobalArmEndpoint;\r\n        } else if (cloudName === this.AzureChina) {\r\n            return AzureConstants.azureChinaArmEndpoint;\r\n        } else if (cloudName === this.AzureUSGov) {\r\n            return AzureConstants.azureUSGovArmEndpoint;\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Make a GET request using ARM token.\r\n     * @param url endpoint for request\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public getRequestWithArmToken(url: string, integrationType?: AzureIntegrationType): Observable<AjaxResponse<any>> {\r\n        return this.getArmToken().pipe(\r\n            mergeMap(token => {\r\n                const ajaxRequest = this.createRequestWithHeaders(token, { integrationType: integrationType });\r\n                return this.http.get(url, ajaxRequest);\r\n            }));\r\n    }\r\n\r\n    /**\r\n     * Make a POST request using ARM token.\r\n     * @param url endpoint for request\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public postRequestWithArmToken(url: string, body: any, integrationType?: AzureIntegrationType): Observable<AjaxResponse<any>> {\r\n        return this.getArmToken().pipe(\r\n            mergeMap(token => {\r\n                const ajaxRequest = this.createRequestWithHeaders(token, { integrationType: integrationType });\r\n                return this.http.post(url, body, ajaxRequest);\r\n            }));\r\n    }\r\n\r\n    /**\r\n     * Make a PUT request using ARM token.\r\n     * @param url endpoint for request\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public putRequestWithArmToken(url: string, body: any, integrationType?: AzureIntegrationType): Observable<AjaxResponse<any>> {\r\n        if (!MsftSme.self().Init.isProduction &&\r\n            (url.includes(AzureConstants.azureGlobalArmEndpoint) ||\r\n             url.includes(AzureConstants.azureChinaArmEndpoint) ||\r\n             url.includes(AzureConstants.azureUSGovArmEndpoint)) &&\r\n            url.includes(AzureConstants.resourceDeploymentSubstring)) {\r\n            Logging.logWarning('putRequestWithArmToken', 'Use DeployAzure Template to create resource.');\r\n        }\r\n\r\n        return this.getArmToken().pipe(\r\n            mergeMap(token => {\r\n                const ajaxRequest = this.createRequestWithHeaders(token, { integrationType: integrationType });\r\n                return this.http.put(url, body, ajaxRequest);\r\n            }));\r\n    }\r\n\r\n    /**\r\n     * Make a DELETE request using ARM token.\r\n     * @param url endpoint for request\r\n     * @param integrationType integration type is used to distinguish which Azure tool/service in Windows Admin Center is making a\r\n     * Azure resource management (ARM) call. If only one repository has one tool/service, this will be inferred based\r\n     * on the name of the module. If a repository has more than one tool/service then the integration type needs to be\r\n     * explicitly set.\r\n     */\r\n    public deleteRequestWithArmToken(url: string, integrationType?: AzureIntegrationType): Observable<AjaxResponse<any>> {\r\n        return this.getArmToken().pipe(\r\n            mergeMap(token => {\r\n                const ajaxRequest = this.createRequestWithHeaders(token, { integrationType: integrationType });\r\n                return this.http.delete(url, ajaxRequest);\r\n            }));\r\n    }\r\n\r\n    public createDeploymentOperationResponse(result: AjaxResponse<any>): DeploymentOperationResponse {\r\n        const headers = result.xhr.getAllResponseHeaders();\r\n        const headerArray = headers && headers.trim().split(/[\\r\\n]+/);\r\n        const headerMap: Map<string, string> = new Map<string, string>();\r\n        headerArray.forEach((x: string) => {\r\n            const parts = x.split(': ');\r\n            const header = parts && parts.shift();\r\n            const value = parts && parts.join(': ');\r\n            headerMap.set(header, value);\r\n        });\r\n\r\n        const response: DeploymentOperationResponse = {\r\n            httpStatusCode: result.status ? result.status : null,\r\n            responseHeaders: headerMap ? headerMap : null,\r\n            response: result.response ? result.response : null\r\n        };\r\n\r\n        return response;\r\n    }\r\n\r\n    public logTelemetry(response: any): void {\r\n        response = response.response ? response.response : response;\r\n\r\n        if (response && response.properties && response.properties.dependencies) {\r\n            let dependentResourceIds = response.properties.dependencies;\r\n            dependentResourceIds = dependentResourceIds.map(dependentResourceId => dependentResourceId.id);\r\n            this.azureLogging.submitCreateResourceTelemetry(dependentResourceIds);\r\n        } else {\r\n            this.azureLogging.submitCreateResourceTelemetry();\r\n        }\r\n    }\r\n}\r\n"]}