tag:blogger.com,1999:blog-20124238924473874682024-03-04T01:26:58.803+01:00Virtual Infrastructure Tips - Azure and VMwareA blog about virtual datacenters, both on-prem (VMware) and off-prem (MS Azure) with howto's, tips, and tools. The purpose of the blog is to act as an electronic notepad - to get those things noted that one discovers during daily operations - as well as, hopefully, being helpful to others in the community.Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.comBlogger190125tag:blogger.com,1999:blog-2012423892447387468.post-47832764984838855182024-01-13T00:42:00.001+01:002024-01-13T00:42:31.032+01:00Azure: Subnets with multiple IP address spaces?<p> We were having a look today at the <a href="https://learn.microsoft.com/en-us/azure/templates/microsoft.network/virtualnetworks?pivots=deployment-language-arm-template" target="_blank">Azure documentation for virtual networks</a> and subnets specifically. Both for Bicep and ARM there are two options to specify the addressprefix (address space) for a subnet. The first one is "addressPrefix" which takes a string as input and the second one is "addressPrefix<b>es" </b>which takes an array, see below. This leads one to expect that you can provide multiple IP address ranges for the subnet in the array in the same way that it can be done for VNets.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgquunFxUrwft-W1ycqZapgyi9V1wT-gMrNz_vnPxS0K-zh5ARAKRdiDf1UQIcKGFj4TOo_KGP9OJ1fmkmtt9FDo1bVGpO-8S_EesmvdQid4BIkjVzuhxf8-EAsz9Na-ePd62fgLxGkE7oKoXWbw3hwn64f-b7t-MtQeLfasGKd2Wv-GKtRiv3ByUXk-Srl/s926/subnet1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="760" data-original-width="926" height="329" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgquunFxUrwft-W1ycqZapgyi9V1wT-gMrNz_vnPxS0K-zh5ARAKRdiDf1UQIcKGFj4TOo_KGP9OJ1fmkmtt9FDo1bVGpO-8S_EesmvdQid4BIkjVzuhxf8-EAsz9Na-ePd62fgLxGkE7oKoXWbw3hwn64f-b7t-MtQeLfasGKd2Wv-GKtRiv3ByUXk-Srl/w400-h329/subnet1.png" width="400" /></a></div><br /><p>If you try to manually add an additional IP range to an existing subnet via the portal, it will show an error.</p><p>If you try to deploy multiple ranges via an ARM template, it still throws an error but we get a bit more information in the error message, see below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN5X0yC7b-T99UHKeOVzo94NpUhuqScxdCN2KcoOoZ9_OLv2CVuECuUNMA7_wgXWmAB3FAb6g7OffRKtJiBA2Xe9RMbbUqYgRBVqIUx3auKF42mX9DdKMSJjKQmTIpGcGRcvpkS4tpwGAT2aye8KjsLc8e1Y5UTpGbT8ROCFJxcR6x-yYXVvk_iBuIOuAq/s1012/subnet2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="178" data-original-width="1012" height="70" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN5X0yC7b-T99UHKeOVzo94NpUhuqScxdCN2KcoOoZ9_OLv2CVuECuUNMA7_wgXWmAB3FAb6g7OffRKtJiBA2Xe9RMbbUqYgRBVqIUx3auKF42mX9DdKMSJjKQmTIpGcGRcvpkS4tpwGAT2aye8KjsLc8e1Y5UTpGbT8ROCFJxcR6x-yYXVvk_iBuIOuAq/w400-h70/subnet2.png" width="400" /></a></div><br /><p>The error states that the subscription is not registered for the following feature:</p><p>Microsoft.Network/AllowMultipleAddressPrefixesOnSubnet</p><p>This is usually handled under Resource Providers for the subscription. If you go to subscriptions -> resource providers in the Portal, this feature is not there to enable, though.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizalxceThzgK-PT5eTOXzIe_Xy7VTIHxg16_yVdg-f1eWqif4_5T7JgOXwNrsVjCCNixbqsmdnr1GPtCQAPrwnB8Y7EP3WRt1hvzzS0zs0StIyLl7UA_yYYJMEaCO4178PBhB9NIHTlyNUGJLLV48JuB0wU1ZEO-6NNTiy6obcdhkA5jybi1-sgwqIh2lf/s2004/subnet5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1066" data-original-width="2004" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizalxceThzgK-PT5eTOXzIe_Xy7VTIHxg16_yVdg-f1eWqif4_5T7JgOXwNrsVjCCNixbqsmdnr1GPtCQAPrwnB8Y7EP3WRt1hvzzS0zs0StIyLl7UA_yYYJMEaCO4178PBhB9NIHTlyNUGJLLV48JuB0wU1ZEO-6NNTiy6obcdhkA5jybi1-sgwqIh2lf/w400-h213/subnet5.png" width="400" /></a></div><br /><p>It is possible however, to register the feature via Azure CLI. But when you run the command, this feature goes from "not registered" to "pending" and then it will just stay like that and never move to registered.</p><p>It looks like below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9nqv00DQWxONatPrXBkutJggTMBweOVM4ZyLS133rcwY6dmvs02b0uxtOsRslvtcuRBlF4NLpgpCotHoSCHFdml16qtHYHhqMIteKZjzlByNaALyrWzVHLUTFNfK2JPjs_wtS1MZQA9nRSFrKumU-XOjm2gPBr9w6rZas0v8XNLzFo-nRY9e49DMOSnqn/s1483/subnet3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="291" data-original-width="1483" height="79" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9nqv00DQWxONatPrXBkutJggTMBweOVM4ZyLS133rcwY6dmvs02b0uxtOsRslvtcuRBlF4NLpgpCotHoSCHFdml16qtHYHhqMIteKZjzlByNaALyrWzVHLUTFNfK2JPjs_wtS1MZQA9nRSFrKumU-XOjm2gPBr9w6rZas0v8XNLzFo-nRY9e49DMOSnqn/w400-h79/subnet3.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1RiyAfw8rZVttwQLEKdpTcWS6NAajwhp_ft3KwIdpSuCaHJkRLpkkmsw070_paqzPG6MkaqqLf9IyDLsS4Zn_7iLJwc0XpVUqU92QQIC09ineYO2sdgX6i99dtATJ5HChPZQw8pnsma-0fXiF0QKCs4vaTfY1gV-GKnMEKx76-tr4WQlVOujYt-rkkxIB/s1470/subnet4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="317" data-original-width="1470" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1RiyAfw8rZVttwQLEKdpTcWS6NAajwhp_ft3KwIdpSuCaHJkRLpkkmsw070_paqzPG6MkaqqLf9IyDLsS4Zn_7iLJwc0XpVUqU92QQIC09ineYO2sdgX6i99dtATJ5HChPZQw8pnsma-0fXiF0QKCs4vaTfY1gV-GKnMEKx76-tr4WQlVOujYt-rkkxIB/w400-h86/subnet4.png" width="400" /></a></div><br /><p>The commands are:</p><p><span style="font-family: courier;">az feature register --namespace Microsoft.Network -n AllowMultipleAddressPrefixesOnSubnet --subscription <subscriptionId></span></p><p>and:</p><p><span style="font-family: courier;">az feature show --namespace Microsoft.Network -n AllowMultipleAddressPrefixesOnSubnet --subscription <subscriptionId></span></p><p>It turns out that this feature is available only to MSFT developers and is not available either in public or private preview. There is not much info around this, as I suppose it is not really a sought after feature.</p><p>I found this explanation and also response from MSFT, see <a href="https://github.com/Azure/azure-cli/issues/18194" target="_blank">link here</a>.</p><p>And then another person had the same issue as late as Jan 8, 2024, see <a href="https://learn.microsoft.com/en-us/answers/questions/1483282/how-can-i-fix-feature-allowmultipleaddressprefixes" target="_blank">link here</a>.</p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-31672814940035447852023-12-22T14:09:00.002+01:002023-12-22T14:09:29.320+01:00Add resource locks to individual records in a private DNS zone<p> It is possible to add resource locks to individual records in private DNS zones. A scenario for this could be a critical or central A record that you don't want to be changed by mistake while still allowing for ongoing updates to the private DNS zone as part of daily operations.</p><p>If you have a <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale#second-deployifnotexists-policy---matching-on-groupid--privatelinkserviceid" target="_blank">centralized private DNS</a> zone setup with Azure <a href="https://www.vi-tips.com/2023/12/azure-policy-auto-create-dns-records.html" target="_blank">policy handling the DNS record creation</a> and you also use private endpoints (PE), then there is a (perhaps small) risk that A records can be overwritten. This is the case if someone creates a new PE and associates with the same resource. The DeployIfNotExist policy will run on PE creation and replace the existing record and so that the PaaS service will resolve to a new local IP (note that if you delete the new PE again, the A record will also be deleted and the original PE will no longer have an A record and so will have to be recreated or the A record re-added).</p><p>Adding locks to individual records is described in further <a href="https://learn.microsoft.com/en-us/azure/dns/dns-protect-zones-recordsets#protecting-individual-records" target="_blank">detail here</a>. Note that this can currently only be done using PowerShell and can't be done via the Azure portal.</p><p>The example from MSFT looks like below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8y0qNyMclYMRDZAYB5RQg3DFLp5kdsqqJsI4TGqPNlWsYgBuh-jxWpps65hZmSDV0dmcwWWy39DlqPHOhk1qP14FvyMUJ0BM74i6Vs4Kf5BYqSz_xe1dpx2MBR92HSfRdGD4EcwEsukRFSJe7SC7BMXg7fpM-afDTENY5VTuzHGoYtMB1V9G-pnWfMCG9/s896/locks1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="291" data-original-width="896" height="130" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8y0qNyMclYMRDZAYB5RQg3DFLp5kdsqqJsI4TGqPNlWsYgBuh-jxWpps65hZmSDV0dmcwWWy39DlqPHOhk1qP14FvyMUJ0BM74i6Vs4Kf5BYqSz_xe1dpx2MBR92HSfRdGD4EcwEsukRFSJe7SC7BMXg7fpM-afDTENY5VTuzHGoYtMB1V9G-pnWfMCG9/w400-h130/locks1.png" width="400" /></a></div><br /><p>An actual example is shown below:</p><p></p><p class="MsoNormal"><span lang="en-DK" style="mso-ansi-language: #0C00;"><span style="font-family: courier;"># Lock a
DNS record set<o:p></o:p></span></span></p>
<p class="MsoNormal"><span style="font-family: courier;">$lvl =
"ReadOnly"</span></p>
<p class="MsoNormal"><span lang="en-DK" style="mso-ansi-language: #0C00;"><span style="font-family: courier;">$lnm =
"dontChangeMe"<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="en-DK" style="mso-ansi-language: #0C00;"><span style="font-family: courier;">$rsc =
"privatelink.blob.core.windows.net/testaccountdelete001"<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="en-DK" style="mso-ansi-language: #0C00;"><span style="font-family: courier;">$rty =
"Microsoft.Network/<b>privateDNSZones</b>/A"<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="en-DK" style="mso-ansi-language: #0C00;"><span style="font-family: courier;">$rsg =
"rg-dns-conn-001"<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="en-DK" style="mso-ansi-language: #0C00;"><o:p><span style="font-family: courier;"> </span></o:p></span></p>
<p class="MsoNormal"><span lang="en-DK" style="mso-ansi-language: #0C00;"><span style="font-family: courier;">New-AzResourceLock
-LockLevel $lvl -LockName $lnm -ResourceName $rsc -ResourceType $rty
-ResourceGroupName $rsg<o:p></o:p></span></span></p>Note that the MSFT example uses a regular DNS zone whereas my example uses private DNS zones (marked in bold above).<p></p><p>See example below for when lock is applied:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZqVCrS_CWOQxxjgl8ndoO-PI6LvIYWCs8B5BPHKulnkWsY65QnTGSMCFoOfaCjToLYlbCLfg6Lx5Bxh-h1iBDQ2BVuUHErVqRXMHminkM2Ejm7VcNHDBBPmOx3SCqbxT7VIt2JZEvdmkAdRzpgwPaB93brpwzbBinHoheo5QmIjnms0ZOJHxv4JGeD9cp/s1475/locks2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="607" data-original-width="1475" height="165" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZqVCrS_CWOQxxjgl8ndoO-PI6LvIYWCs8B5BPHKulnkWsY65QnTGSMCFoOfaCjToLYlbCLfg6Lx5Bxh-h1iBDQ2BVuUHErVqRXMHminkM2Ejm7VcNHDBBPmOx3SCqbxT7VIt2JZEvdmkAdRzpgwPaB93brpwzbBinHoheo5QmIjnms0ZOJHxv4JGeD9cp/w400-h165/locks2.png" width="400" /></a></div><div><br /></div>Once applied, you can see (and edit and delete) the lock in the portal under the private DNS zone -> "you're private DNS zone, e.g. for blob" -> Locks, see below:<div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsXW1Wv2w-h-_faoT-ef1RUAYHvDwl0kQmiNpq6Xu7RH3SOEiSqyWPV22hW8lvDQr5ON_Szn1WmXl5HS_yrXTYquYbZWgi1WMqCUpwXZ_GoZ__X2zR0GeTaNrPKC0sw3o1Qv-Sz8ExPRw664uL9MBNiyibAG5IReONtzn09drTRyfpuIwzcZIfwnsH_z42/s1438/locks3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="533" data-original-width="1438" height="149" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsXW1Wv2w-h-_faoT-ef1RUAYHvDwl0kQmiNpq6Xu7RH3SOEiSqyWPV22hW8lvDQr5ON_Szn1WmXl5HS_yrXTYquYbZWgi1WMqCUpwXZ_GoZ__X2zR0GeTaNrPKC0sw3o1Qv-Sz8ExPRw664uL9MBNiyibAG5IReONtzn09drTRyfpuIwzcZIfwnsH_z42/w400-h149/locks3.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div>The reason that the lock type is set to ReadOnly and not CannotDelete is that the latter option will allow the records to be overwritten which we don't want.</div><div><br /><p><br /></p></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-20275765114207990012023-12-22T12:32:00.003+01:002023-12-22T12:39:55.022+01:00Azure policy: Deploy CanNotDelete locks to resource groups<p> You can use Azure policy to apply resource locks to all resource groups in a defined scope. This can be useful to ensure that critical resources are not deleted by mistake. See general description of <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources?tabs=json" target="_blank">resource locks here</a>. Note that applying locks in the environment can create unforeseen problems so it's good to proceed with a bit of caution. </p><p>The policy in this post is based on a slightly modified version from AzAdvertizer, that can be <a href="https://www.azadvertizer.net/azpolicyadvertizer/0bdc4336-fd4c-4798-b524-a2ecbeed9d0c.html" target="_blank">found here</a>. The original policy will apply locks to a list of resource group names that is specified in an array.</p><p>The modified version, which can be found <a href="https://github.com/norregaard/Azure/blob/main/Policy/Deploy-CanNotDelete-Resource-Lock-on-Resource-Groups-w-exclusionList.json" target="_blank">here on Github</a>, applies locks to all resource groups but excludes resource groups which are specified in the array.</p><p>Note that the only built-in roles (for the system assigned managed identity, SAMI) that can apply locks are Owner and User Access Administrator. Owner has typically too many permissions and User Access Administrator does not have the policy deployment permissions. So a custom RBAC is required.</p><p>To <a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles-portal" target="_blank">create a custom RBAC</a>, go the the subscription or management group level where the policy will be applied, go to Access Control (IAM) -> Roles. From here click +Add to a new role. Add the following permissions:</p><div style="background-color: #fffffe; line-height: 19px;"><div style="font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><span style="color: #a31515;">"Microsoft.Resources/deployments/write"</span>,</div><div style="font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><span style="color: #a31515;">"Microsoft.Authorization/locks/*"</span>,</div><div style="font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><span style="color: #a31515;">"Microsoft.Authorization/policies/deployIfNotExists/action"</span>,</div><div style="font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><span style="color: #a31515;">"Microsoft.Resources/checkPolicyCompliance/read"</span></div><div style="font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;"><span style="color: #a31515;"><br /></span></div><div><div>The full RBAC role in JSON can be found <a href="https://github.com/norregaard/Azure/blob/main/RBAC/RBAC-for-applying-resource-locks.json" target="_blank">here on Github</a> (make sure to update the management group or subscription under assignableScopes).</div><div><br /></div><div>When you assign the policy definition, make sure to choose the new RBAC role when creating the SAMI, see below:</div></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVF8FAsSAaQ9jcpiXxYOm4UnDqr_SUVP-xLd0qtV2QIzhe6cdc4nvFi36BVZ-Fj62-Ht6U1wAp6MiPTcrPmkcjQJ1tMzgRwWKpm9n5QBpNyhHsDGlUSDzSB8LiePnLsCO5JxshyA2ph6PWF5_QMD1Ee0oVK_iRVe3rhoqceDfxNGwb3U3T2IeB36rWPfKw/s1190/rbac1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="785" data-original-width="1190" height="264" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVF8FAsSAaQ9jcpiXxYOm4UnDqr_SUVP-xLd0qtV2QIzhe6cdc4nvFi36BVZ-Fj62-Ht6U1wAp6MiPTcrPmkcjQJ1tMzgRwWKpm9n5QBpNyhHsDGlUSDzSB8LiePnLsCO5JxshyA2ph6PWF5_QMD1Ee0oVK_iRVe3rhoqceDfxNGwb3U3T2IeB36rWPfKw/w400-h264/rbac1.png" width="400" /></a></div><br /><div><br /></div><div><span style="font-family: "Times New Roman"; font-size: medium; white-space: normal;"><br /></span></div><div><span style="font-family: "Times New Roman"; font-size: medium; white-space: normal;"><br /></span></div></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-60316273937672590702023-12-22T11:08:00.002+01:002023-12-22T11:33:59.283+01:00Azure policy: Reducing the default evaluation time for auto A record creation for private endpoints<p> I have previously written about the use of Azure policy to automatically create A records for private endpoints in centrally managed private DNS zones. You can see more <a href="https://www.vi-tips.com/2023/12/azure-policy-auto-create-dns-records.html">here </a>and <a href="https://www.vi-tips.com/2023/03/auto-create-dns-record-for-rsv-with.html">here</a>. And the general recommended setup from MSFT is available <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale" target="_blank">here</a>.</p><p>One of the slightly annoying things about the standard Azure policies is that they use the default evaluation delay time (10 minutes) for when a DeployIfNotExist policy runs. This means that when you create a private endpoint, it takes around 10 minutes before the A record is created in the private DNS zone. This creates, in addition to the added wait time, some confusion from users as they don't know whether their deployments work or not.</p><p>This evaluation delay time can be minimized by using the optional property for DeployIfNotExist type policies called <b>EvaluationDelay</b>. This is described in more <a href="https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effects#deployifnotexists-properties" target="_blank">detail here</a>.</p><p>There are different values that can be set for this property, but to get the best effect, I'd recommend using <b>AfterProvisioningSuccess</b>. This will run the policy as soon as the private endpoint has been successfully deployed.</p><p>You can see an example of a policy using this property <a href="https://github.com/norregaard/Azure/blob/main/Policy/Auto-create-DNS-record-private-DNS-zone-w-subResource-and-privateResourceType.json" target="_blank">here on Github</a>. And below is an example as well.</p><p>Note that this property does not only apply to DNS record creation. It can be used for other DeployIfNotExist policies as well where you want the resources deployed right away.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxPaDUNAeQA-cq-8DEh7lNNeoQoXy72kiJy4ILQELTTe3W_5vxT2DPMG9PPZNKzYBOBbIllgzDlzLPoYcdVuojTgy8SiO2T5cBXuvlKStZsxyCz4CkqXxM_j1loW86cHcQyxCFO9Ryi8ZWLAEJnFywrvFCzKsdbA8y7rKi5GI_3FIARh8cL9gwyU-QbL2k/s992/AfterProvisioningSuccess1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="398" data-original-width="992" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxPaDUNAeQA-cq-8DEh7lNNeoQoXy72kiJy4ILQELTTe3W_5vxT2DPMG9PPZNKzYBOBbIllgzDlzLPoYcdVuojTgy8SiO2T5cBXuvlKStZsxyCz4CkqXxM_j1loW86cHcQyxCFO9Ryi8ZWLAEJnFywrvFCzKsdbA8y7rKi5GI_3FIARh8cL9gwyU-QbL2k/w400-h160/AfterProvisioningSuccess1.png" width="400" /></a></div><br /><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-2910098767473274702023-12-11T09:56:00.005+01:002023-12-22T09:05:46.765+01:00Azure policy: Auto create DNS records using both subResource and private link resource type<p> When using private endpoints at scale, the <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale" target="_blank">recommended setup</a> from Microsoft is to use Azure Policy to automatically create the DNS records in the central private DNS zones when the private endpoints are created. The reason for this is that users or owners of the spokes or landing zones do not have permissions to create A records in the central private DNS zones in the Hub.</p><p>For most private DNS zones, the <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale#first-deployifnotexists-policy---matching-on-groupid-only" target="_blank">regular Azure policy</a> can be used which checks for private DNS zone name and subResource id, see <a href="https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns" target="_blank">list here</a>. However, there are scenarios where this is not sufficient. For example, if a region has to be specified using Recovery Services Vault, see more on <a href="https://www.vi-tips.com/2023/03/auto-create-dns-record-for-rsv-with.html" target="_blank">that here</a>.</p><p>Another example, and the scope of this post, is when there are overlapping subResource values such as for Synapse Analytics and Cosmos DB (which both use 'sql') or Synapse Studio and Storage accounts Web (which both use 'web'). If multiple policies are created using the same subResource, you don't know in which private DNS zone that the A record will be created and you can experience records being created first in one zone and then the other whichever policy is evaluated first.</p><p>To address this, Microsoft has created a policy that, in addition to the subResource, adds a parameter that matches on the private link resource type (also referred to as privateLinkServiceId). The policy can be <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale#second-deployifnotexists-policy---matching-on-groupid--privatelinkserviceid" target="_blank">found here</a>.</p><p>The private link resource type is found in the first column in the table of private DNS zones, <a href="https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns" target="_blank">here</a>. Examples of values are:</p><p></p><ul style="text-align: left;"><li>Microsoft.Synapse/privateLinkHubs</li><li>Microsoft.Synapse/workspaces</li><li>Microsoft.DocumentDB/databaseAccounts</li></ul><p></p><p>For some odd reason, MSFT hardcodes the value of the private link reosurce type in the policy. I've updated the policy slightly to parameterize that value. The updated policy can be found on <a href="https://github.com/norregaard/Azure/blob/main/Policy/Auto-create-DNS-record-private-DNS-zone-w-subResource-and-privateResourceType.json" target="_blank">here on Github</a>.</p><p>Below you can see an example of what it looks like when the policy is assigned in the portal:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWtbQIeI6kRhwTTumUhRkW5aMBIjjrbf25o-A4lHfOQcW24q6vEVuORLQSIB3_lk2-ohUwFb3JlvCBxiTQw5kDeza_pyEb5PH6jclGGpn9Gt77nTWpclMshWDCZ9EmeKC-CIb8B6Dn2Jh_-H2G2q9oHd4PorG-kDflBgo25RSsaSyx5aTs7u7NOa_wkdLY/s1047/dns1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="446" data-original-width="1047" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWtbQIeI6kRhwTTumUhRkW5aMBIjjrbf25o-A4lHfOQcW24q6vEVuORLQSIB3_lk2-ohUwFb3JlvCBxiTQw5kDeza_pyEb5PH6jclGGpn9Gt77nTWpclMshWDCZ9EmeKC-CIb8B6Dn2Jh_-H2G2q9oHd4PorG-kDflBgo25RSsaSyx5aTs7u7NOa_wkdLY/w400-h170/dns1.png" width="400" /></a></div><br /><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-38740240527910857752023-11-30T14:01:00.004+01:002023-11-30T14:01:14.268+01:00Installing tools on Windows with Chocolatey - a package manager<p> Chocolatey is a useful tool to install apps and tools on a fresh laptop or developer VM via command line.</p><h2 style="text-align: left;">Install Chocolatey</h2><p>To install Chocolatey, follow below steps:</p><p></p><ul style="text-align: left;"><li>Install PowerShell 7, <a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.4" target="_blank">see link</a></li><li>Open PowerShell 7 as administrator</li><li>Run the following command to install Chocolatey (copied from the official <a href="https://chocolatey.org/install" target="_blank">install instructions</a>)</li><ul><li>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))</li></ul><li>Close and re-open the PowerShell 7 window, again as administrator</li><li>Run the following command to verify that install is succesful:</li><ul><li><span lang="EN-US" style="font-family: "Segoe UI",sans-serif; font-size: 10.5pt; mso-ansi-language: EN-US; mso-bidi-language: AR-SA; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-US;">choco upgrade all</span></li></ul><li><span style="font-family: Segoe UI, sans-serif;"><span style="font-size: 14px;">Enable long paths in PowerShell (command copied <a href="https://bigfont.ca/enable-long-paths-in-windows-with-powershell/" target="_blank">from here</a>):</span></span></li><ul><li><span style="font-family: Segoe UI, sans-serif;"><span style="font-size: 14px;">New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force</span></span></li></ul></ul><h2 style="text-align: left;">Install apps with Chocolatey</h2><div>Below are some commands for common tools:</div><div><p></p><ul style="text-align: left;"><li><span lang="EN-US">choco install -y
notepadplusplus</span></li><li><span lang="EN-US">choco install -y git</span></li><li><span lang="EN-US">choco install -y vscode</span></li><li><span lang="EN-US">choco install -y 7zip</span></li><li><span lang="EN-US">choco install -y
kubernetes-cli</span></li><li><span lang="EN-US">choco install -y
kubernetes-helm</span></li><li><span lang="EN-US">choco install -y azure-cli</span></li><li><span lang="EN-US">choco install -y az.powershell</span></li><li><span lang="EN-US">choco install -y bicep</span></li><li><span lang="EN-US" style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ansi-language: EN-US; mso-bidi-language: AR-SA; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin;">choco install -y
terraform</span></li><li><span style="font-family: Calibri, sans-serif;"><span style="font-size: 14.6667px;">choco install -y firefox</span></span></li></ul><div><span style="font-family: Calibri, sans-serif;"><span style="font-size: 14.6667px;">You can see a list of <a href="https://community.chocolatey.org/packages" target="_blank">available apps here</a> (and search as well, there are many)</span></span></div><div><span style="font-family: Calibri, sans-serif;"><span style="font-size: 14.6667px;"><br /></span></span></div><div><span style="font-family: Calibri, sans-serif;"><span style="font-size: 14.6667px;">Here's instructions on <a href="https://www.vi-tips.com/2023/10/git-commands-for-setup-and-daily-work.html" target="_blank">initial config</a> for Git (set user.name and user.email)</span></span></div><p></p>
</div><a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.4" target="_blank"></a><p></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-55517884815684942722023-10-20T15:15:00.003+02:002024-01-16T10:37:19.339+01:00Git commands - for setup and daily work<p> This article is just to collect some of the Git commands that are being used on a regular basis.</p><h2 style="text-align: left;">Git commands for daily work</h2><p></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git clone <https link to repo> (clone a remote repo. Make sure you’re
in the correct directory when running)<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git pull (sync the latest changes from remote repo)<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git checkout -b feature/new_branch_name (create new feature branch)<o:p></o:p></span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git checkout main (switch to main branch)</span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git add -A (pre-commit command to add all new files to local staging, run this before local commit)<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git commit -a -m "adding a test file"
(commit all files to local branch and add a message)<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git push --set-upstream origin feature/testingbranch (publish local branch to remote repo, use the same name as the current feature
branch)</span><span face="Arial, sans-serif"><o:p></o:p></span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git branch -d localBranchName (delete local branch)</span></span></p><p class="MsoNormal"><span lang="EN-US"><span style="font-family: courier; font-size: 13.3333px;">git push origin --delete remoteBranchName (delete branch remotely)</span></span></p><p class="MsoNormal"><span lang="EN-US"><span style="font-family: courier; font-size: 13.3333px;">git branch -l (list branches)</span></span></p><h2 style="text-align: left;">Git commands configuring Git</h2><p class="MsoNormal"><span style="font-size: 10pt;"><span style="font-family: courier;">git config --global
user.name "First Last" (user name and email should be set up as a one time config)</span></span></p><p></p><p class="MsoNormal"><span style="font-family: courier;"><span lang="EN-US" style="font-size: 10pt;">git config --global user.email <o:p></o:p></span><span style="font-size: 13.3333px;"><user@email.com></span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --global core.longpaths true (fixes an error with long path names)</span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --list (show git config)<o:p></o:p></span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --list --show-origin (show git config including where variables are defined)<o:p></o:p></span></span></p><p class="MsoNormal"><span style="font-family: courier;"><span lang="EN-US" style="font-size: 10pt;">git config --global http.<a href="https://urldefense.com/v3/__https:/dev.azure.com/.proxy__;!!OrxsNty6D4my!5gcRfyurj8HeQJKKYRAsWWlkOx2uGNc0Pun_izcFjUQ7JnqFJlqgktx0TpELiw__Ba3mGLguomJTFwXcv-Epb02XPrI_QxvrvgzF0j9TedTL$">https://dev.azure.com/.proxy</a> <a href="https://urldefense.com/v3/__https:/someaddress__;!!OrxsNty6D4my!5gcRfyurj8HeQJKKYRAsWWlkOx2uGNc0Pun_izcFjUQ7JnqFJlqgktx0TpELiw__Ba3mGLguomJTFwXcv-Epb02XPrI_QxvrvgzF0rXeSIo1$">https://someaddress</a> </span><span style="font-size: 13.3333px;">(this is a proxy related setting)</span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --global http.<a href="https://urldefense.com/v3/__https:/dev.azure.com/.proxyauthmethod__;!!OrxsNty6D4my!5gcRfyurj8HeQJKKYRAsWWlkOx2uGNc0Pun_izcFjUQ7JnqFJlqgktx0TpELiw__Ba3mGLguomJTFwXcv-Epb02XPrI_QxvrvgzF0mj1konQ$">https://dev.azure.com/.proxyauthmethod</a>
negotiate (this is a proxy related setting)<o:p></o:p></span></span></p><p class="MsoNormal"><span style="font-family: courier;"><span lang="EN-US" style="font-size: 10pt;">git config --global http.<a href="https://urldefense.com/v3/__https:/dev.azure.com/.emptyAuth__;!!OrxsNty6D4my!5gcRfyurj8HeQJKKYRAsWWlkOx2uGNc0Pun_izcFjUQ7JnqFJlqgktx0TpELiw__Ba3mGLguomJTFwXcv-Epb02XPrI_QxvrvgzF0hDzLkS9$">https://dev.azure.com/.emptyAuth</a>
true <o:p></o:p></span><span style="font-size: 13.3333px;">(this is a proxy related setting)</span></span></p><p></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --global --edit (edit the global config file in VI editor)<o:p></o:p></span></span></p><p></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --global --replace-all user.name "username" (replaces current user name)<o:p></o:p></span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --global --replace-all user.email <user@email.com><o:p></o:p></span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --unset-all credential.helper (unset named settings)<o:p></o:p></span></span></p><p class="MsoNormal"><span lang="EN-US" style="font-size: 10pt;"><span style="font-family: courier;">git config --global --set credential.helper (set the credentials helper type)</span></span></p><p class="MsoNormal">
</p><p class="MsoNormal"><span face=""Arial",sans-serif" lang="EN-US" style="font-size: 10pt; mso-ansi-language: EN-US;"> <o:p></o:p></span></p><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-9329105600126605842023-10-10T09:35:00.005+02:002023-10-10T09:40:53.182+02:00Add custom rule to new NSGs via Azure Policy<p> For governance, or operational, reasons there may be a need to ensure that certain rules are applied to all NSGs that are created within a certain scope.</p><p>This can be achieved using Azure Policy with a deployIfNotExist function.</p><p>Such a policy has already been created and is ready to use from AzAdvertizer.net, see <a href="https://www.azadvertizer.net/azpolicyadvertizer/eed215e2-d3d3-4090-8137-24d4a8260170.html" target="_blank">link here</a>:</p><p>I ran a quick test to verify the functionality and it works as expected. At the time of creation of the NSG, the policy kicks in an applies the rule right away.</p><p>The policy will let you specify one rule. So for multiple rules additional assignments can be created.</p><p>The policy looks for a suffix (the last part of the name) in the NSG name and only applies the rule if there's a match. You can re-arrange the check and have it look for a prefix instead, I have uploaded an example <a href="https://github.com/norregaard/Azure/blob/main/Policy/append-nsg-rule.json" target="_blank">here on Github</a> (can be copied in as a new definition via Azure Portal -> Policy -> Definitions).</p><p>If you want to apply the rule to all NSGs, then simply remove this check, see marked part below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvbAecwG_08obcjcYfrtU3cxZCmCkR7D4s9IfXI6OafZ4_4RAGdK__6EUuE3doUf7Mf7z3sX2QdM28n5fmaCovV-JHcGRCBfugMAIXi7wjMO5OL5Jpa5l7PX4kJGe2xByswU2do_JZ_vmuqWEbSGPzyO_TLhy_sA8S6QNRqjF4j4VilocSH7vnfQc-zUOG/s688/nsg1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="424" data-original-width="688" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvbAecwG_08obcjcYfrtU3cxZCmCkR7D4s9IfXI6OafZ4_4RAGdK__6EUuE3doUf7Mf7z3sX2QdM28n5fmaCovV-JHcGRCBfugMAIXi7wjMO5OL5Jpa5l7PX4kJGe2xByswU2do_JZ_vmuqWEbSGPzyO_TLhy_sA8S6QNRqjF4j4VilocSH7vnfQc-zUOG/w400-h246/nsg1.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The policy is parameterized, so when you create an assignment it will request you to add all relevant parameters. See example below:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Note that some of the parameters such as destinationPortRange are arrays. They should be added in the format ["3389"] (for port 3389..).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf8BeWFeDtxBCwzM6RIg2XHRZ9TxJdcYKOu5f3piVIPtdsOPmAaRvNj0avaNYpPl3_SK1klD4J7OL8N418RFSCoeTkY53V8Z715hox5pAYzlO6Mjv1z53eCtFWdNQ0X7nfAqv8jHZ2ws-MbtEL1n4ZAmGvG6zdhP2ep1wCv6G-bu41jwG0DThyphenhyphenk5evkHSM/s898/nsg2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="757" data-original-width="898" height="338" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf8BeWFeDtxBCwzM6RIg2XHRZ9TxJdcYKOu5f3piVIPtdsOPmAaRvNj0avaNYpPl3_SK1klD4J7OL8N418RFSCoeTkY53V8Z715hox5pAYzlO6Mjv1z53eCtFWdNQ0X7nfAqv8jHZ2ws-MbtEL1n4ZAmGvG6zdhP2ep1wCv6G-bu41jwG0DThyphenhyphenk5evkHSM/w400-h338/nsg2.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Below is a screenshot of the inbound rule added post NSG deployment:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0HLkPNZaZNv1JtTyCPegnR65hWNYDknhHQzN_r911yLPMEqCeADmDgt_tv2gihsDFiSfqQdt2pQEmYNJxjUU9QeMJRv-VleeAx-T4GX85N-V3LyQXGcTNadHSRAmxV2EmyQmYyvce42NN56a8zHBTnvzJaha9PIvB_s4p5O5xPxX7rE3deROjp07490lv/s1664/nsg3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="772" data-original-width="1664" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0HLkPNZaZNv1JtTyCPegnR65hWNYDknhHQzN_r911yLPMEqCeADmDgt_tv2gihsDFiSfqQdt2pQEmYNJxjUU9QeMJRv-VleeAx-T4GX85N-V3LyQXGcTNadHSRAmxV2EmyQmYyvce42NN56a8zHBTnvzJaha9PIvB_s4p5O5xPxX7rE3deROjp07490lv/w400-h185/nsg3.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Since this is a deployIfNotExist policy, this means the Assignment requires a system assigned managed identity (or a user assigned managed identity) with Network Contributor permissions which will be automatically created when you create the assignment if you have enough permissions.</div><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-30450841237687508392023-07-07T20:20:00.017+02:002023-07-07T20:36:37.881+02:00Deploying Terraform in Azure using Github Actions workflow<p> This article will summarise the steps I went through to set up Github Actions with a private repo to be able to push Terraform code from VS Code to Github and then to Azure.</p><p>It is based on a great article by Guillermo Musumeci and if you want to replicate this setup, that is the guide you should follow, <a href="https://gmusumeci.medium.com/deploying-terraform-in-azure-using-github-actions-step-by-step-bf8804b17711" target="_blank">see article</a>.</p><p>The overall steps are as follows:</p><p></p><ul style="text-align: left;"><li>Create a Service Principal (SPN) with contributor permissions</li><li>Create a storage account and a container (this is used to hold the backend state and is created up front. I did this via AZ CLI but any method, such as via the Azure Portal, will do)</li><li>Create a Private Repo in Github</li><li>From VS Code, clone the repo to your local machine</li><li>Add 4 x secrets to Github under your private repo: Repo -> Settings -> Secrets -> Actions -> New Repo Secret (these secrets contain the SPN info so that Github Actions can authenticate towards Azure</li><li>Create providers.tf, variables.tf, outputs.tf files according to article mentioned above. And a main.tf file that just contains a resource group to get started</li><li>Create Github Actions workflow using a simple Terraform YAML pipeline file</li></ul><div>These are the base steps. From now on when you create a pull request (PR), the workflow will start and will run terraform init, plan, and apply and if there are no issues the code will be pushed. It doesn't wait for you to complete the PR though but you can do that as the last step.</div><div><br /></div><div>Under Actions, you can see the status of all the workflow runs (it looks quite similar to a push pipeline run in Azure DevOps):</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuUW1Pp1bIIMXQ6zqSxj3GpoftCCHGfFeUnfJNfbTRXB3iyVuwcgAeF3TbFAXbr2gjhPTSvJdDNDgg3_r3G-kF9TMZS4U4Q1pK3iWjM3a7KKUhZes9F8hXH2UfXCMslQTGNYgiB2Dw8BXunrEIINqmnYYGLadfhHhOa-jjPcsr3O9uFZWWq7iDkHoE19iG/s2760/Screenshot%202023-07-07%20at%2019.54.46.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1338" data-original-width="2760" height="194" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuUW1Pp1bIIMXQ6zqSxj3GpoftCCHGfFeUnfJNfbTRXB3iyVuwcgAeF3TbFAXbr2gjhPTSvJdDNDgg3_r3G-kF9TMZS4U4Q1pK3iWjM3a7KKUhZes9F8hXH2UfXCMslQTGNYgiB2Dw8BXunrEIINqmnYYGLadfhHhOa-jjPcsr3O9uFZWWq7iDkHoE19iG/w400-h194/Screenshot%202023-07-07%20at%2019.54.46.png" width="400" /></a></div><br /><div>In the repo I have just placed all the files in the root folder. This works but can likely be organised better as the amount of files grow, see below:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIYnO8vKwopludUUK4_ZpidV-aUNsoasgUoP-scLH98Iiotfcjw4YwIU13DSYLN5rvEiHedFrPqGqY8_JP5pupAOTmUjhYegWty0W3--3YcyPp1k0Sc29L_pSME_RUT8ag4ilg356Q5rsRUAFBAO53WrjT1e6C0nK2N5sR7IhLVMl1tTsecxfIirzkGQTw/s1890/Screenshot%202023-07-07%20at%2020.09.10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="876" data-original-width="1890" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIYnO8vKwopludUUK4_ZpidV-aUNsoasgUoP-scLH98Iiotfcjw4YwIU13DSYLN5rvEiHedFrPqGqY8_JP5pupAOTmUjhYegWty0W3--3YcyPp1k0Sc29L_pSME_RUT8ag4ilg356Q5rsRUAFBAO53WrjT1e6C0nK2N5sR7IhLVMl1tTsecxfIirzkGQTw/w400-h185/Screenshot%202023-07-07%20at%2020.09.10.png" width="400" /></a></div><div><br /></div><div>I ran into two minor issues, these were:</div><div><br /></div><div>An error message saying the following:</div><div>"Error message: state blob is already locked" and "Terraform acquires a state lock to protect the state from being written by multiple users at the same time. Please resolve the issue above and try again. For most commands, you can disable locking with the "-lock=false" flag, but this is not recommended."</div><div><br /></div><div>I tried updating the pipeline file to include the -lock=false under the "terraform plan" command but that didn't work. For some reason I had a lock on the state file in the storage account that was created earlier. From the Azure Portal, you can navigate to the file in the blob container and from there remove the lock (or break the lease, is the terminology, see screenshot below). After doing this, there has been no more issues with the lock.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCEvqWF4yFbil3AmtQbf_ea3-N1m7Pw2J_94ujfY6PzxIgYvmKQux70cM-MFCbdLx-5lQAGRMXnE6tNMbsf1V7A7_EY-DJygkn-f8DObHY4xkv7k7IAGrjksBucNy26sBTIaukOloZ-yAgX0HSIjpXR311enYrFZxNNKsNtcUD9GolsOWl9AIsqRuZRUvi/s1410/Screenshot%202023-07-07%20at%2019.57.22.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1338" data-original-width="1410" height="380" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCEvqWF4yFbil3AmtQbf_ea3-N1m7Pw2J_94ujfY6PzxIgYvmKQux70cM-MFCbdLx-5lQAGRMXnE6tNMbsf1V7A7_EY-DJygkn-f8DObHY4xkv7k7IAGrjksBucNy26sBTIaukOloZ-yAgX0HSIjpXR311enYrFZxNNKsNtcUD9GolsOWl9AIsqRuZRUvi/w400-h380/Screenshot%202023-07-07%20at%2019.57.22.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjH_hXCm8HJBeaXmAJttiS8Sb9wjEaEtozTTfG1ft4cItPs09RXqcDb8DfxFRWyT9f0zbu5AXKLovWk5QM6Db2xxsQM7CFsmiQh-vrhaRPn2Gh0AOrsPik0K8GGG5g5wNnfRR7zZ2XMy1empDvuC84JSOHmYuXVWuK5_u_82d23dSSux6InPm6LYG0ne62A/s2314/Screenshot%202023-07-07%20at%2020.03.57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1134" data-original-width="2314" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjH_hXCm8HJBeaXmAJttiS8Sb9wjEaEtozTTfG1ft4cItPs09RXqcDb8DfxFRWyT9f0zbu5AXKLovWk5QM6Db2xxsQM7CFsmiQh-vrhaRPn2Gh0AOrsPik0K8GGG5g5wNnfRR7zZ2XMy1empDvuC84JSOHmYuXVWuK5_u_82d23dSSux6InPm6LYG0ne62A/w400-h196/Screenshot%202023-07-07%20at%2020.03.57.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">The other issue I ran into was that when I tried to deploy the first resource, the workflow was hanging for a long time and it was unclear what was holding it up.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">It turns out that it was waiting for the Microsoft.DocumentDB resource provider to be registered. This is a one time thing that is done on the under the subscription under Resource Providers (it specifies which resource types are allowed and not all resource types are allowed by default). I would recommend to just go and enable/register it manually before running the Actions workflow (it takes some time for it to register, maybe 30-40 mins).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Once this was in place the workflow has been running smoothly and any errors has been related to my code but the error messages have been pretty clear so it has been easy to address.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0VQ2G0lksynqhqrJpwQap5c_ra2eC-zWAjF6PwvX4ULedHLESym0FxT4GlxlBl9qKJTN1LogZdtWkcwXimf07cHIV1HhnVBQTcmSjc3yn9V0NSfY3jxr2WO9HHG8MCucR-JgY1cJ6KjCSE0tdxnJGE5lGHRisklIXhnvUNRO_oTHpy07HolR6EB_OSooL/s1806/Screenshot%202023-07-07%20at%2020.11.51.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1256" data-original-width="1806" height="279" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0VQ2G0lksynqhqrJpwQap5c_ra2eC-zWAjF6PwvX4ULedHLESym0FxT4GlxlBl9qKJTN1LogZdtWkcwXimf07cHIV1HhnVBQTcmSjc3yn9V0NSfY3jxr2WO9HHG8MCucR-JgY1cJ6KjCSE0tdxnJGE5lGHRisklIXhnvUNRO_oTHpy07HolR6EB_OSooL/w400-h279/Screenshot%202023-07-07%20at%2020.11.51.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">That's it. It may seem like a lot initially, but if you take step by step, it's not that bad. And the final setup is quite cool and useful.</div><div class="separator" style="clear: both; text-align: left;"><br /></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-41569091180722118122023-07-04T23:23:00.004+02:002023-07-04T23:32:55.388+02:00Use Terraform to deploy a simple VM and a VNet to Azure<p> I have had a look at deploying a simple VM that can be used for troubleshooting using Terraform. It is based on this Microsoft <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/windows/quick-create-terraform" target="_blank">Quickstart guide</a>. What I have done is to remove most of the resources from this file so it is only the minimum required resources that will be deployed. These are:</p><p></p><ul style="text-align: left;"><li>A resource group</li><li>A Windows 2022 Server</li><li>A vNIC</li><li>A public IP (so you can RDP to the box)</li></ul><div>It requires that you already have a VNet and subnet running. Preferably, there is an NSG as well which is associated with the subnet which allows for port 3389 tcp inbound.</div><div><br /></div><div>The files are <a href="https://github.com/norregaard/Azure/tree/main/Terraform" target="_blank">here on Github</a> in the Simple VM folder. There are some getting started instructions in the readme.md file as well. The four files in the folder are all part of the template, these are:</div><div><ul style="text-align: left;"><li>main.tf</li><li>outputs.tf</li><li>providers.tf</li><li>variables.tf</li></ul><div>The template creates a random admin password and assigns it to the machine. The password itself can be read post deployment by running:</div></div><div><br /></div><div><span style="font-family: courier;">terraform output -json</span></div><div><br /></div><div>In addition to this, I have also created a small template that creates a VNet and two subnets and place them in a separate resource group. Files are <a href="https://github.com/norregaard/Azure/tree/main/Terraform/Network" target="_blank">located here</a>.</div><div><br /></div><div>I personally prefer to start with smaller building blocks and then combine those later when required.</div><p></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-16359372606108484252023-07-01T18:36:00.006+02:002023-07-01T18:49:05.099+02:00Azure: ARM template for simple Win2k22 VM with trusted launch<p> It was <a href="https://techcommunity.microsoft.com/t5/azure-confidential-computing/announcing-trusted-launch-as-default-in-azure-portal/ba-p/3854872" target="_blank">recently announced</a> that <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/trusted-launch" target="_blank">trusted launch</a> is now enabled by default when deploying new Gen 2 VMs via the portal.</p><p>I have modified an ARM template for a simple Windows Server 2022 to include the Trusted Launch security features. The addition to the template is a "securityProfile" section under the virtual machine resource:<br /></p><div style="background-color: #1f1f1f; color: #cccccc; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #9cdcfe;">"securityProfile"</span>: {</div><div><span style="color: #9cdcfe;"><span> </span>"securityType"</span>: <span style="color: #dcdcaa;">"[</span><span style="color: #4ec9b0;">parameters</span>(<span style="color: #4ec9b0;">'securityType'</span>)<span style="color: #dcdcaa;">]"</span>,</div><div> <span style="color: #9cdcfe;">"uefiSettings"</span>: {</div><div> <span style="color: #9cdcfe;">"secureBootEnabled"</span>: <span style="color: #dcdcaa;">"[</span><span style="color: #4ec9b0;">parameters</span>(<span style="color: #4ec9b0;">'secureBoot'</span>)<span style="color: #dcdcaa;">]"</span>,</div><div> <span style="color: #9cdcfe;">"vTpmEnabled"</span>: <span style="color: #dcdcaa;">"[</span><span style="color: #4ec9b0;">parameters</span>(<span style="color: #4ec9b0;">'vTPM'</span>)<span style="color: #dcdcaa;">]"</span></div><div> }</div><div>}</div></div><p>Where securityType is <span style="background-color: #1f1f1f; color: #ce9178; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">TrustedLaunch</span> and the other two are bool types set to true.</p><p>You can verify that the settings are configured correctly on the Overview page of the VM, see below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXmFTMWTHPL6mIrVYx9TW1khBasebLwhW0YVVGBCgB8ZO2JkebpVmGFRysvqnJ5XdbCHFmL8Jx8jNks9yNsE9DgTS7Jv4_VF9B2FMQqasK3tctZdvy-EtKHaYxUPKxqJ2sZaXKt9kxiSsHG4CF3GOkC_GHQkGm4c2Y2qwPhf1feIlch8vXxGuv7KoDwaTt/s1290/vm1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="657" data-original-width="1290" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXmFTMWTHPL6mIrVYx9TW1khBasebLwhW0YVVGBCgB8ZO2JkebpVmGFRysvqnJ5XdbCHFmL8Jx8jNks9yNsE9DgTS7Jv4_VF9B2FMQqasK3tctZdvy-EtKHaYxUPKxqJ2sZaXKt9kxiSsHG4CF3GOkC_GHQkGm4c2Y2qwPhf1feIlch8vXxGuv7KoDwaTt/w400-h204/vm1.jpg" width="400" /></a></div><p><br /></p><p>There is a bit more info on how the VM is configured <a href="https://www.vi-tips.com/2023/06/azure-arm-template-for-simple-win2k22.html">here</a>.</p><p>The ARM template is available on Github, <a href="https://github.com/norregaard/Azure/tree/main/Simple%20VM%20for%20troubleshooting" target="_blank">link to files</a>:</p><h3 style="background-color: #f6f8fa; box-sizing: border-box; color: #1f2328; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; font-weight: var(--base-text-weight-normal, 400); margin: 0px;"><div aria-label="vm-srv-win2k22_trustedlaunch.json" class="react-directory-truncate" style="box-sizing: border-box; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: top; white-space: pre;" title="vm-srv-win2k22_trustedlaunch.json"><a aria-describedby="item-type-17" class="Link--primary" href="https://github.com/norregaard/Azure/blob/main/Simple%20VM%20for%20troubleshooting/vm-srv-win2k22_trustedlaunch.json" style="background-color: transparent; box-sizing: border-box;">vm-srv-win2k22_trustedlaunch.json</a></div></h3><div><br /></div><div><h3 style="background-color: #f6f8fa; box-sizing: border-box; color: #1f2328; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; font-weight: var(--base-text-weight-normal, 400); margin: 0px;"><div aria-label="vm-srv-win2k22_trustedlaunch.parameters.json" class="react-directory-truncate" style="box-sizing: border-box; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: top; white-space: pre;" title="vm-srv-win2k22_trustedlaunch.parameters.json"><a aria-describedby="item-type-18" class="Link--primary" href="https://github.com/norregaard/Azure/blob/main/Simple%20VM%20for%20troubleshooting/vm-srv-win2k22_trustedlaunch.parameters.json" style="background-color: transparent; box-sizing: border-box;">vm-srv-win2k22_trustedlaunch.parameters.json</a></div></h3></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-77649658573366815052023-07-01T14:55:00.009+02:002023-07-01T16:34:50.337+02:00Minimum setup for private DNS zone infrastructure in Azure<p> In this article we will look at what are the minimum requirements if you want to implement private DNS zone infrastructure and private link in a test setup to be able to use PaaS services with private endpoints. In this example we will use blob storage but it can be extended to most other PaaS services that support private endpoints.</p><p>The concept of <a href="https://learn.microsoft.com/en-us/azure/private-link/private-link-overview" target="_blank">Azure Private Link</a> is a bit complicated and people often get confused around how it works. This article aims to break it down into its simplest setup.</p><p>The following components are required:</p><p></p><ul style="text-align: left;"><li>A VNet and a subnet</li><li>A default NSG (that should be associated with the subnet)</li><li>An inbound rule on the NSG that allows port 3389 from your external IP</li><li>A private DNS zone (privatelink.blob.core.windows.net)</li><li>A VNet link between the private DNS zone and the VNet (it's a configuration on the private DNS zone)</li><li>A test VM running in the subnet (we need a source with a local IP to test connectivity towards the storage account with the private endpoint)</li><li>A public IP associated with the VM</li><li>AzCopy installed on the test VM</li><li>A storage account with a private endpoint (including an A record in the private DNS zone)</li></ul><div>Once it is all set up, we will disable all public access on the storage account and verify that the VM is connecting to the storage account on its private IP address. Following this we will try to copy a file from the VM to the storage account and the other way around as well (<a href="https://www.vi-tips.com/2018/12/installing-azcopy-v10-on-linux.html" target="_blank">using AzCopy</a>) to prove that it works.</div><h2 style="text-align: left;">Steps</h2><div>First deploy a VNet and one subnet, see example below. There are no specific requirements to this part.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMbuQ9m4kgKGNusKAlOfagzvGugwQGFol_-aN2xdsaPHoBOa4USGwLk33ehOOz4iKDED5gAFqH6Y0hc6qBH0p-tsF3P7qQfNxGAYH0OP7K9PPuR5PAQ2KGn91w4_toHzhUl6sG8ytW3ayLY2AwKozVPPVf49gd9hBvY9YKskgJ8nuz09jY6bfUyTNVbH9-/s1303/privatelink1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="495" data-original-width="1303" height="153" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMbuQ9m4kgKGNusKAlOfagzvGugwQGFol_-aN2xdsaPHoBOa4USGwLk33ehOOz4iKDED5gAFqH6Y0hc6qBH0p-tsF3P7qQfNxGAYH0OP7K9PPuR5PAQ2KGn91w4_toHzhUl6sG8ytW3ayLY2AwKozVPPVf49gd9hBvY9YKskgJ8nuz09jY6bfUyTNVbH9-/w400-h153/privatelink1.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Then create a default NSG and associate it with the subnet (this is not strictly required, but it is good practice and will come in handy later):</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_J9_ACIMs_fj3uES_nDmxRjg7YIDNDafGbFj9bNF8XuPe_qCmffpWo97O6GwUtYEsMvLOLgmsYimkN7KwLhY9MC8HfDZWJxrP-QJVDY0OAI3UsXEESq6eDc07ATIdWSvWhzzvi_sZui6CzCO7vFBWBrieYC2-1xGeMgX2hprtdD-iCuHIGhYm9JlAytnk/s1287/privatelink2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="485" data-original-width="1287" height="151" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_J9_ACIMs_fj3uES_nDmxRjg7YIDNDafGbFj9bNF8XuPe_qCmffpWo97O6GwUtYEsMvLOLgmsYimkN7KwLhY9MC8HfDZWJxrP-QJVDY0OAI3UsXEESq6eDc07ATIdWSvWhzzvi_sZui6CzCO7vFBWBrieYC2-1xGeMgX2hprtdD-iCuHIGhYm9JlAytnk/w400-h151/privatelink2.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Create a private DNS zone named: privatelink.blob.core.windows.net</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbFGTs-oMnWOeFkBaEpGZ6jCv623ZMyUqjQ0fl6LXiW0tlbX5LIAY4DO5tqriNn7X28IVsKkXq3Cq1t9VO66vfwrofdqyS0AduZscpbCYHpFWiMbFja9ClUm0ggqeYjBMcWyji1MUKpOfLxtzsfBJ8FkADZDZ5HSFkZR16YQIlxwwbo3F33llKz8NIzTLE/s1580/privatelink3.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="683" data-original-width="1580" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbFGTs-oMnWOeFkBaEpGZ6jCv623ZMyUqjQ0fl6LXiW0tlbX5LIAY4DO5tqriNn7X28IVsKkXq3Cq1t9VO66vfwrofdqyS0AduZscpbCYHpFWiMbFja9ClUm0ggqeYjBMcWyji1MUKpOfLxtzsfBJ8FkADZDZ5HSFkZR16YQIlxwwbo3F33llKz8NIzTLE/w400-h173/privatelink3.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Create a VNet link between the private DNS zone and the VNet. Go to the private DNS zone -> privatelink.blob.core.windows.net -> Virtual Network Links -> click Add</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Give the link a name and choose the VNet you just deployed. Don't check the box around auto registration, this is for VMs and not in scope for this test.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGPYVqSf4UYnVGDiF01t9lO8Dn81fVXIlzjm4IrQ4A6sydfYsNqajBPiInaXu7ft8XAmO5RzUwgQZvsi26I1N-JS_-tpSU6cMU_v9UWt430FOz94dgmGy_iThaX5UA33EXYdw44NCNTv7lkUnDORfKQexhHfYS9WWtawX4796UTxnoP_BqagI4qdzT1pIN/s1247/privatelink4.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="421" data-original-width="1247" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGPYVqSf4UYnVGDiF01t9lO8Dn81fVXIlzjm4IrQ4A6sydfYsNqajBPiInaXu7ft8XAmO5RzUwgQZvsi26I1N-JS_-tpSU6cMU_v9UWt430FOz94dgmGy_iThaX5UA33EXYdw44NCNTv7lkUnDORfKQexhHfYS9WWtawX4796UTxnoP_BqagI4qdzT1pIN/w400-h135/privatelink4.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Deploy a virtual machine with a public IP (so that you can RDP to it). You can do that via the portal or you can use a test Windows Server 2022 VM ARM template, see <a href="https://www.vi-tips.com/2023/06/azure-arm-template-for-simple-win2k22.html" target="_blank">more info here</a> (note that this VM does not have a public IP so it will have to be created separately and associated with the NIC post deployment).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Add a rule to the NSG that allows traffic on port 3389 tcp from your <a href="https://whatismyipaddress.com/" target="_blank">external IP address</a>. This is so that you can RDP to the test VM:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlIcia1vZLUoN4DDAMb2T5LpJmgdMQbSX2rjAJY-wsj-uMZ5NixL6TOFQoEmTAw5jVmY_wTrJ5Fy9zy2LCLtQp5rjVaIS9a02GRcNszEedUXPWhhlpO3WCBGFfmevhA8cvz1h2JAkYn7bWB6a8L5AnQuoSSWF12UuNL7NpJH0wbuJ7iZWnQoczm8oNNyBh/s1645/privatelink5.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="607" data-original-width="1645" height="148" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlIcia1vZLUoN4DDAMb2T5LpJmgdMQbSX2rjAJY-wsj-uMZ5NixL6TOFQoEmTAw5jVmY_wTrJ5Fy9zy2LCLtQp5rjVaIS9a02GRcNszEedUXPWhhlpO3WCBGFfmevhA8cvz1h2JAkYn7bWB6a8L5AnQuoSSWF12UuNL7NpJH0wbuJ7iZWnQoczm8oNNyBh/w400-h148/privatelink5.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">RDP to the test VM and <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10" target="_blank">download AzCopy v10</a> and verify that it can run with the command: .\azcopy (this will show the version and some help info).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKg1RvEGgQPkUpn9k-V3f9EXlink8XK9rHiMtEnvVOvTyf6_OFRX282oeNt2u8Kp_axd0_TGlODVEgZOMum17H1GgtdVZaLeqj8F7xCMmKF7Cq2nROcYd641rsIXdhJ-BnoOaEaYHc8-kK6s_diokQmRJTI_TbUl0mK4ZIGWS47n1gkJHr8HA6y_QQ5_dS/s833/privatelink6.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="384" data-original-width="833" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKg1RvEGgQPkUpn9k-V3f9EXlink8XK9rHiMtEnvVOvTyf6_OFRX282oeNt2u8Kp_axd0_TGlODVEgZOMum17H1GgtdVZaLeqj8F7xCMmKF7Cq2nROcYd641rsIXdhJ-BnoOaEaYHc8-kK6s_diokQmRJTI_TbUl0mK4ZIGWS47n1gkJHr8HA6y_QQ5_dS/w400-h185/privatelink6.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Create a storage account and under Networking -> Firewalls and virtual networks -> Choose the "Enabled from selected virtual networks and IP addresses" and add your external IP address. We will change this later to 'Disabled' but for now we need it to be able to create a blob container and add a file from the Azure portal. In addition this will let us browse the content of the blob containers.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRx9EJdhhmlDWuQYFhYQ3c-SdQPLAdO55rR75JZp-lhvCyyqF5pzdKOkbIrHyQZzXOGzDXMnjbs2IC5AqvEBlZgWuxQnso9ZvTiT2yrpReRV3tixSJC8OA2_R284NbQ_A3Etke5lIxEJFop92c56kUeNvOVi3NQHf1yxBjtNyWEINqz_pAcX4kw_1SzIWS/s1109/privatelink7.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="748" data-original-width="1109" height="270" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRx9EJdhhmlDWuQYFhYQ3c-SdQPLAdO55rR75JZp-lhvCyyqF5pzdKOkbIrHyQZzXOGzDXMnjbs2IC5AqvEBlZgWuxQnso9ZvTiT2yrpReRV3tixSJC8OA2_R284NbQ_A3Etke5lIxEJFop92c56kUeNvOVi3NQHf1yxBjtNyWEINqz_pAcX4kw_1SzIWS/w400-h270/privatelink7.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Still under Networking, choose the Private Endpoint connections tab and add a private endpoint.</div><div class="separator" style="clear: both; text-align: left;">Give the private endpoint a name e.g. pe-<name of storage account> and choose the VNet and subnet that was created earlier. When it asks around integration with a private DNS zone, choose "No". If you choose yes it will create a new Private DNS zone but we have already created a zone in a previous step.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">What adding a private endpoint does is that it creates a vNIC and attaches a local IP address from the subnet via DHCP and then it associates that vNIC with the storage account so that it can be accessed internally.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggkoxTfWJ7xeZ-B892HrEr-wMkf0mwyrglDEvlAy_NAzXgP0gMkuBw9Tdka_X27FP9j0sTNFW0ieDMeqNZpyG0ZXWAY_p-uvQGQmR8UWiVjmKp0vfmm6zeggXiAvjLt1Qa8RXKOFEjC88JNTsrHeUJf0IBYBKqw-Plm_a9Yue8bMqJK40c7moOjYk6W3No/s1470/privatelink8.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="740" data-original-width="1470" height="201" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggkoxTfWJ7xeZ-B892HrEr-wMkf0mwyrglDEvlAy_NAzXgP0gMkuBw9Tdka_X27FP9j0sTNFW0ieDMeqNZpyG0ZXWAY_p-uvQGQmR8UWiVjmKp0vfmm6zeggXiAvjLt1Qa8RXKOFEjC88JNTsrHeUJf0IBYBKqw-Plm_a9Yue8bMqJK40c7moOjYk6W3No/w400-h201/privatelink8.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Once the private endpoint is created it can be seen in the private endpoint connections tab, see above. We need to take a note of assigned local IP address. To do this, click on the private endpoint link and then going to DNS Configuration, see below:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYgRCUqKGkhEXJ8sxBzNX8eKeBNk63T9mE1XgxwOe6-aDTMrdAbGSHNcxI0-Z81VvU3JeVquaBKr9ss1jL_sCj4_4h9LslajakBegp0AgTA4QqLrmnj0jJ13IgD0NUcoOPJCJ1fClm44DnTuGsqpTO2uGK9ZXdWZJz3CpdStz8hygXjLI2C7dCgxsH0yzO/s1615/privatelink9.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="818" data-original-width="1615" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYgRCUqKGkhEXJ8sxBzNX8eKeBNk63T9mE1XgxwOe6-aDTMrdAbGSHNcxI0-Z81VvU3JeVquaBKr9ss1jL_sCj4_4h9LslajakBegp0AgTA4QqLrmnj0jJ13IgD0NUcoOPJCJ1fClm44DnTuGsqpTO2uGK9ZXdWZJz3CpdStz8hygXjLI2C7dCgxsH0yzO/w400-h203/privatelink9.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Now we need to create an A record in the private DNS zone we created earlier that points to the IP address we just noted. Go to Private DNS Zones -> privatelink.blob.core.windows.net -> Click "+ Record set". Under name, add the storage account name and under IP address, add the IP address that was recorded in the previous step, see below.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv2LIsC8Ko4B4dylBnh_Rtz6aNG0NmeDwx4h9HP8Sf7DgMQFyG3UpAVwp89D9i2CAclq0B59qk8K_jvse8zY-2lRfL6JZJ7SJxzTJcn0JCdlG3FWHF7UFTmqhkD2KBHs-DoFo9Ipm_fcl5XWxyJYOMedvapu8SVBXfZqhiA-LEBs515x4Xgu-NAB0aSjVM/s1580/privatelink10.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="683" data-original-width="1580" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv2LIsC8Ko4B4dylBnh_Rtz6aNG0NmeDwx4h9HP8Sf7DgMQFyG3UpAVwp89D9i2CAclq0B59qk8K_jvse8zY-2lRfL6JZJ7SJxzTJcn0JCdlG3FWHF7UFTmqhkD2KBHs-DoFo9Ipm_fcl5XWxyJYOMedvapu8SVBXfZqhiA-LEBs515x4Xgu-NAB0aSjVM/w400-h173/privatelink10.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Now we will verify that from the test VM can resolve the local IP of the storage account by using the regular storage account FQDN (if this doesn't work it will resolve with a public IP address and you will know that something went wrong).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Jump to the test VM and start a command prompt:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Run: nslookup <storageaccountname>.blob.core.windows.net</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The result should be the local IP, see below (the red box in the screenshot shows the result without a private endpoint and the green box shows the result after the private endpoint has been added. In the green box, the local IP 10.100.0.8 correctly shows):</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCSHC7D6P1Sry-HeDbGsiWsYRoMtFLIb-rZQJMm6DPA0dJk4FxvPd3X6G_20fpG_MFq7AM7DXwXIevepWS2bH4OSwDKcA9AqFZw1gievQtae6c9fV8ojQS3n0GYIaYzDCzwWUlAKS5E2Zn9pPYpedJqEGw4i06wEeUQhWYR5vcMplgtMOCxm_SXOZ011fK/s902/privatelink11.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="510" data-original-width="902" height="226" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCSHC7D6P1Sry-HeDbGsiWsYRoMtFLIb-rZQJMm6DPA0dJk4FxvPd3X6G_20fpG_MFq7AM7DXwXIevepWS2bH4OSwDKcA9AqFZw1gievQtae6c9fV8ojQS3n0GYIaYzDCzwWUlAKS5E2Zn9pPYpedJqEGw4i06wEeUQhWYR5vcMplgtMOCxm_SXOZ011fK/w400-h226/privatelink11.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">To verify that we can copy a file from the test VM to a blob container and vice versa, first we will create a dummy file on the VM and then a dummy file in the storage account (any txt file will do).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Go to the storage account and create a new container (in this example I call it <b>webcontent</b>,<b> </b>any name will do):</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZNL_hfV8Lnpap89S-BAnUAzaSzmU8mkc7WjGzfzDkhT6qimEKcuMApd_emmne9M89Swrf0kT4ruj1Gh9PXNf33Uf-WmodgrwD3C4ndSUrSx4-pdGL-a6R6y8teSfxGXkBeiqo1KZsDT7K3lAo6HcZo_-h2jlanTfuPMqyjaSXRBzBdwpFKIzpaCY4e3Ia/s1239/privatelink12.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="386" data-original-width="1239" height="125" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZNL_hfV8Lnpap89S-BAnUAzaSzmU8mkc7WjGzfzDkhT6qimEKcuMApd_emmne9M89Swrf0kT4ruj1Gh9PXNf33Uf-WmodgrwD3C4ndSUrSx4-pdGL-a6R6y8teSfxGXkBeiqo1KZsDT7K3lAo6HcZo_-h2jlanTfuPMqyjaSXRBzBdwpFKIzpaCY4e3Ia/w400-h125/privatelink12.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Then go into the container and click Upload to a file (in this example it's called getmefile.txt):</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjC-QIuH3rTfc5_BGOnmP6bry_4P-ugBP1ol9QoJ1EZeRts2eaNbL5TcPwbHM8as4nAxKTIxyEp66VqtzSEodOTyK2HhfM-h0XyegJUvn3-Tdp6QppZMYItMRjjN8SKicbp1lZ17JGitJtWiZsTATRXMErRxhSU9OW5fnbaVnz25zbvCyuIHQvd2_8QEAva/s1417/privatelink13.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="1417" height="127" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjC-QIuH3rTfc5_BGOnmP6bry_4P-ugBP1ol9QoJ1EZeRts2eaNbL5TcPwbHM8as4nAxKTIxyEp66VqtzSEodOTyK2HhfM-h0XyegJUvn3-Tdp6QppZMYItMRjjN8SKicbp1lZ17JGitJtWiZsTATRXMErRxhSU9OW5fnbaVnz25zbvCyuIHQvd2_8QEAva/w400-h127/privatelink13.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">For the VM to be able to access files in the storage account, we need a SAS token which will be used with the AzCopy command. Go to the storage account -> Shared access signature -> check Blob and check all three resource types (basically just allow all if you're in doubt as this is only for testing) -> click Generate SAS and connection string.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Then copy or take a note of the 'Blob service SAS URL' value at the bottom of the page:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFJagaaNUczO16ug3HMmjs_lR6jp_s3OFD-wSsul7OwQbfN0V8HITZWzIEakpVkyyPhe1obVERmcIRGky2iJ-bz7LbxyzV1UzVCIBQv1w4KK1NvCLJf8nS4psJjbxNZqKIG9ZCu8u0Fc3GirAKFxsGdpYWIivL3ofP20ebE8wd3XZeAMDXIB1LczoYHkoi/s1395/privatelink14.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="893" data-original-width="1395" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFJagaaNUczO16ug3HMmjs_lR6jp_s3OFD-wSsul7OwQbfN0V8HITZWzIEakpVkyyPhe1obVERmcIRGky2iJ-bz7LbxyzV1UzVCIBQv1w4KK1NvCLJf8nS4psJjbxNZqKIG9ZCu8u0Fc3GirAKFxsGdpYWIivL3ofP20ebE8wd3XZeAMDXIB1LczoYHkoi/w400-h256/privatelink14.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Then jump back to the test VM.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">I created a dummy file called index.html and placed in C:\users\localadmin folder. I also have AzCopy located in the same folder.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">To test that we can copy a file from the VM to the storage account, start a PowerShell window (will also work from a regular command prompt) and run the following command:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier;">.\azcopy copy ".\index.html" "https://<storage account name>.blob.core.windows.net/<b>webcontent/</b>?sv=2022-11-02&ss<link has been shortened>U%3D"</span></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">So you use the copy function and then choose a source and destination. For the source we choose the index.html file in the current folder. For the destination, we use the Blob service SAS URL but we modify it by adding the blob folder name and a '/' at the end of the FQDN and before the SAS token info (marked above in bold), also see below:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgba_IFPq18xR1ZKzVEl3mjU1HfYUI86T2pBEN-eJd4R9FsO9dIoOXjAQCuseiTF2LZh8Xof0USg39d0aT2dG09q8kzft3dT977PUe1RX8dpx5r0L24Gt1ZHSkm19AWf5N-wVtqYKiW9w1PjnhTHkfjXZGqWt0SPn90Y_NS2IVqZf25NUp9sBf_Xk743hre/s995/privatelink15.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="462" data-original-width="995" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgba_IFPq18xR1ZKzVEl3mjU1HfYUI86T2pBEN-eJd4R9FsO9dIoOXjAQCuseiTF2LZh8Xof0USg39d0aT2dG09q8kzft3dT977PUe1RX8dpx5r0L24Gt1ZHSkm19AWf5N-wVtqYKiW9w1PjnhTHkfjXZGqWt0SPn90Y_NS2IVqZf25NUp9sBf_Xk743hre/w400-h186/privatelink15.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">For the next test, we copy the content of the blob container we created earlier including the file we added and place it on the local test VM:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">It is basically just changing the source and destination in the AzCopy command (again adding the blob container name after the FQDN):</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier;">.\azcopy copy "https://</span><span style="font-family: courier;"><storage account name></span><span style="font-family: courier;">.blob.core.windows.net/<b>webcontent/</b>?sv=2022-11-</span><span style="font-family: courier;"><link has been shortened></span><span style="font-family: courier;">2BU%3D" "C:\Users\localadmin\" --recursive</span></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqGZLoGhifUprBhhmCDrOFaz2mH2hH-3A1uuYxjaiwPH2TiBlhjB4AQznEOacnk5Ety2X92IE0iDW1RugN9D2qZQXsaiErq6-wygHh0DkleZneDjQtT1R2QS3wR-G5MfYbSE0b72dBoCTOUAhZrQh23QONUj2f5PTey_MisyXPDtLlXhOW3DvJRh7ejhGg/s969/privatelink16.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="457" data-original-width="969" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqGZLoGhifUprBhhmCDrOFaz2mH2hH-3A1uuYxjaiwPH2TiBlhjB4AQznEOacnk5Ety2X92IE0iDW1RugN9D2qZQXsaiErq6-wygHh0DkleZneDjQtT1R2QS3wR-G5MfYbSE0b72dBoCTOUAhZrQh23QONUj2f5PTey_MisyXPDtLlXhOW3DvJRh7ejhGg/w400-h189/privatelink16.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">To ensure that public access is entirely disabled, you can now go to the storage account under Networking -> Firewalls and virtual networks -> Choose <b>Disabled </b>and Save.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">With this change you can no longer browse content in the blob containers via the portal.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">However, you can re-run the two AzCopy commands above and it will still work.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">If you want to verify that the files are available in the blob container, you can access them via the browser (from the test VM) by using the 'Blob service SAS URL' and then modifying it by appending the container name and file name after the FQDN:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier;">https://<storage account name>.blob.core.windows.net/<b>webcontent/getmefile.txt</b>?sv=2022-11-02&s</span><span style="font-family: courier;"><link has been shortened></span><span style="font-family: courier;">2BU%3D</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: inherit;">Another way to represent the same URL is:</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier;">https://<storage account name>.blob.core.windows.net/<b><blob container>/<file name></b></span><span style="font-family: courier;"><SAS Token></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: courier;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: inherit;">The reason you cannot just use the FQDN + the folder and file name is that even if you can technically access the content of the storage account </span>i.e.<span style="font-family: inherit;"> there is no firewall on the storage account blocking access, you still need to present the required credentials to view the content of the storage account which in this case is the SAS token that is added to the URL.</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: inherit;"><br /></span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi90zV9uN3ejNqb-Ewyonic4eS-SqAIUTuIKdiSx6gcVT-Ywtib2W3DF1wNDkTImwhcis-O7YzVcm2VYWDAQNIMLMbe46WWswdjWvbO130oXc56GfJBlJFNEolYeToeM04gZU6bYfFwF-tHwJKtv88WzsAk4hN2aa41ufrssEpWz1Fw6ii7L5n6JdhKczkz/s1271/privatelink17.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="477" data-original-width="1271" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi90zV9uN3ejNqb-Ewyonic4eS-SqAIUTuIKdiSx6gcVT-Ywtib2W3DF1wNDkTImwhcis-O7YzVcm2VYWDAQNIMLMbe46WWswdjWvbO130oXc56GfJBlJFNEolYeToeM04gZU6bYfFwF-tHwJKtv88WzsAk4hN2aa41ufrssEpWz1Fw6ii7L5n6JdhKczkz/w400-h150/privatelink17.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">If you want to get just the SAS token and not the full Blob service SAS URL, it is available under the storage account -> 'Shared access signature' in the same location as the Blob service SAS URL, see below:</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI95GYdWfW6vKMPcv7s_InCFXrbPfq_py1daYS27GAabGANFAIXuM0bHNRWQQGfKmaf_LMf76aT7gh36pYONRvhW2CT3NTihKE3RxN9Wnz1NH55Giglksw2MgfggupVk611QOHzis8TAiKb7PAWmadjlywQOL0MRPGRgz4sEgGw5yk7v8Ej8XWN_dyRs_p/s1303/privatelink18.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="822" data-original-width="1303" height="253" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI95GYdWfW6vKMPcv7s_InCFXrbPfq_py1daYS27GAabGANFAIXuM0bHNRWQQGfKmaf_LMf76aT7gh36pYONRvhW2CT3NTihKE3RxN9Wnz1NH55Giglksw2MgfggupVk611QOHzis8TAiKb7PAWmadjlywQOL0MRPGRgz4sEgGw5yk7v8Ej8XWN_dyRs_p/w400-h253/privatelink18.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">With this we have a proven setup where traffic between a VM and a storage account only runs over the Private Link and all traffic is handled via the internal network using only local IP addresses.</div></div><p></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-27349307093507049452023-06-30T09:09:00.004+02:002023-06-30T09:38:57.359+02:00Azure Firewall with availability zones and forced tunneling - ARM template<p> This firewall has a fairly specific configuration that aligns to a set of client requirements. First of all it's set up for forced tunneling. There is not a requirement to configure a default route to point towards on-prem as the default route can be advertised via BGP (in a case where you have ExpressRoute or VPN to on-prem configured as well). For this to work, 'propagate gateway routes' must be enabled on the AzureFirewallSubnet, <a href="https://learn.microsoft.com/en-us/azure/firewall/forced-tunneling" target="_blank">see here</a> for more info. </p><p>This setup requires a secondary subnet, AzureFirewallGatewaySubnet to be deployed (with a /26 size) and this subnet must have a default route pointing to the Internet.</p><p>In a default setup the firewall will have two public IP addresses but for security purposes one of those two IP addresses has been removed. The remaining public IP on the management interface is a technical requirement for internal communications with Microsoft and it can't be removed.</p><p>The ARM template, referenced below, deploys two resources. The firewall itself and one public IP. Both resources are deployed into three availability zones (AZ) (note that only certain Azure Regions support three AZs). </p><p>If you want an official firewall w. AZs bicep file from MS, see <a href="https://learn.microsoft.com/en-us/azure/firewall/deploy-bicep?tabs=PowerShell" target="_blank">this link</a>.</p><p>ARM template on <a href="https://github.com/norregaard/Azure/tree/main/Firewall" target="_blank">Github</a>:</p><h3 style="background-color: white; box-sizing: border-box; color: #1f2328; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; font-weight: var(--base-text-weight-normal, 400); margin: 0px;"><div aria-label="fw-forced-tunneling-w-az.json" class="react-directory-truncate" style="box-sizing: border-box; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: top; white-space: pre;" title="fw-forced-tunneling-w-az.json"><a aria-describedby="item-type-13" class="Link--primary" href="https://github.com/norregaard/Azure/blob/main/Firewall/fw-forced-tunneling-w-az.json" style="background-color: transparent; box-sizing: border-box; text-decoration-line: none;">fw-forced-tunneling-w-az.json</a></div></h3><div><h3 style="background-color: #f6f8fa; box-sizing: border-box; color: #1f2328; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; font-weight: var(--base-text-weight-normal, 400); margin: 0px;"><div aria-label="fw-forced-tunneling-w-az.parameters.json" class="react-directory-truncate" style="box-sizing: border-box; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: top; white-space: pre;" title="fw-forced-tunneling-w-az.parameters.json"><a aria-describedby="item-type-14" class="Link--primary" href="https://github.com/norregaard/Azure/blob/main/Firewall/fw-forced-tunneling-w-az.parameters.json" style="background-color: transparent; box-sizing: border-box;">fw-forced-tunneling-w-az.parameters.json</a></div></h3></div><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-78185938276938690012023-06-16T09:55:00.008+02:002023-07-04T20:13:54.273+02:00Installing VS Code, PowerShell, Azure PowerShell, and AZ CLI on macOS<p> It's relatively simple to get these tools installed on a Mac so you can start working with Azure and ARM templates via code.</p><p>This article just collects the relevant information and puts it in order:</p><h4 style="text-align: left;">Visual Studio Code</h4><p>This can be installed as a regular app in macOS, follow the link:</p><p class="p1" style="color: #dca10d; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;"><a href="https://code.visualstudio.com/docs/setup/mac">https://code.visualstudio.com/docs/setup/mac</a><span class="s1" style="color: black;"> </span></span></p><p class="p1" style="color: #dca10d; font-family: "Helvetica Neue"; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 13px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span class="s1" style="color: black;"><br /></span></p><h4 style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px; text-align: left;"><span style="font-family: inherit;">Homebrew</span></h4><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;"><br /></span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;">Homebrew is a package manager for macOS and Microsoft's recommended way af installing PowerShell and other tools.</p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><br /></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.3">https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.3</a></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><br /></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;">The commands to run for Homebrew are:</span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;"><br /></span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;">/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;"><br /></span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;">When Homebrew has been installed, it will ask you to run two additional commands (to add Homebrew to you PATH), these are:</span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;"><br /></span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;">(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/<REPLACE WITH USER>/.zprofile</span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;"><br /></span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;">eval "$(/opt/homebrew/bin/brew shellenv)"</span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: inherit;"><br /></span></p><h4 style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px; text-align: left;"><span style="font-family: inherit;">PowerShell</span></h4><div><span style="font-family: inherit;"><br /></span></div><div>To install PowerShell follow the same link as above:</div><div><br /></div><div><a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.3">https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.3</a></div><div><br /></div><div>Or run the following commands:</div><div><br /></div><div><span style="font-family: courier;">brew install --cask powershell</span></div><div><br /></div><div>And to run PowerShell from the terminal:</div><div><br /></div><div><span style="font-family: courier;">pwsh</span></div><h4 style="text-align: left;">Azure PowerShell</h4><div>To install Azure PowerShell, follow this link:</div><div><br /></div><div><a href="https://learn.microsoft.com/en-us/powershell/azure/install-azps-macos?view=azps-10.0.0">https://learn.microsoft.com/en-us/powershell/azure/install-azps-macos?view=azps-10.0.0</a></div><div><br /></div><div>Or run this command:</div><div><br /></div><div><span style="font-family: courier;">Install-Module -Name Az -Repository PSGallery -Force</span></div><div><br /></div><div>This will give you the Azure related commands such as:</div><div><br /></div><div><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;">Connect-AzAccount, Get-AzContext, Set-AzContext, etc<span class="Apple-converted-space"> </span></span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;"><span class="Apple-converted-space"><br /></span></span></p><h4 style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px; text-align: left;"><span style="font-family: inherit;">AZ CLI</span></h4><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;"><br /></span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;">To install, follow this link:</p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><br /></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><a href="https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-macos">https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-macos</a></p></div><div><br /></div><div>Or run:</div><div><br /></div><div><span style="font-family: courier;">brew update && brew install azure-cli</span></div><div><br /></div><div>This gives you all the AZ commands. A separate Az login is required to use the Az commands in Azure.</div><h4 style="text-align: left;">Terraform</h4><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;">To install, <a href="https://developer.hashicorp.com/terraform/downloads" target="_blank">see link</a> or run below two commands:</p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><br /></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;">brew tap hashicorp/tap</span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><span style="font-family: courier;">brew install hashicorp/tap/terraform</span></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><br /></p><p class="p1" style="font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; line-height: normal; margin: 0px;"><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-89776320605943407352023-06-15T21:59:00.001+02:002023-06-15T21:59:15.924+02:00Azure: ARM template for simple Win2k22 server for testing purposesI have previously posted and article with a Win2k19 simple server using a key vault, <a href="https://www.vi-tips.com/2022/06/arm-template-for-simple-win2k19-vm-for.html" target="_blank">see here</a>. This current post will describe a simple Win2k22 server using a regular username and password set in the paremeters file of the ARM template.<div><br /></div><div>The VM is deployed with tcp port 3389 opened locally on the VM, but no public IP is added. If you need to RDP to the VM from the internet, a public IP should be added and associated with the vNIC post deployment. </div><div><br /></div><div>The ARM template files can be found here:</div><div><br /></div><div><h3 style="background-color: #f6f8fa; box-sizing: border-box; color: #1f2328; font-family: -apple-system, "system-ui", "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; font-weight: var(--base-text-weight-normal, 400); margin: 0px;"><div aria-label="vm-srv-win2k22.json" class="react-directory-truncate" style="box-sizing: border-box; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: top; white-space: pre;" title="vm-srv-win2k22.json"><a aria-describedby="item-type-35" class="Link--primary" href="https://github.com/norregaard/Azure/blob/main/Simple%20VM%20for%20troubleshooting/vm-srv-win2k22.json" style="background-color: transparent; box-sizing: border-box;">vm-srv-win2k22.json</a></div></h3></div><div><br /></div><div><h3 style="background-color: white; box-sizing: border-box; color: #1f2328; font-family: -apple-system, "system-ui", "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; font-weight: var(--base-text-weight-normal, 400); margin: 0px;"><div aria-label="vm-srv-win2k22.parameters.json" class="react-directory-truncate" style="box-sizing: border-box; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: top; white-space: pre;" title="vm-srv-win2k22.parameters.json"><a aria-describedby="item-type-36" class="Link--primary" href="https://github.com/norregaard/Azure/blob/main/Simple%20VM%20for%20troubleshooting/vm-srv-win2k22.parameters.json" style="background-color: transparent; box-sizing: border-box; text-decoration-line: none;">vm-srv-win2k22.parameters.json</a></div></h3></div><div><br /></div><div>Remember to update the parameters file with relevant information.</div><div><br /></div><div><a href="https://github.com/norregaard/Azure/tree/main/Simple%20VM%20for%20troubleshooting" target="_blank">See here</a> (the README file) for deployment information via PowerShell.</div><div><br /></div><div>This VM has automatic shutdown configured at 19.00 hrs. UTC, daily.</div><div><br /></div><div><br /></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-17207394402003319772023-06-12T14:34:00.001+02:002023-06-13T09:07:54.992+02:00Azure: Deploy a key vault with a private endpoint<p> This post describes an ARM template that deploys a key vault, with a private endpoint, into an existing VNet and subnet. The key vault does not have to be placed in the same resource group as the VNet.</p><p>It requires the following to already be deployed:</p><p></p><ul style="text-align: left;"><li>VNet</li><li>Subnet</li><li>Resource group (for the key vault and private endpoint)</li><li>Private DNS zone (privatelink.vaultcore.azure.net)</li></ul><p></p><p>The key vault will be configured to use RBAC and will allow ARM templates to retrieve content (in this case it is to allow an ARM template that deploys a VM to retrieve a secret from the key vault), see below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieBlOI151kvrBlPtbsx2tHIPg9pPjZQaVoxYHYCOCJGJBs5qEY6uI2DB3gBvUmHHrPDxL6dUfTBwScYyJFMKDIe6psFo2e7wBqPWONb6pBY7I4n_A-AtwPRIQKGnh1hXmuobPTYdoN5Y-pcAMS0A7qTuQwAatJy0opOTRclnMkIPpvhlOQmuLpNA5-AA/s957/kv1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="726" data-original-width="957" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieBlOI151kvrBlPtbsx2tHIPg9pPjZQaVoxYHYCOCJGJBs5qEY6uI2DB3gBvUmHHrPDxL6dUfTBwScYyJFMKDIe6psFo2e7wBqPWONb6pBY7I4n_A-AtwPRIQKGnh1hXmuobPTYdoN5Y-pcAMS0A7qTuQwAatJy0opOTRclnMkIPpvhlOQmuLpNA5-AA/w400-h304/kv1.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">For the Network settings, all public access is disabled. With a private endpoint you'll be able to create and read secrets from the key vault in the Azure Portal via internal routing. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The "Allow trusted Microsoft services to bypass the firewall" setting is checked. This allows Azure DevOps, via a pipeline, to deploy an ARM template that deploys a VM that retrieves the secret from the KV which typically is the local admin password for the VM, see below:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3zLvIupCSshH_fCwZKqo01QcwP5eBba47IvxqMBHrIuAdyySLZi6TbsJPhR5b9kHPJq9JirJd8Yy1de0cpeKtaLJ6Tx5yFSF-MzLsKsXe8G0fy57nMMNTYLRcMg53aIUaogwXv_o07XlKBbxsRfYR5whFeWIfi-7PMiS66ebidaF4NDAu4_BWWwpM6g/s1405/kv2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="529" data-original-width="1405" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3zLvIupCSshH_fCwZKqo01QcwP5eBba47IvxqMBHrIuAdyySLZi6TbsJPhR5b9kHPJq9JirJd8Yy1de0cpeKtaLJ6Tx5yFSF-MzLsKsXe8G0fy57nMMNTYLRcMg53aIUaogwXv_o07XlKBbxsRfYR5whFeWIfi-7PMiS66ebidaF4NDAu4_BWWwpM6g/w400-h150/kv2.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The private endpoint (PE) can be found under, Networking -> Private endpoint connections.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">A local IP from the specified subnet will be dynamically assigned to the PE.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Note that this template will not create the A record in the private DNS zone for the private endpoint. This is usually automated via Azure Policy, <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale#policy-definitions" target="_blank">see here</a> for more info under point 3.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUUGUGKnyrd3jitMyTCjw6rIUN308ayekP8aAgNwTwPtjwANfHHe9kvf6XkbSmtbC9_X8-x1halTZo4w2QhPFzJPPSFZ_2ZM6FBRiABS7cUdGD2RK4c_PF_DY7NEHMh2ercDtrUb2Z27A28B2AWZhQ-fGs5iJ0qEibonU0Lt3J7_v564KHXW0AsXQFtQ/s1356/kv3.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="445" data-original-width="1356" height="131" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUUGUGKnyrd3jitMyTCjw6rIUN308ayekP8aAgNwTwPtjwANfHHe9kvf6XkbSmtbC9_X8-x1halTZo4w2QhPFzJPPSFZ_2ZM6FBRiABS7cUdGD2RK4c_PF_DY7NEHMh2ercDtrUb2Z27A28B2AWZhQ-fGs5iJ0qEibonU0Lt3J7_v564KHXW0AsXQFtQ/w400-h131/kv3.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The <a href="https://github.com/norregaard/Azure/blob/main/Key%20vault/keyvault-w-privateendpoint-001.json" target="_blank">ARM template</a> and <a href="https://github.com/norregaard/Azure/blob/main/Key%20vault/keyvault-w-privateendpoint-001.parameters.json" target="_blank">parameters file</a> can be found on Github. There's also a <a href="https://github.com/norregaard/Azure/tree/main/Key%20vault" target="_blank">README</a> with a bit more info. For more info on the format of the subnet ID string, <a href="https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-mvss-existing-vnet#identify-subnet" target="_blank">see here</a>.</div><br /><br /><p><br /></p><p><br /></p><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-71008161465350556962023-06-07T12:03:00.000+02:002023-06-07T12:03:00.084+02:00Azure Policy - Allow only specified IP ranges and regions for VNets<p> At current client we had a request to apply a policy to enforce that only VNets using specified IP address ranges and regions will be allowed. So for example, if you create a VNet in West Europe, then it must be within the 10.100.0.0/16 IP address space and if in North Europe it must be within 10.200.0.0/16 and 10.201.0.0/16. And anything outside that must be denied.</p><p>There is no built-in policy for this. It was possible to find simpler versions online of the above but we couldn't find anything that fit all the requirements.</p><p>The closest we got was a slimmed down version of <a href="https://www.azadvertizer.net/azpolicyadvertizer/network_address-space-should-be-pre-allocated-for-region.html" target="_blank">this policy</a> from AzPolicyAdvertizer named "Address space must be pre-allocated for region".</p><p>However, it didn't take into account extended VNets. So if you have a VNet consisting of two or more IP address ranges, it will show as non-compliant.</p><p>We raised a ticket with Microsoft support and after a few days they came back with an updated policy that works.</p><p>The functioning policy can be found <a href="https://github.com/norregaard/Azure/blob/main/Policy/Deny-the-creation-of-vnets-using-non-approved-IP-ranges.json" target="_blank">here on Github</a>. The only thing that needs to be updated is the content of the "spokeAllocations" array.</p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-21089869570493788002023-03-20T15:19:00.003+01:002024-01-26T09:55:38.501+01:00Auto create DNS records for RSV with policy including region code<p> When using private endpoints at scale, the <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale" target="_blank">recommended setup</a> from Microsoft is to use Azure Policy to automatically create the DNS records in the central private DNS zones when the private endpoints are created. The reason for this is that users or owners of the spokes or landing zones do not have permissions to create A records in the central private DNS zones in the Hub.</p><p>The <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/private-link-and-dns-integration-at-scale#policy-definitions" target="_blank">policies specified</a> by Microsoft work as long as a region code does not have to be specified in the private DNS zone name (which is the case for most of them, see full <a href="https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns" target="_blank">list here</a>). However, for e.g. Recovery Services Vault for Azure Backup (and for AKS/Kubernetes), this is the case.</p><p>The zones are region specific, for West Europe it's: privatelink.<b>we</b>.backup.windowsazure.com and for Sweden Central it's privatelink.<b>sdc</b>.backup.windowsazure.com.</p><p>The default policy only does a check on the subResource (or groupId) value which in this case is: <b>AzureBackup</b>. The result of having two policies (e.g. one for West Europe and one for Sweden Central) running with the same subResource value is that DNS records for the private endpoints are randomly generated in the two private DNS zones.</p><p>A fix for this is to add a conditional check in the policy on the location of the private endpoint (PE). This way it is ensured that the DNS records are created in the correct zone that matches the location of the PE.</p><p>The full policy is available on Github, see <a href="https://github.com/norregaard/Azure/blob/main/Policy/Auto-create-DNS-record-private-DNS-zone-w-location.json" target="_blank">link here</a>.</p><p>The main change can be seen below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuCco1MhPYaZMAhAiPG3gp2D9VMBt12wBJQj_HaLMCCVAfpHk_ht5BUth1MBauyfYuqGr9BNR8-BcKBK8Cs5vGowucwy-3OWBk5FK3mWWavxz_nQzMWa4oIqfPYx5cAsYKp-7tSTCMYO4Oh08SwKGDPZG837K5XkS_7KvwIzg6xKVUr9tR8AUlQm2CDw/s961/policy.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="524" data-original-width="961" height="217" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuCco1MhPYaZMAhAiPG3gp2D9VMBt12wBJQj_HaLMCCVAfpHk_ht5BUth1MBauyfYuqGr9BNR8-BcKBK8Cs5vGowucwy-3OWBk5FK3mWWavxz_nQzMWa4oIqfPYx5cAsYKp-7tSTCMYO4Oh08SwKGDPZG837K5XkS_7KvwIzg6xKVUr9tR8AUlQm2CDw/w400-h217/policy.png" width="400" /></a></div><br /><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-32258129746269588492023-02-14T11:37:00.006+01:002023-02-14T11:37:59.727+01:00Azure: Adding a resource lock via ARM template<p> Resource locks can be added on Azure resources to prevent unintended deletion or unwanted changes. There are two types of locks: 1) Do not delete (where you can make updates/changes) and 2) Read only (where you can neither change nor delete), see more <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources?tabs=json" target="_blank">info here</a>.</p><p>It can make sense to add locks to critical infrastructure resources, but note that it also comes with additional management overhead and some caveats, see link above.</p><p>Locks can be added to either a resource or a resource group.</p><p>In the example in this post, we'll look at a read only lock for a specific resource and how to add this to an ARM template.</p><p>It's fairly simple to add. The lock is a separate resource, an ExpressRoute circuit, but the principle is the same for all resources.</p><p>The lock resource itself is as follows:</p><br /><span style="font-family: courier;"> {<br /> <span> </span>"condition": "[parameters('enableReadOnlyLock')]",<br /> <span> </span>"type": "Microsoft.Authorization/locks",<br /> <span> </span>"apiVersion": "2020-05-01",<br /> <span> </span>"name": "ER circuit lock",<br /> <span> </span>"scope": "[concat('Microsoft.Network/expressRouteCircuits/', parameters('circuitName'))]",<br /> <span> </span>"dependsOn": [<br /> <span> </span><span> </span>"[resourceId('Microsoft.Network/expressRouteCircuits', <span> </span>parameters('circuitName'))]"<br /> <span> </span>],<br /> <span> </span>"properties": {<br /> <span> </span>"level": "ReadOnly",<br /> <span> </span>"notes": "ER circuit should not be updated or deleted"<br /> <span> </span>}<br /> } </span><br /><div><span style="font-family: courier;"><br /></span></div>A condition is added which can be set to true or false, this way it easy to disable the lock if required. The parameter is declared at the top of the ARM template:<div><br /></div><div><div><br /><span style="font-family: courier;">"enableReadOnlyLock": {<br /> <span> </span>"type": "bool",<br /> <span> </span>"defaultValue": false,<br /> <span> </span>"metadata": {<br /> <span> </span>"description": "Determines if the resources should be locked to prevent changes or deletion."<br /> <span> </span>}<br /> },</span></div><div><br /><div>The full template including parameters file is uploaded to GitHub, see below:</div><div><br /></div><div><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/ExpressRoute/er-circuit-w-private-peering.json" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; text-decoration-line: none; white-space: nowrap;" title="er-circuit-w-private-peering.json">er-circuit-w-private-peering.json</a></div><div><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/ExpressRoute/er-circuit-w-private-peering.parameters.json" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; white-space: nowrap;" title="er-circuit-w-private-peering.parameters.json">er-circuit-w-private-peering.parameters.json</a></div><div><br /></div><div><br /></div><div><br /></div></div></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-30090595377862037302023-02-14T11:09:00.002+01:002023-02-14T11:13:49.641+01:00Updating ExpressRoute Circuit fails with error: Peering/Circuit was recently updated by service provider<p> Recently I had to make some minor updates to an ExpressRoute Circuit via Azure DevOps. The change was to apply a read-only resource lock via code so no actual changes were introduced to the circuit configuration itself. Still, when running the pipeline it fails showing the error below:</p><p><span style="font-family: courier;">"Error: The Peering/Circuit was recently updated by the service provider and is not currently in sync" </span><span style="font-family: inherit;">(see screenshot below).</span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYgY2AsMGDl0-WJaphp668ADPcnXKkw-3f6PEXkv79VU2AnQxkYYzOXe17iUeWKLQGLenElkr2uPzPnQbMSrpyzquTRlwuPD3u6nzqFhN5fndAiQsCXAVyju1hVOR9ip9G_lv_llxMTYYOetl8jMZTC5ZsgjSKNA3IrvMQtax7WE6UX73S0NKuoM9RPw/s1226/Error_in_AzureDevOps_pipeline.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="432" data-original-width="1226" height="141" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYgY2AsMGDl0-WJaphp668ADPcnXKkw-3f6PEXkv79VU2AnQxkYYzOXe17iUeWKLQGLenElkr2uPzPnQbMSrpyzquTRlwuPD3u6nzqFhN5fndAiQsCXAVyju1hVOR9ip9G_lv_llxMTYYOetl8jMZTC5ZsgjSKNA3IrvMQtax7WE6UX73S0NKuoM9RPw/w400-h141/Error_in_AzureDevOps_pipeline.png" width="400" /></a></div><div><br /></div>Looking in the portal after this failed push, the ExpressRoute (ER) circuit was in failed state and the error message suggests to refresh the circuit, see screenshot below. When clicking refresh, the status goes back into green. Note that even if the portal shows an error, the circuit itself did not drop any traffic (which is good as this is a pretty critical component).<div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc4n418CVCzUQM-IzVdUMUjft8bfITks6ee3M2bNdCz9tLt4dFCKTeFTkRmElwepMLkgNniozBcz9a_AyaH-HQcFPnlJOsNpt2bsab-FfEVvvD3nWEZM75xMd8ljOQcHnXSklYDB_yI2YUY2T7LpIORMDzBxcXAgfQ25c5TOy_rruHOCPvnLMrvpIGrg/s1142/Error_in_AzurePortal_ER_Circuit_v2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="502" data-original-width="1142" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc4n418CVCzUQM-IzVdUMUjft8bfITks6ee3M2bNdCz9tLt4dFCKTeFTkRmElwepMLkgNniozBcz9a_AyaH-HQcFPnlJOsNpt2bsab-FfEVvvD3nWEZM75xMd8ljOQcHnXSklYDB_yI2YUY2T7LpIORMDzBxcXAgfQ25c5TOy_rruHOCPvnLMrvpIGrg/w400-h176/Error_in_AzurePortal_ER_Circuit_v2.png" width="400" /></a></div><br /><div><br /><div><p>This particular ER circuit has a private peering which has been configured by the service provider (this can either be done by the customer or by the service provider). However, we knew that the service provider had not recently updated the circuit so it did not make much sense.</p><p>We raised a ticket to Microsoft and they suggested to specify the "gatewayManagerEtag" in the code, see more <a href="https://learn.microsoft.com/en-us/azure/templates/microsoft.network/expressroutecircuits?pivots=deployment-language-bicep" target="_blank">info here</a>. I didn't find much info around this parameter other than it's a string and supposedly it can be used to ensure that the service provider or the customer don't accidentally override a common setting with an older value. To identify the current value I went to the portal and exported the ARM template for the ER circuit (the value was 7).</p><p>After specifying this parameter, the push went through with no problems. And at the same time, we specified the private peering configuration info in the ARM template, which was not there from the beginning (as it had been set by the service provider). Additionally, we tried updating another ER circuit where we ourself had initially configured the private peering in the ARM template and where the gatewayManagerEtag value was empty or just showing as "". This also works fine and we included this on all circuits for consistency.</p><p>I've uploaded an example ARM template to GitHub which includes private peering configuration, the gatewayManagerEtag and a read-only resource lock, see links below (remember to update the content of the parameters file):</p><p><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/ExpressRoute/er-circuit-w-private-peering.json" style="background-color: #f6f8fa; box-shadow: none; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; outline-offset: -2px; outline: none; white-space: nowrap;" title="er-circuit-w-private-peering.json">er-circuit-w-private-peering.json</a></p><p><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/ExpressRoute/er-circuit-w-private-peering.parameters.json" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; text-decoration-line: none; white-space: nowrap;" title="er-circuit-w-private-peering.parameters.json">er-circuit-w-private-peering.parameters.json</a></p></div></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-42783702313932214912023-02-03T10:43:00.001+01:002023-02-03T10:43:12.635+01:00PowerShell command to deploy ARM templates<p> This is just a quick note on the PowerShell command to deploy ARM templates (I usually push the templates via pipelines so one tend to forget :))</p><p>Command should be run from the location where the ARM templates are located (or update the file path).</p><p></p><p class="MsoNormal"><span lang="EN-US"><span style="font-family: courier;">New-AzResourceGroupDeployment
-Name deploy_some_storage_account -ResourceGroupName rg-some_resource_group-001 `<o:p></o:p></span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;"><span style="font-family: courier;">
-TemplateFile .\deploy_storage_account.json `<o:p></o:p></span></span></p>
<p class="MsoNormal"><span style="font-family: courier;"><span lang="EN-US" style="mso-ansi-language: EN-US;">
-TemplateParameterFile .\</span><span lang="EN-US">deploy_storage_account</span></span><span lang="EN-US"><span style="font-family: courier;">.parameters.json</span><b><o:p></o:p></b></span></p><p class="MsoNormal"><span style="font-family: inherit;">There is another <a href="https://github.com/norregaard/Azure/tree/main/Key%20vault" target="_blank">example here</a> for deploying a key vault.</span></p><p class="MsoNormal"><span style="font-family: inherit;">And here's link to the <a href="https://learn.microsoft.com/en-us/powershell/module/az.resources/new-azresourcegroupdeployment?view=azps-9.3.0" target="_blank">Microsoft documentation</a>.</span></p><br /><p></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-72162368845519220872023-02-03T10:31:00.003+01:002023-02-03T10:31:46.483+01:00Deploy an Azure Firewall with no public IP for data plane with ARM (and forced tunneling)<p> At current client we have multiple Azure Firewalls running with <a href="https://learn.microsoft.com/en-us/azure/firewall/forced-tunneling" target="_blank">forced tunneling</a> to on-prem. By default this requires two public IP addresses, one for the data plane (or for customer traffic) and the other other is for service management traffic (exclusively used by the Azure platform).</p><p>Since there is no ingress or egress to the internet on these firewalls as all traffic is routed to on-prem, there was a request from the security team to remove the public IP for the data plane (so there will just be one public IP left per firewall).</p><p>If deploying from the portal or via PowerShell, this cannot be configured during deployment, but can be done post deployment by going the Azure Firewall -> Public IP configuration -> Choose the three dots to the right of the public IP -> choose Edit -> And then choose None so that no public IP is associated., see screenshot below for example.</p><p>Note that you cannot choose to delete the public IP directly, and the other option to Manage Public IP also cannot be used to disassociate the public IP (which is perhaps a bit confusing).</p><h2 style="text-align: left;">Deploy with ARM template</h2><p>If deploying the firewall using an ARM template, it is possible to configure just one public IP at the time of deployment.</p><p>I've put an example template on GitHub that can be used for reference:</p><p><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/Firewall/fw-no-public-ip.json" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; white-space: nowrap;" title="fw-no-public-ip.json">fw-no-public-ip.json</a></p><p><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/Firewall/fw-no-public-ip.parameters.json" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; white-space: nowrap;" title="fw-no-public-ip.parameters.json">fw-no-public-ip.parameters.json</a></p><p>The following resources should be deployed in advance:</p><p></p><ul style="text-align: left;"><li>A resource group</li><li>A VNet</li><li>2 x subnets (AzureFirewallSubnet and AzureFirewallManagementSubnet, both should be /26 in size)</li><li>A firewall policy</li></ul><p></p><p>References to the subnets and the policy should be updated in the parameters file before running it.</p><p>On the second screenshot below you can see the result post deployment and that the FW has no firewall public IP but it does have one for management traffic. On the first screenshot below you can that there is an entry for the public IP called "DoesNotExist" (just an example name) but it is not associated with a public IP.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga0r07r9zSYAtoEx2g9n5ETN_vP2aijDeVkrAlCIA18FxkAAlMrEBOcOCvuEnQuuheAkpOY5P9btoLB-NmoYfGh2WYFOVwUA2Cw6xYY3HHIWazxv6niUQP_SMC6STSeSDqLVGECOEsWzAG1BWyOwyXJCnzz5ephYQTQmSzCH5r_lJ6QHIvGEtkhr050g/s1299/fw1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="416" data-original-width="1299" height="127" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga0r07r9zSYAtoEx2g9n5ETN_vP2aijDeVkrAlCIA18FxkAAlMrEBOcOCvuEnQuuheAkpOY5P9btoLB-NmoYfGh2WYFOVwUA2Cw6xYY3HHIWazxv6niUQP_SMC6STSeSDqLVGECOEsWzAG1BWyOwyXJCnzz5ephYQTQmSzCH5r_lJ6QHIvGEtkhr050g/w400-h127/fw1.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ8zYebGqmyaBFY5YkoUCdvME6p6lTd6Y5Fj0Q-S0W6zTYQPwyDAB2FJrKIpVVrxy7OaUarzWZYUDdk_N-VF-66oG_un2gC-nYrTY2rLEoCTgRVWWo6KRBjx54bwEbGs0PXd5drEkEUvqGrQ7732ovgOVUi3KqpUs7M0GXoGsQ0aQ7JjYCiYx2sg3MBg/s1385/fw2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="607" data-original-width="1385" height="175" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ8zYebGqmyaBFY5YkoUCdvME6p6lTd6Y5Fj0Q-S0W6zTYQPwyDAB2FJrKIpVVrxy7OaUarzWZYUDdk_N-VF-66oG_un2gC-nYrTY2rLEoCTgRVWWo6KRBjx54bwEbGs0PXd5drEkEUvqGrQ7732ovgOVUi3KqpUs7M0GXoGsQ0aQ7JjYCiYx2sg3MBg/w400-h175/fw2.png" width="400" /></a></div><br /><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-20692249541982131332022-11-29T11:55:00.003+01:002022-11-29T11:55:56.655+01:00Azure: Bulk update secrets in Azure key vault using ARM<p> At current client we have a requirement to periodically rotate some 50 secrets in Azure key vaults that are used for storing the shared keys for S2S VPN connections.</p><p>For keys (not secrets) there is an auto rotation option that is now GA, <a href="https://learn.microsoft.com/en-us/azure/key-vault/keys/how-to-configure-key-rotation" target="_blank">see link</a>, however it doesn't apply to secrets.</p><p>There's an option to use logic apps to rotate secrets but the MS documentation I could find is not very good.</p><p>At first I was looking to write a PowerShell script that pulls the new secrets from a csv file and then applies them to a key vault, but it ended up being easier just creating an ARM template that does the same.</p><p>The secret names and secret values are stored in the parameters file (in the file there are a couple of test secrets for demonstration purposes). The name of the KV should also be updated in the parameters file. The ARM template itself does not have to be changed.</p><p>If you add a secret in the param file that already exists in the KV, it will overwrite the existing secret and add a new/current version and keep the previous version(s) as Older versions, see screenshot below. If you have secrets already in the KV that are not defined in the param file, these secrets will not be changed or deleted. And if you add new secrets in the param file that are not in the KV currently, they will be added.</p><p>The template is available on <a href="https://github.com/norregaard/Azure/tree/main/Key%20vault" target="_blank">Github</a>, the files are:</p><p><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/Key%20vault/deploy_secrets.json" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; text-decoration-line: none; white-space: nowrap;" title="deploy_secrets.json">deploy_secrets.json</a></p><p><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/Key%20vault/deploy_secrets.parameters.json" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; text-decoration-line: none; white-space: nowrap;" title="deploy_secrets.parameters.json">deploy_secrets.parameters.json</a></p><p><a class="js-navigation-open Link--primary" data-turbo-frame="repo-content-turbo-frame" href="https://github.com/norregaard/Azure/blob/main/Key%20vault/Powershell%20cmd%20to%20run%20ARM%20template.txt" style="background-color: #f6f8fa; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; white-space: nowrap;" title="Powershell cmd to run ARM template.txt">Powershell cmd to run ARM template.txt</a></p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguofxf8t1DXismIQJTwJlZqKXL4mWHc1Mk4y8Cda65GuX5VXR-BIlcw00IvNHeGqITfQxYPG1hinWNgjM0xL83EFylPY38XvZnFKRRVdMgLPsv8DeFBqp_NEelXlD_OohL3mMbz1k1WtG0n8VIa8v_MfPBWe6F0fHqSB-x-EVgW8w3LC7cP582vD8g0g/s648/kv-secret.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="404" data-original-width="648" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguofxf8t1DXismIQJTwJlZqKXL4mWHc1Mk4y8Cda65GuX5VXR-BIlcw00IvNHeGqITfQxYPG1hinWNgjM0xL83EFylPY38XvZnFKRRVdMgLPsv8DeFBqp_NEelXlD_OohL3mMbz1k1WtG0n8VIa8v_MfPBWe6F0fHqSB-x-EVgW8w3LC7cP582vD8g0g/s320/kv-secret.png" width="320" /></a></div><br /><p><br /></p><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-19529876763191879032022-07-11T00:15:00.004+02:002022-07-11T00:15:58.846+02:00Azure Firewall with Azure policy and IP groups using Bicep<p> In the <a href="https://www.vi-tips.com/2022/07/azure-deploy-azure-firewall-in.html">previous</a> post I deployed a firewall with some additional components such as VMs, NSGs, and route tables to have a working test setup. In this post it will mainly be the FW, the FW policies and the IP groups that will be deployed.</p><p>This post is based on the this <a href="https://github.com/Azure/azure-quickstart-templates/tree/master/quickstarts/microsoft.network/azurefirewall-create-with-firewallpolicy-apprule-netrule-ipgroups" target="_blank">Quickstart template</a> with only a few modifications. The main reason I do this is to verify that the files work in my environment to inspect deployed resources.</p><p>I have uploaded the modified files to <a href="https://github.com/norregaard/Azure/tree/main/Firewall" target="_blank">Github</a> (fw-with-policy-001.bicep and fw-with-policy-001.parameters.json). </p><p>The resources deployed can be seen below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7Va9lC0V-_mjLu5iZY3RNOfVLLhAjD7eYc-66rvLpANLYdXbnQ6375FO-3EhnIdnZ48Xy7O1frVqnOqnhiM7gfEdJmgdD4BCbpvxOVhYEnZohr7CUnJ0wD2Y6bMh1mVXoJ5AVFY7B2gA_uJfiuoay3qvNHwp-eDod8Gq67pv95xx3Crqaut0LdanrNA/s829/fwpolicy1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="246" data-original-width="829" height="95" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7Va9lC0V-_mjLu5iZY3RNOfVLLhAjD7eYc-66rvLpANLYdXbnQ6375FO-3EhnIdnZ48Xy7O1frVqnOqnhiM7gfEdJmgdD4BCbpvxOVhYEnZohr7CUnJ0wD2Y6bMh1mVXoJ5AVFY7B2gA_uJfiuoay3qvNHwp-eDod8Gq67pv95xx3Crqaut0LdanrNA/s320/fwpolicy1.png" width="320" /></a></div><br /><p>The two IP groups are used in combination with the policy. The policy is associated with the firewall. The are two rule collection groups defined with a total of three rules, see below two screenshots:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR-g6FY6msCPmP2dyEe5MfnaeUxS7Vbvz0e-v_tcEj1-rhAqW2jnU14KDNZHoW5pLDxG9JBiJIzX2HOCfocdhtMtilRRspePqqPS2OS0wb5uo89LyiO6hUcMvsBN4mRR7ykUZfMsKpG3MKe74B0qpZxK-gUt43TUO9kSP_6CrBbPIwug5CeYq24p0a6A/s1160/fwpolicy2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="401" data-original-width="1160" height="111" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR-g6FY6msCPmP2dyEe5MfnaeUxS7Vbvz0e-v_tcEj1-rhAqW2jnU14KDNZHoW5pLDxG9JBiJIzX2HOCfocdhtMtilRRspePqqPS2OS0wb5uo89LyiO6hUcMvsBN4mRR7ykUZfMsKpG3MKe74B0qpZxK-gUt43TUO9kSP_6CrBbPIwug5CeYq24p0a6A/s320/fwpolicy2.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAVplXvteeA3IrwXrIsO8Zx5ADuKUyMBCZ_ih0A7ovVRmyZc9qAsLlpKQNHgOqAkl6p8zdYzBiBDPPrIsX7YwYa13STATcGSK27z-mixDeDFWK8RbLUZDZc28X1IiTuqALk-SHQrvbJhikhveN13aN-zH2BjUr46iNuk5tYh086A6QEzhifFK--jONKg/s1266/fwpolicy3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="636" data-original-width="1266" height="161" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAVplXvteeA3IrwXrIsO8Zx5ADuKUyMBCZ_ih0A7ovVRmyZc9qAsLlpKQNHgOqAkl6p8zdYzBiBDPPrIsX7YwYa13STATcGSK27z-mixDeDFWK8RbLUZDZc28X1IiTuqALk-SHQrvbJhikhveN13aN-zH2BjUr46iNuk5tYh086A6QEzhifFK--jONKg/s320/fwpolicy3.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p><br /></p>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0tag:blogger.com,1999:blog-2012423892447387468.post-90803628206667883352022-07-10T23:05:00.002+02:002022-07-11T01:17:01.242+02:00Azure: Deploy Azure Firewall in availability zones using Bicep<p> Microsoft has some great Quickstart templates for deploying resources as codes. For this post I've looked at deploying an Azure Firewall into multiple availability zones using Bicep.</p><p>The Quickstart guide used can be <a href="https://azure.microsoft.com/da-dk/resources/templates/azurefirewall-with-zones-sandbox/" target="_blank">found here</a>.</p><p>The template needs only a few changes. I updated the VNet name and IP address info and inserted a reference to a key vault for the VM admin password.</p><p>My updated files can be found on <a href="https://github.com/norregaard/Azure/tree/main/Firewall" target="_blank">Github</a> (fw-conn-weu-001.bicep and fw-conn-weu-001.parameters.json).</p><p>The firewall is deployed into three availability zones in West Europe. This increases the availability to 99.99%. There is still only one logical firewall deployed, so the increased availability is managed in the background by Microsoft. And from a Azure Portal point of view it looks the same as if only deploying to one availability zone.</p><p>The availability zones can be verified under Azure Firewall -> Properties, see below:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi54VzKCbAtgBkwp6bQhbtPQkMq1PcgD4qcQqNDShyWqkFMsVL9fdIrmvKer3_uzAsof9huZ9ARzynA73y2zuMgw4Y1sIrmaZMfcK18iZ62YqYHZrWgfPdsZ29ilghQ3KqWK9fSVx1HMMRQ_irCk9ewKo8fkQZs_Dd13Vp2Nw56VxV6WxfcdowePnkY5Q/s1105/fw1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="445" data-original-width="1105" height="129" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi54VzKCbAtgBkwp6bQhbtPQkMq1PcgD4qcQqNDShyWqkFMsVL9fdIrmvKer3_uzAsof9huZ9ARzynA73y2zuMgw4Y1sIrmaZMfcK18iZ62YqYHZrWgfPdsZ29ilghQ3KqWK9fSVx1HMMRQ_irCk9ewKo8fkQZs_Dd13Vp2Nw56VxV6WxfcdowePnkY5Q/s320/fw1.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">There is no immediate additional cost to deploying to multiple availability zones, however, there is an increased bandwidth cost, see below (or <a href="https://docs.microsoft.com/en-us/azure/firewall/features#availability-zones" target="_blank">link</a>):</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN-fuN7w4TFEgiOlHylRN0qbjaWdKIN5TZk3nNeYWwNxud_9jvI-lso1FJ1B5bLjIaRbyOzC6iWJu-A_JcWdVGzHo-QO4trNHiBlK9dGf55XHloZavd2h2owFtinmOCjTkCS9Dr3X1Wx4rI1xoIqvtzHqYC_tvmve-V2pYQpfQPVewLkzyhZTSgK0z5A/s923/fw3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="682" data-original-width="923" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN-fuN7w4TFEgiOlHylRN0qbjaWdKIN5TZk3nNeYWwNxud_9jvI-lso1FJ1B5bLjIaRbyOzC6iWJu-A_JcWdVGzHo-QO4trNHiBlK9dGf55XHloZavd2h2owFtinmOCjTkCS9Dr3X1Wx4rI1xoIqvtzHqYC_tvmve-V2pYQpfQPVewLkzyhZTSgK0z5A/s320/fw3.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The bicep file deploys the following resources:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsJNPfr3kTffZRygBIkDyy0CpP3WnOPniYFc8MebfY3w7rrJjVd5LFYymiAaP5E5rAFuUe4UEFZBn2PcDFzZ19MSV6EDnttAuFmc85T6okWbzv5mUvrLZY2QyW61MHkrwXmARHy13qDarIYujFppz4pTM2HlUEyFWBS-z4w7xcXSpsQZnhFcCAwy59CQ/s879/fw2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="446" data-original-width="879" height="162" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsJNPfr3kTffZRygBIkDyy0CpP3WnOPniYFc8MebfY3w7rrJjVd5LFYymiAaP5E5rAFuUe4UEFZBn2PcDFzZ19MSV6EDnttAuFmc85T6okWbzv5mUvrLZY2QyW61MHkrwXmARHy13qDarIYujFppz4pTM2HlUEyFWBS-z4w7xcXSpsQZnhFcCAwy59CQ/s320/fw2.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">If you are testing in a Visual Studio subscription make sure to delete resources again when done as the cost of especially the FW can run up quickly.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">One network rule and one application rule are added under Firewall -> Rules (classic) -> Network/Application rule collection tabs, see below. Usually a Firewall Policy will be created and associated with the FW but for testing purposes this can be used.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSrI82zbK2FGUWqd5kRxtnC3JnKUTDLxuETwaXrzOVY0AC7j3rZRoo0maZFM94YuvShgYKB_B0EYfp_Ewayph2cKjXWE81MbbTG2fDMl-UoxBY-KUGJIVeJUzZQSrGE1jocX36ZyprCMRH_UArFfAkFQmJELbzAqKygQ6Kzu0DjWnJIVvAxFvnzCUGPA/s1203/fw4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="387" data-original-width="1203" height="103" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSrI82zbK2FGUWqd5kRxtnC3JnKUTDLxuETwaXrzOVY0AC7j3rZRoo0maZFM94YuvShgYKB_B0EYfp_Ewayph2cKjXWE81MbbTG2fDMl-UoxBY-KUGJIVeJUzZQSrGE1jocX36ZyprCMRH_UArFfAkFQmJELbzAqKygQ6Kzu0DjWnJIVvAxFvnzCUGPA/s320/fw4.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Using the Bicep visualizer from VS Code you can get a nice graphical overview of the resources to be deployed. To do this, right click the bicep file in VS code and choose "Open Bicep Visualizer", see below: </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwhelVdO1VmCvX7miZnPaLEqi6c1CGR8st_RlOk-QVhi-SWb0lgG6QkAQYbtoX9biR7zS9FHfNNpF3qnlR4-IeQoW_mVFLdck8Skp_G3r8xame-B0dog5w8F6RZsBAjGiRxr4pJFjASdWWDbJ76pLlBDAn4OcWJAHouyAzvjhOqnpuSiHgJgKvTruGhQ/s928/fw5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="669" data-original-width="928" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwhelVdO1VmCvX7miZnPaLEqi6c1CGR8st_RlOk-QVhi-SWb0lgG6QkAQYbtoX9biR7zS9FHfNNpF3qnlR4-IeQoW_mVFLdck8Skp_G3r8xame-B0dog5w8F6RZsBAjGiRxr4pJFjASdWWDbJ76pLlBDAn4OcWJAHouyAzvjhOqnpuSiHgJgKvTruGhQ/s320/fw5.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Jakob Fabritius Nørregaardhttp://www.blogger.com/profile/16058367372474379189noreply@blogger.com0