Use-Custom-PowerShell-modules-in-Azure-Functions

Azure Functions will be able to support customers bringing their own modules. These modules will reside in a folder named modules that is located in the same directory where the PowerShell script resides.

We will not support customers installing their own modules using the Install-Module cmdlet, however, customers may upload their modules into the modules folder.

All modules in the modules folder will be loaded automatically, so customers will not have to explicitly use the Import-Module cmdlet.

We will support script, binary and manifest modules. These modules will reside in a flat structure within the modules folder.

For runtime 1, the folder should be located in the directory of each azure function.

1
2
3
4
5
Install-PackageProvider nuget -force -scope CurrentUser;

mkdir ./[function folder]/Modules;

save-module [module-name] -path ./[function folder]/Modules

For runtime 2, the folder should be located int the /wwwroot directory to be shared by all azure functions.

1
2
3
4
5
Install-PackageProvider nuget -force -scope CurrentUser;

mkdir ./Modules;

save-module [module-name] -path ./Modules

1

2

HTH. By Jacky 2020-3-2

How-to-config-a-CA-certificate-with-API-management-via-PowerShell

$password = ConvertTo-SecureString -String “YourPassword” -Force -AsPlainText

$rootCa = New-AzApiManagementSystemCertificate -StoreName “Root”-PfxPassword $password -PfxPath “C:\YourDirectory\your-cert-file.pfx”

$systemCert = @($rootCa)

New-AzApiManagement -ResourceGroupName “YourResourceGroupName” -Location “YourLocation” -Name “YourAPIMNname” -Organization YourOrg -AdminEmail youremail@jacky.com -SystemCertificateConfiguration $systemCert

This command creates and initializes an instance of PsApiManagementSystemCertificate with a root CA certificate. It then creates and API Management service which installs the CA cert to the Root store.

HTH. By Jacky 2020-Feb.-27

How-to-integrate-application-insight-with-API-Management-Service-via-ARM-template

  1. Please save two attachments to a folder. Using a command-line tool, navigate to the directory.

  2. Run following Azure CLI command to start the process:

    1
    2
    3
    az login

    az group deployment create --resource-group <resource-group> --template-file ./application-insights.template.json --parameters ApimServiceName=<apim-instance-name> --parameters @application-insights.parameters.json

    Note: be sure to replace as well as with their corresponding parameters and update the application-insights.parameters.json file with appropriate parameters that pertain to your environment.

  3. Once completed, your existing API Management instance should be integrated with a newly created Application Insights instance.

application-insights.template.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"ApimServiceName": {
"type": "string"
},
"ApplicationInsightsLocation": {
"type": "string"
},
"ApplicationInsightsInstanceName": {
"type": "string"
},
"SamplingRate": {
"type": "int"
}
},
"variables": {},
"resources": [
{
"name": "[parameters('ApplicationInsightsInstanceName')]",
"type": "Microsoft.Insights/components",
"apiVersion": "2015-05-01",
"location": "[parameters('ApplicationInsightsLocation')]",
"tags": {},
"kind": "other",
"properties": {
"Application_Type": "other"
}
},
{
"type": "Microsoft.ApiManagement/service/loggers",
"name": "[concat(parameters('ApimServiceName'), '/', parameters('ApplicationInsightsInstanceName'))]",
"apiVersion": "2018-06-01-preview",
"properties": {
"loggerType": "applicationInsights",
"description": "Logger resources to APIM",
"credentials": {
"instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('ApplicationInsightsInstanceName')), '2015-05-01').InstrumentationKey]"
}
}
},
{
"type": "Microsoft.ApiManagement/service/diagnostics",
"name": "[concat(parameters('ApimServiceName'), '/applicationinsights')]",
"apiVersion": "2018-06-01-preview",
"properties": {
"alwaysLog": "allErrors",
"loggerId": "[concat('/loggers/', parameters('ApplicationInsightsInstanceName'))]",
"sampling": {
"samplingType": "fixed",
"percentage": "[parameters('SamplingRate')]"
},
"frontend": {
"request": {
"headers": [],
"body": {}
},
"response": {
"headers": [],
"body": {}
}
},
"backend": {
"request": {
"headers": [],
"body": {}
},
"response": {
"headers": [],
"body": {}
}
},
"enableHttpCorrelationHeaders": false
},
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service/loggers', parameters('ApimServiceName'), parameters('ApplicationInsightsInstanceName'))]"
]
}

]
}

application-insights.parameters.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"$schema":
"http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"ApplicationInsightsLocation": {
"value": "centralus"
},
"ApplicationInsightsInstanceName": {
"value": "jackyapimai2"
},
"SamplingRate": {
"value": 50
}
}
}

Enjoy. By Jacky 2020-Feb.-26

Troubleshooting connectivity issues for Service Bus

The following steps may help you with troubleshooting connectivity/certificate/timeout issues for all services under *.servicebus.windows.net.

Browse to or wget https://jackysb1.servicebus.windows.net/. It helps with checking whether you have IP filtering or virtual network or certificate chain issues (most common when using java SDK).

Run the following command to check if any port is blocked on the firewall. Depending on the library you use, other ports are also used. For example: 443, 5672, 9354.

1
tnc jackysb1.servicebus.windows.net -port 5671

When there are intermittent connectivity issues, run the following command to check if there are any dropped packets. This command will try to establish 25 different TCP connections every 1 second with the service then you can check how many succeeded/failed and also see TCP connection latency.

1
.\psping.exe -n 25 -i 1 -q jackysb1.servicebus.windows.net:5671 -nobanner     

For more information:
https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-exceptions#connectivity-certificate-or-timeout-issues

Enjoy 2020-01-17 By Jacky

Azure-Resource-Manager-Service-tag-for-NSG

You can set it by command:

1
2
3
4
$nsg = Get-AzureRmNetworkSecurityGroup -Name <sg name> -ResourceGroupName <resource group name>
Add-AzureRmNetworkSecurityRuleConfig -NetworkSecurityGroup $nsg -Name allow-arm -Description "Allow ARM" -Access Allow `
-Protocol Tcp -Direction Outbound -Priority 100 -SourceAddressPrefix * `
-SourcePortRange * -DestinationAddressPrefix AzureResourceManager -DestinationPortRange * | Set-AzureRmNetworkSecurityGroup

Enjoy. By Jacky 2019-12-17

Newtonsoft.Json-(Json.NET)-is-removed-from-ASP.NET-Core-3.0-shared-framework

Assemblies removed from the ASP.NET Core shared framework
The most notable assemblies removed from the ASP.NET Core 3.0 shared framework are:

Newtonsoft.Json (Json.NET). To add Json.NET to ASP.NET Core 3.0, see Add Newtonsoft.Json-based JSON format support. ASP.NET Core 3.0 introduces System.Text.Json for reading and writing JSON. For more information, see New JSON serialization in this document.
Entity Framework Core
For a complete list of assemblies removed from the shared framework, see Assemblies being removed from Microsoft.AspNetCore.App 3.0. For more information on the motivation for this change, see Breaking changes to Microsoft.AspNetCore.App in 3.0 and A first look at changes coming in ASP.NET Core 3.0.

For more information:
https://docs.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-3.0?view=aspnetcore-3.0#assemblies-removed-from-the-aspnet-core-shared-framework

HTH. 2019-12-6 By Jacky

Azure Funcion App with Hybrid Connection To OnPrem Server

Simulate Function App -> HTTPClient request -> Hybrid Connection Manager -> OnPremIIS

  1. Create a Azure VM and install IIS

  2. Create a Web API project by Visual Studio and deploy to IIS server

  3. Create a function with Standard plan

  4. Config Hybrid connection manager

  5. Install Hybrid connection manager connector in OnPrem Server (step 1)

  6. Create a Function App project simulate HTTPClient to send request to IIS Server via Hybrid Connection Manager

  7. Deploy Function App to Azure Function

  8. Execute request -> OnPremIIS (Thread sleep 50 seconds) -> get response

    Function log

     
    1
    2
    3
    4
    5
    6
    7
    2019-11-07T02:46:21.684 [Information] Executing 'Function1' (Reason='This function was programmatically called via the host APIs.', Id=efdfa4cb-446d-4102-b7ce-3d3eea05b04c)
    2019-11-07T02:46:21.806 [Information] C# HTTP trigger function processed a request.
    2019-11-07T02:46:22.289 [Information] Start client.SendAsync
    2019-11-07T02:47:12.848 [Information] End client.SendAsync
    2019-11-07T02:47:12.849 [Information] 200
    2019-11-07T02:47:12.849 [Information] "50 seconds, you can see it."
    2019-11-07T02:47:12.903 [Information] Executed 'Function1' (Succeeded, Id=efdfa4cb-446d-4102-b7ce-3d3eea05b04c)

    HTTP 200 with Output:

     
    1
    {"Timestamp":"2:46:22 AM","FileName":"YourFileName: 3","FileSize":"ProvideFileSize","ReturnCode":"0","ReturnMessage":null}
  9. Execute request -> OnPremIIS (Thread sleep 150 seconds) -> get exception

    Function Log:

    1
    2
    3
    4
    5
    6
    2019-11-07T02:31:43.647 [Information] Executed 'Function1' (Succeeded, Id=61383988-8e65-4cf8-9995-3cd718ed289e)
    2019-11-07T02:32:50 No new trace in the past 1 min(s).
    2019-11-07T02:32:50.030 [Information] One or more errors occurred. (A task was canceled.)
    2019-11-07T02:32:50.035 [Information] A task was canceled.
    2019-11-07T02:32:50.035 [Information] [null]
    2019-11-07T02:32:50.036 [Information] Executed 'Function1' (Succeeded, Id=3961b106-3f59-4909-90d3-4d16a1f7bb53)

    HTTP 200 with Output:

    1
    {"Timestamp":"2:31:10 AM","FileName":"YourFileName: 1","FileSize":"ProvideFileSize","ReturnCode":"9","ReturnMessage":"Type: System.AggregateException, Msg: One or more errors occurred. (A task was canceled.)"}
  10. Reviewed Exception in Application Insights

Root cause:

Responding to requests
The receiver MUST respond. Repeated failure to respond to requests while maintaining the connection might result in the listener getting blacklisted.
Responses may be sent in any order, but each request must be responded to within 60 seconds or the delivery will be reported as having failed. The 60-second deadline is counted until the response frame has been received by the service. An ongoing response with multiple binary frames cannot become idle for more than 60 seconds or it is terminated.

For more information: https://docs.microsoft.com/en-us/azure/service-bus-relay/relay-hybrid-connections-protocol

Sample Code:

My Function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Threading;
using System.Net.Http;
using System.Text;

namespace TestAyncFunApp
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");

MyDemoReturn returnValue = new MyDemoReturn();

string name = req.Query["name"];

string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
try
{
// give timestemp
returnValue.Timestamp = DateTime.Now.ToLongTimeString();
returnValue.FileName = "YourFileName: " + name;
returnValue.FileSize = "ProvideFileSize";
returnValue.ReturnCode = "1"; //1 => Start

var requestData = JsonConvert.SerializeObject("test");

var requestURI = "http://OnPremIIS/api/values/" + name;
using (var client = new HttpClient())
{
var httpContent = new StringContent(requestData, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage
{
RequestUri = new Uri(requestURI),
Method = HttpMethod.Get
};
log.LogInformation("Start client.SendAsync");
var response = client.SendAsync(request).Result;
log.LogInformation("End client.SendAsync");
int statusCode = (int)response.StatusCode;
log.LogInformation(statusCode.ToString());
var responseContent = response.Content;
var responseString = responseContent.ReadAsStringAsync().Result;

log.LogInformation(responseString);
returnValue.ReturnCode = "0"; //0 => Success
}
}
catch (Exception ex)
{
returnValue.ReturnCode = "9"; //9 => exception, you can add more return code
returnValue.ReturnMessage = string.Format("Type: {0}, Msg: {1}", ex.GetType().FullName, ex.Message);
log.LogInformation(ex.Message);
if(ex.InnerException != null)
{
log.LogInformation(ex.InnerException.Message);
log.LogInformation(ex.InnerException.StackTrace);
}
}

return name != null
? (ActionResult)new OkObjectResult(Newtonsoft.Json.JsonConvert.SerializeObject(returnValue))
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}

public class MyDemoReturn
{
public string Timestamp { get; set; }
public string FileName { get; set; }
public string FileSize { get; set; }
public string ReturnCode { get; set; }

public string ReturnMessage { get; set; }
}
}
}

############################

My Web API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public string Get(int id)
{
string result = "value";
if(id.Equals(1))
{
System.Threading.Thread.Sleep(150000);
result = "150 seconds, you should not get it.";
}
else
{
System.Threading.Thread.Sleep(50000);
result = "50 seconds, you can see it.";
}

return result;
}

######################################

Function app timeout duration
The timeout duration of a function app is defined by the functionTimeout property in the host.json project file. The following table shows the default and maximum values in minutes for both plans and in both runtime versions:
More information: https://docs.microsoft.com/en-us/azure/azure-functions/functions-scale

HTH. By Jacky 2019-11-19

How to create a function app in Azure Portal without VS

Here is the steps that create a function app in Azure Portal without VS.

  1. New function

  2. HTTP Trigger

  3. Enter a Name.

  4. View files -> Add -> function.proj file -> copy my sample into it -> Save

Please copy below xml doc into function.proj

1
2
3
4
5
6
7
8
9
10
11
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Management.ResourceManager.Fluent" Version="1.27.2" />
<PackageReference Include="Microsoft.Azure.Management.Websites" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.29" />
</ItemGroup>
</Project>
  1. Click “run.csx” file -> copy my code and paste it -> Save -> Test it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#r "Newtonsoft.Json"

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Rest;
using System.Linq;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");

int capacity;

if (!string.IsNullOrEmpty(req.Query["capacity"]) && int.TryParse(req.Query["capacity"], out capacity))
{
log.LogInformation("You want to chagne the capacity to "+ capacity.ToString());
// Step1. How to: Use the portal to create an Azure AD application and service principal that can access resources
// https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal

// Step2. How to: Add app roles in your application and receive them in the token
// https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
// For security concerns, I only give the resource group => contributor
// and the service plan read permission

var subscriptionId = "Your Subscription Id";
var appId = "e6f6d231-Your-App-Id";
var secretKey = "2z]pj0vy2JRapjo:R@TJSoW1AmOCC=o8";
var tenantId = "72f988bf-Your Tenant-id";
var resourceGroup = "jackywebl1rg";
var servicePlanName = "ASP-jackywebl1rg-81ee";


// Step 3. change capacity
var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
ClientCredential clientCredential = new ClientCredential(appId, secretKey);
var tokenResponse = context.AcquireTokenAsync("https://management.azure.com/", clientCredential).Result;
var accessToken = tokenResponse.AccessToken;
TokenCredentials credential = new TokenCredentials(accessToken);
var webSiteManagementClient = new Microsoft.Azure.Management.WebSites.WebSiteManagementClient(credential);
webSiteManagementClient.SubscriptionId = subscriptionId;
var servicePlan = webSiteManagementClient.AppServicePlans.ListByResourceGroupWithHttpMessagesAsync(resourceGroup).Result.Body.Where(x => x.Name.Equals(servicePlanName)).FirstOrDefault();

var appServicePlanRequest = await webSiteManagementClient.AppServicePlans.ListByResourceGroupWithHttpMessagesAsync(resourceGroup);
appServicePlanRequest.Body.ToList().ForEach(x => log.LogInformation($">>>{x.Name}"));
var appServicePlan = appServicePlanRequest.Body.Where(x => x.Name.Equals(servicePlanName)).FirstOrDefault();
if (appServicePlan == null)
{
log.LogError("Could not find app service plan.");
}

//scale up/down
//servicePlan.Sku.Family = "P";
//servicePlan.Sku.Name = "P1v2";
//servicePlan.Sku.Size = "P1v2";
//servicePlan.Sku.Tier = "PremiumV2";
servicePlan.Sku.Capacity = capacity; // scale out: number of instances
var updateResult = webSiteManagementClient.AppServicePlans.CreateOrUpdateWithHttpMessagesAsync(resourceGroup, servicePlanName, servicePlan).Result;
log.LogInformation("Completed!!");
return (ActionResult)new OkObjectResult($"Hello, {capacity} {updateResult.Response.StatusCode}");
}
else
{
return new BadRequestObjectResult("Please pass a capacity on the query string or in the request body");
}

}

HTH. By Jacky 2019-11-18

How to scale out web app base on custom metrics

Scenario: How to scale out web app base on custom metrics. For example: request, response time and etc.

Currently, Web App Linux only CPU and memory are available.

Here is the end to end steps/screenshots:

**Step 1. Use the portal to create an Azure AD application and service principal that can access resources

  1. Click “App registration”

  2. “New registration”

  3. Enter a Name and click Register

  4. In Overview page, you can find the Application Id and Tenant Id.

  5. Create a client secret.

  1. Copy the client secret in Azure Portal

For more information: https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal  

**Step 2. Add app roles in your application and receive them in the token

  1. Go to your resource group and “Access Control” and “Add a role assignment”

  2. Select “Contributor” and select your app (Registered in Step1)

  3. Go to your App Service Plan

  4. Select “Reader” Role

  5. Publish your function app.

  6. Test Your Function App in Azure Portal

  7. Verify the Instance Count in Azure Portal

  8. Get the Function URI:

  1. Test it in browser.

  2. Verify it in Azure Portal

For more information: https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps

**Step 3. Setup an alert to change capacity

  1. Create an alert and choose a custom metric

  2. Create a action group and action type “Webhook”, the function URI is from step2.8

  3. Save alert

  4. Simulate a User Load and verify the Instance Count:

  1. Instance Count changed to 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Rest;
using System.Linq;
 
namespace ScaleWebAppFunction
{
    public static class Function1
    {
        [FunctionName("ScaleWebApp")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
 
            int capacity;
 
            if (!string.IsNullOrEmpty(req.Query["capacity"]) && int.TryParse(req.Query["capacity"], out capacity))
            {
                log.LogInformation("You want to chagne the capacity to "+ capacity.ToString());
                // Step1. How to: Use the portal to create an Azure AD application and service principal that can access resources
                //      https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal
 
                // Step2. How to: Add app roles in your application and receive them in the token
                //      https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
                // For security concerns, I only give the resource group => contributor
                //                          and the service plan read permission
 
                var subscriptionId = "Your subscription id";
                var appId = "e6f6d231-Your-App_id";
                var secretKey = "2z]pj0vy2JRapjo:R@TJSoW1AmOCC=o8";
                var tenantId = "72f988bf-Your-Tenant-Id";
                var resourceGroup = "jackywebl1rg";
                var servicePlanName = "ASP-jackywebl1rg-81ee";
 
 
                // Step 3. change capacity
                var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
                ClientCredential clientCredential = new ClientCredential(appId, secretKey);
                var tokenResponse = context.AcquireTokenAsync("https://management.azure.com/", clientCredential).Result;
                var accessToken = tokenResponse.AccessToken;
                TokenCredentials credential = new TokenCredentials(accessToken);
                var webSiteManagementClient = new Microsoft.Azure.Management.WebSites.WebSiteManagementClient(credential);
                webSiteManagementClient.SubscriptionId = subscriptionId;
                var servicePlan = webSiteManagementClient.AppServicePlans.ListByResourceGroupWithHttpMessagesAsync(resourceGroup).Result.Body.Where(x => x.Name.Equals(servicePlanName)).FirstOrDefault();
 
                var appServicePlanRequest = await webSiteManagementClient.AppServicePlans.ListByResourceGroupWithHttpMessagesAsync(resourceGroup);
                appServicePlanRequest.Body.ToList().ForEach(x => log.LogInformation($">>>{x.Name}"));
                var appServicePlan = appServicePlanRequest.Body.Where(x => x.Name.Equals(servicePlanName)).FirstOrDefault();
                if (appServicePlan == null)
                {
                    log.LogError("Could not find app service plan.");
                }
 
                //scale up/down
                //servicePlan.Sku.Family = "P";
                //servicePlan.Sku.Name = "P1v2";
                //servicePlan.Sku.Size = "P1v2";
                //servicePlan.Sku.Tier = "PremiumV2";
                servicePlan.Sku.Capacity = capacity; // scale out: number of instances
                var updateResult = webSiteManagementClient.AppServicePlans.CreateOrUpdateWithHttpMessagesAsync(resourceGroup, servicePlanName, servicePlan).Result;
                log.LogInformation("Completed!!");
                return (ActionResult)new OkObjectResult($"Hello, {capacity} {updateResult.Response.StatusCode}");
            }
            else
            {
                return new BadRequestObjectResult("Please pass a capacity on the query string or in the request body");
            }
           
        }
    }
}

HTH. 2019-11-18 By Jacky