{"id":1292,"date":"2020-09-16T20:35:57","date_gmt":"2020-09-17T03:35:57","guid":{"rendered":"http:\/\/blog.nillsf.com\/?p=1292"},"modified":"2020-09-16T20:36:06","modified_gmt":"2020-09-17T03:36:06","slug":"connect-azure-functions-securely-to-key-vault-using-vnet-integration-and-private-link","status":"publish","type":"post","link":"https:\/\/nillsf.com\/index.php\/2020\/09\/16\/connect-azure-functions-securely-to-key-vault-using-vnet-integration-and-private-link\/","title":{"rendered":"Connect Azure Functions securely to Key Vault using VNET integration and Private Link"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I did some work with a customer last week to integrate Functions with Key Vault without using the public IP of the Key Vault. This setup worked perfectly, although there were a couple of steps involved. To document those steps, I decided to write this post.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this post we&#8217;ll build a new Azure Function and a new Key Vault. We&#8217;ll integrate the Function in our VNET using VNET injection, we&#8217;ll integrate the Key Vault into our VNET using Private Link and then connect the two. There&#8217;s a little bit of extra configuration on the Function that we&#8217;ll have to do to make it use the DNS private Zone to get the right IP address for Key Vault.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For reference, here is some good documentation about what we&#8217;re setting up:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-create-vnet\">Azure Functions VNET integration.<\/a> (<em>important bit here is that this requires premium functions<\/em>). There&#8217;s two ways Azure Functions can integrate with a VNET:<ul><li><strong>VNET Integration<\/strong>: This is useful when the Function needs to connect to another resource in our through the VNET (like we&#8217;re doing with Key Vault). This is for connections FROM your function.<\/li><li><strong>Private Endpoints <\/strong>(preview right now): This is useful to protect your function and only make it available for resources in the network. This is for connections TO your function.<\/li><\/ul><\/li><li><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/key-vault\/general\/private-link-service#:~:text=Establish%20a%20private%20link%20connection%20to%20an%20existing,private%20endpoint.%20Select%20the%20%22Networking%22%20tab%20under%20Settings\">Azure Key Vault Private Link<\/a><\/li><li><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/azure-functions\/functions-networking-options#azure-dns-private-zones\">Azure Functions, use Private DNS Zones<\/a><\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">So, in terms of workflow this is what I&#8217;m planning to implement in this post:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Create Azure Function and Key Vault<\/li><li>Give managed identity to Azure Function.<\/li><li>Have function query public Key Vault to verify things work.<\/li><li>Integrate Key Vault using PrivateLink and function using VNET integration.<\/li><li>Configure Azure Function to use Private DNS Zone.<\/li><li>Have function query private Key Vault to verify things work<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">So, let&#8217;s get started!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create Key Vault and Azure Function.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In this section, we will create the function and the key vault. Let&#8217;s start with the function. Look for functions in the Azure search bar, and hit the create button. The first blade asks for some details. In this case, I&#8217;ll be running a Python based function.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"903\" src=\"\/wp-content\/uploads\/2020\/09\/image-29-1024x903.png\" alt=\"\" class=\"wp-image-1298\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-29-1024x903.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-29-300x265.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-29-768x677.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-29.png 1111w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Function creation blade. We&#8217;ll create python 3.8 in a new resource group.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we&#8217;ll need to create the hosting plan. The plan needs to be Premium to work with VNET integration. I&#8217;ll stick with the EP1 size, to run this in the cheapest way possible:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"981\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/09\/image-30-981x1024.png\" alt=\"\" class=\"wp-image-1299\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-30-981x1024.png 981w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-30-287x300.png 287w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-30-768x802.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-30.png 1094w\" sizes=\"auto, (max-width: 981px) 100vw, 981px\" \/><figcaption>Make sure you&#8217;re deploying this on a premium plan.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">I will enable application insights, to have access to logs in case I need them:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"486\" src=\"\/wp-content\/uploads\/2020\/09\/image-31-1024x486.png\" alt=\"\" class=\"wp-image-1300\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-31-1024x486.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-31-300x142.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-31-768x364.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-31.png 1128w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Enabling application insights. Just in case.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And finally, review and create (I&#8217;m not tagging for now)<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"577\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/09\/image-32-577x1024.png\" alt=\"\" class=\"wp-image-1301\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-32-577x1024.png 577w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-32-169x300.png 169w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-32-768x1363.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-32.png 773w\" sizes=\"auto, (max-width: 577px) 100vw, 577px\" \/><figcaption>Review and create.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">While this is running, let&#8217;s create our new Key Vault. We&#8217;ll create this in the same region and the same resource group. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"680\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/09\/image-33-680x1024.png\" alt=\"\" class=\"wp-image-1302\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-33-680x1024.png 680w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-33-199x300.png 199w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-33-768x1156.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-33-1020x1536.png 1020w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-33.png 1151w\" sizes=\"auto, (max-width: 680px) 100vw, 680px\" \/><figcaption>Create the Key Vault in the same RG and the same region. This is not mandatory, but will allow me to delete this easily later on. <\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next up is access policies. We won&#8217;t touch this now, we&#8217;ll touch this later on once our Function has a managed identity.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"453\" src=\"\/wp-content\/uploads\/2020\/09\/image-34-1024x453.png\" alt=\"\" class=\"wp-image-1303\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-34-1024x453.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-34-300x133.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-34-768x340.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-34-1536x680.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-34-2048x907.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>We&#8217;ll assign my own user access (the default), we&#8217;ll add the function later on.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For now, we&#8217;ll also not touch networking and default to the public networking. I first want to make sure public networking works (to ensure the code works), and then we&#8217;ll switch to Private Link.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"483\" src=\"\/wp-content\/uploads\/2020\/09\/image-35-1024x483.png\" alt=\"\" class=\"wp-image-1304\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-35-1024x483.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-35-300x142.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-35-768x362.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-35.png 1068w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>We&#8217;ll keep public networking <em>for now.<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Then we&#8217;ll review and create, and create the Key Vault.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"614\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/09\/image-36-614x1024.png\" alt=\"\" class=\"wp-image-1305\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-36-614x1024.png 614w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-36-180x300.png 180w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-36.png 717w\" sizes=\"auto, (max-width: 614px) 100vw, 614px\" \/><figcaption>Review and create.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now wait for all of this to create (should take about a minute to finish), and then we can move to the next step.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Give managed identity to Azure Function.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In this section we&#8217;ll do two things:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Give our Function a managed identity.<\/li><li>Give that managed identity permissions on Key Vault.<\/li><li>Create a secret in Key Vault.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let start with the first thing, giving the managed identity to Key Vault. To do this, open the function in the Azure portal, and in the left hand navigation look for identity. I&#8217;ll stick with System Assigned identity for now, but this also works for user assigned identities:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"343\" src=\"\/wp-content\/uploads\/2020\/09\/image-37-1024x343.png\" alt=\"\" class=\"wp-image-1306\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-37-1024x343.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-37-300x100.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-37-768x257.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-37-1536x514.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-37.png 1607w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Creating the system assigned managed identity.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Once the identity is created, the blade will change to this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"553\" src=\"\/wp-content\/uploads\/2020\/09\/image-38-1024x553.png\" alt=\"\" class=\"wp-image-1307\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-38-1024x553.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-38-300x162.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-38-768x415.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-38-1536x830.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-38.png 1598w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Grab the object ID once the managed identity is setup.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To make things easier, copy the object ID. We&#8217;ll need this in the next step, giving access to key vault.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To give access to key vault, open the key vault and open the access policies. Click the &#8220;+Add Access Policy&#8221; button here.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"522\" src=\"\/wp-content\/uploads\/2020\/09\/image-39-1024x522.png\" alt=\"\" class=\"wp-image-1308\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-39-1024x522.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-39-300x153.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-39-768x391.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-39.png 1391w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Add an access policy to key vault<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Here, I&#8217;ll give get and list secrets permissions to my managed identity. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"515\" src=\"\/wp-content\/uploads\/2020\/09\/image-40-1024x515.png\" alt=\"\" class=\"wp-image-1309\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-40-1024x515.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-40-300x151.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-40-768x386.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-40-1536x772.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-40.png 1621w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Give the managed identity permissions to secrets.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Once this is done, don&#8217;t forget to hit the save button on the access policies.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"773\" src=\"\/wp-content\/uploads\/2020\/09\/image-41-1024x773.png\" alt=\"\" class=\"wp-image-1310\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-41-1024x773.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-41-300x226.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-41-768x579.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-41.png 1421w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Don&#8217;t forget to save the access policy.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">With that out the way, let&#8217;s create a secret. Click on secrets on the left, and create a new secret.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"560\" height=\"744\" src=\"\/wp-content\/uploads\/2020\/09\/image-42.png\" alt=\"\" class=\"wp-image-1312\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-42.png 560w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-42-226x300.png 226w\" sizes=\"auto, (max-width: 560px) 100vw, 560px\" \/><figcaption>Creating a new secret.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And with the secret created, we can switch over to the function and try to get the secret through code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Have function query public Key Vault to verify things work.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;ll be doing the functions development locally in Visual Studio Code, and push the Function once it&#8217;s ready. To do this I run VSCode in WSL2, and installed the Functions extension. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"261\" src=\"\/wp-content\/uploads\/2020\/09\/image-43-1024x261.png\" alt=\"\" class=\"wp-image-1313\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-43-1024x261.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-43-300x77.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-43-768x196.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-43-1536x392.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-43.png 1555w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>The Azure Functions extension for VS Code. This is a fantastic tool! I loved working with it.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">First up, we&#8217;ll create a new function locally. To do this, open the command palette (CTRL-shift-P) and look for Azure Functions: create Function&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"919\" height=\"100\" src=\"\/wp-content\/uploads\/2020\/09\/image-44.png\" alt=\"\" class=\"wp-image-1314\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-44.png 919w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-44-300x33.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-44-768x84.png 768w\" sizes=\"auto, (max-width: 919px) 100vw, 919px\" \/><figcaption>Create a new function locally<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll then get a popup to create a new project, select yes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"207\" src=\"\/wp-content\/uploads\/2020\/09\/image-46.png\" alt=\"\" class=\"wp-image-1316\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-46.png 640w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-46-300x97.png 300w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><figcaption>Create a new project.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Language will be Python:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"936\" height=\"314\" src=\"\/wp-content\/uploads\/2020\/09\/image-48.png\" alt=\"\" class=\"wp-image-1318\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-48.png 936w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-48-300x101.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-48-768x258.png 768w\" sizes=\"auto, (max-width: 936px) 100vw, 936px\" \/><figcaption>We&#8217;ll be working with Python.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">As a trigger we&#8217;ll pick HTTP trigger. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"583\" src=\"\/wp-content\/uploads\/2020\/09\/image-49.png\" alt=\"\" class=\"wp-image-1319\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-49.png 900w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-49-300x194.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-49-768x497.png 768w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><figcaption>We&#8217;ll work with a HTTP trigger.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll call this HttpTrigger1:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"925\" height=\"157\" src=\"\/wp-content\/uploads\/2020\/09\/image-50.png\" alt=\"\" class=\"wp-image-1320\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-50.png 925w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-50-300x51.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-50-768x130.png 768w\" sizes=\"auto, (max-width: 925px) 100vw, 925px\" \/><figcaption>Give the function a name. I wasn&#8217;t very creative.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And we&#8217;ll use Functions authorization<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"226\" src=\"\/wp-content\/uploads\/2020\/09\/image-51.png\" alt=\"\" class=\"wp-image-1321\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-51.png 920w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-51-300x74.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-51-768x189.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><figcaption>Authorization level as Function for some additional security.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This will create the Azure function <em>locally. <\/em>You&#8217;ll see a number of files and a very simple function.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"524\" src=\"\/wp-content\/uploads\/2020\/09\/image-52-1024x524.png\" alt=\"\" class=\"wp-image-1322\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-52-1024x524.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-52-300x154.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-52-768x393.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-52-1536x786.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-52.png 1710w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Sample function being created<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s deploy this &#8220;raw&#8221; function to Azure, to see if everything is working as expected. To deploy to Azure, open the command palette again (CTRL+shift+P) and look for Azure Functions: Deploy to Function app&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"909\" height=\"273\" src=\"\/wp-content\/uploads\/2020\/09\/image-53.png\" alt=\"\" class=\"wp-image-1323\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-53.png 909w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-53-300x90.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-53-768x231.png 768w\" sizes=\"auto, (max-width: 909px) 100vw, 909px\" \/><figcaption>Deploy the function to Azure.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re not logged into an Azure account, it&#8217;ll ask you to sign in. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"911\" height=\"157\" src=\"\/wp-content\/uploads\/2020\/09\/image-54.png\" alt=\"\" class=\"wp-image-1324\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-54.png 911w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-54-300x52.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-54-768x132.png 768w\" sizes=\"auto, (max-width: 911px) 100vw, 911px\" \/><figcaption>Login to your Azure account.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After logging in, select your subscription. Then, select the function we created earlier:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"917\" height=\"187\" src=\"\/wp-content\/uploads\/2020\/09\/image-55.png\" alt=\"\" class=\"wp-image-1325\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-55.png 917w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-55-300x61.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-55-768x157.png 768w\" sizes=\"auto, (max-width: 917px) 100vw, 917px\" \/><figcaption>Select the Function we created earlier.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This will throw a little warning about overwriting the current function, click Deploy here.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"852\" height=\"219\" src=\"\/wp-content\/uploads\/2020\/09\/image-56.png\" alt=\"\" class=\"wp-image-1326\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-56.png 852w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-56-300x77.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-56-768x197.png 768w\" sizes=\"auto, (max-width: 852px) 100vw, 852px\" \/><figcaption>Deploy and overwrite what&#8217;s there now.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This will trigger the deployment to happen. The deployment should take about half a minute to complete. Once this is complete, open the Azure extension view in VSCode, and navigate to your subscription and function. Right click on HTTPTrigger1 and copy the link. Open this in a browser, and you should see the output of the python code we saw earlier:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"611\" height=\"430\" src=\"\/wp-content\/uploads\/2020\/09\/image-57.png\" alt=\"\" class=\"wp-image-1327\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-57.png 611w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-57-300x211.png 300w\" sizes=\"auto, (max-width: 611px) 100vw, 611px\" \/><figcaption>Copy the Function URL and enter it in a browser.<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"79\" src=\"\/wp-content\/uploads\/2020\/09\/image-58-1024x79.png\" alt=\"\" class=\"wp-image-1328\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-58-1024x79.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-58-300x23.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-58-768x59.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-58-1536x119.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-58.png 1554w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>The return from the default function.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">With that working, we can now get to work on the actual code we want to write. We are lucky, since the <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/developer\/python\/azure-sdk-authenticate?tabs=cmd#when-does-authentication-and-authorization-occur\">Azure documentation<\/a> contains an example of this scenario: using managed identities to access a key vault secret. I modified this a little, and this is the end result we&#8217;ll deploy to functions in a second.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import logging\nimport os\nimport azure.functions as func\nfrom azure.identity import DefaultAzureCredential\nfrom azure.keyvault.secrets import SecretClient\n\ndef main(req: func.HttpRequest) -> func.HttpResponse:\n    logging.info('Python HTTP trigger function processed a request.')\n\n    vault_url = \"https:\/\/nf-func-kv.vault.azure.net\/\"\n    credential = DefaultAzureCredential()\n    logging.info('Created managed identity.')\n\n    secret_client = SecretClient(vault_url=vault_url, credential=credential)\n    logging.info('Created secret client.')\n\n    retrieved_secret = secret_client.get_secret(\"superSecretApiKey\")\n    logging.info('Got secret from Key Vault.')\n\n    if retrieved_secret:\n        return func.HttpResponse(f\"superSecretApiKey is set to  {retrieved_secret.value} in key vault.\")\n    else:\n        return func.HttpResponse(\n             \"Function triggered successfully, but failed to get secret from key vault.\",\n             status_code=200\n        )<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Since we&#8217;re importing from two new python packages, we&#8217;ll need to add those to the requirements.txt file.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"787\" height=\"815\" src=\"\/wp-content\/uploads\/2020\/09\/image-59.png\" alt=\"\" class=\"wp-image-1329\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-59.png 787w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-59-290x300.png 290w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-59-768x795.png 768w\" sizes=\"auto, (max-width: 787px) 100vw, 787px\" \/><figcaption>Add the packages to the requirements.txt file.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">With that done, we can redeploy the function to Azure. To do this, we can also use the graphical explorer in VSCode. Open the Azure window again, right click in your function and click on deploy to function.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"731\" height=\"396\" src=\"\/wp-content\/uploads\/2020\/09\/image-60.png\" alt=\"\" class=\"wp-image-1330\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-60.png 731w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-60-300x163.png 300w\" sizes=\"auto, (max-width: 731px) 100vw, 731px\" \/><figcaption>Using the graphical way to Deploy a function in VS Code.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll get another warning about overwriting, and can then deploy to the function. This will take another 30 seconds, and once live, you should be able to open the function url as we did before, and now see the secret outputted:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"936\" height=\"117\" src=\"\/wp-content\/uploads\/2020\/09\/image-61.png\" alt=\"\" class=\"wp-image-1331\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-61.png 936w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-61-300x38.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-61-768x96.png 768w\" sizes=\"auto, (max-width: 936px) 100vw, 936px\" \/><figcaption>We&#8217;re getting the secret from key vault.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Cool! So we were able to connect a function to key vault using public networking. Let&#8217;s convert this to private networking now. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Integrate Key Vault using PrivateLink and function using VNET integration.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OK, now we can get to the actual meat of this blog post: setting up private networking. To begin, let&#8217;s create a new VNET in the resource group we created earlier.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"659\" src=\"\/wp-content\/uploads\/2020\/09\/image-62-1024x659.png\" alt=\"\" class=\"wp-image-1332\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-62-1024x659.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-62-300x193.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-62-768x494.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-62.png 1148w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Creating new VNET<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll create 2 subnets in this VNET, one for the key vault private link endpoint, another for the function VNET integration:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"760\" src=\"\/wp-content\/uploads\/2020\/09\/image-63-1024x760.png\" alt=\"\" class=\"wp-image-1333\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-63-1024x760.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-63-300x223.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-63-768x570.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-63.png 1129w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Setting up address space and subnets<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll skip security and tagging, review and hit the create button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"790\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/09\/image-64-790x1024.png\" alt=\"\" class=\"wp-image-1334\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-64-790x1024.png 790w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-64-231x300.png 231w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-64-768x996.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-64.png 835w\" sizes=\"auto, (max-width: 790px) 100vw, 790px\" \/><figcaption>Review and create.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Creating the VNET should take a couple seconds. Once it&#8217;s created, head on over to your key vault so we can start integrating that into the VNET. Open the networking blade, and change the Firewalls and virtual networks option to Private endpoint and selected networks.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"661\" src=\"\/wp-content\/uploads\/2020\/09\/image-74-1024x661.png\" alt=\"\" class=\"wp-image-1344\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-74-1024x661.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-74-300x194.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-74-768x496.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-74-1536x991.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-74.png 1543w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Change the firewall option to private endpoint.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now switch to Private endpoint connections and hit the new Private endpoint button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"673\" src=\"\/wp-content\/uploads\/2020\/09\/image-65-1024x673.png\" alt=\"\" class=\"wp-image-1335\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-65-1024x673.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-65-300x197.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-65-768x505.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-65.png 1189w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Create new private endpoint.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll start with providing it a name and the resource group details:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"596\" src=\"\/wp-content\/uploads\/2020\/09\/image-66-1024x596.png\" alt=\"\" class=\"wp-image-1336\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-66-1024x596.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-66-300x175.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-66-768x447.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-66.png 1134w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Creating the new private endpoint basics.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Then we will select our key vault as the target resource:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"586\" src=\"\/wp-content\/uploads\/2020\/09\/image-67-1024x586.png\" alt=\"\" class=\"wp-image-1337\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-67-1024x586.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-67-300x172.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-67-768x439.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-67.png 1197w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Select the key vault we created.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we&#8217;ll pick our VNET that we created, and allow this wizard to create a private DNS zone.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"789\" src=\"\/wp-content\/uploads\/2020\/09\/image-68-1024x789.png\" alt=\"\" class=\"wp-image-1338\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-68-1024x789.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-68-300x231.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-68-768x592.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-68.png 1156w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Integrate it into the VNET we created and create a new DNS private zone.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll skip tagging again, review and then create the private endpoint.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"876\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/09\/image-69-876x1024.png\" alt=\"\" class=\"wp-image-1339\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-69-876x1024.png 876w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-69-257x300.png 257w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-69-768x898.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-69.png 1078w\" sizes=\"auto, (max-width: 876px) 100vw, 876px\" \/><figcaption>Review and create<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">While this is creating, we can setup the functions VNET integration. Open your function in the Azure portal, and open the networking blade. Here we need to select the first option, VNET Integration:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"439\" src=\"\/wp-content\/uploads\/2020\/09\/image-70-1024x439.png\" alt=\"\" class=\"wp-image-1340\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-70-1024x439.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-70-300x129.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-70-768x330.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-70.png 1312w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>We need VNET integration here.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In the configuration blade, hit the add VNET button, and select the VNET and subnet for the functions:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"520\" src=\"\/wp-content\/uploads\/2020\/09\/image-72-1024x520.png\" alt=\"\" class=\"wp-image-1342\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-72-1024x520.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-72-300x152.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-72-768x390.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-72-1536x780.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-72.png 1753w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Select the VNET and subnet we created earlier.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This will only take a second, but it should show an updated VNET configuration blade:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"936\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/09\/image-73-936x1024.png\" alt=\"\" class=\"wp-image-1343\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-73-936x1024.png 936w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-73-274x300.png 274w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-73-768x840.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-73.png 1054w\" sizes=\"auto, (max-width: 936px) 100vw, 936px\" \/><figcaption>Blade should update quickly to show updated VNET configuration.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This should be it in terms of networking configuration. Before we can make this work completely, we&#8217;ll need to make a small configuration change in Functions to use private DNS. We&#8217;ll do that next.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configure Azure Function to use Private DNS Zone.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Our key vault is now only accessible using private link. However, Azure Functions by default uses public DNS, so doesn&#8217;t know how to find the IP address of the key vault it needs to connect to. To change this, we need to set two application settings in Azure Functions:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li> WEBSITE_DNS_SERVER with value 168.63.129.16<\/li><li>WEBSITE_VNET_ROUTE_ALL with value 1 <\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">To set these app settings, you can either use the Azure portal, or stay inside VSCode to set them. I did it in VSCode. Right click application settings, and select Add New Setting&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"655\" height=\"684\" src=\"\/wp-content\/uploads\/2020\/09\/image-75.png\" alt=\"\" class=\"wp-image-1345\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-75.png 655w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-75-287x300.png 287w\" sizes=\"auto, (max-width: 655px) 100vw, 655px\" \/><figcaption>Add an application setting<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And enter both settings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This should do it. Let&#8217;s test this out in our function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Have function query private Key Vault to verify things work<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The thing now is, it just works. Open the function again, and you&#8217;ll notice that you&#8217;re getting the output you&#8217;re expecting. I was expecting that we would have had to change the key vault URL to the privatelink url, but it works using the regular URL. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"940\" height=\"121\" src=\"\/wp-content\/uploads\/2020\/09\/image-76.png\" alt=\"\" class=\"wp-image-1346\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-76.png 940w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-76-300x39.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-76-768x99.png 768w\" sizes=\"auto, (max-width: 940px) 100vw, 940px\" \/><figcaption>This magically works, but how do we verify it works?<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">But there&#8217;s no easy way to confirm that this is flowing over the private connection rather than the public connection.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To verify that the function actually was using Private Link, I decided to cut the tie between the function and the VNET (meaning, I disabled the VNET integration).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"407\" src=\"\/wp-content\/uploads\/2020\/09\/image-77.png\" alt=\"\" class=\"wp-image-1347\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-77.png 650w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-77-300x188.png 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><figcaption>We&#8217;ll disconnect the Function from the VNET.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><br>Give this about a minute to disconnect (disconnecting is weirdly slower than connecting), and see now that the web page is throwing an error: <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"759\" height=\"631\" src=\"\/wp-content\/uploads\/2020\/09\/image-78.png\" alt=\"\" class=\"wp-image-1348\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-78.png 759w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-78-300x249.png 300w\" sizes=\"auto, (max-width: 759px) 100vw, 759px\" \/><figcaption>HTTP 500 error once disconnected from the VNET<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We can dive into the details of this error by opening the log of our application in VScode:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"565\" src=\"\/wp-content\/uploads\/2020\/09\/image-79-1024x565.png\" alt=\"\" class=\"wp-image-1349\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-79-1024x565.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-79-300x165.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-79-768x424.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-79-1536x847.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-79.png 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>More details on the error.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Which clearly tells us that now the client address is not authorized, which proves that we were using privatelink for the connection. Let&#8217;s reconnect the VNET, and restart the function (I kept getting errors, a restart of the function cleared it for me) and this will be working again:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"480\" height=\"571\" src=\"\/wp-content\/uploads\/2020\/09\/image-80.png\" alt=\"\" class=\"wp-image-1350\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-80.png 480w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-80-252x300.png 252w\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And that&#8217;s it. That is how you connect a function to key vault using private connections.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Storing secrets in key vault is a security best practice. Protecting that key vault using private link is also a good practice, because it disables connections coming over public IPs. You can connect functions to that key vault using that private link connection by integrating your function into your VNET, and configuring it to use the private DNS zone. Once all of this is configured, you can securely and privately connect your Azure functions to key vault.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I did some work with a customer last week to integrate Functions with Key Vault without using the public IP of the Key Vault. This setup worked perfectly, although there were a couple of steps involved. To document those steps, I decided to write this post. In this post we&#8217;ll build a new Azure Function [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1340,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2,3,36,31],"tags":[8,150,38,66,122,121,50],"class_list":["post-1292","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-devops","category-networking","category-software-development","tag-azure","tag-azure-functions","tag-networking","tag-private-endpoint","tag-private-link","tag-privatelink","tag-security"],"jetpack_featured_media_url":"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/09\/image-70.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1292","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=1292"}],"version-history":[{"count":2,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1292\/revisions"}],"predecessor-version":[{"id":1351,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1292\/revisions\/1351"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media\/1340"}],"wp:attachment":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=1292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=1292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=1292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}