Skip to content

Instantly share code, notes, and snippets.

@maskati
Created December 4, 2024 12:48
Show Gist options
  • Save maskati/03fa61c3ce846e44fcafe2668e6435a3 to your computer and use it in GitHub Desktop.
Save maskati/03fa61c3ce846e44fcafe2668e6435a3 to your computer and use it in GitHub Desktop.
Call REST APIs in Azure Bicep templates without deployment scripts

Did you know you can call REST APIs from your Bicep deployment templates using just a function call?

var getResponse = httpClient.listHttpRequest(httpClient.apiVersion, {
  method: 'GET'
  uri: 'https://mallow.fi/'
})

var postResponse = httpClient.listHttpRequest(httpClient.apiVersion, {
  method: 'POST'
  uri: 'https://api.mallow.fi/jobs'
  headers: {
    favoriteDrink: 'Coffee'
  }
  body: {
    position: 'Azure Architect'
  }
})

This technique makes use of a list* resource function on a custom resource provider. The list* functions are special because unlike most resource provider REST API operations, you can call them directly as functions from your Bicep deployments. You do this by defining a custom action on a resource provider and naming it list*. To implement a generic HTTP proxy that can perform any HTTP request, we define an interface that takes the request method, uri and optional headers and body. The actual proxy is implemented as a Logic App.

Assume you have deployed the Logic App and custom resource provider defined by http-client.bicep. You can reference the deployed provider by ID in a different deployment scope and perform HTTP requests using the provider defined listHttpRequest action. The below example retireves the business details of my employer Mallow Oy from the Finnish Patent and Registration Office Open Data API and extracts some information from the response as output parameters. One major advantage of this over deployment scripts is that the operation is very fast, with the below example deployment taking less than 2 seconds to complete.

@description('The resource ID of the HTTP client custom resource provider containing the `listHttpRequest` action')
param httpclient_id string

// reference the custom resource provider in the deployed scope
var httpclient_parts = split(httpclient_id, '/')
var httpclient_subscription = httpclient_parts[2]
var httpclient_resourceGroup = httpclient_parts[4]
var httpclient_name = httpclient_parts[8]
resource httpClient 'Microsoft.CustomProviders/resourceProviders@2018-09-01-preview' existing = {
  name: httpclient_name
  scope: resourceGroup(httpclient_subscription, httpclient_resourceGroup)
}

// perform a rest request against the Finnish patent and registration office open data API
// to retrieve the company details for Mallow Oy with business ID 3200030-4
var httpResponse = httpClient.listHttpRequest(httpClient.apiVersion, {
  method: 'GET'
  uri: 'https://avoindata.prh.fi/opendata-ytj-api/v3/companies?businessId=3200030-4'
})

// get the first matching company
var company = first(httpResponse.body.companies)

// extract some information from the response
output names array = map(company.names, name => name.name)
output businessline string = first(filter(company.mainBusinessLine.descriptions, d => d.languageCode == '3')).description
output website string = company.?website.url ?? ''
output addresses array = map(company.addresses, address => '${address.street} ${address.buildingNumber}${address.entrance} ${address.postCode}')
output entries array = map(sort(company.registeredEntries, (a, b) => dateTimeToEpoch(a.registrationDate) < dateTimeToEpoch(b.registrationDate)), re => '${first(filter(re.descriptions, d => d.languageCode == '3')).description}@${re.registrationDate}')

The resulting output of the deployment shows the current business names and office address for Mallow Oy:

image

@allowed([
'australiaeast'
'australiasoutheast'
'eastus'
'westus2'
'westeurope'
'northeurope'
'canadacentral'
'canadaeast'
])
param location string = 'westeurope'
resource httpClientLogicApp 'Microsoft.Logic/workflows@2019-05-01' = {
name: 'logic-httpclient'
location: location
properties: {
state: 'Enabled'
definition: {
'$schema': 'https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#'
contentVersion: '1.0.0.0'
triggers: {
Request: {
type: 'Request'
kind: 'Http'
inputs: {
method: 'POST'
schema: {
type: 'object'
properties: {
method: {
type: 'string'
enum: ['GET', 'POST']
}
uri: {
type: 'string'
}
headers: {
type: 'object'
}
body: {
type: 'object'
}
}
required: ['method', 'uri']
}
}
operationOptions: 'EnableSchemaValidation'
}
}
actions: {
Response: {
runAfter: {
HTTP: [
'Succeeded'
]
}
type: 'Response'
kind: 'Http'
inputs: {
statusCode: '200'
body: {
deploymentRequestPath: '@triggerOutputs()?[\'headers\']?[\'x-ms-customproviders-requestpath\']'
deploymentCorrelationId: '@triggerOutputs()?[\'headers\']?[\'x-ms-correlation-request-id\']'
statusCode: '@outputs(\'HTTP\')?[\'statusCode\']'
headers: '@outputs(\'HTTP\')?[\'headers\']'
body: '@body(\'HTTP\')'
}
}
}
HTTP: {
runAfter: {}
type: 'Http'
inputs: {
method: '@triggerBody()?[\'method\']'
uri: '@triggerBody()?[\'uri\']'
headers: '@triggerBody()?[\'headers\']'
body: '@triggerBody()?[\'body\']'
}
limit: {
timeout: 'PT1M'
}
}
}
outputs: {}
parameters: {}
}
}
resource trigger 'triggers' existing = {
name: 'Request'
}
}
resource httpClient 'Microsoft.CustomProviders/resourceProviders@2018-09-01-preview' = {
name: 'rp-httpclient'
location: location
properties: {
actions: [
{
name: 'listHttpRequest'
routingType: 'Proxy'
endpoint: httpClientLogicApp::trigger.listCallbackUrl().value
}
]
}
}
output httpClient_id string = httpClient.id
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment