public async deployQnAReource()

in extensions/azurePublish/src/node/azureResourceManager/azureResourceManager.ts [255:476]


  public async deployQnAReource(config: QnAResourceConfig): Promise<{ endpoint: string; subscriptionKey: string }> {
    try {
      this.logger({
        status: BotProjectDeployLoggerType.PROVISION_INFO,
        message: 'Deploying QnA Resource ...',
      });

      // initialize the name
      const qnaMakerServiceName = `${config.name}-qna`;
      const qnaMakerSearchName = `${qnaMakerServiceName}-search`.toLowerCase().replace('_', '');
      const qnaMakerWebAppName = `${qnaMakerServiceName}-qnahost`.toLowerCase().replace('_', '');
      const qnaMakerServicePlanName = `${qnaMakerServiceName}-serviceplan`;

      // only support westus in qna
      config.location = 'westus';

      // deploy search service
      const searchManagementClient = new SearchManagementClient(this.creds, this.subscriptionId, this.options);
      const searchServiceDeployResult = await searchManagementClient.services.createOrUpdate(
        config.resourceGroupName,
        qnaMakerSearchName,
        {
          location: config.location,
          sku: {
            name: 'standard',
          },
          replicaCount: 1,
          partitionCount: 1,
          hostingMode: 'default',
        }
      );

      if (searchServiceDeployResult._response.status >= 300) {
        this.logger({
          status: BotProjectDeployLoggerType.PROVISION_ERROR,
          message: searchServiceDeployResult._response.bodyAsText,
        });
        throw createCustomizeError(ProvisionErrors.CREATE_QNA_ERROR, searchServiceDeployResult._response.bodyAsText);
      }

      // deploy websites
      // Create new Service Plan or update the exisiting service plan created before
      const webSiteManagementClient = new WebSiteManagementClient(this.creds, this.subscriptionId, this.options);
      const servicePlanResult = await webSiteManagementClient.appServicePlans.createOrUpdate(
        config.resourceGroupName,
        qnaMakerServicePlanName,
        {
          location: config.location,
          sku: {
            name: 'S1',
            tier: 'Standard',
            size: 'S1',
            family: 'S',
            capacity: 1,
          },
        }
      );

      if (servicePlanResult._response.status >= 300) {
        this.logger({
          status: BotProjectDeployLoggerType.PROVISION_ERROR,
          message: servicePlanResult._response.bodyAsText,
        });
        throw createCustomizeError(ProvisionErrors.CREATE_QNA_ERROR, servicePlanResult._response.bodyAsText);
      }

      // deploy or update exisiting app insights component
      const applicationInsightsManagementClient = new ApplicationInsightsManagementClient(
        this.creds,
        this.subscriptionId,
        this.options
      );
      const appinsightsName = config.resourceGroupName;
      const appinsightsDeployResult = await applicationInsightsManagementClient.components.createOrUpdate(
        config.resourceGroupName,
        appinsightsName,
        {
          location: config.location,
          applicationType: 'web',
          kind: 'web',
        }
      );
      if (appinsightsDeployResult._response.status >= 300 || appinsightsDeployResult.provisioningState != 'Succeeded') {
        this.logger({
          status: BotProjectDeployLoggerType.PROVISION_ERROR,
          message: appinsightsDeployResult._response.bodyAsText,
        });
        throw createCustomizeError(ProvisionErrors.CREATE_QNA_ERROR, appinsightsDeployResult._response.bodyAsText);
      }

      // add web config for websites
      const azureSearchAdminKey = (
        await searchManagementClient.adminKeys.get(config.resourceGroupName, qnaMakerSearchName)
      ).primaryKey;
      const appInsightsComponent = await applicationInsightsManagementClient.components.get(
        config.resourceGroupName,
        appinsightsName
      );
      const userAppInsightsKey = appInsightsComponent.instrumentationKey;
      const userAppInsightsName = appinsightsName;
      const userAppInsightsAppId = appInsightsComponent.appId;
      const primaryEndpointKey = `${qnaMakerWebAppName}-PrimaryEndpointKey`;
      const secondaryEndpointKey = `${qnaMakerWebAppName}-SecondaryEndpointKey`;
      const defaultAnswer = 'No good match found in KB.';
      const QNAMAKER_EXTENSION_VERSION = 'latest';
      const EnableMultipleTestIndex = 'true';

      // deploy qna host webapp
      const webAppResult = await webSiteManagementClient.webApps.createOrUpdate(
        config.resourceGroupName,
        qnaMakerWebAppName,
        {
          name: qnaMakerWebAppName,
          serverFarmId: servicePlanResult.name,
          location: config.location,
          siteConfig: {
            cors: {
              allowedOrigins: ['*'],
            },
            appSettings: [
              {
                name: 'AzureSearchName',
                value: qnaMakerSearchName,
              },
              {
                name: 'AzureSearchAdminKey',
                value: azureSearchAdminKey,
              },
              {
                name: 'UserAppInsightsKey',
                value: userAppInsightsKey,
              },
              {
                name: 'UserAppInsightsName',
                value: userAppInsightsName,
              },
              {
                name: 'UserAppInsightsAppId',
                value: userAppInsightsAppId,
              },
              {
                name: 'PrimaryEndpointKey',
                value: primaryEndpointKey,
              },
              {
                name: 'SecondaryEndpointKey',
                value: secondaryEndpointKey,
              },
              {
                name: 'DefaultAnswer',
                value: defaultAnswer,
              },
              {
                name: 'QNAMAKER_EXTENSION_VERSION',
                value: QNAMAKER_EXTENSION_VERSION,
              },
              {
                name: 'EnableMultipleTestIndex',
                value: EnableMultipleTestIndex,
              },
            ],
          },
          enabled: true,
        }
      );

      if (webAppResult._response.status >= 300) {
        this.logger({
          status: BotProjectDeployLoggerType.PROVISION_ERROR,
          message: webAppResult._response.bodyAsText,
        });
        throw createCustomizeError(ProvisionErrors.CREATE_QNA_ERROR, webAppResult._response.bodyAsText);
      }

      // Create qna account
      const cognitiveServicesManagementClient = new CognitiveServicesManagementClient(
        this.creds,
        this.subscriptionId,
        this.options
      );
      const deployResult = await cognitiveServicesManagementClient.accounts.create(
        config.resourceGroupName,
        qnaMakerServiceName,
        {
          kind: 'QnAMaker',
          sku: {
            name: config.sku ?? 'S0',
          },
          location: config.location,
          properties: {
            apiProperties: {
              qnaRuntimeEndpoint: `https://${webAppResult.hostNames?.[0]}`,
            },
          },
        }
      );
      if (deployResult._response.status >= 300) {
        this.logger({
          status: BotProjectDeployLoggerType.PROVISION_ERROR,
          message: deployResult._response.bodyAsText,
        });
        throw createCustomizeError(ProvisionErrors.CREATE_QNA_ERROR, deployResult._response.bodyAsText);
      }

      const endpoint = webAppResult.hostNames?.[0];
      const keys = await cognitiveServicesManagementClient.accounts.listKeys(
        config.resourceGroupName,
        qnaMakerServiceName
      );
      const subscriptionKey = keys?.key1 ?? '';
      return {
        endpoint: endpoint,
        subscriptionKey: subscriptionKey,
      };
    } catch (err) {
      this.logger({
        status: BotProjectDeployLoggerType.PROVISION_ERROR,
        message: JSON.stringify(err, Object.getOwnPropertyNames(err)),
      });
      throw createCustomizeError(ProvisionErrors.CREATE_QNA_ERROR, stringifyError(err));
    }
  }