Publishing Microsoft Azure Stack TP3 on the Internet via NAT
As you may know? Azure Stack TP3 is here. This blog will outline how to publish your azure stack instance on the internet using NAT rules to redirect your external IP Address to the internal, external IPs. Our group published another article on how to do this for TP2 and this is the updated version for TP3. Starting Point This article assumes you have a host ready for installation with the TP3 VHDx loaded onto your host and you are familiar with the Azure Stack installation Process. The code in this article is extracted from a larger process but should be enough to get you through the process end to end. Azure Stack Installation First things first, I like to install a few other tools to help me edit code and access the portal, this is not required.
[powershell] iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex choco install notepadplusplus -y choco install googlechrome -y --ignore-checksums choco install visualstudiocode -y choco install beyondcompare -y choco install baretail -y choco install powergui -y --ignore-checksums [/powershell]
Next, you want to open up this file C:\clouddeployment\setup\DeploySingleNode.ps1
Editing these values allow you to create different internal naming and external space. As you can see the ExternalDomainFQDN is made up of the region and external suffix.
This is a lot easier now the domain parameters are used from the same place, no need to hunt down domain names in files.
[powershell] $AdminPassword = 'SuperSecret!'|ConvertTo-SecureString -AsPlainText -force $AadAdminPass= 'SuperSecret!'|ConvertTo-SecureString -AsPlainText -Force $aadCred = New-Object PSCredential('stackadmin@poc.xxxxx.com',$AadAdminPass)
. c:\clouddeployment\setup\InstallAzureStackPOC.ps1 -AzureEnvironment "AzureCloud" ` -AdminPassword $AdminPassword ` -PublicVLanId 97 ` -NATIPv4Subnet '172.20.51.0/24' ` -NATIPv4Address '172.20.51.51' ` -NATIPv4DefaultGateway '172.20.51.1' ` -InfraAzureDirectoryTenantAdminCredential $aadCred ` -InfraAzureDirectoryTenantName 'poc.xxxxx.com' ` -EnvironmentDNS '172.20.11.21' ` [/powershell]
Remember to only have one nic enabled. We also have slightly less than the minimum space required for the OS disk and simply edit the XML file here C:\CloudDeployment\Configuration\Roles\Infrastructure\BareMetal\OneNodeRole.xml and change the value of this node Role.PrivateInfo.ValidationRequirements.MinimumSizeOfSystemDiskGB. The rest is over to TP3 installation, so far our experience of TP3 is much more stable to install, just the occasional rerun using
[powershell]InstallAzureStackPOC.ps1 -rerun[/powershell]
Once the installation completes obviously check you can access the portal. I use chrome as it asks a lot less questions to confirm the portal is running. We use a JSON file defined by a larger automation script to deploy these NAT rules. Here I will simply share a portion of the resulting JSON file that is saved to C:\CloudDeployment\Setup\StackRecord.json.
[xml] { "Region": "SV5", "ExternalDomain": "AS01.poc.xxxxx.com", "nr_Table": "192.168.102.2:80,443:172.20.51.133:3x.7x.xx5.133", "nr_Queue": "192.168.102.3:80,443:172.20.51.134:3x.7x.xx5.134", "nr_blob": "192.168.102.4:80,443:172.20.51.135:3x.7x.xx5.135", "nr_adfs": "192.168.102.5:80,443:172.20.51.136:3x.7x.xx5.136", "nr_graph": "192.168.102.6:80,443:172.20.51.137:3x.7x.xx5.137", "nr_api": "192.168.102.7:443:172.20.51.138:3x.7x.xx5.138", "nr_portal": "192.168.102.8:13011,30015,13001,13010,13021,13020,443,13003,13026,12648,12650,12499,12495,12647,12646,12649:172.20.51.139:3x.7x.xx5.139", "nr_publicapi": "192.168.102.9:443:172.20.51.140:3x.7x.xx5.140", "nr_publicportal": "192.168.102.10:13011,30015,13001,13010,13021,13020,443,13003,12495,12649:172.20.51.141:3x.7x.xx5.141", "nr_crl": "192.168.102.11:80:172.20.51.142:3x.7x.xx5.142", "nr_extensions": "192.168.102.12:443,12490,12491,12498:172.20.51.143:3x.7x.xx5.143", }
[/xml]
This is used by this script also saved to the setup folder
[powershell] param ( $StackBuildJSONPath='C:\CloudDeployment\Setup\StackRecord.json' )
$server = 'mas-bgpnat01' $StackBuild = Get-Content $StackBuildJSONPath | ConvertFrom-Json
[scriptblock]$ScriptBlockAddExternal = { param($ExIp) $NatSetup=Get-NetNat Write-Verbose 'Adding External Address $ExIp' Add-NetNatExternalAddress -NatName $NatSetup.Name -IPAddress $ExIp -PortStart 80 -PortEnd 63356 }
[scriptblock]$ScriptblockAddPorts = { param( $ExIp, $natport, $InternalIp ) Write-Verbose "Adding NAT Mapping $($ExIp):$($natport)->$($InternalIp):$($natport)" Add-NetNatStaticMapping -NatName $NatSetup.Name -Protocol TCP -ExternalIPAddress $ExIp -InternalIPAddress $InternalIp -ExternalPort $natport -InternalPort $NatPort }
$NatRules = @() $NatRuleNames = ($StackBuild | get-member | ? {$_.name -like "nr_*"}).name foreach ($NATName in $NatRuleNames ) { $NatRule = '' | select name, Internal, External, Ports $NatRule.name = $NATName.Replace('nr_','') $rules = $StackBuild.($NATName).split(':') $natrule.Internal = $rules[0] $natrule.External = $rules[2] $natrule.Ports = $rules[1] $NatRules += $NatRule }
$session = New-PSSession -ComputerName $server
foreach ($NatRule in $NatRules) { Invoke-Command -Session $session -ScriptBlock $ScriptBlockAddExternal -ArgumentList $NatRule.External $NatPorts = $NatRule.Ports.Split(',').trim() foreach ($NatPort in $NatPorts) { Invoke-Command -Session $session -ScriptBlock $ScriptblockAddPorts -ArgumentList $NatRule.External,$NatPort,$NatRule.Internal } }
remove-pssession $session [/powershell]
Next, you need to publish your DNS Records. You can do this by hand if you know your NAT Mappings and as a reference, you can open up the DNS server on the MAS-DC01.
However, here are some scripts I have created to help automate this process. I do run this from another machine but have edited it to run in the context of the AzureStack Host. First, we need a couple of reference files.
DNSMappings C:\clouddeployment\setup\DNSMapping.json
[xml] [ { "Name": "nr_Table", "A": "*", "Subdomain": "table", "Zone": "RegionZone.DomainZone" }, { "Name": "nr_Queue", "A": "*", "Subdomain": "queue", "Zone": "RegionZone.DomainZone" }, { "Name": "nr_blob", "A": "*", "Subdomain": "blob", "Zone": "RegionZone.DomainZone" }, { "Name": "nr_adfs", "A": "adfs", "Subdomain": "RegionZone", "Zone": "DomainZone" }, { "Name": "nr_graph", "A": "graph", "Subdomain": "RegionZone", "Zone": "DomainZone" }, { "Name": "nr_api", "A": "api", "Subdomain": "RegionZone", "Zone": "DomainZone" }, { "Name": "nr_portal", "A": "portal", "Subdomain": "RegionZone", "Zone": "DomainZone" }, { "Name": "nr_publicapi", "A": "publicapi", "Subdomain": "RegionZone", "Zone": "DomainZone" }, { "Name": "nr_publicportal", "A": "publicportal", "Subdomain": "RegionZone", "Zone": "DomainZone" }, { "Name": "nr_crl", "A": "crl", "Subdomain": "RegionZone", "Zone": "DomainZone" }, { "Name": "nr_extensions", "A": "*", "Subdomain": "vault", "Zone": "RegionZone.DomainZone" }, { "Name": "nr_extensions", "A": "*", "Subdomain": "vaultcore", "Zone": "RegionZone.DomainZone" } ]
[/xml]
ExternalMapping C:\clouddeployment\setup\ExternalMapping.json This is a smaller section the contain on the NAT mappings reference in this example.
[xml] [ { "External": "3x.7x.2xx.133", "Internal": "172.20.51.133" }, { "External": "3x.7x.2xx.134", "Internal": "172.20.51.134" }, { "External": "3x.7x.2xx.135", "Internal": "172.20.51.135" }, { "External": "3x.7x.2xx.136", "Internal": "172.20.51.136" }, { "External": "3x.7x.2xx.137", "Internal": "172.20.51.137" }, { "External": "3x.7x.2xx.138", "Internal": "172.20.51.138" }, { "External": "3x.7x.2xx.139", "Internal": "172.20.51.139" }, { "External": "3x.7x.2xx.140", "Internal": "172.20.51.140" }, { "External": "3x.7x.2xx.141", "Internal": "172.20.51.141" }, { "External": "3x.7x.2xx.142", "Internal": "172.20.51.142" }, { "External": "3x.7x.2xx.143", "Internal": "172.20.51.143" } ] [/xml]
Bringing it altogether with this script
[powershell] Param ( $StackJSONPath = 'c:\clouddeployment\setup\StackRecord.json' )
$stackRecord = Get-Content $StackJSONPath | ConvertFrom-Json $DNSMappings = get-content c:\clouddeployment\setup\DNSMapping.json | ConvertFrom-Json $ExternalMapping = get-content c:\clouddeployment\setup\ExternalMapping.json | ConvertFrom-Json
$DNSRecords = @() foreach ($DNSMapping in $DNSMappings) { $DNSRecord = '' | select Name, A, IP, Subdomain, Domain $DNS = $stackRecord.($DNSMapping.Name).split(':') $DNSRecord.IP = ($ExternalMapping | ? {$_.Internal -eq $DNS[2]}).external $DNSRecord.Name = $DNSMapping $DNSRecord.A = $DNSMapping.A $DNSRecord.Subdomain = $DNSMapping.Subdomain.Replace("RegionZone",$stackRecord.Region.ToLower()).Replace("DomainZone",$stackRecord.ExternalDomain.ToLower()) $DNSRecord.Domain = $DNSMapping.zone.Replace("RegionZone",$stackRecord.Region.ToLower()).Replace("DomainZone",$stackRecord.ExternalDomain.ToLower()) $DNSRecords += $DNSRecord } #here you can use this array to do what you need, 2 examples follow
#CSV host file for import $DNSRecords | select a,IP, Subdomain, domain | ConvertTo-CSV -NoTypeInformation | Set-Content c:\clouddeployment\setup\DNSRecords.csv
$SubDomains = $DNSRecords | group subdomain foreach ($SubDomain in ($SubDomains | Where {$_.name -ne ''}) ) { Write-Output ("Records for " +$SubDomain.name) foreach ($record in $SubDomain.Group) { # Initialize $resourceAName = $record.A $PublicIP = $record.ip $resourceSubDomainName = $record.Subdomain $zoneName = $record.Domain $resourceName = $resourceAName + "." + $resourceSubDomainName + "." + $zoneName
Write-Output ("Record for $resourceName ") #Create individual DNS records here
} } [/powershell]
The array will give you the records you need to create.
All things being equal and a little bit of luck...
To access this external Azure Stack instance via Powershell you will need a few details and IDs. Most of this is easy enough, however, to get your $EnvironmentID from the deployment Host, open c:\ecetore\ and find your deployment XML. Approx 573kb. Inside this file search for 'DeploymentGuid' This is your Environment ID. Or you can run this code on the host, you may need to change the $deploymentfile parameter
[powershell] param ( $DeploymentFile = 'C:\EceStore\403314e1-d945-9558-fad2-42ba21985248\80e0921f-56b5-17d3-29f5-cd41bf862787' )
[Xml]$DeploymentStore=Get-Content $DeploymentFile | Out-String $InfraRole=$DeploymentStore.CustomerConfiguration.Role.Roles.Role|? Id -eq Infrastructure $BareMetalInfo=$InfraRole.Roles.Role|? Id -eq BareMetal|Select -ExpandProperty PublicInfo $PublicInfoRoles=$DeploymentStore.CustomerConfiguration.Role.Roles.Role.Roles.Role|Select Id,PublicInfo|Where-Object PublicInfo -ne $null $DeploymentDeets=@{ DeploymentGuid=$BareMetalInfo.DeploymentGuid; IdentityApplications=($PublicInfoRoles.PublicInfo|? IdentityApplications -ne $null|Select -ExpandProperty IdentityApplications|Select -ExpandProperty IdentityApplication|Select Name,ResourceId); VIPs=($PublicInfoRoles.PublicInfo|? Vips -ne $null|Select -ExpandProperty Vips|Select -ExpandProperty Vip); } $DeploymentDeets.DeploymentGuid [/powershell]
Plug all the details into this connection script to access your stack instance. Well Commented code credit to Chris Speers.
[powershell] #Random Per Insall $EnvironmentID='xxxxxxxx-xxxx-4e03-aac2-6c2e2f0a517a' #The DNS Domain used for the Install $StackDomain='sv5.as01.poc.xxxxx.com' #The AAD Domain Name (e.g. bobsdomain.onmicrosoft.com) $AADDomainName='poc.xxxxx.com' #The AAD Tenant ID $AADTenantID = 'poc.xxxxx.com' #The Username to be used $AADUserName='stackadmin@poc.xxxxx.com' #The Password to be used $AADPassword='SuperSecret!'|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="graph.$($StackDomain)"
$ResourceManager = "https://api.$($StackDomain)/$($EnvironmentID)" $Portal = "https://portal.$($StackDomain)/$($EnvironmentID)" $PublicPortal = "https://publicportal.$($StackDomain)/$($EnvironmentID)" $Policy = "https://policy.$($StackDomain)/$($EnvironmentID)" $Monitoring = "https://monitoring.$($StackDomain)/$($EnvironmentID)"
#Add the Azure Stack Environment Get-azurermenvironment -Name 'Azure Stack AS01'|Remove-AzureRmEnvironment Add-AzureRmEnvironment -Name "Azure Stack AS01" ` -ActiveDirectoryEndpoint $AadAuthUri ` -ActiveDirectoryServiceEndpointResourceId $ApiAADResourceID ` -ResourceManagerEndpoint $StackARMUri ` -GalleryEndpoint $StackGalleryUri ` -GraphEndpoint $GraphApiEndpoint
#Add the environment to the context using the credential $env = Get-azurermenvironment -Name 'Azure Stack AS01' Add-AzureRmAccount -Environment $env -Credential $AADCredential -Verbose Login-AzureRmAccount -EnvironmentName 'Azure Stack AS01'
get-azurermcontext Write-output "ResourceManager" Write-output $ResourceManager Write-output "`nPortal" Write-output $Portal Write-output "`nPublicPortal" Write-output $PublicPortal Write-output "`nPolicy" Write-output $policy Write-output "`nMonitoring " Write-output $Monitoring [/powershell]
Returning something like this.
Thanks for reading. Hopefullly this helped you in some way.
Topic Search
-
Securing TLS in WAC (Windows Admin Center) https://t.co/klDc7J7R4G
Posts by Date
- 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