Advertisement
Guest User

Untitled

a guest
May 22nd, 2018
106
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. Get-AzureRmSubscription | Where-Object {$_.Name -like "*shared*dev*"} | Set-AzureRmContext
  3. ## Helper Functions
  4. <#
  5.     Helper function for merging hashtables
  6.     <https://stackoverflow.com/questions/8800375/merging-hashtables-in-powershell-how>
  7. #>
  8.  
  9. function Merge-HashTable {
  10.     param(
  11.         [hashtable] $OriginTable, # your original set
  12.         [hashtable] $Append # the set you want to update/Append to the original set
  13.     )
  14.  
  15.     # clone for idempotence
  16.     $OriginTable1 = $OriginTable.Clone() ;
  17.  
  18.     # we need to remove any key-value pairs in $OriginTable1 that we will
  19.     # be replacing with key-value pairs from $uppend
  20.     foreach ($key in $Append.Keys) {
  21.         if ($OriginTable1.ContainsKey($key)) {
  22.             $OriginTable1.Remove($key) ;
  23.         }
  24.     }
  25.  
  26.     # union both sets
  27.     return $OriginTable1 + $Append ;
  28. }
  29.  
  30. ## Runbook Variables
  31. # Resource types that are untaggable
  32. $exAzureResources = @(
  33.     "Microsoft.ClassicNetwork/virtualNetworks"
  34.     "Microsoft.ClassicStorage/storageAccounts"
  35.     "Microsoft.ClassicCompute/domainNames"
  36.     "Microsoft.ClassicCompute/virtualMachines"
  37.     "Microsoft.OperationsManagement/solutions"
  38.     "Microsoft.DomainRegistration/domains"
  39.     "microsoft.insights/alertrules"
  40.     "microsoft.insights/webtests"
  41.     "TrendMicro.DeepSecurity/accounts"
  42.     "Microsoft.Automation/automationAccounts/configurations"
  43.     #"Microsoft.Sql/servers/elasticpools"
  44.     "Microsoft.StorageSync/storageSyncServices"
  45.     "microsoft.insights/activityLogAlerts"
  46. )
  47.  
  48. # Counters for results
  49. $tagCounterSuccess      = 0
  50. $tagCounterFail         = 0
  51. $tagCounterSkip         = 0
  52. $tagCounterNull         = 0
  53. $tagCounterUntaggable   = 0
  54. $tagCounterError        = 0
  55.  
  56. # Debugging Var
  57. $untaggableresourcesList = @{}
  58.  
  59. ## Script main
  60. try {
  61.     # Capture Tags from Resource Group
  62.     $azResourceGroups = Get-AzureRmResourceGroup
  63.  
  64.     # Iterate through groups and set tags
  65.     foreach ($azResourceGroup in $azResourceGroups) {
  66.         # Get Resources in the Resource Group
  67.         $azResources = Get-AzureRmResource | Where-Object {
  68.             $_.ResourceGroupName -like $azResourceGroup.ResourceGroupName
  69.         }
  70.         # Standard TFL tags
  71.         $tflTags = @{
  72.             "SvcName"       = $null
  73.             "SvcOwner"      = $null
  74.             "CrgCostCode"   = $null
  75.             "Environment"   = $null
  76.         }
  77.         # Temp var to store RG tags before merge
  78.         $tempTable = @{}
  79.         # Test for Tags property
  80.         switch ($azResourceGroup.PSObject.Properties['Tags']) {
  81.             # If Tags property is present
  82.             ({$PSItem.Value})
  83.             {
  84.                 # Iterate through Resource Group Tags
  85.                 foreach ($kvpRG in $azResourceGroup.Tags.GetEnumerator()) {
  86.                     # Iterate through TFL Tags
  87.                     foreach ($kvpTag in $tflTags.GetEnumerator()) {
  88.                         # If match is found add Resource Group Tag/Value to $tempTable
  89.                         if ($kvpTag.Key -like $kvpRG.Key) {
  90.                             $tempTable.Add($kvpRG.Key,$kvpRG.Value)
  91.                         }
  92.                     }
  93.                 }
  94.             }
  95.             # If Tags property is not present
  96.             ({!($PSItem.Value)})
  97.             {
  98.                 $tagCounterNull++
  99.                 # Log this for informational purposes
  100.                 Write-Warning "No Tags set on $($azResourceGroup.ResourceGroupName)"
  101.             }
  102.         }
  103.  
  104.         # Merge TFL Tags with the Resource Group Tags
  105.         # We do it this way so that even if it's blank the key is added
  106.         $mergeTableRGParams = @{
  107.             "OriginTable"   = $tflTags
  108.             "Append"        = $tempTable
  109.         }
  110.         $mergeTableRG = Merge-HashTable @mergeTableRGParams
  111.  
  112.         # Iterate through the Resources
  113.         foreach ($azResource in $azResources) {
  114.             # Reset resource tagging skip variables
  115.             $untaggableResource = $false
  116.             $skipResource = $false
  117.             # Variables used in loop / To be cleared
  118.             @(
  119.                 "azResourceTags"
  120.                 "newTags"
  121.                 "currentTags"
  122.                 "mergeTable"
  123.                 "verifyTagsTest"
  124.                 "mergeTableParams"
  125.                 "compareObjectParams"
  126.                 "getAzureRmResourceParams"
  127.                 "setAzureRmResourceParams"
  128.                 "verifyCompareObjectParams"
  129.             ).ForEach({if (Get-Variable $PSItem -ErrorAction SilentlyContinue) {Clear-Variable $PSItem}})
  130.             # Excluded resource tests
  131.             $exTest1 = $azResource.ResourceType -in $exAzureResources
  132.             $exTest2 = ($azResource.Kind -like "*system") -and ($azResource.ResourceType -like "Microsoft.Sql/servers/*")
  133.             # Test if resourceType is classic
  134.             switch ($azResource) {
  135.                 # If in the exclude list ignore
  136.                 ({$exTest1 -or $exTest2})
  137.                 {
  138.                     $tagCounterUntaggable++
  139.                     $untaggableResource = $true
  140.                     # Uncomment for debugging purposes
  141.                     #Write-Output "$($azResource.ResourceName) is not a taggable resource"
  142.                     $Key = $azResource.ResourceName
  143.                     $Value = $azResource.ResourceType
  144.                     $untaggableresourcesList.Add($Key,$Value)
  145.                     break
  146.                 }
  147.                 # If not in the exclude list
  148.                 ({!($exTest1 -or $exTest2)})
  149.                 {
  150.                     break
  151.                 }
  152.             } # End Classic Resource test
  153.             # Test if resource is taggable
  154.             if ($untaggableResource) {
  155.                 # Move on to next item
  156.                 continue
  157.             }
  158.             # Test each Resource for Tags property
  159.             switch ($azResource.PSObject.Properties['Tags']) {
  160.                 # If Tags property is present
  161.                 ({$PSItem})
  162.                 {
  163.                     # Capture Tags hashtable to var
  164.                     $azResourceTags = $azResource.Tags
  165.                 }
  166.                 # If Tags property is not present
  167.                 ({!($PSItem)})
  168.                 {
  169.                     # Create blank/dummy hashtable for the merge
  170.                     $azResourceTags = @{}
  171.                 }
  172.             }
  173.             # Parameters for the merge table below
  174.             $mergeTableParams = @{
  175.                 OriginTable = $mergeTableRG
  176.                 Append      = $azResourceTags
  177.             }
  178.             # Final merge with all tags for resource
  179.             $mergeTable = Merge-HashTable @mergeTableParams
  180.             if ($azResourceTags.Count -eq 0) {
  181.                 $azResourceTags = @{
  182.                     (Get-Date).ToString('sshhddmmyy') = (Get-Date).ToString('sshhddmmyy')
  183.                 }
  184.             }
  185.             # Test if tags are up to date
  186.             # Convert hastables to PSCustomObject so that we can use Compare-Object
  187.             $currentTags = ([PSCustomObject]$azResourceTags).PSObject.Properties | Sort-Object Name
  188.             $newTags = ([PSCustomObject]$mergeTable).PSObject.Properties | Sort-Object Name
  189.             # Parameters for the Compare-Object
  190.             $compareObjectParams = @{
  191.                 ReferenceObject     = $currentTags
  192.                 DifferenceObject    = $newTags
  193.             }
  194.             # Compare objects to test if any action should be taken
  195.             switch (!(Compare-Object @compareObjectParams)) {
  196.                 # If tags match skip
  197.                 ({$PSItem})
  198.                 {
  199.                     $tagCounterSkip++
  200.                     $skipResource = $true
  201.                     # Commented out to avoid noise
  202.                     # Uncomment for debugging purposes
  203.                     #Write-Output "Skip $($azResource.ResourceName)"
  204.                     break
  205.                 }
  206.                 # If mismatch then tag
  207.                 ({!($PSItem)})
  208.                 {
  209.                     break
  210.                 }
  211.             } # End Tagging switch
  212.             # Test if resource can be skipped
  213.             if ($skipResource) {
  214.                 # Move on to next item
  215.                 continue
  216.             }
  217.             try {
  218.                 # Get-AzureRmResource Paramaters
  219.                 $getAzureRmResourceParams = @{
  220.                     ResourceName        = $azResource.ResourceName
  221.                     ResourceGroupName   = $azResourceGroup.ResourceGroupName
  222.                 }
  223.                 # Set-AzureRmResource Parameters
  224.                 $setAzureRmResourceParams = @{
  225.                     ResourceName        = $azResource.ResourceName
  226.                     ResourceGroupName   = $azResource.ResourceGroupName
  227.                     ResourceType        = $azResource.ResourceType
  228.                     Tag                 = $mergeTable
  229.                     Force               = $true
  230.                 }
  231.                 # Run tagging command
  232.                 Write-Output "Tagging $($azResource.ResourceName)..."
  233.                 $taggingJob = Start-Job -ScriptBlock {
  234.                     Set-AzureRmResource @setAzureRmResourceParams | Out-Null
  235.                 }
  236.                 # Tagging runs as a job with 2 min timeout
  237.                 $taggingJob | Wait-Job -Timeout 120 | Out-Null
  238.                 # End job
  239.                 if (!($taggingJob | Where-Object {$_.State -eq "Completed"})) {
  240.                     $PSItem | Stop-Job
  241.                     Write-Warning -Message "$($azResource.ResourceName) tagging job timed out after 2 minutes"
  242.                 }
  243.                 # Verify Tagging was successful
  244.                 # Grab new Tags
  245.                 $verifyTagsTest = [PSCustomObject](Get-AzureRmResource @getAzureRmResourceParams).Tags
  246.                 $verifyTagsTest = $verifyTagsTest.PSObject.Properties | Sort-Object Name
  247.                 $verifyCompareObjectParams = @{
  248.                     ReferenceObject     = $verifyTagsTest
  249.                     DifferenceObject    = $newTags
  250.                 }
  251.                 # Compare current tags to what *SHOULD* have been applied
  252.                 switch (!(Compare-Object @verifyCompareObjectParams)) {
  253.                     # If match, log successfull
  254.                     ({$PSItem})
  255.                     {
  256.                         $tagCounterSuccess++
  257.                         Write-Output "$($azResource.ResourceName) was tagged successfully"
  258.                     }
  259.                     # If they don't match log and log last error for debugging
  260.                     ({!($PSItem)})
  261.                     {
  262.                         $tagCounterFail++
  263.                         Write-Warning -Message "$($azResource.ResourceName) failed tagging"
  264.                         Write-Warning -Message "Error message: $($_.Exception.Message)"
  265.                     }
  266.                 }
  267.             }
  268.             catch {
  269.                 $tagCounterError++
  270.                 Write-Warning -Message "Unable to set tag"
  271.                 Write-Warning -Message "Error message: $($_.Exception.Message)"
  272.             }
  273.         } # End Resource loop
  274.     } # End Resource Group loop
  275.  
  276.     ## Final Script Output
  277.     $outputTable = @{
  278.         "Resources Tagged"          = $tagCounterSuccess
  279.         "Resources Failed Tagging"  = $tagCounterFail
  280.         "Resources Skipped Tagging" = $tagCounterSkip
  281.         "Resource Groups w/o Tags"  = $tagCounterNull
  282.         "Resources Untaggable"      = $tagCounterUntaggable
  283.         "Errors"                    = $tagCounterError
  284.     }
  285.     $Output = [PSCustomObject]$outputTable
  286.     return $Output
  287. }
  288. catch {
  289.     # Capture all errors
  290.     $errorMessage = @(
  291.         "Error in Runbook: Unhandled exception ::"
  292.         "Line: $($_.InvocationInfo.ScriptLineNumber)"
  293.         "Line: $($_.InvocationInfo.Line.Trim())"
  294.         "Error message: $($_.Exception.Message)"
  295.     )
  296.     # Output errors for debugging and halt runbook
  297.     Write-Error -Message ($errorMessage -join " ") -ErrorAction Stop
  298. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement