Using a local Open API Standard file to to create an ARM template for a web service - json

I am working on an old web service where I generate the rest endpoints documentation that comply with OAS standards using a custom tool. Using this OAS json file I can deploy the API to Azure API Managements services through the portal and it all works fine. However, I need to automate this process and hence need to use ARM templates to deploy all web services to Azure APIM. I have been looking into the examples provided https://learn.microsoft.com/en-us/azure/templates/microsoft.apimanagement/service/apis but just can't seem to wrap my head around how to use a local OAS.json file or a file in github.
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"apiManagementServiceName": "price-capture"
},
"resources": [
{
"apiVersion": "2018-01-01",
"type": "Microsoft.ApiManagement/service/apis",
"name": "[variables('apiManagementServiceName')]",
"properties": {
"displayName": "Service display Name",
"apiRevision": "1",
"description": "API description",
//need help since it's not a swagger url
//wondering if there is a way to ref a local file like the option
//provided in the portal when we register api's manually.
"serviceUrl": "----",
"path": "----",
"protocols": [
"https"
],
"isCurrent": true,
"apiVersion": "v1",
"apiVersionDescription": "apiVersionDescription"
}
}
]
}

You can deploy and configure an entire API on API Management via ARM templates, but you cannot use a local file to provide the OpenApi/Swagger.
In your case the OpenApi/Swagger needs to be publicly accessible so the resource manager can read from it, so if the Github URL is freely accessible it should work.
I typically store the OpenApi/Swagger to a storage account and use the SAS token to access it from the ARM template.
You can check out this blog for details on automating API deployment in APIM:
https://blog.eldert.net/api-management-ci-cd-using-arm-templates-linked-template/

You can deploy the API using an Azure Resource Manager template of type Microsoft.ApiManagement/service/apis, and to use an Open API / swagger definition you need to specify the contentValue and and contentFormat parameters of the template
{
"name": "awesome-api-management/petstore",
"type": "Microsoft.ApiManagement/service/apis",
"apiVersion": "2018-06-01-preview",
"properties": {
"path": "petstore"
"contentValue": "petstore swagger file contents here", // or it's URL
"contentFormat": "swagger-json", // or swagger-link-json if externally available
}
}

I don't think it's possible to deploy the APIs configs via templates.
I've been trying to figure this out myself but I'm pretty sure you can't include the actual APIs you want in the service.
From what I can tell, you can't do that with the GIT repo either because that needs authentication that is manually created in the portal
I think the only thing you can automate with the ARM template is the actual API Management service and then you need to use the Azure API to add and configure the APIs on it.
However, I have yet to figure out how to do that myself.
I actually have a service ticket open to get help on that.

The API has changed slightly so this works:
The yaml file (calculatorApiFile) needs to be uploaded first to a blob storage, but this can be done as part of the deployment pipeline
{
"type": "Microsoft.ApiManagement/service/apis",
"apiVersion": "2019-01-01",
"name": "[concat(parameters('service_name'), '/b12b1d5ab8204cg6b695e3e861fdd709')]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service', parameters('service_name'))]"
],
"properties": {
"displayName": "Calculator",
"apiRevision": "1",
"description": "A simple Calculator ",
"path": "calc",
"value": "[concat(parameters('containerUri'), parameters('calculatorApiFile'), parameters('containerSasToken'))]",
"format": "openapi-link",
"protocols": [
"https"
],
"isCurrent": true
}
}

I figured out the answer ..all I had to do was write an azure function that fetches the oas.yaml file from a private github repository.
"variables":{
"swagger_json":"[concat(parameters('url_of_azurefunctionwithaccesskey'),'&&githuburi='parameter('raw_url'),'&githubaccesstoken=',parameter('personalaccesstoken')]"
},
"resources": [
{
"type": "Microsoft.ApiManagement/service/apis",
"name": "[concat(parameters('apimName') ,'/' ,parameters('serviceName'))]",
"apiVersion": "2018-06-01-preview",
"properties": {
"apiRevision": "[parameters('apiRevision')]",
"path": "pricecapture",
"contentValue": "[variables('swagger_json')]",
"contentFormat": "openapi-link"
}
}]
The Azure function that I had to write was something like this:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.IO;
using System.Text;
public static async Task<HttpResponseMessage> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var gitHubUri = req.Query["githuburi"];
var gitHubAccessToken = req.Query["githubaccesstoken"];
var encoding = Encoding.ASCII;
if (string.IsNullOrEmpty(gitHubUri))
{
var errorcontent = new StringContent("please pass the raw file content URI (raw.githubusercontent.com) in the request URI string", Encoding.ASCII);
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
Content = errorcontent
};
}
else if (string.IsNullOrEmpty(gitHubAccessToken))
{
var errorcontent = new StringContent("please pass the GitHub personal access token in the request URI string", Encoding.ASCII);
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
Content = errorcontent
};
}
else
{
var strAuthHeader = "token " + gitHubAccessToken;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3.raw");
client.DefaultRequestHeaders.Add("Authorization", strAuthHeader);
var response = await client.GetAsync(gitHubUri);
return response;
}
}

If you load your YAML into a variable, that can be passed to the ARM template and be passed as the value:
deploy.bat:
SETLOCAL EnableDelayedExpansion
set API_DEPLOYMENT=<deployment name>
set API_GROUP=<deployment group>
set API=<api file path.yml>
set OPENAPI=
for /f "delims=" %%x in ('type %API%') do set "OPENAPI=!OPENAPI!%%x\n"
call az deployment group create -n %API_DEPLOYMENT% -g %API_GROUP% --mode Complete -f deploy.json -p openApi="!OPENAPI!"
ENDLOCAL
deploy.json (note the use of replace)
...
{
"type": "Microsoft.ApiManagement/service/apis",
"apiVersion": "2020-12-01",
"name": "[variables('apiName')]",
"properties": {
"path": "[variables('service')]",
"apiType": "http",
"displayName": "[variables('apiDisplayName')]",
"format": "openapi",
"value": "[replace(parameters('openApi'), '\\n', '\n')]"
},
...
},
...

Related

Substituting service url is arm template

I have an ARM template that deploys API's to an API Management instance
Here is an example of one API
{
"properties": {
"authenticationSettings": {
"subscriptionKeyRequired": false
},
"subscriptionKeyParameterNames": {
"header": "Ocp-Apim-Subscription-Key",
"query": "subscription-key"
},
"apiRevision": "1",
"isCurrent": true,
"subscriptionRequired": true,
"displayName": "DDD.CRM.PostLeadRequest",
"serviceUrl": "https://test1/api/FuncCreateLead?code=XXXXXXXXXX",
"path": "CRMAPI/PostLeadRequest",
"protocols": [
"https"
]
},
"name": "[concat(variables('ApimServiceName'), '/mms-crm-postleadrequest')]",
"type": "Microsoft.ApiManagement/service/apis",
"apiVersion": "2019-01-01",
"dependsOn": []
}
When I am deploying this to different environments I would like to be able to substitute the service url depending on the environment. I'm wondering the best approach?
Can I read in a config file or something like that?
At the time of deployment I have a variable that tells me the environment so I can base decisions on that. Just not sure the best way to do it
See about ARM template parameters: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authoring-templates#parameters They can be specified in a separate file. So you will have single template, but environment specific parameter files.

How can I retrieve the query key for Bing Maps API for Enterprise in an Azure Resource Group Template?

I am working on an ARM template that deploys an entire infrastructure from scratch:
The resource group
App Service plans
Application Insights
an so forth...
At some point I get to the part where I write the scripts for deploying my App Service (for hosting and deploying my web app later on) to my resource group. Prior to that I have my BingMaps API deployed in the same script.
I am stuck at the part where I am setting the Application Settings for my web app:
"type": "Microsoft.Web/sites",
"properties": {
"siteConfig": {
"appSettings": [
{
"name": "SomeKey",
"value": "SomeValue"
}, //rest of the code omitted
I would like to know how could I retrieve my BING MAPS query key within an ARM template?
I have tried, and have a feeling that this might be close to it, something like:
"value": "[reference(resourceId('Microsoft.BingMaps/mapApis', variables('bingMapsName')), '2016-08-18').queryKey]"
Anybody who has done this before? Many thanks in advance! Cheers
If you want to access query key in your ARM template for your web app setting, I would suggest you to use something like below:
{
"name": "appsettings",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('webSiteName'))]"
],
"tags": {
"displayName": "WebAppSettings"
},
"properties": {
"key1": "[parameter('AppSetting_Key1_Value')]",
"key2": "value2"
}
}
and then in your template.Parmeter.jso file , you can declare the key AppSetting_Key1_Value with the value of your Bing maps query key.
Specify the Parameter Value
After the Parameter has been added to the ARM Template and it’s being used to populate an Application Setting, the final step is to define the Parameter value within the ARM Templates Parameter file used for deployments. In the Azure Resource Group project template in Visual Studio the Parameters file for the default deployment is the file that ends with “.parameters.json”.
Here’s a screenshot of the “WebSite.parameters.json” file created in the previous articles in this series with the “AppSetting_Key1_Value” Parameter set to a value:
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": {
"value": "WebApp1HostingPlan"
},
"WebApplication1PackageFolder": {
"value": "WebApplication1"
},
"WebApplication1PackageFileName": {
"value": "package.zip"
},
"WebApp_ConnString1": {
"value": "Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;"
},
"AppSetting_Key1_Value": {
"value": "Template Value 1"
}
}
}
for security complaint solution , you can move all your secure key and connection string to Azure key vault if you are not comfortable to have keys in param file.
This should work. Hope it helps.

Azure : create file through template

We have a requirement where a config file is to be created and sent to a linux VM for later setup. Is there is any way of doing that. We are using ARM template for the setup
If my understanding is right, you could use Azure Custom Script Extension to do this.
The Custom Script Extension downloads and executes scripts on Azure
virtual machines. This extension is useful for post deployment
configuration, software installation, or any other configuration /
management task. Scripts can be downloaded from Azure storage or other
accessible internet location, or provided to the extension run time.
Example
{
"name": "scriptextensiondemo",
"type": "extensions",
"location": "[resourceGroup().location]",
"apiVersion": "2015-06-15",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('scriptextensiondemoName'))]"
],
"tags": {
"displayName": "scriptextensiondemo"
},
"properties": {
"publisher": "Microsoft.Azure.Extensions",
"type": "CustomScript",
"typeHandlerVersion": "2.0",
"autoUpgradeMinorVersion": true,
"settings": {
"fileUris": [
"https://gist.github.com/ahmetalpbalkan/b5d4a856fe15464015ae87d5587a4439/raw/466f5c30507c990a4d5a2f5c79f901fa89a80841/hello.sh"
],
"commandToExecute": "sh hello.sh"
}
}
}
You could create a script to create the file and execute Linux commands.

Azure RM Templates. How to upload assets automatically with VS instead of fetching them from GitHub

I would like to be able to deploy a complex ARM template that utilizes DSC extensions and nested templates from my local Visual Studio.
The example is set to download assets from GitHub:
https://github.com/Azure/azure-quickstart-templates/tree/master/active-directory-new-domain-ha-2-dc
What changes do I have to make that I can tie the assets to my local Visual Studio project and use them instead of downloading them from GitHub?
Here is the strip down version of the template responsible for downloading:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
...
"adPDCVMName": {
"type": "string",
"metadata": {
"description": "The computer name of the PDC"
},
"defaultValue": "adPDC"
},
"assetLocation": {
"type": "string",
"metadata": {
"description": "The location of resources such as templates and DSC modules that the script is dependent"
},
"defaultValue": "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/active-directory-new-domain-ha-2-dc"
}
...
},
"variables": {
...
"adPDCModulesURL": "[concat(parameters('assetLocation'),'/DSC/CreateADPDC.ps1.zip')]",
"adPDCConfigurationFunction": "CreateADPDC.ps1\\CreateADPDC",
...
},
"resources": [
...
{
"name": "[parameters('adPDCVMName')]",
"type": "Microsoft.Compute/virtualMachines",
...
"resources": [
{
"name": "[concat(parameters('adPDCVMName'),'/CreateADForest')]",
"type": "Microsoft.Compute/virtualMachines/extensions",
...
"properties": {
...
"settings": {
"ModulesUrl": "[variables('adPDCModulesURL')]",
"ConfigurationFunction": "[variables('adPDCConfigurationFunction')]",
...
}
}
}
}
}
]
}
]
}
Do the following in your 'Azure Resource Group' project in Visual Studio:
Copy the files to your project in Visual Studio using the same
directory structure. So a DSC directory and a nestedtemplates directory with
the files that belong there.
Set the files in the directories as content (azuredeploy.json is not needed, only the files you are referring to). This way the powershell script to deploy the templates will upload it to a storage account in azure.
Make it possible to use files uploaded to the storage account. In this case the template that you are referring to is not using the common
namingconvention. So you need to change it a bit:
Change azuredeploy.json: Change the name of parameter
assetLocation to _artifactsLocation.
Second: Add a parameter
_artifactsLocationSasToken as securestring. These 2 parameters will be filled automatically by the powershell script in Visual Studio.
part of the azuredeploy.json:
"parameters": {
"_artifactsLocation": {
"type": "string",
"metadata": {
"description": "The base URI where artifacts required by this template are located. When the template is deployed using the accompanying scripts, a private location in the subscription will be used and this value will be automatically generated."
}
},
"_artifactsLocationSasToken": {
"type": "securestring",
"metadata": {
"description": "The SAS token to access the storage account"
}
},
Because the original azuredeploy.json is not using the _artifactsLocationSasToken parameter. You need to change all variables where the assetlocation is used. Change all variables so it uses the _artifactsLocation and add a part to use the _artifactsLocationSasToken.
one sample:
"vnetTemplateUri": "[concat(parameters('_artifactsLocation'),'/nestedtemplates/vnet.json', parameters('_artifactsLocationSasToken'))]",
After you changed all variables. You can deploy the template from Visual Studio using the resources in your project instead of from github.

How to get customAPI url from node.js app?

I am just starting with BlueMix and in my space I have:
a Cloud Integration service: using a Basic Secure Connection, for which I have created an API endpoint; then in that Cloud Integration service I have added the corresponding API by importing a swagger 1.2 file, and published that customAPI to my organization;
a pretty simple node.js application;
From the Cloud Integration service> API view, I can get the URLs for the different resources (for instance http://endpoint_ip:endpoint_port/api/version/path_to_resource), so I can hardcode these URLs in my node.js application and it works.
But if I bind the Cloud Integration service and even the customAPI to my node.js application, I don't get any information in VCAP_SERVICES about the endpoint URL; but I have seen examples of VCAP_SERVICES where the API URL is available.
Below is my VCAP_SERVICES
{"CloudIntegration": [
{
"name": "Cloud Integration-b9",
"label": "CloudIntegration",
"plan": "cloudintegrationplan",
"credentials": {
"userid": "apiuser#CloudIntegration",
"password": "S!2w3e40",
"apis": [
{
"name": "Catalog Manager API",
"desc": "Catalog Manager API",
"resource": ""
}
]
}
}
]
}
What I am trying to achieve is to avoid hardcoding URLs in my application, since I can bind a BlueMix service to it, and perhaps get info from the environment.
Am I doing something wrong? Or is that not the way it is supposed to work?
Also I don't really get why there is nothing in the VCAP_SERVICES.CloudIntegration[0].credentials.apis[0].resource even though I have my customAPI specifies resources.
#Rick
Make sure you "publish" your API after configuring the Cloud Integration service. Then service credentials will reflect the changes:
"CloudIntegration": [
{
"name": "Cloud Integration-v5",
"label": "CloudIntegration",
"plan": "cloudintegrationplan",
"credentials": {
"userid": "apiuser#CloudIntegration",
"password": "S!2w3e40",
"apis": [
{
"name": "SwaggerPetStore",
"desc": "SwaggerPetStore",
"resource": "http",
"baseurl": "http://mypypatchank.mybluemix.net"
}
]
}
}
]
in the same way, if you use the API management service, you will have a corresponding VCAP_SERVICES entry
"Swagger Petstore v1 : Sandbox 551b2dcf0cf2521d98d061d4 prod": [
{
"name": "Swagger Petstore v1 : Sandbox prod-w0",
"label": "Swagger Petstore v1 : Sandbox 551b2dcf0cf2521d98d061d4 prod",
"plan": "plan1 : Sandbox prod",
"credentials": {
"clientID": "55cfe3fa-ff59-474c-a1b6-46d3cc9871",
"clientSecret": "uK3xM3eF4cA1qF7yW8mC2lP6wS6aG7sQ5cL2yJ4sC6iS1dE7",
"url": "https://api.eu.apim.ibmcloud.com/garciatemx1ibmcom/sb/api"
}
}
]
Since your goal is to "to avoid hardcoding URLs in my application, since I can bind a BlueMix service to it, and perhaps get info from the environment." I would like to suggest using a user provided service.
This will create a user provided service and start interactive input for you to enter the api url and a password. You can add more parameters if you need.
cf cups servicename -p "url, password"
Bind this service to your application and restage. You can access these parameters in your Node.js application easily with the cfenv module.
var cfenv = require("cfenv");
var appEnv = cfenv.getAppEnv();
var myService = appEnv.getService("servicename");
//use myService.credentials.url to access the url value.
//use myService.credentials.password to access the password value.
The user provided services VCAP_SERVICES looks like:
{
"user-provided": [
{
"name": "servicename",
"label": "user-provided",
"credentials": {
"url": "myURL",
"password": "myPassword"
}
}
]
}