From 0150c3c49d030c45e51cd316a081daf3406f9b6d Mon Sep 17 00:00:00 2001 From: Sergey Plotnikov Date: Mon, 5 Apr 2021 17:20:11 -0400 Subject: [PATCH] Changed cosmosDb connection string added resource templates --- Infrastructure/az-resourse-management.ps1 | 91 +++++++++++++++++++ src/AzureFunctions/AzureFunctions.csproj | 16 +++- .../Cosmos/BaseCosmosCommandHandler.cs | 16 ++-- .../Cosmos/Init/InitCosmosCommandHandler.cs | 5 + .../Configuration/Options/CosmosOptions.cs | 3 +- .../Functions/ActionFunctions.cs | 8 -- src/AzureFunctions/Functions/TestFunctions.cs | 36 +++++--- .../SimpleFuncs - Zip Deploy.pubxml | 18 ++++ .../appInsights1.arm.json | 67 ++++++++++++++ .../storage1.arm.json | 70 ++++++++++++++ ...Dependencies.SimpleFuncs - Zip Deploy.json | 15 +++ .../Properties/serviceDependencies.json | 7 +- src/AzureFunctions/appsettings.json | 22 ++--- 13 files changed, 323 insertions(+), 51 deletions(-) create mode 100644 Infrastructure/az-resourse-management.ps1 create mode 100644 src/AzureFunctions/Properties/PublishProfiles/SimpleFuncs - Zip Deploy.pubxml create mode 100644 src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/appInsights1.arm.json create mode 100644 src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/storage1.arm.json create mode 100644 src/AzureFunctions/Properties/serviceDependencies.SimpleFuncs - Zip Deploy.json diff --git a/Infrastructure/az-resourse-management.ps1 b/Infrastructure/az-resourse-management.ps1 new file mode 100644 index 0000000..6bf5fda --- /dev/null +++ b/Infrastructure/az-resourse-management.ps1 @@ -0,0 +1,91 @@ +#az login + +$resourceGroupName = "FuncsResourceGroup" +$location = "eastus" +$storageAccountName = "funcsstorageaccount" +$azureFunctionsPremiumPlanName = "FuncsPremiumPlan" +$appName = "SimpleFuncs" +#$appInsightsName = $appName + "Insight"; + +az group create ` + -l $location ` + -n $resourceGroupName + +az storage account create ` + -g $resourceGroupName ` + -n $storageAccountName ` + -l $location ` + --sku Standard_LRS + +az functionapp plan create ` + --resource-group $resourceGroupName ` + --name $azureFunctionsPremiumPlanName ` + --location $location ` + --number-of-workers 1 ` + --sku EP1 ` + --is-linux + +az functionapp create --name $appName ` + --storage-account $storageAccountName ` + --resource-group $resourceGroupName ` + --plan $azureFunctionsPremiumPlanName ` + --runtime dotnet ` + --functions-version 3 + +az functionapp deployment slot create ` + --resource-group $resourceGroupName ` + --name $appName ` + -slot staging + +az cosmosdb create ` + --name $storageAccountName ` + --resource $resourceGroupName ` + --default-consistency-level Eventual ` + --locations regionName=$location failoverPriority=0 isZoneRedundant=False + + +$appInsightsInstrumentationKey="$(az resource show -g $resourceGroupName -n $appName --resource-type 'Microsoft.Insights/components' --query properties.InstrumentationKey)" +# this instrumentation key comes with quotes, which we need to remove +$appInsightsInstrumentationKey = $appInsightsInstrumentationKey -replace '"', "" + +#storage connection string +$storageConnectionString = "$(az storage account show-connection-string ` + --resource-group $($resourceGroupName) ` + --name $($storageAccountName) ` + --query connectionString ` + --output tsv)" + +$cosmosConnectionString = "$(az cosmosdb keys list ` + --type connection-strings ` + --name $storageAccountName ` + --resource-group $resourceGroupName ` + --query connectionStrings[0].connectionString)" + + +$settings = @( + "APPINSIGHTS_INSTRUMENTATIONKEY=$appInsightsInstrumentationKey", + "AzureWebJobsStorage=$storageConnectionString", + "WEBSITE_RUN_FROM_PACKAGE=1", + "Cosmos:ConnectionString=$cosmosConnectionString", + "Cosmos:DatabaseId=FunctionsData", + "Cosmos:ContainerId=Primes" +) + +az webapp config appsettings set ` + --resource-group $resourceGroupName ` + --name $appName ` + --settings $settings ` + +//---------- + +// CI-CD + +az functionapp deployment container config --enable-cd --query CI_CD_URL ` + --output tsv ` + --name $appName ` + --resource-group $resourceGroupName + +az functionapp deployment container show-cd-url --name $appName ` + --resource-group $resourceGroupName + +func azure functionapp fetch-app-settings $appName \ No newline at end of file diff --git a/src/AzureFunctions/AzureFunctions.csproj b/src/AzureFunctions/AzureFunctions.csproj index f001946..ac57ec8 100644 --- a/src/AzureFunctions/AzureFunctions.csproj +++ b/src/AzureFunctions/AzureFunctions.csproj @@ -5,16 +5,22 @@ 248c2688-a5ac-4c7c-862a-1459e865012e + + + + + PreserveNewest + + + + - - + + - - Always - PreserveNewest diff --git a/src/AzureFunctions/Commands/Cosmos/BaseCosmosCommandHandler.cs b/src/AzureFunctions/Commands/Cosmos/BaseCosmosCommandHandler.cs index 674d85f..0493d26 100644 --- a/src/AzureFunctions/Commands/Cosmos/BaseCosmosCommandHandler.cs +++ b/src/AzureFunctions/Commands/Cosmos/BaseCosmosCommandHandler.cs @@ -7,8 +7,8 @@ namespace AzureFunctions.Commands.Cosmos { - public abstract class BaseCosmosCommandHandler: ICommandHandler - where TCommand: ICommand + public abstract class BaseCosmosCommandHandler : ICommandHandler + where TCommand : ICommand { public readonly CosmosOptions CosmosOptions; public CosmosClient CosmosClient { private set; get; } @@ -18,12 +18,12 @@ public abstract class BaseCosmosCommandHandler: ICommandHandl protected BaseCosmosCommandHandler(IOptions cosmosOptions) { CosmosOptions = cosmosOptions.Value; - CosmosClient = - new CosmosClient(CosmosOptions.EndpointUri, CosmosOptions.PrimaryKey, - new CosmosClientOptions - { - ApplicationName = Global.ApplicationName - }); + CosmosClient = !string.IsNullOrWhiteSpace(CosmosOptions.ConnectionString) + ? new CosmosClient(CosmosOptions.ConnectionString, new CosmosClientOptions + { + ApplicationName = Global.ApplicationName + }) + : null; } public abstract Task Execute(TCommand command); diff --git a/src/AzureFunctions/Commands/Cosmos/Init/InitCosmosCommandHandler.cs b/src/AzureFunctions/Commands/Cosmos/Init/InitCosmosCommandHandler.cs index 2eef1aa..fd6a532 100644 --- a/src/AzureFunctions/Commands/Cosmos/Init/InitCosmosCommandHandler.cs +++ b/src/AzureFunctions/Commands/Cosmos/Init/InitCosmosCommandHandler.cs @@ -16,6 +16,11 @@ public override async Task Execute(InitCosmosCommand command) { try { + if (CosmosClient == null) + { + return false; + } + Database database = await CosmosClient.CreateDatabaseIfNotExistsAsync(CosmosOptions.DatabaseId, 400); Container container = diff --git a/src/AzureFunctions/Configuration/Options/CosmosOptions.cs b/src/AzureFunctions/Configuration/Options/CosmosOptions.cs index 0684474..9fe7bd3 100644 --- a/src/AzureFunctions/Configuration/Options/CosmosOptions.cs +++ b/src/AzureFunctions/Configuration/Options/CosmosOptions.cs @@ -2,8 +2,7 @@ { public class CosmosOptions { - public string EndpointUri { get; set; } - public string PrimaryKey { get; set; } + public string ConnectionString { get; set; } public string DatabaseId { get; set; } public string ContainerId { get; set; } } diff --git a/src/AzureFunctions/Functions/ActionFunctions.cs b/src/AzureFunctions/Functions/ActionFunctions.cs index 499387c..d6a5234 100644 --- a/src/AzureFunctions/Functions/ActionFunctions.cs +++ b/src/AzureFunctions/Functions/ActionFunctions.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Net; using System.Threading.Tasks; using AzureFunctions.Commands.IsItPrime; using AzureFunctions.Infrastructure.Commands; @@ -7,10 +6,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; using Newtonsoft.Json; namespace AzureFunctions.Functions @@ -26,10 +22,6 @@ public ActionFunctions(ICommandHandler isItPrime) [FunctionName(nameof(IsItPrime))] - [OpenApiOperation(operationId: nameof(IsItPrime), tags: new[] { "math" })] - [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)] - [OpenApiParameter(name: "number", In = ParameterLocation.Query, Required = true, Type = typeof(long), Description = "The **Number** parameter")] - [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(long), Description = "The OK response")] public async Task IsItPrime([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "math/prime")] HttpRequest req, ILogger log) { diff --git a/src/AzureFunctions/Functions/TestFunctions.cs b/src/AzureFunctions/Functions/TestFunctions.cs index 43b0c4d..cd91554 100644 --- a/src/AzureFunctions/Functions/TestFunctions.cs +++ b/src/AzureFunctions/Functions/TestFunctions.cs @@ -1,17 +1,13 @@ using System; using System.IO; -using System.Net; using System.Threading.Tasks; using AzureFunctions.Configuration.Options; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; using Newtonsoft.Json; namespace AzureFunctions.Functions @@ -19,16 +15,16 @@ namespace AzureFunctions.Functions public class TestFunctions { private readonly ConfigurationItems _configurationItems; + private readonly CosmosOptions _cosmos; - public TestFunctions(IOptions configurationItems) + public TestFunctions(IOptions configurationItems, + IOptions cosmos) { + _cosmos = cosmos.Value; _configurationItems = configurationItems.Value; } [FunctionName(nameof(Configuration))] - [OpenApiOperation(operationId: nameof(Configuration), tags: new[] { "example" })] - [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)] - [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")] public async Task Configuration([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "example/config")] HttpRequest req, ILogger log) { @@ -38,17 +34,29 @@ public async Task Configuration([HttpTrigger(AuthorizationLevel.A var commonValue = _configurationItems.CommonValue; var secretValue = _configurationItems.SecretValue; + var result = new + { + ConfigurationItems = new + { + LocalSettingValue = localSettings, + CommonValue = commonValue, + SecretValue = secretValue + }, + + Cosmos = new + { + ConnectionString = _cosmos.ConnectionString, + DatabaseId = _cosmos.DatabaseId, + ContainerId = _cosmos.ContainerId + } + }; + return - new OkObjectResult($"Local Value : '{localSettings}' | Global Value : '{commonValue}' | Secret Value : '{secretValue}'"); + new OkObjectResult(result); } [FunctionName(nameof(SayHello))] - [OpenApiOperation(operationId: nameof(SayHello), tags: new[] { "example" })] - [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)] - [OpenApiParameter(name: "name", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **Name** parameter")] - [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")] - public async Task SayHello([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "example/hello")] HttpRequest req, ILogger log) { diff --git a/src/AzureFunctions/Properties/PublishProfiles/SimpleFuncs - Zip Deploy.pubxml b/src/AzureFunctions/Properties/PublishProfiles/SimpleFuncs - Zip Deploy.pubxml new file mode 100644 index 0000000..5532061 --- /dev/null +++ b/src/AzureFunctions/Properties/PublishProfiles/SimpleFuncs - Zip Deploy.pubxml @@ -0,0 +1,18 @@ + + + + + ZipDeploy + AzureWebSite + Release + Any CPU + http://simplefuncs.azurewebsites.net + False + /subscriptions/0edc09bb-e123-4bb8-8f5a-9b382654704a/resourceGroups/FuncsResourceGroup/providers/Microsoft.Web/sites/SimpleFuncs + $SimpleFuncs + <_SavePWD>True + https://simplefuncs.scm.azurewebsites.net/ + + \ No newline at end of file diff --git a/src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/appInsights1.arm.json b/src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/appInsights1.arm.json new file mode 100644 index 0000000..72f7c0e --- /dev/null +++ b/src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/appInsights1.arm.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceGroupName": { + "type": "string", + "defaultValue": "FuncsResourceGroup", + "metadata": { + "_parameterType": "resourceGroup", + "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." + } + }, + "resourceGroupLocation": { + "type": "string", + "defaultValue": "eastus", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource group. Resource groups could have different location than resources." + } + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[parameters('resourceGroupLocation')]", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('resourceGroupLocation')]", + "apiVersion": "2019-10-01" + }, + { + "type": "Microsoft.Resources/deployments", + "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat('SimpleFuncs', subscription().subscriptionId)))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2019-10-01", + "dependsOn": [ + "[parameters('resourceGroupName')]" + ], + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "name": "SimpleFuncs", + "type": "microsoft.insights/components", + "location": "[parameters('resourceLocation')]", + "kind": "web", + "properties": {}, + "apiVersion": "2015-05-01" + } + ] + } + } + } + ], + "metadata": { + "_dependencyType": "appInsights.azure" + } +} \ No newline at end of file diff --git a/src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/storage1.arm.json b/src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/storage1.arm.json new file mode 100644 index 0000000..b769ef2 --- /dev/null +++ b/src/AzureFunctions/Properties/ServiceDependencies/SimpleFuncs - Zip Deploy/storage1.arm.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceGroupName": { + "type": "string", + "defaultValue": "FuncsResourceGroup", + "metadata": { + "_parameterType": "resourceGroup", + "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." + } + }, + "resourceGroupLocation": { + "type": "string", + "defaultValue": "eastus", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource group. Resource groups could have different location than resources." + } + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[parameters('resourceGroupLocation')]", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('resourceGroupLocation')]", + "apiVersion": "2019-10-01" + }, + { + "type": "Microsoft.Resources/deployments", + "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat('funcsstorageaccount', subscription().subscriptionId)))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2019-10-01", + "dependsOn": [ + "[parameters('resourceGroupName')]" + ], + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "kind": "StorageV2", + "name": "funcsstorageaccount", + "type": "Microsoft.Storage/storageAccounts", + "location": "[parameters('resourceLocation')]", + "apiVersion": "2017-10-01" + } + ] + } + } + } + ], + "metadata": { + "_dependencyType": "storage.azure" + } +} \ No newline at end of file diff --git a/src/AzureFunctions/Properties/serviceDependencies.SimpleFuncs - Zip Deploy.json b/src/AzureFunctions/Properties/serviceDependencies.SimpleFuncs - Zip Deploy.json new file mode 100644 index 0000000..e4d7ec5 --- /dev/null +++ b/src/AzureFunctions/Properties/serviceDependencies.SimpleFuncs - Zip Deploy.json @@ -0,0 +1,15 @@ +{ + "dependencies": { + "storage1": { + "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.Storage/storageAccounts/funcsstorageaccount", + "type": "storage.azure", + "connectionId": "AzureWebJobsStorage" + }, + "appInsights1": { + "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/microsoft.insights/components/SimpleFuncs", + "type": "appInsights.azure", + "connectionId": "APPINSIGHTS_CONNECTIONSTRING", + "secretStore": "AzureAppSettings" + } + } +} \ No newline at end of file diff --git a/src/AzureFunctions/Properties/serviceDependencies.json b/src/AzureFunctions/Properties/serviceDependencies.json index df4dcc9..41441f6 100644 --- a/src/AzureFunctions/Properties/serviceDependencies.json +++ b/src/AzureFunctions/Properties/serviceDependencies.json @@ -1,11 +1,12 @@ { "dependencies": { - "appInsights1": { - "type": "appInsights" - }, "storage1": { "type": "storage", "connectionId": "AzureWebJobsStorage" + }, + "appInsights1": { + "type": "appInsights", + "connectionId": "APPINSIGHTS_CONNECTIONSTRING" } } } \ No newline at end of file diff --git a/src/AzureFunctions/appsettings.json b/src/AzureFunctions/appsettings.json index 36dfe9e..ceecfa3 100644 --- a/src/AzureFunctions/appsettings.json +++ b/src/AzureFunctions/appsettings.json @@ -1,13 +1,13 @@ { - "ConfigurationItems": { - "CommonValue": "Setting From appsettings.json", - "SecretValue": "Do not save secret values in appsettings" - }, + "ConfigurationItems": { + "CommonValue": "Setting From appsettings.json", + "SecretValue": "Do not save secret values in appsettings" + }, - "Cosmos": { - "EndpointUri": "", - "PrimaryKey": "", - "DatabaseId": "FunctionsData", - "ContainerId": "Primes" - } -} \ No newline at end of file + "Cosmos": { + "EndpointUri": "", + "PrimaryKey": "", + "DatabaseId": "FunctionsData", + "ContainerId": "Primes" + } +}