Azure Stack TP2 Hacks: Custom Domain Names and Exposing to the Internet
In some previous posts, we covered some "hacks" to Azure Stack TP1, primarily enabling a customized domain name and exposing to the internet. If you have not noticed yet, the installation has changed greatly. The process is now driven by ECEngine and should be far more indicative of how the final product gets deployed. While the installer has greatly changed, fortunately, the process to expose the stack publicly has only changed in a few minor ways. Without getting too involved in how it works, the installation operates from a series of PowerShell modules and Pester tests tied to a configuration composed from a number XML configuration files. The configuration files support use of variables and parameters to drive most of the PowerShell action. As with TP1, the stack is wired so that the DNS domain name for Active Directory must match the public DNS domain name (think certificates and host headers). This is a much less involved change to TP2, it mostly requires replacing a couple of straggling hard coded entries with variables in some OneNodeConfig.xml files and changing the installer bootstrapper to use it. Once again, I will admonish you that this is wholly unsupported.
There are 6 files that need minor changes, we will start with the XML config files.
Config Files
C:\CloudDeployment\Configuration\Roles\Fabric\IdentityProvider\OneNodeRole.xml Line 11 From
[xml] <IdentityApplication Name="Deployment" ResourceId="https://deploy.azurestack.local/[Deployment_Guid]" DisplayName="Deployment Application" CertPath="{Infrastructure}\ASResourceProvider\Cert\Deployment.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Deployment.IdentityApplication.Configuration.json" > </IdentityApplication> [/xml]
To
[xml] <IdentityApplication Name="Deployment" ResourceId="https://deploy.[DOMAINNAMEFQDN]/[Deployment_Guid]" DisplayName="Deployment Application" CertPath="{Infrastructure}\ASResourceProvider\Cert\Deployment.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Deployment.IdentityApplication.Configuration.json" > </IdentityApplication> [/xml]
C:\CloudDeployment\Configuration\Roles\Fabric\KeyVault\OneNodeRole.xml Line 12 From
[xml] <IdentityApplication Name="KeyVault" ResourceId="https://vault.azurestack.local/[Deployment_Guid]" DisplayName="AzureStack KeyVault" CertPath="{Infrastructure}\ASResourceProvider\Cert\KeyVault.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\KeyVault.IdentityApplication.Configuration.json" > <AADPermissions> <ApplicationPermission Name="ReadDirectoryData" /> </AADPermissions> <OAuth2PermissionGrants> <FirstPartyApplication FriendlyName="PowerShell" /> <FirstPartyApplication FriendlyName="VisualStudio" /> <FirstPartyApplication FriendlyName="AzureCLI" /> </OAuth2PermissionGrants> </IdentityApplication> [/xml]
To
[xml] <IdentityApplication Name="KeyVault" ResourceId="https://vault.[DOMAINNAMEFQDN]/[Deployment_Guid]" DisplayName="AzureStack KeyVault" CertPath="{Infrastructure}\ASResourceProvider\Cert\KeyVault.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\KeyVault.IdentityApplication.Configuration.json" > <AADPermissions> <ApplicationPermission Name="ReadDirectoryData" /> </AADPermissions> <OAuth2PermissionGrants> <FirstPartyApplication FriendlyName="PowerShell" /> <FirstPartyApplication FriendlyName="VisualStudio" /> <FirstPartyApplication FriendlyName="AzureCLI" /> </OAuth2PermissionGrants> </IdentityApplication> [/xml]
Line 26 From
[xml] <AzureKeyVaultSuffix>vault.azurestack.local</AzureKeyVaultSuffix> [/xml]
To
[xml] <AzureKeyVaultSuffix>vault[DOMAINNAMEFQDN]</AzureKeyVaultSuffix> [/xml]
C:\CloudDeployment\Configuration\Roles\Fabric\WAS\OneNodeRole.xml Line(s) 96-97 From
[xml] <IdentityApplication Name="ResourceManager" ResourceId="https://api.azurestack.local/[Deployment_Guid]" HomePage="https://api.azurestack.local/" DisplayName="AzureStack Resource Manager" CertPath="{Infrastructure}\ASResourceProvider\Cert\ResourceManager.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\ResourceManager.IdentityApplication.Configuration.json" Tags="MicrosoftAzureStack" > [/xml]
To
[xml] <IdentityApplication Name="ResourceManager" ResourceId="https://api.[DOMAINNAMEFQDN]/[Deployment_Guid]" HomePage="https://api.[DOMAINNAMEFQDN]/" DisplayName="AzureStack Resource Manager" CertPath="{Infrastructure}\ASResourceProvider\Cert\ResourceManager.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\ResourceManager.IdentityApplication.Configuration.json" Tags="MicrosoftAzureStack" > [/xml]
Line(s) 118-120 From
[xml] <IdentityApplication Name="Portal" ResourceId="https://portal.azurestack.local/[Deployment_Guid]" HomePage="https://portal.azurestack.local/" ReplyAddress="https://portal.azurestack.local/" DisplayName="AzureStack Portal" CertPath="{Infrastructure}\ASResourceProvider\Cert\Portal.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Portal.IdentityApplication.Configuration.json" > [/xml]
To
[xml] <IdentityApplication Name="Portal" ResourceId="https://portal.[DOMAINNAMEFQDN]/[Deployment_Guid]" HomePage="https://portal.[DOMAINNAMEFQDN]/" ReplyAddress="https://portal.[DOMAINNAMEFQDN]/" DisplayName="AzureStack Portal" CertPath="{Infrastructure}\ASResourceProvider\Cert\Portal.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Portal.IdentityApplication.Configuration.json" > [/xml]
Line 129 From
[xml] <ResourceAccessPermissions> <UserImpersonationPermission AppURI="https://api.azurestack.local/[Deployment_Guid]" /> </ResourceAccessPermissions> [/xml]
To
[xml] <ResourceAccessPermissions> <UserImpersonationPermission AppURI="https://api.[DOMAINNAMEFQDN]/[Deployment_Guid]" /> </ResourceAccessPermissions> [/xml]
Line 133 From
[xml] <IdentityApplication Name="Policy" ResourceId="https://policy.azurestack.local/[Deployment_Guid]" DisplayName="AzureStack Policy Service" CertPath="{Infrastructure}\ASResourceProvider\Cert\Policy.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Policy.IdentityApplication.Configuration.json" > [/xml]
To
[xml] <IdentityApplication Name="Policy" ResourceId="https://policy.[DOMAINNAMEFQDN]/[Deployment_Guid]" DisplayName="AzureStack Policy Service" CertPath="{Infrastructure}\ASResourceProvider\Cert\Policy.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Policy.IdentityApplication.Configuration.json" > [/xml]
Line 142 From
[xml] <IdentityApplication Name="Monitoring" ResourceId="https://monitoring.azurestack.local/[Deployment_Guid]" DisplayName="AzureStack Monitoring Service" CertPath="{Infrastructure}\ASResourceProvider\Cert\Monitoring.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Monitoring.IdentityApplication.Configuration.json" > </IdentityApplication> [/xml]
To
[xml] <IdentityApplication Name="Monitoring" ResourceId="https://monitoring.[DOMAINNAMEFQDN]/[Deployment_Guid]" DisplayName="AzureStack Monitoring Service" CertPath="{Infrastructure}\ASResourceProvider\Cert\Monitoring.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Monitoring.IdentityApplication.Configuration.json" > </IdentityApplication> [/xml]
C:\CloudDeployment\Configuration\Roles\Fabric\FabricRingServices\XRP\OneNodeRole.xml Line 114 From
[xml] <IdentityApplication Name="Monitoring" ResourceId="https://monitoring.azurestack.local/[Deployment_Guid]" DisplayName="AzureStack Monitoring Service" CertPath="{Infrastructure}\ASResourceProvider\Cert\Monitoring.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Monitoring.IdentityApplication.Configuration.json" > </IdentityApplication> [/xml]
To
[xml] <IdentityApplication Name="Monitoring" ResourceId="https://monitoring.[DOMAINNAMEFQDN]/[Deployment_Guid]" DisplayName="AzureStack Monitoring Service" CertPath="{Infrastructure}\ASResourceProvider\Cert\Monitoring.IdentityApplication.ClientCertificate.pfx" ConfigPath="{Infrastructure}\ASResourceProvider\Config\Monitoring.IdentityApplication.Configuration.json" > </IdentityApplication> [/xml]
Scripts
Now we will edit the installation bootstrapping scripts.
We will start by adding two new parameters ($ADDomainName and $DomainNetbiosName) to C:\CloudDeployment\Configuration\New-OneNodeManifest.ps1 and have the manifest generation use them.
[powershell] param ( [Parameter(Mandatory=$true)] [Xml] $InputXml,
[Parameter(Mandatory=$true)] [String] $OutputFile,
[Parameter(Mandatory=$true)] [System.Guid] $DeploymentGuid,
[Parameter(Mandatory=$false)] [String] $Model,
[Parameter(Mandatory=$true)] [String] $HostIPv4Address,
[Parameter(Mandatory=$true)] [String] $HostIPv4DefaultGateway,
[Parameter(Mandatory=$true)] [String] $HostSubnet,
[Parameter(Mandatory=$true)] [bool] $HostUseDhcp,
[Parameter(Mandatory=$true)] [string] $PhysicalMachineMacAddress,
[Parameter(Mandatory=$true)] [String] $HostName,
[Parameter(Mandatory=$true)] [String] $NatIPv4Address,
[Parameter(Mandatory=$true)] [String] $NATIPv4Subnet,
[Parameter(Mandatory=$true)] [String] $NatIPv4DefaultGateway,
[Parameter(Mandatory=$false)] [Int] $PublicVlanId,
[Parameter(Mandatory=$true)] [String] $TimeServer,
[Parameter(Mandatory=$true)] [String] $TimeZone,
[Parameter(Mandatory=$true)] [String[]] $EnvironmentDNS,
[Parameter(Mandatory=$false)] [String] $ADDomainName='azurestack.local',
[Parameter(Mandatory=$false)] [String] $DomainNetbiosName='azurestack',
[Parameter(Mandatory=$true)] [string] $AADDirectoryTenantName,
[Parameter(Mandatory=$true)] [string] $AADDirectoryTenantID,
[Parameter(Mandatory=$true)] [string] $AADAdminSubscriptionOwner,
[Parameter(Mandatory=$true)] [string] $AADClaimsProvider ) $Xml.InnerXml = $Xml.InnerXml.Replace('[PREFIX]', 'MAS') $Xml.InnerXml = $Xml.InnerXml.Replace('[DOMAINNAMEFQDN]', $ADDomainName) $Xml.InnerXml = $Xml.InnerXml.Replace('[DOMAINNAME]', $DomainNetbiosName) [/powershell]
The final edit(s) we need to make are to C:\CloudDeployment\Configuration\InstallAzureStackPOC.ps1. We will start by adding the same parameters to this script.
[powershell] [CmdletBinding(DefaultParameterSetName="DefaultSet")] param ( [Parameter(Mandatory=$false, ParameterSetName="RerunSet")] [Parameter(Mandatory=$true, ParameterSetName="AADSetStaticNAT")] [Parameter(Mandatory=$true, ParameterSetName="DefaultSet")] [SecureString] $AdminPassword,
[Parameter(Mandatory=$false)] [PSCredential] $AADAdminCredential,
[Parameter(Mandatory=$false)] [String] $AdDomainName='azurestack.local',
[Parameter(Mandatory=$false)] [String] $DomainNetbiosName='AzureStack',
[Parameter(Mandatory=$false)] [String] $AADDirectoryTenantName,
[Parameter(Mandatory=$false)] [ValidateSet('Public Azure','Azure - China', 'Azure - US Government')] [String] $AzureEnvironment = 'Public Azure',
[Parameter(Mandatory=$false)] [String[]] $EnvironmentDNS,
[Parameter(Mandatory=$true, ParameterSetName="AADSetStaticNAT")] [String] $NATIPv4Subnet,
[Parameter(Mandatory=$true, ParameterSetName="AADSetStaticNAT")] [String] $NATIPv4Address,
[Parameter(Mandatory=$true, ParameterSetName="AADSetStaticNAT")] [String] $NATIPv4DefaultGateway,
[Parameter(Mandatory=$false)] [Int] $PublicVlanId,
[Parameter(Mandatory=$false)] [string] $TimeServer = 'time.windows.com',
[Parameter(Mandatory=$false, ParameterSetName="RerunSet")] [Switch] $Rerun ) [/powershell]
The next edit will occur at lines 114-115 From
[powershell] $FabricAdminUserName = 'AzureStack\FabricAdmin' $SqlAdminUserName = 'AzureStack\SqlSvc' [/powershell]
To
[powershell] $FabricAdminUserName = "$DomainNetbiosName\FabricAdmin" $SqlAdminUserName = "$DomainNetbiosName\SqlSvc" [/powershell]
Finally we will modify the last statement of the script from line 312 to pass the new parameters.
[powershell] & $PSScriptRoot\New-OneNodeManifest.ps1 -InputXml $xml ` -OutputFile $outputConfigPath ` -Model $model ` -DeploymentGuid $deploymentGuid ` -HostIPv4Address $hostIPv4Address ` -HostIPv4DefaultGateway $hostIPv4Gateway ` -HostSubnet $hostSubnet ` -HostUseDhcp $hostUseDhcp ` -PhysicalMachineMacAddress $physicalMachineMacAddress ` -HostName $hostName ` -NATIPv4Address $NATIPv4Address ` -NATIPv4Subnet $NATIPv4Subnet ` -NATIPv4DefaultGateway $NATIPv4DefaultGateway ` -PublicVlanId $PublicVlanId ` -TimeServer $TimeServer ` -TimeZone $timezone ` -EnvironmentDNS $EnvironmentDNS ` -AADDirectoryTenantName $AADDirectoryTenantName ` -AADDirectoryTenantID $AADDirectoryTenantID ` -AADAdminSubscriptionOwner $AADAdminSubscriptionOwner ` -AADClaimsProvider $AADClaimsProvider ` -ADDomainName $AdDomainName ` -DomainNetbiosName $DomainNetbiosName [/powershell]
NAT Configuration
So, you now have customized the Domain for your one node Azure Stack install and want to get it on the internet. This process is almost identical to TP1 save for two changes. In TP1 there were both BGPVM and NATVM machines; while there is now a single machine MAS-BGPNAT01. The BGPNAT role only exists in the one node (HyperConverged) installation. The other change is the type of Remote Access installation. TP1 also used the "legacy" RRAS for NAT, where all configuration was UI or netsh based. TP2 has transitioned to "modern" Remote Access that is only really manageable through PowerShell. To enable the appropriate NAT mappings we will need to use three PowerShell Cmdlets. Get-NetNat Add-NetNatExternalAddress Add-NetNatStaticMapping I use a script to create all the mappings which takes a simple object, which in our use case is deserialized from JSON. This file is a simple collection of the NAT entries and mappings to be created.
[javascript] { "Portal": { "External": "172.20.40.39", "Ports": [ 80,443,30042,13011,30011,30010,30016,30015,13001,13010,13021,30052,30054,13020,30040,13003,30022,12998,12646,12649,12647,12648,12650,53056,57532,58462,58571,58604,58606,58607,58608,58610,58613,58616,58618,58619,58620,58626,58627,58628,58629,58630,58631,58632,58633,58634,58635,58636,58637,58638,58639,58640,58641,58642,58643,58644,58646,58647,58648,58649,58650,58651,58652,58653,58654,58655,58656,58657,58658,58659,58660,58661,58662,58663,58664,58665,58666,58667,58668,58669,58670,58671,58672,58673,58674,58675,58676,58677,58678,58679,58680,58681,58682,58683,58684,58685,58686,58687,58688,58689,58690,58691,58692,58693,58694,58695,58696,58697,58698,58699,58701 ], "Internal": "192.168.102.5" }, "API": { "External": "172.20.40.38", "Ports": [ 80,443,30042,13011,30011,30010,30016,30015,13001,13010,13021,30052,30054,13020,30040,13003,30022,12998,12646,12649,12647,12648,12650,53056,57532,58462,58571,58604,58606,58607,58608,58610,58613,58616,58618,58619,58620,58626,58627,58628,58629,58630,58631,58632,58633,58634,58635,58636,58637,58638,58639,58640,58641,58642,58643,58644,58646,58647,58648,58649,58650,58651,58652,58653,58654,58655,58656,58657,58658,58659,58660,58661,58662,58663,58664,58665,58666,58667,58668,58669,58670,58671,58672,58673,58674,58675,58676,58677,58678,58679,58680,58681,58682,58683,58684,58685,58686,58687,58688,58689,58690,58691,58692,58693,58694,58695,58696,58697,58698,58699,58701 ], "Internal": "192.168.102.4" }, "DataVault": { "External": "172.20.40.43", "Ports": [80,443], "Internal": "192.168.102.3" }, "CoreDataVault": { "External": "172.20.40.44", "Ports": [80,443], "Internal": "192.168.102.3" }, "Graph": { "External": "172.20.40.40", "Ports": [80,443], "Internal": "192.168.102.8" }, "Extensions": { "External": "172.20.40.41", "Ports": [ 80,443,30042,13011,30011,30010,30016,30015,13001,13010,13021,30052,30054,13020,30040,13003,30022,12998,12646,12649,12647,12648,12650,53056,57532,58462,58571,58604,58606,58607,58608,58610,58613,58616,58618,58619,58620,58626,58627,58628,58629,58630,58631,58632,58633,58634,58635,58636,58637,58638,58639,58640,58641,58642,58643,58644,58646,58647,58648,58649,58650,58651,58652,58653,58654,58655,58656,58657,58658,58659,58660,58661,58662,58663,58664,58665,58666,58667,58668,58669,58670,58671,58672,58673,58674,58675,58676,58677,58678,58679,58680,58681,58682,58683,58684,58685,58686,58687,58688,58689,58690,58691,58692,58693,58694,58695,58696,58697,58698,58699,58701 ], "Internal": "192.168.102.7" }, "Storage": { "External": "172.20.40.42", "Ports": [80,443], "Internal": "192.168.102.6" } } [/javascript]
In the one node TP2 deployment 192.168.102.0 is the subnet for "Public" IP addresses, and if you notice all the VIP's for the stack reside on that subnet. We have 1-to-1 NAT for all the "External" addresses we associate with a given Azure Stack instance.
[powershell] [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [psobject] $NatConfig )
#There's only one could do -Name BGPNAT ... $NatSetup=Get-NetNat
$NatConfigNodeNames=$NatConfig|Get-Member -MemberType NoteProperty|Select-Object -ExpandProperty Name
foreach ($NatConfigNodeName in $NatConfigNodeNames) { Write-Verbose "Configuring NAT for Item $NatConfigNodeName" $ExIp=$NatConfig."$NatConfigNodeName".External $InternalIp=$NatConfig."$NatConfigNodeName".Internal $NatPorts=$NatConfig."$NatConfigNodeName".Ports Write-Verbose "Adding External Address $ExIp" Add-NetNatExternalAddress -NatName $NatSetup.Name -IPAddress $ExIp -PortStart 80 -PortEnd 63356 Write-Verbose "Adding Static Mappings"
foreach ($natport in $NatPorts) { #TCP Write-Verbose "Adding NAT Mapping $($ExIp):$($natport)->$($InternalIp):$($natport)" Add-NetNatStaticMapping -NatName $NatSetup.Name -Protocol TCP ` -ExternalIPAddress $ExIp -InternalIPAddress $InternalIp ` -ExternalPort $natport -InternalPort $NatPort
} } [/powershell]
DNS Records
The final step will be adding the requisite DNS Entries, which have changed slightly as well. In the interest of simplicity assume the final octet of the IP addresses on the 172.20.40.0 subnet have a 1 to 1 NAT mapping to 38.77.x.0 (e.g. 172.20.40.40 –> 38.77.x.40)
| A Record | IP Address |
| api | 38.77.x.38 |
| portal | 38.77.x.39 |
| *.blob | 38.77.x.42 |
| *.table | 38.77.x.42 |
| *.queue | 38.77.x.42 |
| *.vault | 38.77.x.43 |
| data.vaultcore | 38.77.x.44 |
| control.vaultcore | 38.77.x.44 |
| xrp.tenantextensions | 38.77.x.44 |
| compute.adminextensions | 38.77.x.41 |
| network.adminextensions health.adminextensions | 38.77.x.41 |
| storage.adminextensions | 38.77.x.41 |
Connecting to the Stack
You will need to export the root certificate from the CA for your installation for importing on any clients that will access your deployment. Exporting the root certificate is very simple as the host system is joined to the domain which hosts the Azure Stack CA. To export the Root certificate to your desktop run this simple one-liner in the PowerShell console of your Host system (the same command will work from the Console VM).
[powershell] Get-ChildItem -Path Cert:\LocalMachine\Root| ` Where-Object{$_.Subject -like "CN=AzureStackCertificationAuthority*"}| ` Export-Certificate -FilePath "$env:USERPROFILE\Desktop\$($env:USERDOMAIN)RootCA.cer" -Type CERT [/powershell]
The process for importing this certificate on your client will vary depending on the OS version; as such I will avoid giving a scripted method.
Right click the previously exported certificate.
Choose Current User for most use-cases.
Select Browse for the appropriate store.
Select Trusted Root Certificate Authorities
Confirm the Import Prompt
To connect with PowerShell or REST API you will need the deployment GUID. This can be obtained from the host with the following snippet.
[powershell] [xml]$deployinfo = Get-content "C:\CloudDeployment\Config.xml" $deploymentguid = $deployinfo.CustomerConfiguration.Role.Roles.Role.Roles.Role | % {$_.PublicInfo.DeploymentGuid} [/powershell]
This value can then be used to connect to your stack.
[powershell] #Deployment GUID $EnvironmentID='4bc6f444-ff15-4fd7-9bfa-5495891fe876' #The DNS Domain used for the Install $StackDomain="yourazurestack.com" #The AAD Domain Name (e.g. bobsdomain.onmicrosoft.com) $AADDomainName='youraadtenant.com' #The AAD Tenant ID $AADTenantID = "youraadtenant.com" #The Username to be used $AADUserName="username@$AADDomainName" #The Password to be used $AADPassword='P@ssword1'|ConvertTo-SecureString -Force -AsPlainText #The Credential to be used. Alternatively could use Get-Credential $AADCredential=New-Object PSCredential($AADUserName,$AADPassword) #The AAD Application Resource URI $ApiAADResourceID="https://api.$StackDomain/$EnvironmentID" #The ARM Endpoint $StackARMUri="Https://api.$StackDomain/" #The Gallery Endpoint $StackGalleryUri="Https://portal.$($StackDomain):30016/" #The OAuth Redirect Uri $AadAuthUri="https://login.windows.net/$AADTenantID/" #The MS Graph API Endpoint $GraphApiEndpoint="https://graph.windows.net/"
#Add the Azure Stack Environment Add-AzureRmEnvironment -Name "Azure Stack" ` -ActiveDirectoryEndpoint $AadAuthUri ` -ActiveDirectoryServiceEndpointResourceId $ApiAADResourceID ` -ResourceManagerEndpoint $StackARMUri ` -GalleryEndpoint $StackGalleryUri ` -GraphEndpoint $GraphApiEndpoint
#Add the environment to the context using the credential $env = Get-AzureRmEnvironment 'Azure Stack' Add-AzureRmAccount -Environment $env -Credential $AADCredential -Verbose [/powershell]
Note: You will need the a TP2 specific version of the Azure PowerShell for many operations. Enjoy, and stay tuned for more.
Test Microsoft Azure Stack in Avanade Labs
Hi Folks, Normally we stick to the technical stuff here, but today we're cross posting some Avanade news: We are excited to announce that Avanade is making available access to Azure Stack Technical Preview 2 in our labs, so you can quickly test the new technology no set-up required. Microsoft Azure Stack is a new hybrid cloud platform that enables organizations to deliver Azure services from their own datacenter. We know many of you are excited to try it out, but may not be ready to commit to buying hardware to run it on yet.. With our new offering, you can run Azure Stack in the Avanade-hosted Azure Stack Labs, and we’ll take care of the planning and hardware acquisition.
We have a limited number of openings for clients to test a dedicated Azure Stack TP2 environment. These environments are hosted and supported by Avanade and are provided in lengths of four week segments. Each environment is a preconfigured dedicated instance, allowing you to immediately test advanced scenarios. “Avanade has been a key partner on the Azure Stack journey and this platform provides our joint customers a chance to test Azure Stack almost instantly,” said Mark Jewett, Senior Director of Cloud Platform Marketing at Microsoft.
To learn more or request access, contact your local Avanade sales contact or send an email to azurestackpoc@avanade.com.
More info on Avanade.com: https://www.avanade.com/en/media-center/in-the-news/test-azure-stack-in-avanade-labs
Break an Infinite Lease on an Azure Storage Account Container
On rare occasions you may find yourself with an empty storage account container that has an infinite lease on it. This can make it near impossible to remove the container without opening a support case with Microsoft. While there are several tools and script samples available to break the lease on a blob there are few that address a lease on a container. The following should help identify if your container has an “infinite” lease and how to remove the lease.
Symptoms:
The following are symptoms of an infinite lease on a empty container:
- When viewing the properties of the container through the portal (https://portal.azure.com) you will see the lease period set to “infinite”.
- When deleting the Azure storage container though the new portal (https://portal.azure.com) you get the following error:
- “Failed to delete storage container 'MyLeasedContainer'. Error: There is currently a lease on the container and no lease ID was specified in the request.”"
- When deleting the Azure storage container though the old portal(https://manage.windowsazure.com) you get the following error:
- “There is currently a lease on the container and no lease ID was specified in the request. RequestId:5f0e6c7c-0001-00ec-7df9-0fc697000000 Time:2016-09-16T09:04:45.6778058Z”
Resolution:
The following script sample can help break this lease. Replace the property values of $StorageAccountName, $StorageAccountKey, and $ContainerName with the correct values of the container with the infinite lease.
[powershell]
$StorageAccountName = "MyStorageAccount"
$StorageAccountKey = "flmuQKvp7L6G8ga0IXLMnPJogSacsFPofKBv1g2sJhfcecVyrWtu+AMweHNwgSk6dAkOWTGx1viyQp/0aMRkBw=="
$ContainerName = "MyLeasedContainer"
$Creds = New-Object Microsoft.WindowsAzure.Storage.Auth.StorageCredentials("$StorageAccountName","$StorageAccountKey")
$CloudStorageAccount = New-Object Microsoft.WindowsAzure.Storage.CloudStorageAccount($creds, $true)
$CloudBlobClient = $CloudStorageAccount.CreateCloudBlobClient()
$BlobContainer = $CloudBlobClient.GetContainerReference($ContainerName)
$BlobContainer.BreakLease($(New-TimeSpan), $null, $null, $null)
[/powershell]
Network Security Group Rule Tags Deep Dive
Network Security Groups are a simple yet powerful tool within the Azure networking toolkit. They are an Azure resources that provides the ability to apply an Access Control List (ACL) ruleset to allow or deny network traffic to a VM. NSGs can be associated with either a subnet within a Virtual Network, or to a VM’s NIC (ARM). Think of them as a lightweight firewall. There is already some excellent documentation available on what they are (https://azure.microsoft.com/en-us/documentation/articles/virtual-networks-nsg/) and best practices on how to implement them (https://blogs.msdn.microsoft.com/igorpag/2016/05/14/azure-network-security-groups-nsg-best-practices-and-lessons-learned/) but for this post I wanted to focus on a particular part of the NSG rule set, the ‘tags’ functionality. Note, this is separate from the resource tags functionality in Resource Manager.
NSG Rule Tags: What are they?
Azure offers three ‘tags’ that can be used as a source or destination within a NSG rule. They are as follows:
- AzureLoadBalancer
- Internet
- VirtualNetwork
All three of these tags are utilised in the Default Rules created with any new Network Security Group resource:
Inbound Default Rules
Outbound Default Rules
They also can be utilised for any custom rules you wish to add to a NSG Ruleset.
Diving Deeper
Recently Microsoft has released the Diagnostics feature for Network Security Groups. This feature allows us to see Events and Rule Counts on each of the rules within the NSG. It also, however, allows us to dig a bit deeper into what these tags are and how traffic is handled by the default rules. (For how to enable Diagnostics logs see here.)
AzureLoadBalancer
Let’s start with the ‘AzureLoadBalancer’ tag. MS describes this tag as "denotes Azure’s Infrastructure load balancer. This will translate to an Azure datacenter IP where Azure’s health probes originate." Looking into the diagnostics logs we can see that this tag actually translates to the following CIDR block: 168.63.129.16/32.
This IP Address is a special IP Address within Azure and is the same for every Azure region. It maps to the physical IP address of the server machine (host node) hosting the virtual machine. Although they label as Load Balancer, it’s actually the same IP address that is used for the Azure Load Balancer health probe, DHCP, DNS and the virtual machine health probe. Any communication from this IP address should be considered as trusted and if blocked it will cause the Azure virtual machine to become unresponsive (i.e. never remove this rule).
Internet
Things get a little more interesting when we look at the ‘Internet’ tag. Microsoft’s documentation here says that this tag "denotes the IP address space that is outside the virtual network and reachable by public Internet. This range includes Azure owned public IP space as well."
When we look into the diagnostics logs, rules that use this tag have the following value for the IP Address array to match (Warning – long list):
1.0.0.0/8, 2.0.0.0/7, 4.0.0.0/6, 8.0.0.0/7, 11.0.0.0/8, 12.0.0.0/6, 16.0.0.0/5, 24.0.0.0/8, 26.0.0.0/7, 28.0.0.0/6, 32.0.0.0/3, 64.0.0.0/3, 96.0.0.0/6, 100.0.0.0/10, 100.128.0.0/9, 101.0.0.0/8, 102.0.0.0/7, 104.0.0.0/5, 112.0.0.0/5, 120.0.0.0/6, 124.0.0.0/7, 126.0.0.0/8, 128.0.0.0/3, 160.0.0.0/5, 168.0.0.0/8, 169.0.0.0/9, 169.128.0.0/10, 169.192.0.0/11, 169.224.0.0/12, 169.240.0.0/13, 169.248.0.0/14, 169.252.0.0/15, 169.255.0.0/16, 170.0.0.0/7, 172.0.0.0/12, 172.32.0.0/11, 172.64.0.0/10, 172.128.0.0/9, 173.0.0.0/8, 174.0.0.0/7, 176.0.0.0/4, 192.0.0.0/23, 192.0.3.0/24, 192.0.4.0/22, 192.0.8.0/21, 192.0.16.0/20, 192.0.32.0/19, 192.0.64.0/18, 192.0.128.0/17, 192.1.0.0/16, 192.2.0.0/15, 192.4.0.0/14, 192.8.0.0/13, 192.16.0.0/12, 192.32.0.0/11, 192.64.0.0/10, 192.128.0.0/11, 192.160.0.0/13, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/8, 194.0.0.0/7, 196.0.0.0/7, 198.0.0.0/12, 198.16.0.0/15, 198.20.0.0/14, 198.24.0.0/13, 198.32.0.0/11, 198.64.0.0/10, 198.128.0.0/9, 199.0.0.0/8, 200.0.0.0/5, 208.0.0.0/4
Now, if you are Rain Man you can see there are a few gaps in there but this is because these are reserved IP addresses (for reference the list of reserved IPv4 addresses is located here: http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml). Thus this tag is exactly what is advertised: public internet addresses.
Note: As stated in the Microsoft summary, this list does include the Azure public IP addresses. Therefore if a rule exists with the Internet tag and Deny action, it can result in some unexpected behaviour of virtual machine extensions (https://blogs.msdn.microsoft.com/mast/2016/04/27/vm-stuck-in-updating-when-nsg-rule-restricts-outbound-internet-connectivity/) unless those Azure Public IP Addresses are allowed with a lower priority.
VirtualNetwork
The 'VirtualNetwork' tag is the one that is a little misleading. Microsoft states that this tag "denotes all of your network address space. It includes the virtual network address space (CIDR ranges defined in Azure) as well as all connected on-premises address spaces and connected Azure VNets (local networks)." Now I’m not 100% sure, but I think it means that this tag should correspond to all of the routes the VNet’s gateway can see.
To prove this out, I created an NSG and attached it to a few different virtual networks. The first virtual network I attached it to was an isolated VNet with no VPN or Express Route connections (i.e. no VNet Gateway). When I retrieved the NSG diagnostic log, the list of addresses for the ‘Virtual Network’ tag was as follows: 10.1.0.0/16 and 168.63.129.16/32. The first CIDR block corresponds to the Address Space for the attached virtual network and the second is the same special IP Address as the ‘AzureLoadBalancer’ listed above.
The second virtual network I attached the NSG to was one that had a site to site connection to a VNet in a separate Azure Region. The list of addresses within the ‘Virtual Network’ tag and what they correspond to are as follows:
- 10.1.0.0/16 – Local Virtual Network Address Space
- 10.2.0.0/16 – Remote Virtual Network Address Space
- 10.2.0.0/24 – Remote Virtual Network first subnet (only subnet configured)
- 10.2.254.0/24 – Remote Virtual Network gateway subnet
- 168.63.129.16/32 – Special Azure VM Host IP Address
Now the ‘VirtualNetwork’ tag includes the complete address space of the combined Site to Site VPN (plus the subnets of the remote VNet curiously).
The third and final virtual network was one that had an Express Route circuit back to an on premise datacenter. To make things more interesting, this Express Route circuit also had a public peering connection to Azure (if you’re unsure what Peering connections are see here: https://azure.microsoft.com/en-us/documentation/articles/expressroute-circuit-peerings/) which means it also contained routes to Azure Public IPs. When I pulled the NSG log, the list of addresses to match contained a list of 400+ CIDR blocks.
On closer analysis, the list of subnets within this NSG log seemed to be much broader than just the virtual network, on premise and Azure public IPs. It turns out that although it’s broken down into 400+ subnets, it actually contains nearly every IPv4 address from 0.0.0.0 to 255.255.255.255. This is likely because the Gateway of the Virtual Network its attached to was a member of a BGP domain (as it’s an Express Route Gateway) which included advertised routes to on premise private and public subnets, Azure public subnets as well as public internet routes.
Summary
Overall, Network Security Groups are a great tool within the Azure security stack with the Rule Tags functionality allowing some extra capabilities for restricting traffic to and from your Virtual Networks and VMs, albeit with some considerations:
- The ‘AzureLoadBalancer’ tag is actually the Azure VM Host IP Address, and should always be permitted.
- The ‘Internet’ tag corresponds to the public IP address space, and should be allowed or denied accordingly.
- The ‘VirtualNetwork’ tag address space depends entirely on the VPN/Express Route configuration of your virtual network. The rules using this tag within a Network Security Group might be allowing a lot more traffic than first thought. If you want to make a rule to just allow or deny your virtual network’s address space, the best way would be to define that address space as a CIDR Block rather than use the ‘VirtualNetwork’ tag. If you want to have a rule that restricts traffic to your 'known' network then this is a good candidate.
Topic Search
-
Securing TLS in WAC (Windows Admin Center) https://t.co/klDc7J7R4G
Posts by Date
- August 2025 1
- March 2025 1
- February 2025 1
- October 2024 1
- August 2024 1
- July 2024 1
- October 2023 1
- September 2023 1
- August 2023 3
- July 2023 1
- June 2023 2
- May 2023 1
- February 2023 3
- January 2023 1
- December 2022 1
- November 2022 3
- October 2022 7
- September 2022 2
- August 2022 4
- July 2022 1
- February 2022 2
- January 2022 1
- October 2021 1
- June 2021 2
- February 2021 1
- December 2020 2
- November 2020 2
- October 2020 1
- September 2020 1
- August 2020 1
- June 2020 1
- May 2020 2
- March 2020 1
- January 2020 2
- December 2019 2
- November 2019 1
- October 2019 7
- June 2019 2
- March 2019 2
- February 2019 1
- December 2018 3
- November 2018 1
- October 2018 4
- September 2018 6
- August 2018 1
- June 2018 1
- April 2018 2
- March 2018 1
- February 2018 3
- January 2018 2
- August 2017 5
- June 2017 2
- May 2017 3
- March 2017 4
- February 2017 4
- December 2016 1
- November 2016 3
- October 2016 3
- September 2016 5
- August 2016 11
- July 2016 13




