Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ## Helper Functions
- <#
- Helper function for merging hashtables
- <https://stackoverflow.com/questions/8800375/merging-hashtables-in-powershell-how>
- #>
- function Merge-HashTable {
- param(
- [hashtable] $OriginTable, # your original set
- [hashtable] $Append # the set you want to update/Append to the original set
- )
- # clone for idempotence
- $OriginTable1 = $OriginTable.Clone() ;
- # we need to remove any key-value pairs in $OriginTable1 that we will
- # be replacing with key-value pairs from $uppend
- foreach ($key in $Append.Keys) {
- if ($OriginTable1.ContainsKey($key)) {
- $OriginTable1.Remove($key) ;
- }
- }
- # union both sets
- return $OriginTable1 + $Append ;
- }
- ## Runbook Variables
- # Resource types that are untaggable
- $exAzureResources = @(
- "Microsoft.ClassicNetwork/virtualNetworks"
- "Microsoft.ClassicStorage/storageAccounts"
- "Microsoft.ClassicCompute/domainNames"
- "Microsoft.ClassicCompute/virtualMachines"
- "Microsoft.OperationsManagement/solutions"
- "Microsoft.DomainRegistration/domains"
- "microsoft.insights/alertrules"
- "microsoft.insights/webtests"
- "TrendMicro.DeepSecurity/accounts"
- "Microsoft.Automation/automationAccounts/configurations"
- )
- # Counters for results
- $tagCounterSuccess = 0
- $tagCounterFail = 0
- $tagCounterSkip = 0
- $tagCounterNull = 0
- $tagCounterUntaggable = 0
- $tagCounterError = 0
- ## Connect to Azure with Run As account
- $Conn = Get-AutomationConnection -Name AzureRunAsConnection
- $azRMAccountParams = @{
- ServicePrincipal = $true
- Tenant = $Conn.TenantID
- ApplicationId = $Conn.ApplicationID
- CertificateThumbprint = $Conn.CertificateThumbprint
- }
- Add-AzureRMAccount @azRMAccountParams | Out-Null
- ## Script main
- try {
- /write-host
- # Capture Tags from Resource Group
- $azResourceGroups = Get-AzureRmResourceGroup
- # Iterate through groups and set tags
- foreach ($azResourceGroup in $azResourceGroups) {
- # Get Resources in the Resource Group
- $azResources = Get-AzureRmResource | Where-Object {$_.ResourceGroupName -like $azResourceGroup.ResourceGroupName}
- # Standard TFL tags
- $tflTags = @{
- "SvcName" = $null
- "SvcOwner" = $null
- "CrgCostCode" = $null
- "Environment" = $null
- }
- # Temp var to store RG tags before merge
- $tempTable = @{}
- # Test for Tags property
- switch ($azResourceGroup.PSObject.Properties['Tags']) {
- # If Tags property is present
- ({$PSItem.Value})
- {
- # Iterate through Resource Group Tags
- foreach ($kvpRG in $azResourceGroup.Tags.GetEnumerator()) {
- # Iterate through TFL Tags
- foreach ($kvpTag in $tflTags.GetEnumerator()) {
- # If match is found add Resource Group Tag/Value to $tempTable
- if ($kvpTag.Key -like $kvpRG.Key) {
- $tempTable.Add($kvpRG.Key,$kvpRG.Value)
- }
- }
- }
- }
- # If Tags property is not present
- ({!($PSItem.Value)})
- {
- $tagCounterNull++
- # Log this for informational purposes
- Write-Output "No Tags set on $($azResourceGroup.ResourceGroupName)"
- }
- }
- # Merge TFL Tags with the Resource Group Tags
- # We do it this way so that even if it's blank the key is added
- $mergeTableRG = Merge-HashTable -OriginTable $tflTags -Append $tempTable
- # Iterate through the Resources
- foreach ($azResource in $azResources) {
- # Test if resourceType is classic
- switch ($azResource.ResourceType) {
- # If in the exclude list ignore
- ({$PSItem -in $exAzureResources})
- {
- $tagCounterUntaggable++
- # Uncomment for debugging purposes
- #Write-Output "$($azResource.ResourceName) is not a taggable resource"
- }
- # If not in the exclude list
- ({!($PSItem -in $exAzureResources)})
- {
- # Test each Resource for Tags property
- switch ($azResource.PSObject.Properties['Tags']) {
- # If Tags property is present
- ({$PSItem})
- {
- # Capture Tags hashtable to var
- $azResourceTags = $azResource.Tags
- }
- # If Tags property is not present
- ({!($PSItem)})
- {
- # Create blank/dummy hashtable for the merge
- $azResourceTags = @{}
- }
- }
- # Final merge with all tags for resource
- $mergeTable = Merge-HashTable -OriginTable $mergeTableRG -Append $azResourceTags
- # Test if tags are up to date
- # Convert hastables to PSCustomObject so that we can use Compare-Object
- $currentTags = [PSCustomObject]$azResourceTags
- $newTags = [PSCustomObject]$mergeTable
- switch (!(Compare-Object $currentTags.PSObject.Properties $newTags.PSObject.Properties)) {
- # If tags match skip
- ({$PSItem})
- {
- $tagCounterSkip
- # Commented out to avoid noise
- # Uncomment for debugging purposes
- #Write-Output "Skip $($azResource.ResourceName)"
- }
- # If mismatch then tag
- ({!($PSItem)})
- {
- try {
- # Get-AzureRmResource Paramaters
- $GetAzureRmResourceParams = @{
- ResourceName = $azResource.ResourceName
- ResourceGroupName = $azResourceGroup.ResourceGroupName
- }
- # Set-AzureRmResource Parameters
- $SetAzureRmResourceParams = @{
- Tag = $mergeTable
- Force = $true
- }
- # Run tagging command
- Write-Output "Tagging $($azResource.ResourceName)..."
- Get-AzureRmResource @GetAzureRmResourceParams | Set-AzureRmResource @SetAzureRmResourceParams | Out-Null
- # Verify Tagging was successful
- # Grab new Tags
- $verifyTagsTest = [PSCustomObject](Get-AzureRmResource @GetAzureRmResourceParams).Tags
- # Compare current tags to what *SHOULD* have been applied
- switch (!(Compare-Object $verifyTagsTest.PSObject.Properties $newTags.PSObject.Properties)) {
- # If match, log successfull
- ({$PSItem})
- {
- $tagCounterFail++
- Write-Warning -Message "$($azResource.ResourceName) failed tagging"
- Write-Warning -Message "Error message: $($Error[0])"
- }
- # If they don't match log and log last error for debugging
- ({!($PSItem)})
- {
- $tagCounterSuccess++
- Write-Output "$($azResource.ResourceName) was tagged successfully"
- }
- }
- }
- catch {
- $tagCounterError++
- Write-Warning -Message "Unable to set tag"
- Write-Warning -Message "Error message: $($Error[0])"
- }
- } # End mismatch
- } # End Tagging switch
- } # End not Classic Resource
- } # End Classic Resource test
- } # End Resource loop
- } # End Resource Group loop
- ## Final Script Output
- $outputTable = @{
- "Resources Tagged" = $tagCounterSuccess
- "Resources Failed Tagging" = $tagCounterFail
- "Resources Skipped Tagging" = $tagCounterSkip
- "Resource Groups w/o Tags" = $tagCounterNull
- "Resources Untaggable" = $tagCounterUntaggable
- "Errors" = $tagCounterError
- }
- $Output = [PSCustomObject]$outputTable
- return $Output
- }
- catch {
- # Capture all errors
- $errorMessage = "Error in Runbook: Unhandled exception :: " +
- "Line: $($_.InvocationInfo.ScriptLineNumber) " +
- "Line: $($_.InvocationInfo.Line) " +
- "Error message: $($_.exception.message)"
- # Output errors for debugging and halt runbook
- Write-Error -Message $errorMessage -ErrorAction Stop
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement