Warning: Undefined variable $file in /customers/a/e/3/tunecom.be/httpd.www/stg_ba12f/wp-content/plugins/fix-my-feed-rss-repair/rss-feed-fixr.php on line 14
Warning: Cannot modify header information - headers already sent by (output started at /customers/a/e/3/tunecom.be/httpd.www/stg_ba12f/wp-content/plugins/fix-my-feed-rss-repair/rss-feed-fixr.php:14) in /customers/a/e/3/tunecom.be/httpd.www/stg_ba12f/wp-content/plugins/onecom-vcache/vcaching.php on line 549
Warning: Cannot modify header information - headers already sent by (output started at /customers/a/e/3/tunecom.be/httpd.www/stg_ba12f/wp-content/plugins/fix-my-feed-rss-repair/rss-feed-fixr.php:14) in /customers/a/e/3/tunecom.be/httpd.www/stg_ba12f/wp-content/plugins/onecom-vcache/vcaching.php on line 557
Warning: Cannot modify header information - headers already sent by (output started at /customers/a/e/3/tunecom.be/httpd.www/stg_ba12f/wp-content/plugins/fix-my-feed-rss-repair/rss-feed-fixr.php:14) in /customers/a/e/3/tunecom.be/httpd.www/stg_ba12f/wp-includes/feed-rss2.php on line 8
The post How to use SNAT (Source Network Address Translation) for outbound Windows Virtual Desktop connections appeared first on Tunecom.
]]>Since there is no physical network hardware layer you can troubleshoot, one of the rather obvious cases which are often overlooked is SNAT (Source Network Address Translation). In a traditional on-premises environment you would have a reverse proxy or other networking equipment in place that would translate all of your internal workspace IP Addresses to a single public IP address.
Windows Virtual Desktop is an Azure Native solution built on IaaS. Virtual Machines running on Azure have direct internet connectivity by using the Azure backplane. Just like Microsoft 365 a wide range of public IP addresses and ports is used to connect to online services.
This wide range of public IP addresses might just be the reason for the previously mentioned issues.
What is SNAT? The following Microsoft Docs site explains more in detail all of the possible options & configurations for SNAT.
In our use case, we want to use SNAT to masquerade our back-end WVD Host IP Addresses to a single Public IP address.
What is required? We need a Standard Public Azure Loadbalancer configured on top of our WVD hosts and a SNAT rule configured to allow outbound connections.
Let’s get started with deploying the new load balancer and assigning the SNAT rules to the WVD hosts.
You can run the powershell script provided below or review it on my GitHub Repo.
#region clear variables & in memory parameters $slb = $null $vm = $null $NI = $null $natrules = $null $NIConfig = $null $ELBPurpose = $null $ELBlocation = $null $SKU = $null #endregion #region input variables $ELBPurpose = "enter the purpose of your loadbalancer (ex. wvd)" $ELBlocation = "enter the location of your loadbalancer (ex. westeurope)" $SKU = "enter the SKU of your loadbalancer (ex. standard)" $ELBResourceGroup = "enter the resource group name of your loadbalancer (ex. prd-network-rg)" #endregion #region naming convention $ELBconvention = "-elb" $PIPconvention = "-pip" $FrontEndConvention = "-fep" $BackEndConvention = "-bep" $OutboundRuleConvention = "-obr" $ELBname = $ELBPurpose + $ELBconvention $ELBpip = $ELBname + $PIPconvention $ELBFrontEndName = $ELBname + $FrontEndConvention $ELDBackEndPoolName = $ELBname + $BackEndConvention $ELBOutboundRulename = $ELBname + $OutboundRuleConvention #endregion #region loadbalancer deployment # Step 1: Create a new static public IP address $publicip = New-AzPublicIpAddress -ResourceGroupName $ELBResourceGroup -name $ELBpip -Location $ELBlocation -AllocationMethod Static -Sku $SKU # Step 2: Create a new front end pool configuration and assign the public IP $frontend = New-AzLoadBalancerFrontendIpConfig -Name $ELBFrontEndName -PublicIpAddress $publicip # Step 3: Create a new back end pool configuration $backendAddressPool = New-AzLoadBalancerBackendAddressPoolConfig -Name $ELDBackEndPoolName # Step 4: Create the actual load balancer $slb = New-AzLoadBalancer -Name $ELBname -ResourceGroupName $ELBResourceGroup -Location $ELBlocation -FrontendIpConfiguration $frontend -BackendAddressPool $backendAddressPool -Sku $SKU # Step 5: Assign the back end VMs to the loadbalancer $VMs = Get-AzVM | Out-GridView -PassThru -Title "Select your WVD hosts" foreach ($vm in $VMs) { $NI = Get-AzNetworkInterface | Where-Object { $_.name -like "*$($VM.name)*" } $NI.IpConfigurations[0].Subnet.Id $bep = Get-AzLoadBalancerBackendAddressPoolConfig -Name $ELDBackEndPoolName -LoadBalancer $slb $NI.IpConfigurations[0].LoadBalancerBackendAddressPools = $bep $NI | Set-AzNetworkInterface } # Step 6: Assign the outbound SNAT rules $myelb = Get-AzLoadBalancer -Name $slb.Name $myelb | Add-AzLoadBalancerOutboundRuleConfig -Name $ELBOutboundRulename -FrontendIpConfiguration $frontend -BackendAddressPool $backendAddressPool -Protocol "All" # Step 7: Configure the loadbalancer $myelb | Set-AzLoadBalancer #endregion
The end result will look similar to below screenshots.
The scripts are provided as-is, please be very careful and test run the scripts on a “test” environment or an environment that allows you to perform some quick checks and tests. Adding a standard load balancer with no SNAT rules can cause internet connectivity loss for Windows Virtual Desktop users.
Thank you for reading through this blog post, I hope I have been able to assist in adding SNAT rules to WVD.
If you encounter any new insights, feel free to drop me a comment or contact me via mail or other social media channels
The post How to use SNAT (Source Network Address Translation) for outbound Windows Virtual Desktop connections appeared first on Tunecom.
]]>The post How to retrieve lingering FSLogix profiles on Windows Virtual Desktop, mounted from an Azure File share appeared first on Tunecom.
]]>In some very particular cases it happens that when a user logs off its session from a WVD (Windows Virtual Desktop) host, the corresponding FSLogix profile is not dismounted from the host.
When the user tries to login again to the environment, this results in the following error.
Status : 0x0000000B : Cannot open virtual disk
Reason : 0x00000000 : The container is attached
Error code : 0x00000020 : The process cannot access the file because it is being used by another process
During normal behavior of the login and log off process to Windows Virtual Desktop in combination with an FSLogix profile, the profile is mounted from the underlying storage provider and correctly dismounted upon successful log off of the Windows Virtual Desktop host.
The root cause of why the profile container is not dismounted from the host is hard to find, in most cases, an update of the FSLogix components is required, please make sure to read through the latest FSLogix release notes.
During the days that we had our profile shares/data hosted on a traditional IaaS fileserver, we would just open up an MMC console and look for any open files or sessions.
Since our profiles are now being hosted on an Azure File share, this process is slightly different. I’ve written a small PowerShell script for you to use and/or alter to your needs.
The input variables are pretty straightforward :
Note: The script is currently “designed” to query only one storage account/file share, and only one host pool per run. You could of course alter this to check all host pools and related storage accounts.
The script loops through your active Windows Virtual Desktop sessions and active storage handles.
It then checks each storage handle, whether or not it has a corresponding active WVD session. If not you are presented with the virtual machine name where the FSLogix container is mounted.
Save this PowerShell script as “Clean-LingeringFSLogixProfiles.ps1” Read through the blog post to retrieve the InVM script. The scripts can be download from my GitRepo as well.
<# .SYNOPSIS Dismount lingering FSLogix VHD(X) profiles. .DESCRIPTION Dismount lingering FSLogix VHD(X) profiles. .PARAMETER Mode Provide the execution mode of the script. Alerting : Generates an alert whenever a lingering FSLogix VHDX profile is found React : Tries to dismount the lingering FSLogix Profile on the host where it is attached .PARAMETER ProfileStorageAccount Provide the storage account where the FSLogix profiles are located .PARAMETER ProfileStorageAccount Provide the fileshare where the FSLogix profiles are located .PARAMETER StorageAccountResourceGroupName Provide the resource group name of your storage account .PARAMETER OverrideErrorActionPreference Provide the ErrorActionPreference setting, as descibed in about_preference_variables. (https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7#erroractionpreference). When running locally we should use "Break" mode, which breaks in to the debugger when an error is thrown. .EXAMPLE PS C:\> .\Clean-LingeringFSLogixProfiles.ps1 -Mode "Alerting" -ProfileStorageAccount "storageaccountname" -ProfileShare "profileshare" -StorageAccountResourceGroupName "resourcegroupname" #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateSet('alerting', 'react')] [string] $Mode, [Parameter(Mandatory = $true)] [string] $ProfileStorageAccount, [Parameter(Mandatory = $true)] [string] $ProfileShare, [Parameter(Mandatory = $true)] [string] $StorageAccountResourceGroupName, [Parameter(Mandatory = $false)] [string] $OverrideErrorActionPreference = "Break" ) $ErrorActionPreference = $OverrideErrorActionPreference # The following cmd retrieves your storage account details and puts it in a context variable $context = Get-AzStorageAccount -ResourceGroupName $StorageAccountResourceGroupName -Name $ProfileStorageAccount #region retrieve details per hostpool # Retrieves the hostpools => Alter the script here to check for additional host pools $hostpools = get-azwvdhostpool foreach ($hostpool in $hostpools) { $wvdrg = Get-AzResource -ResourceId $hostpools.Id # This is tricky, so if you only need 1 host pool remove the foreach loop completely and comment the line below $hostpools = $hostpool #region gather all open files & sessions $OpenFiles = Get-AzStorageFileHandle -Context $Context.Context -ShareName $ProfileShare -Recursive $UserSessions = Get-AzWvdUserSession -HostPoolName $hostpools.Name -ResourceGroupName $wvdrg.ResourceGroupName | Select-Object ActiveDirectoryUserName, ApplicationType, SessionState, UserPrincipalName, name #endregion #region fill Open Files array $pathusers = @() foreach ($openfile in $OpenFiles) { If ($openfile.path) { #Write-host $openfile.Path $FilePath = $openfile.Path.Split("/")[0] $pathusers += $FilePath } } $pathusers = $pathusers | Select-Object -Unique #endregion #region fill Open Sessions array $sessionusers = @() foreach ($usersession in $UserSessions) { If ($usersession) { #Write-host $usersession $Username = $UserSession.ActiveDirectoryUserName.Split("\")[1] $sessionusers += $Username } } $sessionusers = $sessionusers | Select-Object -Unique #endregion #region loop through every open file and find a corresponding user session foreach ($pathuser in $pathusers) { If ($sessionusers -contains $pathuser) { Write-host -ForegroundColor green "Active session user: " $pathuser } else { If ($mode -eq "alerting") { $OpenFilesDetails = Get-AzStorageFileHandle -Context $Context.Context -ShareName $ProfileShare -Recursive | Where-Object { $_.Path -like "*$($pathuser)*" } # the following retrieves the virtual machine name of the lingering VHDX file $IPNic = ((Get-AzNetworkInterface | Where-Object { $_.IpConfigurations.PrivateIpAddress -eq $($OpenFilesDetails.ClientIp.IPAddressToString[0]) }).virtualmachine).Id $vmname = ($IPNic -split '/') | Select-Object -Last 1 $VM = Get-AzVm -Name $vmname Write-host -ForegroundColor red "Inactive session user: $pathuser has a FSLogix mounted on the following virtual machine $vmname" } Else { $OpenFilesDetails = Get-AzStorageFileHandle -Context $Context.Context -ShareName $ProfileShare -Recursive | Where-Object { $_.Path -like "*$($pathuser)*" } # the following retrieves the virtual machine name of the lingering VHDX file $IPNic = ((Get-AzNetworkInterface | Where-Object { $_.IpConfigurations.PrivateIpAddress -eq $($OpenFilesDetails.ClientIp.IPAddressToString[0]) }).virtualmachine).Id $vmname = ($IPNic -split '/') | Select-Object -Last 1 $VM = Get-AzVm -Name $vmname Write-host -ForegroundColor red "Inactive session user: $pathuser has a FSLogix mounted on the following virtual machine $vmname" # double check whether or not you want to dismount the profile $YesNo = Read-Host "Are you sure you want to dismount the user profile off $pathuser on the following server $vmname: Yes/No" If ($YesNo -eq "Yes") { $domainupn = Read-Host "Please enter your domain admin username:" $domainpwd = Read-Host "Please enter your domain admin password:" $runDismount = Invoke-AzVMRunCommand -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -CommandId 'RunPowerShellScript' -ScriptPath "scripts\AzVMRunCommands\Clean-InVMLingeringFSLogixProfiles.ps1" -Parameter @{"Upn" = "$domainupn"; "Pass" = "$domainpwd";"pathuser" = $pathuser } If ($runDismount.Status -Ne "Succeeded") { Write-Error "Run failed" } else { Write-Host "FSLogix profile has been dismounted for $($pathuser) on $($vmname)" } } else { # Exit script Write-Host "We are now exiting the script, you've entered the wrong option: Yes/No is required" Exit } } } } #endregion } #endregion
Before launching the script above, make sure to save the script that needs to be run within the virtual machine.
Save the PowerShell script below as “InVMLingeringFSLogixProfiles.ps1” and alter the script path in the script above. The scripts can be download from my GitRepo as well.
param ( [Parameter(Mandatory = $true)] [string] $pathuser, [Parameter(Mandatory = $true)] [string] $upn, [Parameter(Mandatory = $true)] [string] $pass, [Parameter(Mandatory = $false)] [string] $OverrideErrorActionPreference = "Break" ) #This script is run within the virtual machine $ziptargetfolder = "c:\troubleshooting\" $innerscriptlocation = $ziptargetfolder + "Dismount-VHD.ps1" If (!(Test-Path $ziptargetfolder)) { mkdir $ziptargetfolder } @" `$ProfileNamingConvention = "Profile-" + "$pathuser" `$Volume = Get-Volume | Where-Object { `$_.filesystemlabel -eq `$ProfileNamingConvention } | % { Get-DiskImage -DevicePath `$(`$_.Path -replace "\\`$") } Dismount-DiskImage -ImagePath `$Volume.ImagePath "@ | Out-File -FilePath $innerscriptlocation $taskName = "Dismount-FSLogixProfile" $Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -File $innerscriptlocation" -WorkingDirectory $ziptargetfolder $Settings = New-ScheduledTaskSettingsSet -Compatibility Win8 $TaskPath = "\CustomTasks" Register-ScheduledTask -TaskName $taskName -User $upn -Password $pass -RunLevel Highest -Action $Action -Settings $Settings Start-ScheduledTask -TaskName $taskName -TaskPath $TaskPath while ((Get-ScheduledTask -TaskName $taskName).State -ne 'Ready') { Start-Sleep -Seconds 2 } Unregister-ScheduledTask -TaskName $taskName -Confirm:$False Remove-Item -Path $innerscriptlocation -Recurse -Force
The scripts are provided as-is, please be very careful and test run the scripts on a “test” environment or an environment that allows you to perform some quick checks and tests. Dismounting VHD(X) files can cause unwanted effects when performed against an Active user.
Thank you for reading through this blog post, I hope I have been able to assist in troubleshooting FSLogix profile mounting issues.
If you encounter any new insights, feel free to drop me a comment or contact me via mail or other social media channels
The post How to retrieve lingering FSLogix profiles on Windows Virtual Desktop, mounted from an Azure File share appeared first on Tunecom.
]]>The post How to monitor Azure Migrate replication issues appeared first on Tunecom.
]]>Azure Migrate does provide a built-in solution for this within the Azure Migrate project(s). You can manually review the status or use PowerShell to retrieve the replication health of your IaaS machines. However, this lacks some kind of notification or alerting mechanism.
If you’re interested in how to automatically get notified when something goes wrong, please continue reading below.
When looking at the bundle of products included in an Azure Migrate project, one key product is Azure Site Recovery (ASR) which is part of Recovery Services Vault.
Azure Site Recovery is used to replicate your origin machines to Azure.
This means that when we encounter any replication issues, we will have to look at our replication product in place.
When browsing the Recovery Services Vault blade, scroll down to the “Monitoring” section and select “Site Recovery Events“
On the “Site Recovery Events” page you will see a very similar page as displayed in the Azure Migrate Events page. Select “E-mail Notifications“
Enable the “E-mail notifications” by selecting On, select “Other administrators” if you want to set up alerts to non-Azure Services admins/co-admins. Enter an e-mail address and select save.
Whenever a new site recovery event or alert is triggered you will receive a mail notification.
#Select your Azure Site Recovery Services Vault $rsv = Get-AzRecoveryServicesVault | Out-GridView -OutputMode Single #Set the recovery services vault context Set-AzRecoveryServicesAsrVaultContext -Vault $rsv #Retrieve current alerting configuration Get-AzRecoveryServicesAsrAlertSetting #Set alerts (Remove -EnableEmailSubscriptionOwner if you do now want the default owners to be notified) $EmailAddressess = "test.test@test.be" Set-AzRecoveryServicesAsrAlertSetting -CustomEmailAddress $EmailAddressess -EnableEmailSubscriptionOwner
Thank you for reading through this blog post, I hope I have been able to assist in your Azure Migration journey.
If you encounter any new insights, feel free to drop me a comment or contact me via mail or other social media channels
The post How to monitor Azure Migrate replication issues appeared first on Tunecom.
]]>The post How to resolve WVD-Agent service is being stopped: NAME_ALREADY_REGISTERED, This VM needs to be properly registered in order to participate in the deployment appeared first on Tunecom.
]]>The following picture shows our host which is unavailable.
Logged on to the host, we can see that the RDAgentBootloader has stopped.
Looking at the event log of the specific host, you’ll see an error entry each time you try to restart the RDAgentBootLoader service.
Error message: How to resolve WVD-Agent service is being stopped: NAME_ALREADY_REGISTERED, This VM needs to be properly registered in order to participate in the deployment
Navigate to the host pool section, select your host. When you click on the settings icon, you can remove the host from the host pool.
If you have just installed the RDAgent & RDAgentBootloader, please skip step 2 and go to step 3.1. If you are not sure whether the RDAgent install went fine and you’ve entered a registration key before. Continue here.
Navigate to your host pool and select “Registration key”.
Select “Generate new key”.
Enter an expiration date and time for this specific key and select “OK”.
You can now copy or download the registration key.
Continue to step 3.2
Restart the RDAgentBootloader service or restart the entire virtual machine if you feel more comfortable in doing so.
On your WVD host download the latest version of the following software:
RDAgent: link to Microsoft Docs
RDAgentbootloader: link to Microsoft Docs
If you have previously installed the RDAgent & RDAgentBootLoader, make sure to remove it first.
During the installation process of the RDAgent, you will be prompted to enter the registration key. Fill in the key that you have copied or downloaded.
After having installed the RDAgent, please install the RDAgentBootLoader.
Reboot the WVD host and verify if the host is available in the pool again.
Thank you for reading through this blog post, I hope I have been able to assist in resolving this issue.
If you encounter any new insights, feel free to drop me a comment or contact me via mail or other social media channels
The post How to resolve WVD-Agent service is being stopped: NAME_ALREADY_REGISTERED, This VM needs to be properly registered in order to participate in the deployment appeared first on Tunecom.
]]>The post How to clean up replica disks after VMWare Virtual Machine migration with Azure Migrate appeared first on Tunecom.
]]>The following migration process is usually followed when migrating VMWare VM’s to Azure IaaS VM’s with Azure Migrate.
Starting as of step 5, the Azure Migrate appliance will be using Azure Site Recovery to start replicating your on-premises VMDK (virtual disk) files to the Subscription & Resource Group that you have selected in the migration settings.
As you can see, a specific naming convention is applied by default to the ASR disks.
asrseeddisk-(VMName)-GUID
Once the initial delta sync of your virtual machine has been completed, you are now able to perform a test migration.
There are multiple reasons why you should perform a test migration, a major one is to find out if your server and corresponding applications are working properly in Azure.
During the test migration, a snapshot is taken of the ASR disks and a new virtual machine is being created based upon your migration settings.
Please note that your VM is being created with a “test” suffix, to indicate that this machine is being “test migrated”.
The virtual disk names can be altered in the migration settings pane, however, it is advised to keep the disk names as is, to avoid any confusion.
At this point, you will have 3 replica sets of your virtual machine disks.
Once you’ve confirmed that your virtual machine is Azure capable and corresponds to your needs, you can clean up the test migration.
Before cleaning up the test migration, make sure that you have documented or automated the steps that you have performed on this virtual machine. All changes made on the “test migration” Virtual Machine will be lost.
When performing a clean up of the test migration, the virtual machine and corresponding managed disks are being deleted.
During the final migration step, a final sync of the on-premises virtual machine will be made.
It is advised to mark the “shutdown local machine” option when performing the migration, this ensures that no data is being altered on the machine which is being migrated
Like the test migration step, a new virtual machine is being created based on a snapshot of the latest version of the ASR disks. Once the migration has been completed. Make sure to validate the server en perform the necessary actions that you have performed during the test migration.
Your new virtual machine name will now have the exact naming convention as your on-premises virtual machine, including the attached virtual disks.
Step 9 demystified – Clean up ASR disks
When looking at your Azure Migrate project, you will find a mix of servers that have been migrated and/or are pending a test migration or clean up.
When browsing to your VM in Azure Migrate, select disks. Note down the replica disk names, these are the replica disks that are still stored as a managed disk within your target resource group.
To clean up the ASR disks, make sure to stop the replication as soon as your migration has been completed.
After having stopped the replication, the managed disks are deleted from your resource group.
Below script can be used in order to automate the clean up of migrated virtual machines.
#Migration Project Input Variables $AzMigrateProjectName = "project name here" $AzMigrateSubscriptionID = "subscription id here" $AzMigrateResourceGroupName = "resource group name here" #Required Modules Write-Output "Required modules loading" #Requires -Modules @{ ModuleName="Az.Accounts"; ModuleVersion="2.2.3" } #Requires -Modules @{ ModuleName="Az.Migrate"; ModuleVersion="0.1.1" } Import-Module Az.Accounts Import-Module Az.Migrate #Account Login Disconnect-AzAccount Login-AzAccount Set-AzContext -SubscriptionId $AzMigrateSubscriptionID #Clean up $MigrationProject = Get-AzMigrateProject -Name $AzMigrateProjectName -SubscriptionId $AzMigrateSubscriptionID -ResourceGroupName $AzMigrateResourceGroupName $MigrationStatus = Get-AzMigrateServerReplication -ResourceGroupName $AzMigrateResourceGroupName -ProjectName $AzMigrateProjectName -SubscriptionId $AzMigrateSubscriptionID | Where-Object {$_.MigrationState -eq "MigrationSucceeded"} | select MachineName, MigrationState, AllowedOperation, Id foreach ($migrationobject in $MigrationStatus) { $ObjectID = Get-AzMigrateServerReplication -TargetObjectID $migrationobject.id Write-host "Following replication job will be removed: " $migrationobject.MachineName -foregroundcolor green Remove-AzMigrateServerReplication -InputObject $ObjectID }
Thank you for reading through this blog post, I hope I have been able to assist in keeping your Azure Migration journey as lean and mean as possible.
If you encounter any new insights, feel free to drop me a comment or contact me via mail or other social media channels
The post How to clean up replica disks after VMWare Virtual Machine migration with Azure Migrate appeared first on Tunecom.
]]>The post How to fix “The Azure Migrate unified appliance <ApplianceName> is in a disconnected state, Ensure that the appliance is running and has connectivity before proceeding” issue appeared first on Tunecom.
]]>You have a single Azure Migrate appliance, which you have used to perform the suitability analysis and you’ve enabled the same appliance in the migration project as well.
Which means that we will be targeting an agentless migration.
The Azure Migrate Virtual Appliance ‘appliance name’ is in a disconnected state, please verify network connectivity.
The following troubleshooting steps should help you resolve this issue.
Run the following commands in an administrative powershell or cmd prompt.
Net Stop asrgwy Net Start asrgwy Net Stop dra Net Start dra
Check your connection status in the Appliances blade of the Azure Migrate resource on the Azure Portal.
Thank you for reading through this blog post, I hope I have saved you some time on researching the disconnected state issue.
If you encounter any new insights, feel free to drop me a comment or contact me via mail or other social media channels
The post How to fix “The Azure Migrate unified appliance <ApplianceName> is in a disconnected state, Ensure that the appliance is running and has connectivity before proceeding” issue appeared first on Tunecom.
]]>The post Azure AD Domain Services – SKU updates appeared first on Tunecom.
]]>Recently (31/01/2020) Microsoft has made a couple of changes to the way that Azure AD Domain Services are being consumed and billed.
Previous version:
When deploying a new Azure AD Domain Services instance, you weren’t able to provide any type of SKU. The following tiered pricing was being applied to your AADS instance based on the amount of objects.
So if you started with a brand new instance of Azure AD Domain Services, you would end up with the standard tier. Generating about € 96.72 when calculated with 744 hours of uptime. Depending on the amount of objects, you would tier up to the Enterprise grade.
Current release:
As with the “new” release of AADS the tier based pricing hasn’t changed much.
! But be aware, that if you are using the same deployment scripts as you did before, you will end up with an Enterprise SKU level of AADS by default.
! Note the additional option to create a resource forest, which what I believe was included in the previous pricing model, starting from a Standard SKU. To review the latest changes with regards to Azure AD Domain Services, be sure to check out the latest docs article.
Note the price difference
If you are an MSP managing multiple customers via Azure Lighthouse, you can use Azure Resource Graph Explorer.
Use the following query to identitfy in Enterprise SKU AADS instances.
where type == "microsoft.aad/domainservices" | where properties.sku == "Enterprise"
If all is good, you should’t see any results.
Just to verify that your instances are on a Standard Tier. Run the following query.
where type == "microsoft.aad/domainservices" | where properties.sku == "Standard"
You should now see the “Standard” SKU enabled AADS instances.
Navigate to your Azure AD Domain Services Instance and select SKU
Verify that you are running on a Standard Tier. If needed change to your required Tier based on the necessary requirements.
The post Azure AD Domain Services – SKU updates appeared first on Tunecom.
]]>The post Virtual Datacenter Concept | 3 of 10 | Resource Groups appeared first on Tunecom.
]]>In our previous posts about naming conventions and Azure Governance, we’ve defined our rules of play. Now it’s time to start populating our Azure environment with some Resource Groups.
When deploying new resources in Azure, they reside in the Azure Resource Manager model. The Azure Resource Manager model is the successor of the Azure Service Management model which is often referred to as Azure v1 or Azure Classic. Since we are deploying resources in Azure, we want some kind of containerization or logical management layer on top of that. And that is exactly why we need a resource group before we can deploy a resource in Azure.
A resource group is a logical boundary that can organize your resources based on the environment, application or other specific characteristics. We can harden our resource group from unwanted changes by implementing role based access controls and resource locks.
Prior to deploying our first resource in a resource group, we have a couple of recommendations and/or limitations that we need to take care of:
Taking the above information into account, we don’t want to go and start deploying resources and resource groups via the Azure Portal. Rule of thumb is to use the Azure Portal as much as you can for view / read only actions and perform your deployments with ARM templates, powershell or CLI.
Now before we are going to deploy our base set of resource groups, let’s have some thought on which resource groups we are going to deploy and why. Below table gives you an example that you can use in any of your Azure designs to provide an holistic overview of the required resource groups. Have a look here at how naming conventions are provisioned / enforced.
Resource Group Name | Resources |
HUB | |
<cus>-hub-storage-rg | All components related to central storage |
<cus>-hub-management-rg | All components related to central management tools |
<cus>-hub-network-rg | All components related to central networking |
<cus>-hub-mig-rg | All components related to migration workloads |
<cus>-hub-backup-rg | All components related to the central backup instance |
<cus>-hub-identity-rg | All components related to the central Identity instance |
ENV | |
<cus>-dev-app-rg | All components related to the development application tier |
<cus>-dev-web-rg | All components related to the development web tier |
<cus>-dev-network-rg | All components related to the development network tier |
<cus>-dev-db-rg | All components related to the development database tier |
<cus>-dev-storage-rg | All components related to the development storage tier |
The table above gives you guidance on how to deploy a central HUB for resources and create a set of resource groups per environment.
Our environment specific resource groups are designed in a way that we can re-utilize them for multiple purposes.
Below PowerShell script can be used to deploy a tier based environment starting from a simple hub and production approach to a full blow DTAP (Development, Test, Acceptance, Production) environment.
Powershell Script
################## Param( [string]$RG_PurposeHUB, [string]$RG_PurposePRD, [string]$RG_PurposeACC, [string]$RG_PurposeTST, [string]$RG_PurposeDEV, [string]$Cus, [string]$fullLocation, [string]$owner, [string]$EnvironmentTier ) ##################################################################################### ######## START OF NAMING CONVENTION RESOURCE GROUPS HUB,PRD,DEV,TST ######## ##################################################################################### ################### HUB $HUBRGID = $Cus + '-' + $RG_PurposeHUB + '-' + 'identity' + '-rg' $HUBRGSTOR = $Cus + '-' + $RG_PurposeHUB + '-' + 'storage' + '-rg' $HUBRGMGM = $Cus + '-' + $RG_PurposeHUB + '-' + 'management' + '-rg' $HUBRGNET = $Cus + '-' + $RG_PurposeHUB + '-' + 'network' + '-rg' $HUBRGMIG = $Cus + '-' + $RG_PurposeHUB + '-' + 'mig' + '-rg' $HUBRGRSV = $Cus + '-' + $RG_PurposeHUB + '-' + 'backup' + '-rg' ################### PRD $PRDRGAPP = $Cus + '-' + $RG_PurposePRD + '-' + 'app' + '-rg' $PRDRGDB = $Cus + '-' + $RG_PurposePRD + '-' + 'db' + '-rg' $PRDRGSTOR = $Cus + '-' + $RG_PurposePRD + '-' + 'storage' + '-rg' $PRDRGNET = $Cus + '-' + $RG_PurposePRD + '-' + 'network' + '-rg' $PRDRGWEB = $Cus + '-' + $RG_PurposePRD + '-' + 'web' + '-rg' ################### ACC $ACCRGAPP = $Cus + '-' + $RG_PurposeACC + '-' + 'app' + '-rg' $ACCRGDB = $Cus + '-' + $RG_PurposeACC + '-' + 'db' + '-rg' $ACCRGSTOR = $Cus + '-' + $RG_PurposeACC + '-' + 'storage' + '-rg' $ACCRGNET = $Cus + '-' + $RG_PurposeACC + '-' + 'network' + '-rg' $ACCRGWEB = $Cus + '-' + $RG_PurposeACC + '-' + 'web' + '-rg' ################### TST $TSTRGAPP = $Cus + '-' + $RG_PurposeTST + '-' + 'app' + '-rg' $TSTRGDB = $Cus + '-' + $RG_PurposeTST + '-' + 'db' + '-rg' $TSTRGSTOR = $Cus + '-' + $RG_PurposeTST + '-' + 'storage' + '-rg' $TSTRGNET = $Cus + '-' + $RG_PurposeTST + '-' + 'network' + '-rg' $TSTRGWEB = $Cus + '-' + $RG_PurposeTST + '-' + 'web' + '-rg' ################### DEV $DEVRGAPP = $Cus + '-' + $RG_PurposeDEV + '-' + 'app' + '-rg' $DEVRGDB = $Cus + '-' + $RG_PurposeDEV + '-' + 'db' + '-rg' $DEVRGSTOR = $Cus + '-' + $RG_PurposeDEV + '-' + 'storage' + '-rg' $DEVRGNET = $Cus + '-' + $RG_PurposeDEV + '-' + 'network' + '-rg' $DEVRGWEB = $Cus + '-' + $RG_PurposeDEV + '-' + 'web' + '-rg' ##################################################################################### ######## END OF NAMING CONVENTION RESOURCE GROUPS HUB,PRD,DEV,TST ######## ##################################################################################### ##################################################################################### ######## START OF CREATION RESOURCE GROUPS HUB,PRD,DEV,TST,ACC ######## ##################################################################################### function new-resourcegroups { Param ([string]$fullLocation,[string]$owner,[string]$rsgapp,[string]$rsgdb,[string]$rsgstor,[string]$rsgnet,[string]$rsgweb,[string]$rsgpurpose) New-AzResourceGroup -Name $rsgapp -Location $fullLocation -Tag @{Environment="$rsgpurpose";Purpose="Application";Owner="$owner"} New-AzResourceGroup -Name $rsgdb -Location $fullLocation -Tag @{Environment="$rsgpurpose";Purpose="Database";Owner="$owner"} New-AzResourceGroup -Name $rsgstor -Location $fullLocation -Tag @{Environment="$rsgpurpose";Purpose="Storage";Owner="$owner"} New-AzResourceGroup -Name $rsgnet -Location $fullLocation -Tag @{Environment="$rsgpurpose";Purpose="Networking";Owner="$owner"} New-AzResourceGroup -Name $rsgweb -Location $fullLocation -Tag @{Environment="$rsgpurpose";Purpose="Web";Owner="$owner"} } function new-hubresourcegroups { Param ([string]$fullLocation,[string]$owner,[string]$rsgmgm,[string]$rsgdb,[string]$rsgstor,[string]$rsgnet,[string]$rsgweb,[string]$rsgpurpose,[string]$rsgmig,[string]$rsgrsv,[string]$rsgidentity) ### HUB AZ Powershell New-AzResourceGroup -Name $rsgstor -Location $fullLocation -Tag @{Environment="$RG_PurposeHUB";Purpose="Storage";Owner="$owner"} New-AzResourceGroup -Name $rsgmgm -Location $fullLocation -Tag @{Environment="$RG_PurposeHUB";Purpose="Management";Owner="$owner"} New-AzResourceGroup -Name $rsgnet -Location $fullLocation -Tag @{Environment="$RG_PurposeHUB";Purpose="Networking";Owner="$owner"} New-AzResourceGroup -name $rsgmig -Location $fullLocation -Tag @{Environment="$RG_PurposeHUB";Purpose="Migration";Owner="$owner"} New-AzResourceGroup -Name $rsgidentity -Location $fullLocation -Tag @{Environment="$rsgpurposeHUB";Purpose="Identity";Owner="$owner"} New-AzResourceGroup -Name $rsgrsv -Location $fullLocation -Tag @{Environment="$rsgpurpose";Purpose="Backup";Owner="$owner"} } $EnvironmentTier # Select the setup steps required for this environment Switch ($EnvironmentTier) { 1 { # new HUB Resource Groups new-hubresourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $HUBRGAPP -rsgdb $HUBRGDB -rsgstor $HUBRGSTOR -rsgnet $HUBRGNET -rsgweb $HUBRGWEB -rsgpurpose $RG_PurposeHUB -rsgmgm $HUBRGMGM -rsgmig $HUBRGMIG -rsgidentity $HUBRGID -rsgrsv $HUBRGRSV # new PRD Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $PRDRGAPP -rsgdb $PRDRGDB -rsgstor $PRDRGSTOR -rsgnet $PRDRGNET -rsgweb $PRDRGWEB -rsgpurpose $RG_PurposePRD } 2 { # new HUB Resource Groups new-hubresourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $HUBRGAPP -rsgdb $HUBRGDB -rsgstor $HUBRGSTOR -rsgnet $HUBRGNET -rsgweb $HUBRGWEB -rsgpurpose $RG_PurposeHUB -rsgmgm $HUBRGMGM -rsgmig $HUBRGMIG -rsgidentity $HUBRGID -rsgrsv $HUBRGRSV # new PRD Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $PRDRGAPP -rsgdb $PRDRGDB -rsgstor $PRDRGSTOR -rsgnet $PRDRGNET -rsgweb $PRDRGWEB -rsgpurpose $RG_PurposePRD # new ACC Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $ACCRGAPP -rsgdb $ACCRGDB -rsgstor $ACCRGSTOR -rsgnet $ACCRGNET -rsgweb $ACCRGWEB -rsgpurpose $RG_PurposeACC } 3 { # new HUB Resource Groups new-hubresourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $HUBRGAPP -rsgdb $HUBRGDB -rsgstor $HUBRGSTOR -rsgnet $HUBRGNET -rsgweb $HUBRGWEB -rsgpurpose $RG_PurposeHUB -rsgmgm $HUBRGMGM -rsgmig $HUBRGMIG -rsgidentity $HUBRGID -rsgrsv $HUBRGRSV # new PRD Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $PRDRGAPP -rsgdb $PRDRGDB -rsgstor $PRDRGSTOR -rsgnet $PRDRGNET -rsgweb $PRDRGWEB -rsgpurpose $RG_PurposePRD # new ACC Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $ACCRGAPP -rsgdb $ACCRGDB -rsgstor $ACCRGSTOR -rsgnet $ACCRGNET -rsgweb $ACCRGWEB -rsgpurpose $RG_PurposeACC # new TST Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $TSTRGAPP -rsgdb $TSTRGDB -rsgstor $TSTRGSTOR -rsgnet $TSTRGNET -rsgweb $TSTRGWEB -rsgpurpose $RG_PurposeTST } 4 { # new HUB Resource Groups new-hubresourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $HUBRGAPP -rsgdb $HUBRGDB -rsgstor $HUBRGSTOR -rsgnet $HUBRGNET -rsgweb $HUBRGWEB -rsgpurpose $RG_PurposeHUB -rsgmgm $HUBRGMGM -rsgmig $HUBRGMIG -rsgidentity $HUBRGID -rsgrsv $HUBRGRSV # new PRD Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $PRDRGAPP -rsgdb $PRDRGDB -rsgstor $PRDRGSTOR -rsgnet $PRDRGNET -rsgweb $PRDRGWEB -rsgpurpose $RG_PurposePRD # new ACC Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $ACCRGAPP -rsgdb $ACCRGDB -rsgstor $ACCRGSTOR -rsgnet $ACCRGNET -rsgweb $ACCRGWEB -rsgpurpose $RG_PurposeACC # new TST Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $TSTRGAPP -rsgdb $TSTRGDB -rsgstor $TSTRGSTOR -rsgnet $TSTRGNET -rsgweb $TSTRGWEB -rsgpurpose $RG_PurposeTST # new DEV Resource Groups new-resourcegroups -fullLocation $fullLocation -owner $owner -rsgapp $DEVRGAPP -rsgdb $DEVRGDB -rsgstor $DEVRGSTOR -rsgnet $DEVRGNET -rsgweb $DEVRGWEB -rsgpurpose $RG_PurposeDEV } } ##################################################################################### ######## END OF CREATION RESOURCE GROUPS HUB,PRD,DEV,TST ######## #####################################################################################
If we save the script as “1._Create_Az_ResourceGroups_v2.ps1” and run it with the parameters below this should give us the following result.
.\1._Create_Az_ResourceGroups_v2.ps1 -RG_PurposeHUB "hub" -RG_PurposePRD "prd" -RG_PurposeACC "acc" -RG_PurposeTST "tst" -RG_PurposeDEV "dev" -Cus "cus" -fullLocation "westeurope" -owner "Yannick Dils" -EnvironmentTier 4
View / Read-only on the Azure Portal
Thank you for reading through the resource groups blog post, our aim was to give you an overview and set of best practices on how to implement resource groups based on a couple of design standards. When looking at the virtual datacenter concept. We now have a clear naming standard with a set of policies that are being applied to our management groups, subscriptions and resource groups. Our resource groups have been pre-configured in order to facilitate the deployment of our future resources.
The following aspects of the virtual datacenter concept will be highlighted in the following upcoming posts:
Missed a part or want to review a previous section? Be sure to check out my previous posts:
The post Virtual Datacenter Concept | 3 of 10 | Resource Groups appeared first on Tunecom.
]]>The post Virtual Datacenter Concept | 2 of 10 | Governance appeared first on Tunecom.
]]>Today let’s focus on probably the most important pillar of the Azure Governance scaffold: Azure Governance.
We will talk about Azure Policies and auditing, resource tagging, managing subscriptions, locking down resources and role based access controls.
As stated before, this is probably the most important pillar, so what is it exactly?
Azure Governance allows you to define the rules of play to all your subscriptions within your Azure AD tenant. As with typical on-premises scenario’s, you would deploy group policies within your Windows Server Active Directory estate in order to meet your corporate requirements and standards. And most of all to make sure you’re gaining a certain amount of control over your infrastructure.
So, comparable with group policies you say? Not exactly, with Azure Policies we can do so much more and on different levels.
Before we start deploying policies, we need some kind of hierarchy to which we can apply our policies. And as with traditional group policies, we needed organizational units to group our resources into logical containers.
A management group is a logical container that can have policies, blueprints and role based access controls applied. Subscriptions can be added to a management group, so they can inherit the settings that are being applied to a management group. Below example provided by Microsoft, shows you how you can organize management groups, subscriptions and resource groups per Business Unit, Geo-location and environment type.
When on-boarding your first subscription into your tenant, it will be added to your root management group.
Best practice is to create a management group attached to the root management group and start from there. As with group policies, you usually start with a new OU and apply policies on that level. You never start with changing the default domain policies.
The following powershell demonstrates how to create a very simple management group hierarchy.
########### # # Let us define our variables here # ########### $ParentGroupname = "Contoso_Management_Group" $ITManagementGroupname = "IT_OPS_Management_Group" $PRDManagementGroupname = "Production_Environment_Management_Group" $DEVManagementGroupname = "Development_Environment_Management_Group" ########### # # Create the parent management group which is located just below the root management group # ########### $ParentGroup = New-AzManagementGroup -GroupName $ParentGroupname -DisplayName $ParentGroupname $parentobject = Get-AzManagementGroup -GroupName $ParentGroupname $ParentID = $ParentGroup.ParentId ########### # # Create the IT Operations management group as a member of the parent group # ########### $ITGroup = New-AzManagementGroup -GroupName $ITManagementGroupname -DisplayName $ITManagementGroupname -ParentObject $parentobject $ITGroupObject = Get-AzManagementGroup -GroupName $ITManagementGroupname ########### # # Create the Production and Development management groups as a member of the IT Operations management group # ########### $PRDGroup = New-AzManagementGroup -GroupName $PRDManagementGroupname -DisplayName $PRDManagementGroupname -ParentObject $ITGroupObject $DEVGroup = New-AzManagementGroup -GroupName $DEVManagementGroupname -DisplayName $DEVManagementGroupname -ParentObject $ITGroupObject ########### # # Move our subscriptions to the correct management group # ########### $PRDsubscription = Get-AzSubscription | where {$_.Name -eq "Azure IT Ops Prd"} $DEVsubscription = Get-AzSubscription | where {$_.Name -eq "Azure IT OPS Dev"} New-AzManagementGroupSubscription -GroupName $PRDGroup.Name -SubscriptionId $PRDsubscription.SubscriptionId New-AzManagementGroupSubscription -GroupName $DEVGroup.Name -SubscriptionId $DEVsubscription.SubscriptionId ########### # # End of script # ###########
In the previous example, we’ve added our production and development subscription to a management group. By adding our subscriptions to a management group, we can start defining our rules of play at 2 levels.
A subscription is a billing and management boundary to deploy your resources in and manage them via the right set of role based access controls.
Your subscription(s) are linked to your Azure AD tenant and can be made available to your Administrators by delegating permissions on a per subscriptions basis.
Our previous example provisioned a Production and Development subscription. With RBAC we can assign our developers the Contributor role to the Development subscription, so they can deploy and manage their resources. That same developer can only be granted a Read-Only role, to make sure no changes are being made on our production platform. If we really want to utilize the power of Azure we will deploy the roles on management group level, so every subscription in the PRD and DEV managemen group is inheriting the right set of roles and permissions.
And yes, Role Based Access Control is exactly what is states. We are giving access to a set of resources based on the specific role a security principal has. A security principal can be one of the following.
Now how does this work in Azure? RBAC is applied at 2 levels.
As an example, you can have a global administrator within your Azure Active Directory tenant. That global administrator will have the ability to perform everything within your Azure AD tenant. However, that same global administrator, doesn’t necessarily have the same amount of power within your Azure Subscriptions. Your Azure Subscriptions or Management Groups are using their own built-in roles. If you want your global administrator to have all the keys to the kingdom, you’ll have to grant him Subscription Owner rights within your Azure Subscription.
Below is an overview of the Global Administrator permissions.
Below is an overview of the Azure Owner permissions.
As an administrator, I would love to have the keys to the kingdom, for demo purposes, the below powershell script gives you an idea on how to assign the required roles to a user.
Best practice is to assign roles to a security principal based on the least privilege principle.
########### # # Connect to your Azure AD Tenant # ########### $MyAzureADTenant = Connect-AzureAD $tenantname = $MyAzureADTenant.TenantDomain ########### # # Define our new admin user # ########### $DisplayName = "MyAdmin" $MailNickName = "MyAdmin" $CompanyName = "MyCompany" $UPN = $MailNickName + "@" + $tenantname ########### # # Generate a new complex password # ########### function Get-RandomCharacters($length, $characters) { $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length } $private:ofs="" return [String]$characters[$random] } function Scramble-String([string]$inputString){ $characterArray = $inputString.ToCharArray() $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length $outputString = -join $scrambledStringArray return $outputString } $password = Get-RandomCharacters -length 5 -characters 'abcdefghiklmnoprstuvwxyz' $password += Get-RandomCharacters -length 1 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ' $password += Get-RandomCharacters -length 1 -characters '1234567890' $password += Get-RandomCharacters -length 1 -characters '!"§$%&/()=?}][{@#*+' $password = Scramble-String $password ########### # # Store the password in a password profile # ########### $PasswordProfile = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordProfile $PasswordProfile.Password = $Password ########### # # Create my new admin user # ########### New-AzureADUser -DisplayName $DisplayName -CompanyName $CompanyName -UserPrincipalName $UPN -AccountEnabled $true -PasswordProfile $PasswordProfile -MailNickName $MailNickName $User = Get-AzureADUser -ObjectId $UPN ########### # # Assign the Azure AD Global Administrator Role # ########### $GlobalAdminRole = Get-AzureADDirectoryRole | Where-Object {$_.displayname -eq 'Company Administrator'} Add-AzureADDirectoryRoleMember -ObjectId $GlobalAdminRole.ObjectId -RefObjectId $User.ObjectId ########### # # !!! Login with an account that has subcription owner permissions in order to add a new security principal to the role # Grant permissions to my Azure Subscription within my Parent Management Group # ########### $MyAzureSubscriptionAccount = Login-AzAccount $MyAzureSubscription = Get-azsubscription New-AzRoleAssignment -SignInName $UPN -RoleDefinitionName "Owner" -Scope "/providers/Microsoft.Management/managementGroups/$($ParentGroupname)" ########### # # End of script # ###########
Taking the above into account, we’ve made our environment ready with the following structure.
Now that we have our management structure up and running, let’s see how we can make use of Azure Policy to start locking down our environment.
Locking down… sounds like a plan, so let’s look at Resource Locks.
Now what is a resource lock and how is it applied.
There are 2 types of resource locks:
Where can this be applied?
When can this be applied?
You can apply a lock during the creation of the specific subscription, resource groups or resource. A lock can be applied via powershell or through the portal on existing resources. Or you can leverage Azure Policy to apply locks at scale.
Let us walk through the steps in order to make sure that a Delete lock is being applied to all of our resources.
For the sake of this blogpost series, I won’t dig too deep into Azure Policies and how the policy structure is being assembled. My colleague @ThomasVanLaere has made a detailed blog-post on Azure Policy, make sure to check that one out as well.
We will make use of a predefined Azure Policy and let’s alter this one to meet our needs. What are our needs?
Below Policy Definition is exactly what we need to Audit and deploy is a resource lock is not available on our resource groups.
{ "mode": "All", "policyRule": { "if": { "allOf": [ { "field": "type", "equals": "Microsoft.Resources/subscriptions/resourceGroups" }, "then": { "effect": "deployIfNotExists", "details": { "type": "Microsoft.Authorization/locks", "existenceCondition": { "field": "Microsoft.Authorization/locks/level", "equals": "CanNotDelete" }, "roleDefinitionIds": [ "/providers/Microsoft.Authorization/roleDefinitions/0000-0000-0000-0000-0000000" ], "deployment": { "properties": { "mode": "incremental", "template": { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json", "contentVersion": "1.0.0.0", "parameters": { "location": { "type": "string" } }, "resources": [ { "type": "Microsoft.Authorization/locks", "apiVersion": "2017-04-01", "name": "ResourceLock", "properties": { "level": "CanNotDelete", "notes": "Prevent accidental deletion of resource groups" } } ] } } } } } }
Now let’s apply this via Azure Policy. Create a new policy definition
Select a scope, since we are going to remediate a setting, we are forced to select a subscription. As of writing, management groups don’t support remediation tasks yet. Enter your details and create a new category if you like.
Now paste in the previous code
And here we go, we have our custom policy definition ready to be assigned.
Now navigate to policy assignments and select assign policy
Select your policy, enter a description and click next.
We have no parameters that need to be defined, so let’s continue.
Next up, yes we would like to remediate our policy. And this requires a managed identity in order to modify our settings. When applying a remediation task via the Azure Portal, the managed identity will receive the required permissions that are required to perform it’s remediating tasks.
Final step, review and create!
Now let’s wait for the assignment to kick in and review our compliance state.
It can take up to 30 minutes before your policy is evaluated. Once the policy is evaluated you see a compliance or non-compliance report.
In this case.. all our resource groups are compliant.
Now that we’ve seen our first policy at work, it’s time to take a look at resource tags. And how we can benefit from applying a right set of tags to our environment. The below list represents a series of tags that we commonly use in our Azure environments.
Description | Tag Name | Values/Format |
Cost Center | CostCenter | IT , HR, Finance, Sales, Marketing |
Environment | Environment | PRD, ACC, TST, DEV, HUB |
Owner | Owner | owner@company.com |
Application | App | Public Website, Sales App, .. |
Importance | Importance | Critical, high, medium, low |
Tags are most used in the following 2 scenario’s:
Before you start applying tags, make sure to go through the supported tag mechanisms. In our next blogpost we’ll talk about resource groups and as with resource locks we’ll have a look at how we can use Azure Policy to apply tags at scale.
This blogpost sort of includes the high level steps to start of with managing your subscriptions with management groups. Applying RBAC at scale and locking down your environment with Azure Policy.
The following aspects of the virtual datacenter concept will be highlighted in the following upcoming posts:
Missed a part or want to review a previous section? Be sure to check out my previous posts:
The post Virtual Datacenter Concept | 2 of 10 | Governance appeared first on Tunecom.
]]>The post Virtual Datacenter Concept | 1 of 10 | Naming Conventions appeared first on Tunecom.
]]>Let’s talk about one of the key pilars from the Azure Governance scaffold: naming conventions
Naming standards and conventions have been around for decades.
They are commonly used to identify objects and are used in most industries.
Let’s take car manufacturer BMW as an example, their cars are named with certain key characteristics in mind.
BMW 118D Hatch
Brand = BMW
Motorization = 1800 CC
Fuel Type = Diesel
Bodywork = Hatch (5 doors)
Pretty simple example on how a well defined naming standard can immediately give you the necessary info about a certain object. In essence, that’s why naming conventions are used.
As with regular industry naming conventions, standardizing the way you define your Azure Resources is crucial.
Microsoft has a predefined set of “policies” that need to be met with regards to naming your resources, the following docs article gives you an overview on how naming standards can be applied.
Below you can find a couple of commonly used resources that need to be uniquely identified globally across Microsoft Azure.
Entity | Scope |
API management | Global |
Key vault | Global |
Function app | Global |
Web app | Global |
Storage account name (data) | Global |
Storage account name (disks) | Global |
Data Lake Storage | Global |
Container registry | Global |
Service Bus namespace | Global |
Event Hubs namespace | Global |
Log Analytics Workspace | Global |
Taking the above information into account, we’ve generated a set of scripts that allow you to easily define a naming policy.
To start building our naming conventions we first need a couple of input variables that are unique to our setup.
##################################################################################### # # This script provides you with an overview of all naming conventions that are being used in the Virtual Datacenter Concept # Version: 0.1 # Author: Yannick Dils # ##################################################################################### ##################################################################################### # # Below is a set of variables that is being used in order to populate the naming conventions # ##################################################################################### # Variable abbreviation for the resource group that will be used for central shared services $RG_PurposeHUB = "hub" # Variable abbreviation for the resource group that will be used for production workloads $RG_PurposePRD = "prd" # Variable abbreviation for the resource group that will be used for acceptance workloads $RG_PurposeACC = "acc" # Variable abbreviation for the resource group that will be used for test workloads $RG_PurposeTST = "tst" # Variable abbreviation for the resource group that will be used for development workloads $RG_PurposeDEV = "dev" # Variable abbreviation for the customer / environment $Cus = "<proj>" # Variable abbreviation for the resource location $Location = "weu" # Variable which provides the desired resource location $FullLocation = "WestEurope" # Variable abbreviation for the resource owner $owner = "YD" # Variable abbreviation for the environment tier # 1 : HUB + PRD # 2 : HUB + PRD + ACC # 3 : HUB + PRD + ACC + TST # 4 : HUB + PRD + ACC + TST + DEV $EnvironmentTier = "4" # Variable required for resource generalization $Guid = [guid]::NewGuid() $MyGUID = $Guid.Guid.Remove(8) ##################################################################################### #####################################################################################
Resource group naming conventions are provided as per below. A resource group should be able to clearly define the customer or project, the type of environment and the purpose of the resources that are being created in the resource group.
Customer or project (3 letter abbreviation) | – | Tier (3 letter abbreviation of the Tier, HUB; PRD, TST, ACC, DEV) | Purpose (Resource Group Purpose Abbreviation) | – | Resource Purpose | |
<proj> | – | hub | – | identity | – | rg |
$HUBRGID = $Cus + '-' + $RG_PurposeHUB + '-' + 'identity' + '-rg'
Networking related naming conventions are provided as per below. In order to be able to perform smart discovery over your networking resources, Virtual Networks (VLANs), Subnets, Network Security Groups are named according to the endpoints and services that are located in the specified network topology.
Customer or project (3 letter abbreviation) | – | Tier (3 letter abbreviation of the Tier, PRD, TST, ACC, DEV) | – | Location (3 letter abbreviation of the location) | – | Resource Purpose |
<proj> | – | hub | – | weu | – | vn |
$virtualnetworkHUBname = $Cus + '-' + $RG_PurposeHUB + '-' + $Location + '-vn'
Customer or project (3 letter abbreviation) | – | Tier (3 letter abbreviation of the Tier, PRD, TST, ACC, DEV) | – | Subnet purpose | – | Resource Purpose |
<proj> | – | hub | – | identity | – | sn |
$hubsubnetname1identity = $Cus + '-' + $RG_PurposeHUB + '-' + 'identity' + '-sn'
Customer or project (3 letter abbreviation) | – | Tier (3 letter abbreviation of the Tier, PRD, TST, ACC, DEV) | – | Subnet purpose | – | Resource Purpose |
<proj> | – | hub | – | identity | – | nsg |
$hubnsgid = $Cus + '-' + $RG_PurposeHUB + '-' + 'identity' + '-' +'nsg'
Public IP usage | – | Public IP abbreviation |
resourcename | – | pip |
$vmpip = $VirtMachName + '-pip'
Azure provides several cloud native load balancing solutions, as with other Azure Resources, they require a logical naming convention.
Internal Load Balancer | – | Purpose |
ilb | – | adfs |
ilb | – | sql |
$adfsintlb = 'ilb-' + 'adfs'
External Load Balancer | – | Purpose |
elb | – | adfswap |
elb | – | rdgw |
$adfsextlb = 'elb-' + 'adfswap'
Compute resources contain virtual machines, availability sets, storage and everything related to the infrastructure you need to run your apps.
Storage Account | Redundancy level | Customer Abbreviation | Location | Tier | Purpose |
st | lrs | <proj> | weu | prd | logs |
$SA_Logs = 'stlrs' + $Cus + $location + $RG_PurposeHUB + 'logs'
Customer Abbreviation | – | Tier | – | Purpose | – | Resource Purpose |
<proj> | – | hub | – | sql | – | as |
#$hubavsql = $Cus + '-' + $RG_PurposeHUB + '-' + 'sql' + '-' + 'as'
Customer Abbreviation | Location | Optional Tier | Purpose | ## |
<proj> | weu | prd | sql | 01 |
$VMShortName = "sql01" $VirtMachName = $Cus.ToLower() + $location.ToLower() + $RG_PurposePRD + $VMShortName
Virtual Machine Name | – | Disk drive letter |
<vmname> | – | c |
<vmname> | – | e |
$OSDiskName = $VirtMachName + '-c'
In this blogpost, we’ve provided some guidance with regards to naming conventions and standards. The powershell “script” provided can be used for your convenience. In the upcoming series of posts we will be re-using these variables in order to build our Virtual Datacenter Concept topology.
Checkout our previous blogpost to recap on the Virtual Datacenter Concept.
The following aspects of the virtual datacenter concept will be highlighted in the following upcoming posts:
The post Virtual Datacenter Concept | 1 of 10 | Naming Conventions appeared first on Tunecom.
]]>