Получить список функциональных приложений Azure в Node JS?

#node.js #azure #azure-functions

#node.js #лазурный #azure-функции

Вопрос:

С az cli помощью этого az functionapp list метода вы можете получить массив всех ваших функциональных приложений и связанных с ними метаданных (например appServicePlanId , defaultHostName , lastModifiedTimeUtc … и т.д.)

Я потратил последний час на поиски повсюду, в том числе в azure-sdk-for-js src, но, похоже, я не могу найти подходящий NodeJS SDK, чтобы просто перечислить все мои функциональные приложения Azure. Я хотел бы использовать для этого Node SDK, А НЕ вручную создавать HTTP-запрос. Есть идеи, где живет эта функциональность?

Чтобы быть предельно ясным, я хочу сделать что-то вроде этого:

    await new AzureFunctionClient(credentials,subscriptionId).functionApps.listAll() 

Ответ №1:

Вы можете использовать REST API для составления списка функций

Listing functions

получить /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/{functionapp}/functions?api-version=2015-08-01

Ответ №2:

Очень раздражает, но, похоже, по какой-то причине нет NodeJS SDK для базовых действий CRUD с функциями Azure (акцент на R в CRUD), чтобы делать такие вещи, как просто перечислять все ваши функциональные приложения или функции. Мне пришлось написать это вручную:

 import { URLSearchParams } from 'url'

import * as Sentry from '@sentry/node'

import fetch from 'node-fetch'

import * as head from 'lodash/head'

import { ResourceManagementClient } from '@azure/arm-resources'

const functionapp = 'functionapp'

const getAadToken = async ({ clientId, clientSecret, tenantId }) => {
  const body = new URLSearchParams()
  body.append('grant_type', 'client_credentials')
  body.append('resource', 'https://management.azure.com/')
  body.append('client_id', clientId)
  body.append('client_secret', clientSecret)

  return fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/token`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body,
  })
}

const listResourceGroupsForSubscription = async ({
  returnIds = false,
  credentials,
  subscriptionId,
}) => {
  const resourceGroups = []

  const client = new ResourceManagementClient(credentials, subscriptionId)

  const getAllResourceGroupsData = async (nextLinkToken = '') => {
    let rsgs
    let nextLink
    /**
     * Then this is the nth time this has been called
     */
    try {
      if (nextLinkToken) {
        rsgs = await client.resourceGroups.listNext(nextLinkToken)
        nextLink = rsgs.nextLink
      } else {
        /**
         * Then this is the first time this has been called
         */
        rsgs = await client.resourceGroups.list()
        nextLink = rsgs.nextLink
      }
    } catch (e) {
      logger.error(e)
      Sentry.captureException(e)
    }

    /**
     * Add the rsgs to our resourceGroups array so we can aggregate as we go
     */
    resourceGroups.push(...rsgs)

    if (nextLink) {
      /**
       * If there is another page of data, get it
       */
      await getAllResourceGroupsData(nextLink)
    }

    return rsgs
  }

  await Promise.all(await getAllResourceGroupsData())

  return returnIds ? resourceGroups.map(({ id }) => id) : resourceGroups
}

const createUriForMsRestApiRequest = ({ id }) =>
  [`https://management.azure.com`, id, '?api-version=2020-06-01'].join('')

const getDataFromMsRestApi = async ({
  authToken,
  initialUrl,
  kindSelector = '',
}) => {
  const allData = []

  const getAllData = async (nextLinkToken = '') => {
    try {
      const url = nextLinkToken || initialUrl

      const response = await fetch(url, {
        headers: { Authorization: `Bearer ${authToken}` },
      })

      const { value: data, nextLink } = await response.json()

      allData.push(
        ...(kindSelector
          ? data.filter(({ kind }) => kind.includes(functionapp))
          : data)
      )

      if (nextLink) {
        logger.info(lt.fetchingMoreRestApiData)
        await getAllData(nextLink)
      }

      logger.info(lt.fetchedDataFromRestApi(url))
      return data
    } catch (e) {
      logger.error(e)
      Sentry.captureException(e)
    }
  }

  await Promise.all(await getAllData())

  return allData
}

const getAzureFunctionAppsData = async ({
  credentials,
  subscriptionId,
  clientId,
  clientSecret,
  tenantId,
}) => {
  /**
   * In order to get a list of all the function apps and functions we need to create a uri that looks like this:
   * /subscriptions/${subscriptionId}/resourceGroups/${resourceGroupId}/providers/Microsoft.Web/sites/${functionAppId}/functions
   * And since Azure does not provide this functionaliy in any of their SDKS, we have to fetch a lot of data ourselves...
   */

  try {
    /**
     * Step 1, get all the resource groups (we just need the ids)
     */
    const resourceGroupsIds = await listResourceGroupsForSubscription({
      returnIds: true,
      credentials,
      subscriptionId,
    })

    /**
     * Step 2, Grab the bearer token for auth with the REST API
     */
    const response = await getAadToken({
      clientId,
      clientSecret,
      tenantId,
    })

    const { access_token: authToken } = await response.json()

    /**
     * Step 3, for each resource group get all the sites grab the Function Apps
     */
    const functionApps = (
      await Promise.all(
        (resourceGroupsIds || []).map(
          async resourceGroupId =>
            await getDataFromMsRestApi({
              authToken,
              kindSelector: functionapp,
              initialUrl: createUriForMsRestApiRequest({
                id: `${resourceGroupId}/providers/Microsoft.Web/sites`,
              }),
            })
        )
      )
    ).flat()

    /**
     * Step 4, now we can finally grab the actual functions for each function app
     */
    const azureFunctions = (
      await Promise.all(
        (functionApps || []).map(
          async ({ id: functionAppId }) =>
            await getDataFromMsRestApi({
              authToken,
              initialUrl: createUriForMsRestApiRequest({
                id: `${functionAppId}/functions`,
              }),
            })
        )
      )
    ).flat()

    return (functionApps || []).map(({ name, ...data }) => ({
      name,
      ...data,
      functions: azureFunctions.filter(
        ({ name: funcName }) => head(funcName.split('/')) === name
      ),
    }))
  } catch (e) {
    logger.error(e)
    Sentry.captureException(e)
  }
}