sjAzureTools.psm1
function updateResourceTag { param( [Parameter(Mandatory)] [Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource]$ResourceObject, [Parameter(Mandatory)] [System.Collections.Hashtable]$Tags ) Write-Verbose "Get the existing tags from the Resource" try { $ResourceTags = $ResourceObject.Tags if (-not($ResourceTags)) { Write-Verbose "No tags exist. Set ResourceTags variable as empty array" $ResourceTags = @{ } } else { if ($ResourceTags.Count + $Tags.Count -gt 15) { Write-Error "The number of tags for $($ResourceObject.Name) is greater than 15" Return } } } catch { $pscmdlet.ThrowTerminatingError($_) } #add new tags to the array foreach ($key in $Tags.keys) { $ResourceTags[$key] = $Tags[$key] } try { Write-Verbose "Setting tags on ResourceID: $($ResourceObject.ResourceId)" Set-AzResource -Tag $ResourceTags -ResourceId $ResourceObject.ResourceId -Force -ErrorAction Stop Write-Verbose "Updated Tags on $($ResourceObject.Name)" } catch { $pscmdlet.ThrowTerminatingError($_) } } #end function updateResourceTag function Update-sjAzResourceTags { #Requires -Version 3.0 # Requires -Modules Az #don't require Az module, it takes a LONG time to resolve this requirement <# .NOTES PowerShell function written to add tags to Azure Resources Written by Steven Judd on 2019/06/25 Version 20190710 Updated by Steven Judd on 2019/07/09 to do the following: Removed the UpdatedBy parameter and made it a variable in the begin block Added UpdatedBy tag as an automatically updated variable Updated by Steven Judd on 2019/07/10 to do the following: Updated the help examples to use the Tags parameter and to have better explanations Updated the username to be xplat compatible Added an example for using the Subscription parameter Set the Tags parameter to Mandatory for the updateResourceTag inline function Added a check to see if the number of tags will be greater than 15 (current Azure limit) Add Features: ... .SYNOPSIS Add tags to an Azure Resource. .DESCRIPTION This function will add tags to an Azure Resource. It will not remove any existing tags. Rather, it will add those that need to be added and update tags that already exist with new data provided. .LINK XXXX .PARAMETER ResourceID Enter the Azure ResourceID for the Resource to be tagged. The benefit of using the ResourceID is the Subscription and Resource Group information is not required to be known or entered. This is the default parameter and will be required if no parameters are entered. .PARAMETER SubscriptionName Enter the SubscriptionName for the Resource to be tagged. .PARAMETER ResourceGroupName Enter the Resource Group Name for the Resource to be tagged. .PARAMETER Name Enter the Name for the Resource to be tagged. .PARAMETER Tags Enter a Hashtable object for the tags to be added to the specified Resource. This function will not remove any existing tags, it will only add or update tags. .EXAMPLE Update-sjAzResourceTags -SubscriptionName NotFree -ResourceGroupName VMs -Name Server1 -Tags @{Environment = 'Prd'} This example will tag the Server1 resource in the VMs resource group in the NotFree subscription with the Environment:Prd tag. .EXAMPLE Update-sjAzResourceTags -ResourceID (Get-AzResource Server1).ResourceID -Tags @{Environment = 'Prd'} This example will add the Environment:Prd tag on the Server1 Resource in the current subscription. Note: Make sure the Get-AzResource command returns the proper object. .EXAMPLE Get-AzResource Server1 | Update-sjAzResourceTags -Tags @{Environment = 'Prd'} This example will add the Divest:Yes tag on the Server1 Resource in the current subscription. This method shows that you can pipe an object to the Update-sjAzResourceTags function and it will tag the resource. Note: Make sure the Get-AzResource command returns the proper object. .EXAMPLE Get-Content "C:\temp\ServersToTag.csv" Name,SubscriptionName,ResourceGroupName Server1,NotFree,VMs Server2,NotFree,VMs Server3,NotFree,VMs Server4,NotFree,VMs Import-Csv "C:\Temp\ServersToTag.csv" | TagAzResourceYesDivest -Verbose -OutVariable updatedAzResources This example uses a CSV file with the Resource Names, Subs, and RGs and pipes the contents to the TagAzResourceYesDivest function. It will return Verbose output as well as put the output into the $updatedAzResources variable to make it easy to review the results of the function. #> [cmdletbinding(DefaultParameterSetName = "ResourceID")] param( [Parameter(Position = 0, Mandatory, ParameterSetName = "ResourceID", ValueFromPipelineByPropertyName)] [string]$ResourceID, [Parameter(Position = 0, Mandatory, ParameterSetName = "SubRgName", ValueFromPipelineByPropertyName)] [string]$SubscriptionName, [Parameter(Position = 1, Mandatory, ParameterSetName = "SubRgName", ValueFromPipelineByPropertyName)] [string]$ResourceGroupName, [Parameter(Position = 2, Mandatory, ParameterSetName = "SubRgName", ValueFromPipelineByPropertyName)] [string]$Name, # Placeholder for adding the ability to pass a PSResource object # [Parameter(Position=0,Mandatory,ParameterSetName="InputObject", # ValueFromPipeline)] # [Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource]$InputObject, [Parameter(Position = 3, Mandatory, ValueFromPipelineByPropertyName)] [System.Collections.Hashtable]$Tags ) begin { Write-Verbose "Adding tags:`n$($Tags | Out-String)" #region check to see if connected to Azure and if not initiate a connection try { Write-Verbose "Checking connection to Azure Resource Manager" $AzureContext = Get-AzContext -ErrorAction Stop #if not connected the Id will be $null if (-not($AzureContext.Account.Id)) { $pscmdlet.ThrowTerminatingError("Run Add-AzAccount to login to Azure") #this will move to the Catch block instead of throwing the error message } } catch { try { Write-Verbose "CONNECTING connection to Azure Resource Manager" if ($SubscriptionName) { $null = Add-AzAccount -Subscription $SubscriptionName -ErrorAction Stop } else { $null = Add-AzAccount -ErrorAction Stop } $AzureContext = Get-AzContext -ErrorAction Stop } catch { $pscmdlet.throwTerminatingError($_) } } Write-Verbose "Azure Context: $($AzureContext.Tenant.Id)" #endregion #get initial subscription to return to at the end of the function $InitialSubscription = Get-AzContext Write-Verbose "Initial subscription set to $((Get-AzSubscription | Where-Object SubscriptionId -eq $InitialSubscription.Subscription).Name)" #add the UpdatedBy tag $Tags['UpdatedBy'] = [Environment]::UserName } #end begin block process { #get the Subscription from the ResourceId if ($ResourceID) { $SubscriptionName = (Get-AzSubscription -SubscriptionId ((Get-AzResource -ResourceId $ResourceID).ResourceId -split '/')[2]).Name } #set the subscription Write-Verbose "Set the subscription to '$SubscriptionName'" try { $null = Set-AzContext -Subscription $SubscriptionName -ErrorAction Stop } catch { $pscmdlet.ThrowTerminatingError($_) } if ($ResourceGroupName) { Write-Verbose "Check to ensure the Resource Group exists" try { $ResourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction Stop #the below check may not be necessary based on how Get-AzResourceGroup handles not finding the specified RG if (-not($ResourceGroup)) { $pscmdlet.ThrowTerminatingError("Unable to find specified Resource Group: $ResourceGroup") } } catch { Write-Error "Unable to find the specified Resource Group '$ResourceGroupName' in the '$SubscriptionName' Subscription" Continue } } #end if $ResourceGroupName #get Resource object if ($ResourceID) { try { $ResourceObject = Get-AzResource -ResourceId $ResourceID } catch { Write-Error $_ continue } } #end if not $ResourceID else { try { $ResourceObject = Get-AzResource -Name $Name -ResourceGroupName $ResourceGroupName } catch { Write-Error $_ continue } } updateResourceTag -ResourceObject $ResourceObject -Tags $Tags #if resource is a VM, get and tag the Disks and NICs if ($ResourceObject.Type -eq "Microsoft.Compute/virtualMachines") { Write-Verbose "Object is a VM. Tagging the disks and NICs" Write-Verbose "Getting the VM object" $vmObject = Get-AzVm -ResourceGroupName $ResourceObject.ResourceGroupName -Name $ResourceObject.ResourceName Write-Verbose "Getting OS disk ResourceObject and tagging it" $osDiskObject = Get-AzResource -Name $vmObject.StorageProfile.OsDisk.Name -ResourceGroupName $vmObject.ResourceGroupName updateResourceTag -ResourceObject $osDiskObject -Tags $Tags Write-Verbose "Getting Data disks ResourceObject and tagging them" foreach ($dataDisk in $vmObject.StorageProfile.DataDisks) { $dataDiskObject = Get-AzResource -Name $dataDisk.Name -ResourceGroupName $vmObject.ResourceGroupName updateResourceTag -ResourceObject $dataDiskObject -Tags $Tags } Write-Verbose "Getting NICs ResourceObject and tagging them" foreach ($nic in $vmObject.NetworkProfile.NetworkInterfaces) { $nicObject = Get-AzResource -ResourceId $nic.Id updateResourceTag -ResourceObject $nicObject -Tags $Tags } } } #end process block end { if ((Get-AzContext).TenantId -ne $InitialSubscription.TenantId) { Write-Verbose "Return to initial subscription: $((Get-AzSubscription | Where-Object SubscriptionId -eq $InitialSubscription.Subscription).Name)" $null = Set-AzContext -Subscription ($InitialSubscription.Subscription) } #end if not in Initial Subscription } } #end Update-sjAzResourceTags function #test runs #Get-AzResource -Name Server1 | Export-Clixml -path $env:temp\azresourcedata.xml #$serverstoupdate = Import-Clixml -Path $env:temp\azresourcedata.xml #$serverstoupdate | Update-sjAzResourceTags -Tags @{test = 'test' } -Verbose #Update-sjAzResourceTags -ResourceID '/subscriptions/8a5f240d-3141-4a50-936b-81999ba32d01/resourceGroups/VMs/providers/Microsoft.Compute/virtualMachines/Server1' -Tags @{test = 'test' } #Update-sjAzResourceTags -ResourceID (Get-AzResource -Name Server1).ResourceID -Tags @{test = 'test' } -Verbose #Get-AzResource -Name Server1 | Update-sjAzResourceTags -Tags @{test = 'test' } #Import-Csv C:\temp\ServersToTag.csv | Update-sjAzResourceTags -Tags @{test = 'test' } -Verbose #Import-Csv C:\temp\ServerList.csv | select -first 2 -Skip 1 | % {if(-not(Get-AzResource -Name $_.server -TagName Divest -TagValue Yes)){Get-AzResource -Name $_.server}} | Update-sjAzResourceTags -Tags @{test = 'test' } -Verbose #Import-Csv C:\temp\ServerListWithSubRgInfo.csv | select -first 2 -Skip 4 | Update-sjAzResourceTags -Tags @{test = 'test' } -Verbose Export-ModuleMember -Function *-* |