3

I'm looking for some help troubleshooting comparing .key values to objects.

Basically what's happening here is I'm connecting to two VMware vCenters and downloading a list of roles and putting those roles into two hash tables, then comparing them.

The problem comes down to the Process-Roles function where the comparing logic is flawed somewhere. It outputs all of the roles in both lists. I think the (-not .containskey) isn't working right. I've debugged in powerGUI and both hashtables and mstr_roles/slave_roles are all filled correctly.

The roles lists should be object lists, as they were filled with Get-VIRole. The hash table should be object-in-key, value null lists. Is it possible to compare these two? I'm trying to check if the $role object in the roles list exists in the .key values list of the hash table.

$creds = Get-Credential
$mst = Read-Host "`n Master Server: "
$slv = Read-Host "`n Slave Server: "
$hsh_mstr_roles = @{}
$hsh_slave_roles = @{}
$mstr_roles = ""
$slave_roles = ""

Get-Roles -MasterServer $mst -SlaveServer $slv

Process-Roles 

.

function Get-Roles() {
Param(
 [Parameter(Mandatory=$True,Position=0)]
 [string]$MasterServer,

 [Parameter(Mandatory=$True,Position=1)]
 [string]$SlaveServer
 )

#Get Master Roles
Connect-VIServer $MasterServer -Credential $creds

$mstr_roles = Get-VIrole

foreach ($role in $mstr_roles) {
    $hsh_mstr_roles.add($role, $null)
}

Disconnect-VIServer $MasterServer -Confirm:$false

#Get Slave Roles
Connect-VIServer $SlaveServer -Credential $creds

$slave_roles = Get-VIrole

foreach ($role in $slave_roles) {
    $hsh_slave_roles.add($role, $null)
}

Disconnect-VIServer $SlaveServer -Confirm:$false

Write-Host "`n + Retrieved Roles Successfully"
}    

.

function Process-Roles () { 
#Get Roles on Master NOT ON SLAVE

Write-Host "`n"

foreach ($role in $mstr_roles){
    if(-not $hsh_slave_roles.containsKey($role)){
    Write-Host $role "doesn't exist on slave"
    }
}

#Get Roles on Slave NOT ON MASTER
foreach ($role in $slave_roles){
    if(-not $hsh_mstr_roles.containsKey($role)){
    Write-Host $role "doesn't exist on master"
    }
}

Write-Host "`n + Processed Roles Successfully"
}
kxf951
  • 155
  • 1
  • 2
  • 11

2 Answers2

3

The easiest way to do this is by finding the complement to one of the two sets of Keys that each hashtable has, using -notcontains:

function Process-Roles {
    param(
        [hashtable]$MasterRoles,
        [hashtable]$SlaveRoles
    )

    # Complement to slave roles (those ONLY in $MasterRoles)
    $MasterRoles.Keys |Where-Object { $SlaveRoles -notcontains $_ }|ForEach-Object {
        Write-Host "$_ not in Slave Roles"
    } 

    # and the other way around (those ONLY in $SlaveRoles)
    $SlaveRoles.Keys |Where-Object { $MasterRoles -notcontains $_ }|ForEach-Object {
        Write-Host "$_ not in Master Roles"
    } 

}

I'll have to add that your way of working with variables in different scopes is sub-optimal.

  1. Define the parameters that the function needs in order to "do its job"
  2. Return output from your functions where it make sense (any Get-* function should at least)
  3. Depend on the Global and Script scopes as little as possible, preferably not at all

I would go with something like this instead:

Add a Credential parameter to the Get-Roles function and return the results rather than modifying a variable in a parent scope (here, using a Hashtable of role categories):

function Get-Roles {
    Param(
        [Parameter(Mandatory=$True,Position=0)]
        [string]$MasterServer,

        [Parameter(Mandatory=$True,Position=1)]
        [string]$SlaveServer,

        [Parameter(Mandatory=$True,Position=2)]
        [pscredential]$Credential
    )

    $DiscoveredRoles = @{}

    # Get Master Roles
    Connect-VIServer $MasterServer -Credential $Credential
    $DiscoveredRoles["MasterRoles"] = Get-VIRole
    Disconnect-VIServer $MasterServer -Confirm:$false

    #Get Slave Roles
    Connect-VIServer $SlaveServer -Credential $Credential
    $DiscoveredRoles["SlaveRoles"] = Get-VIrole
    Disconnect-VIServer $SlaveServer -Confirm:$false

    Write-Verbose "`n + Retrieved Roles Successfully"

    return $DiscoveredRoles

}    

Define parameters for the Process-Roles function, that match the hashtable you expect to generate from Get-Roles and do the same comparison of the role names as above, only this time we grab them directly from the Role objects:

function Process-Roles { 
    param(
        [Parameter(Mandatory=$true)]
        [ValidateScript({ $_.ContainsKey("MasterRoles") -and $_.ContainsKey("SlaveRoles") })]
        [hashtable]$RoleTable
    )

    $MasterRoleNames = $RoleTable["MasterRoles"] |Select-Object -ExpandProperty Name

    $SlaveRoleNames = $RoleTable["SlaveRoles"] |Select-Object -ExpandProperty Name

    $MasterRoleNames |Where-Object { $SlaveRoleNames -notcontains $_ } |ForEach-Object {
        Write-Host "$_ doesn't exist on slave"    
    }

    $SlaveRoleNames |Where-Object { $MasterRoleNames -notcontains $_ } |ForEach-Object {
        Write-Host "$_ doesn't exist on Master"    
    }

    Write-Host "`n + Processed Roles Successfully"
}

Update your executing script with the new parameters:

$creds = Get-Credential
$MasterServer = Read-Host "`n Master Server: "
$SlaveServer  = Read-Host "`n Slave Server: "

$RoleTable = Get-Roles -MasterServer $MasterServer -SlaveServer $SlaveServer -Credential $creds

Process-Roles -RoleTable $RoleTable

Next step would be to add pipeline support to the Process-Roles function, converting Write-Host statements to Write-Verbose and adding error handling, but I'll leave that as an exercise to OP :-)

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thank you very much, I'll get back to you soon if I have errors, but you're amazing. I hope I can get to this level of understanding soon. – kxf951 Aug 04 '15 at 14:22
  • @kxf951 It'll take some time, trust me ;) just keep trying things and keep asking questions, it definitely helps :) – Mathias R. Jessen Aug 04 '15 at 14:24
1

try:

if(!$hsh_slave_roles.containsKey($role))
Ryan
  • 219
  • 3
  • 12