Search code examples
azurepowershellforeachtagsenumerate

Powershell ForEach-Object: You cannot call a method on a null-valued expression


I am trying to extract the tag values for SystemOwner and TechnicalOwner. However I get the error "ForEach-Object: You cannot call a method on a null-valued expression" I already know that the "$Tags = $RG.Tags" could be the issue because running this variable does not give me any results. Then how do I get the resource group tags?

$RGs = (Get-AzResourceGroup).ResourceGroupName
ForEach-Object $RG in $RGs
{
# Get email address value for each SystemOwner and TechnicalOwner Tag in the RG 
$Tags = $RG.Tags
$TO = ($Tags.GetEnumerator() | Where-Object {$_.Key -eq "Technical*"}).value
If ($TO)
{
    if ($TO.Value -ne $null) {
        Write-Host "Technical Owner Found in the Resource Group, building an Array"
        $contactemailadress += $TO.Value
     }
}
$SO = $Tags.GetEnumerator() | Where-Object {$_.Key -eq "SystemOwner"} #looking for Resource Group tag Names that have a space in the name
If ($SO)
{
    if ($SO.Value -ne $null) {
        Write-Host "System Owner Found in the Resource Group, building an Array"
        $contactemailadress += $SO.Value
     }
}
}

Solution

  • I think you could do something like this:

    $contactemailadress = @()
    $RGs = Get-AzResourceGroup
    $RGs | ForEach-Object {
        # Get email address value for each SystemOwner and TechnicalOwner Tag in the RG 
        $Tags = $_.Tags
    
        If ($Tags -and $Tags['TechnicalOwner'])
        {
                Write-Host "Technical Owner Found in the Resource Group, building an Array"
                $contactemailadress += $Tags['TechnicalOwner']
        }
    
        If ($Tags -and $Tags['SystemOwner'])
        {
                Write-Host "System Owner Found in the Resource Group, building an Array"
                $contactemailadress += $Tags['SystemOwner']
        }
    }
    

    The main issue you have is that (Get-AzResourceGroup).ResourceGroupName returns the Resource Group Name - however, in order for the tags to be returned you would need the full object.

    You can prove this by checking for Properties on the Get-AzResourceGroup command you are running:

    (Get-AzResourceGroup -Name resource_group_name).ResourceGroupName | Get-Member -Type Property
    
       TypeName: System.String
    
    Name   MemberType Definition
    ----   ---------- ----------
    Length Property   int Length {get;}
    

    As you can see, this command only has one property - length, which is not useful for your scripts purpose.

    If you run this instead, you get the full object with all properties:

    Get-AzResourceGroup -Name resource_group_name | Get-Member -Type Property
    
       TypeName: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup
    
    Name              MemberType Definition
    ----              ---------- ----------
    Location          Property   string Location {get;set;}
    ManagedBy         Property   string ManagedBy {get;set;}
    ProvisioningState Property   string ProvisioningState {get;set;}
    ResourceGroupName Property   string ResourceGroupName {get;set;}
    ResourceId        Property   string ResourceId {get;set;}
    Tags              Property   hashtable Tags {get;set;}
    TagsTable         Property   string TagsTable {get;}
    

    You can use foreach or ForEach-Object, both should work but when using the latter you can query the variable inside the loop using the $_ notation.

    Lastly, in your script there seem to be some variables unnecessarily used (but I don't know if you would need them later on), such as $TO and two if-statements that more or less do the same (i.E. If ($SO) and if ($SO.Value -ne $null)).