#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 для составления списка функций
получить /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)
}
}