{"id":413,"date":"2020-08-25T09:15:32","date_gmt":"2020-08-25T16:15:32","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sdk\/?p=413"},"modified":"2020-08-25T09:15:32","modified_gmt":"2020-08-25T16:15:32","slug":"secretless-azure-functions-dev-with-the-new-azure-identity-libraries","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sdk\/secretless-azure-functions-dev-with-the-new-azure-identity-libraries\/","title":{"rendered":"Secretless Azure Functions dev with the new Azure Identity Libraries"},"content":{"rendered":"<p><a href=\"https:\/\/docs.microsoft.com\/azure\/azure-functions\/functions-overview\">Azure Functions<\/a> is a particularly versatile and powerful service in Azure that allows developers to quickly deploy and run code in production. It provides great scalability with minimal upfront cost (both in terms of money and technical effort). And because it is so easy, we want to ensure that our Functions operate securely, whether we need to pull data from a storage account, communicate with another API or work with SQL Server.<\/p>\n<p>In this blog, we\u2019ll examine how to build an Azure Function that uses the latest <a href=\"https:\/\/aka.ms\/azsdk\">Azure SDKs<\/a> to communicate with Azure Storage securely both during local development and in production by leveraging the Azure.Identity library and Managed Identities.<\/p>\n<h2>Prerequisites<\/h2>\n<p>You\u2019ll need to have the following available:<\/p>\n<ul>\n<li>Azure Subscription (<a href=\"https:\/\/azure.microsoft.com\/free\">get one for free<\/a>)<\/li>\n<li><a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet-core\/3.1\">.NET Core 3.1<\/a><\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/azure-functions-core-tools\">Azure Functions Core Tools (CLI)<\/a><\/li>\n<li><a href=\"https:\/\/code.visualstudio.com\/Download\">Visual Studio Code<\/a><\/li>\n<li><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-azuretools.vscode-azurefunctions\">Azure Functions extension<\/a> for VS Code<\/li>\n<\/ul>\n<h2>Getting started with Azure Functions<\/h2>\n<p>There are many ways to work with Azure Functions locally, especially if you\u2019re a .NET developer. You can use Visual Studio, Visual Studio Code, Visual Studio for Mac, or your favorite text editor with the Azure Functions Core Tools. For this blog, we\u2019ll start by creating a new .NET Azure Function with an HTTP Trigger using the CLI. Open your favorite terminal and type:<\/p>\n<pre><code>func init &lt;YourProjectName&gt;\n<\/code><\/pre>\n<p>Select <code>dotnet<\/code> and press <strong>Enter<\/strong>:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-func-init.png\" alt=\"func init\" \/><\/p>\n<p>This has created the Function app but we don\u2019t have an actual Function yet. Therefore, we need to use the CLI to add some code. On the terminal, type:<\/p>\n<pre><code>func new\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-func-new.png\" alt=\"func init\" \/><\/p>\n<p>We can now use your favorite IDE to start working with the code. In this case we\u2019ll use VS Code so we can type <code>code .<\/code> in the current directory in the CLI to open the project in VS Code.<\/p>\n<h2>Create the Azure Managed Identity<\/h2>\n<p><a href=\"https:\/\/docs.microsoft.com\/azure\/active-directory\/managed-identities-azure-resources\/overview\">Azure Managed Identities<\/a> allow our resources to communicate with one another without the need to configure connection strings or API keys. In this instance, our Azure Function needs to be able to retrieve data from an Azure Storage account. Traditionally, this would involve either the use of a storage name and key or a <a href=\"https:\/\/docs.microsoft.com\/rest\/api\/storageservices\/delegate-access-with-shared-access-signature\">SAS<\/a>. However, they both need to be stored somewhere and this can potentially compromise the security of our solution. To solve this, we can use Manage Identities which are \u201cservice accounts\u201d that are backed by Azure Active Directory and are provisioned to allow communication between services using the least privilege principle. There are two types of Managed Identities:<\/p>\n<ul>\n<li><strong>System-assigned identities<\/strong> are tied to the service they are provisioned and have the same lifecycle as the service they belong to. There is a strict one-to-one mapping. So, an Azure Function app will have a system-assigned Managed Identity and as soon as the app is deleted, the Manage Identity is deleted with it.<\/li>\n<li><strong>User-assigned<\/strong>. These managed Identities are created by the user and can span multiple services. A resource can also have multiple user-assigned identities defined. They are separate resources with their own lifecycle. <\/li>\n<\/ul>\n<p>To create a new Managed Identity we can use the Azure CLI, PowerShell or the portal. Let\u2019s use the Portal. In the Azure Portal we can search for Managed Identity using the global search. If you\u2019re not using global search yet, you should as you\u2019re missing out on a big productivity trick.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-azure-search.png\" alt=\"azure search\" \/><\/p>\n<p>Open the resource and create a new managed identity by clicking on the <strong>Add<\/strong> button.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-managed-identities-create.png\" alt=\"Create Managed Identity\" \/><\/p>\n<p>Give it a meaningful name, select the right subscription, add the right resource group and location, and click the <strong>Create<\/strong> button.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-managed-identity-add.png\" alt=\"Managed Identity settings\" \/><\/p>\n<p>Make a note of the <strong>Client ID<\/strong> as we will need it later. Next, we need to create the Function app that will host our code. In the Azure Portal, search for Azure Functions, open the resource and create a new Function app using the <strong>Add<\/strong> button:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-function-app-add.png\" alt=\"Create New Function\" \/><\/p>\n<p>Choose the appropriate values for &#8211; Subscription &#8211; Resource group &#8211; Function app name &#8211; should be globally unique &#8211; Runtime stack \u2013 should be .NET Core 3.1 &#8211; Region<\/p>\n<p>Press <strong>Review + Create<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-function-app-add.png\" alt=\"Function settings\" \/><\/p>\n<p>Once the Function app is provisioned, we need to add our user-defined managed identity via the <strong>Identity<\/strong> tab.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-func-add-identity.png\" alt=\"Function Identity add new\" \/><\/p>\n<p>Open a new browser window, select the right subscription and select the user-defined managed identity we created in the previous step. Click on the correct identity and press <strong>Add<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-func-identity-configure.png\" alt=\"Function Identity configure\" \/><\/p>\n<p>This means that any code running in the Function app can leverage the managed identity to communicate with other services. However, there is one more step left to ensure that our Function can retrieve data from our Storage account. Search for the Storage account you want to work with and open the <strong>Access Control IAM<\/strong> tab. There you need to add the same user-defined managed identity with the appropriate permissions. Select the <code>Storage Blob Data Reader<\/code> role, find the subscription and managed identity you want to add and click <strong>Save<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-storage-iam.png\" alt=\"Storage Access Management\" \/><\/p>\n<p>Back in our Azure Function, we need to add two new Application settings. One for the storage account name and one for the managed identity (client) id. In the Azure Function app, open the Configuration tab and add the two new settings using the <strong>New application setting<\/strong> button as per below:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-msi-add-to-func.png\" alt=\"Azure Functions app settings\" \/><\/p>\n<h2>Let\u2019s write some code!<\/h2>\n<p>Back in our VS Code, we can now edit our Function code to retrieve the Storage containers and return a list of names in the HTTP response. To be able to authenticate and interact with Azure Storage, we will make use of the latest <a href=\"https:\/\/aka.ms\/azsdk\">Azure SDKs for .NET<\/a>. The latest SDKs make this a breeze and provide you with the tools to work both locally and in production without the need to change the code.<\/p>\n<p>For our example, we need to add 2 NuGet packages:<\/p>\n<ul>\n<li><code>Azure.Identity<\/code> that will do the heavy lifting on acquiring the access token for us<\/li>\n<li><code>Azure.Storage.Blobs<\/code> to work with the Storage account.<\/li>\n<\/ul>\n<p>Open the <code>*.csproj<\/code> and add the following two package references:<\/p>\n<blockquote>\n<p>Check the <a href=\"https:\/\/aka.ms\/azsdk\/releases\">Azure SDK Release page<\/a> for the latest version numbers.<\/p>\n<\/blockquote>\n<pre><code>&lt;PackageReference Include=\"Azure.Storage.Blobs\" Version=\"12.5.1\" \/&gt;\n&lt;PackageReference Include=\"Azure.Identity\" Version=\"1.2.1\" \/&gt;\n<\/code><\/pre>\n<p>Press <strong>Save<\/strong> and restore the unresolved dependencies. Now we can add the necessary code. Open the <code>GetStorageContainers.cs<\/code> file and update the code with the following:<\/p>\n<pre><code>using System;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Mvc;\nusing Microsoft.Azure.WebJobs;\nusing Microsoft.Azure.WebJobs.Extensions.Http;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.Logging;\nusing Azure.Identity;\nusing Azure.Storage.Blobs;\nusing Microsoft.Extensions.Configuration;\nusing System.IO;\nusing System.Collections.Generic;\n\nnamespace Company.Function\n{\n    public static class GetStorageContainers\n    {\n        [FunctionName(\"GetStorageContainers\")]\n        public static async Task&lt;IActionResult&gt; Run(\n            [HttpTrigger(AuthorizationLevel.Anonymous, \"get\", \"post\", Route = null)] HttpRequest req,\n            ILogger log)\n        {\n            var config = new ConfigurationBuilder()\n                .SetBasePath(Directory.GetCurrentDirectory())\n                .AddJsonFile(\"local.settings.json\", optional: true, reloadOnChange: true)\n                .AddEnvironmentVariables()\n                .Build();\n\n            var existingContainers = new List&lt;string&gt;();\n\n            var credential = new ChainedTokenCredential(\n                new ManagedIdentityCredential(string.IsNullOrEmpty(config[\"UserAssignedIdentity\"])\n                    ? null \n                    : config[\"UserAssignedIdentity\"]),\n                new AzureCliCredential());\n            try\n            {\n                var blobServiceClient = new BlobServiceClient(new Uri(config[\"StorageAccountName\"]), credential);\n                var containers = blobServiceClient.GetBlobContainers();\n                foreach(var container in containers)\n                {\n                    existingContainers.Add(container.Name);\n                }\n            }\n            catch (Exception e)\n            {\n                return new BadRequestObjectResult(e);\n            }\n\n            return new OkObjectResult(existingContainers);\n        }\n    }\n}\n<\/code><\/pre>\n<p>You\u2019ll notice that there is not much code for working with Azure Storage. First, we create <code>ChainedTokenCredential<\/code> object that can iterate through our defined TokenCredential options. For local development, we rely on the <code>AzureCliCredential<\/code>. For this to work, we need to have the Azure CLI installed locally and be signed in (open the Azure CLI and type <code>az login<\/code>). On the other hand, in production, we will be using the managed identity. The reason why we add logic in the <code>ManagedIdentityCredential<\/code> is because we may want to use the system-assigned instead of the user-defined one and this way we don\u2019t have to change the code. We only need to remove the <code>UserAssignedIdentity<\/code> application setting from the Function app and the code will work as expected.<\/p>\n<pre><code>var credential = new ChainedTokenCredential(\n                 new ManagedIdentityCredential(string.IsNullOrEmpty(config[\"UserAssignedIdentity\"])\n                    ? null \n                    : config[\"UserAssignedIdentity\"]),\n                 new AzureCliCredential());\n<\/code><\/pre>\n<p>The code to retrieve the Storage containers is only 2 lines:<\/p>\n<pre><code>var blobServiceClient = new BlobServiceClient(new Uri(config[\"StorageAccountName\"]), credential);\nvar containers = blobServiceClient.GetBlobContainers();\n<\/code><\/pre>\n<p>Finally, since we are running locally and we don\u2019t have access to the Function app settings, we need to add a new property in the <code>local.settings.json<\/code>. This file is only used during local development but there is a way to sync the settings, if desired, either during the deploy via VS Code or through the Azure Function Core Tools (CLI). In the local.setting.json, add the new property as per the example below:<\/p>\n<pre><code>\"StorageAccountName\": \"https:\/\/azfuncwithmsistorage.blob.core.windows.net\"\n<\/code><\/pre>\n<p>We can now run and test our Function locally. In VS Code, switch to the <strong>Debug<\/strong> tab, then choose the <strong>Attach to .NET Functions<\/strong> option and press <strong>Run<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-debug-function.png\" alt=\"Function debugging\" \/><\/p>\n<p>We can test the Function in the browser or a tool like Postman.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-local-results.png\" alt=\"Function response\" \/><\/p>\n<p>We can now deploy our Function to Azure (production environment) and test it there as well. VS Code is excellent for this so make sure to leverage the Azure Functions extension. Alternatively, you can use the Core Tools or, even better yet, Azure DevOps\/GitHub Actions to deploy the code. Once our Function code is deployed, we can navigate to the Azure portal and test that everything\u2019s working as expected. In the Azure Function app, we select the function that we just deployed and choose the <strong>Code+Test<\/strong> tab. Our Function is read-only as the code was pre-compiled but we can test it by selecting the <strong>Test\/Run<\/strong> menu option and hitting the <strong>Run<\/strong> button.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2020\/08\/20200819-run-in-prod.png\" alt=\"Test Function in prod\" \/><\/p>\n<h2>Summary<\/h2>\n<p>Working with Azure resources is now a lot more straight forward and consistent thanks to the great work that the Azure SDK team has done. The <code>Azure.Identity<\/code> library handles all our authN\/authZ needs and <strong>Managed Identities<\/strong> can help make our solutions much more secure by eliminating the need to store connection strings and API keys in plain text. And with RBAC and <strong>Azure Active Directory<\/strong> backing up this whole setup, developers can confidently and securely develop and run applications anywhere using their favorite tools.<\/p>\n<h2>Azure SDK Links<\/h2>\n<ul>\n<li>Azure SDK Website: <a href=\"https:\/\/aka.ms\/azsdk\">aka.ms\/azsdk<\/a><\/li>\n<li>Azure SDK Intro (3 minute video): <a href=\"https:\/\/aka.ms\/azsdk\/intro\">aka.ms\/azsdk\/intro<\/a><\/li>\n<li>Azure SDK Intro Deck (PowerPoint deck): <a href=\"https:\/\/aka.ms\/azsdk\/intro\/deck\">aka.ms\/azsdk\/intro\/deck<\/a><\/li>\n<li>Azure SDK Releases: <a href=\"https:\/\/aka.ms\/azsdk\/releases\">aka.ms\/azsdk\/releases<\/a><\/li>\n<li>Azure SDK Blog: <a href=\"https:\/\/aka.ms\/azsdk\/blog\">aka.ms\/azsdk\/blog<\/a><\/li>\n<li>Azure SDK Twitter: <a href=\"https:\/\/twitter.com\/AzureSDK\">twitter.com\/AzureSDK<\/a><\/li>\n<li>Azure SDK Design Guidelines: <a href=\"https:\/\/aka.ms\/azsdk\/guide\">aka.ms\/azsdk\/guide<\/a><\/li>\n<li>Azure SDKs &amp; Tools: <a href=\"https:\/\/azure.microsoft.com\/downloads\">azure.microsoft.com\/downloads<\/a><\/li>\n<li>Azure SDK Central Repository: <a href=\"https:\/\/github.com\/azure\/azure-sdk#azure-sdk\">github.com\/azure\/azure-sdk<\/a><\/li>\n<li>Azure SDK for .NET: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-net\">github.com\/azure\/azure-sdk-for-net<\/a><\/li>\n<li>Azure SDK for Java: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-java\">github.com\/azure\/azure-sdk-for-java<\/a><\/li>\n<li>Azure SDK for Python: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-python\">github.com\/azure\/azure-sdk-for-python<\/a><\/li>\n<li>Azure SDK for JavaScript\/TypeScript: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-js\">github.com\/azure\/azure-sdk-for-js<\/a><\/li>\n<li>Azure SDK for Android: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-android\">github.com\/Azure\/azure-sdk-for-android<\/a><\/li>\n<li>Azure SDK for iOS: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-ios\">github.com\/Azure\/azure-sdk-for-ios<\/a><\/li>\n<li>Azure SDK for Go: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-go\">github.com\/Azure\/azure-sdk-for-go<\/a><\/li>\n<li>Azure SDK for C: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-c\">github.com\/Azure\/azure-sdk-for-c<\/a><\/li>\n<li>Azure SDK for C++: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-cpp\">github.com\/Azure\/azure-sdk-for-cpp<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this post you&#8217;ll find out how to connect Azure Functions with Azure resources with the new Azure Identity library which eliminates the need to use secrets or connection strings and hence allows us to create more secure and robust solutions. Read on!<\/p>\n","protected":false},"author":845,"featured_media":396,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[701,736,737,735,705],"class_list":["post-413","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sdk","tag-net","tag-azuread","tag-managedidentity","tag-microsoftidentity","tag-sdk"],"acf":[],"blog_post_summary":"<p>In this post you&#8217;ll find out how to connect Azure Functions with Azure resources with the new Azure Identity library which eliminates the need to use secrets or connection strings and hence allows us to create more secure and robust solutions. Read on!<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/413","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/users\/845"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/comments?post=413"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/413\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media\/396"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media?parent=413"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/categories?post=413"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/tags?post=413"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}