snowcoder
snowcoder

Reputation: 491

Handling Array in PowerShell Script

I wrote PowerShell script to automate some of the AD commands. Here is the script.

 $usergroup="TACACS Admins"
 $computerrole="1626-APPCONF"
 $zone="AWS"
 $username="<>"
 $password="<>";
 [String[]] $HostServers ='smp001-01','sl1ps01-13-9';

 $ValidatedHostServers = @()
 $NotValidatedHostServers = @()

 Import-Module ActiveDirectory
 Import-Module Centrify.DirectControl.PowerShell

 $Password = ConvertTo-SecureString $password -AsPlainText -Force
 $Cred = New-Object     System.Management.Automation.PSCredential($username,$Password)
Set-CdmCredential -Domain <> -Credential $Cred



function Add-HostComputer{
Param($cred,$zone,$computerrole,$ValidatedHostServers)

# Get Computer Role
$GroupName = Get-ADGroup -Identity $computerrole 
Write-Output $GroupName

foreach($Host in $ValidatedHostServers)
{
  #Get -Member 
  $DistinguishedName = Get-ADComputer -Identity $Host
  Write-Output $ValidatedHostServers

  #Add $Hostserver to $comprole computer Role
  $Output = Add-ADGroupMember -Identity $GroupName -Members $DistinguishedName -Credential $Cred
}
}



function ValidateHost{
  Param($HostServers)
  foreach($HostName in $HostServers)
 {
 try{
      $HostOutput = Get-ADComputer -Identity $HostName -ErrorAction SilentlyContinue
     $ValidatedHostServers += $HostName
 }
catch
{
    $NotValidatedHostServers += $HostName
}
}

 Write-Host $ValidatedHostServers
 $Result = @{"Valid_hosts"=$ValidatedHostServers;"Invalid_hosts"=$NotValidatedHostServers} | ConvertTo-Json -Compress
 Write-Host $Result
 Write-Host $ValidatedHostServers.Count
 If($ValidatedHostServers.Count -ne 0) 
  { 
  Write-Host $ValidatedHostServers
  Add-HostComputer -Cred $Cred -zone $zone -computerrole $computerrole - 
  ValidatedHostServers $ValidatedHostServers

  }

}
 $Final = ValidateHost -HostServers $HostServers
 Write-Host $Final

I have tried to write-host the inputs and found $ValidatedHostServers array does not coming as array. May I be I am missing something in syntax.

There are two values in array but

       Write-Host $ValidatedHostServers.Count display as 1.

Thanks in advance for helping here.

This is Error displayed in powerShell

  smp001-01sl1ps01-13-9
  {"Invalid_hosts":[],"Valid_hosts":"smp001-01sl1ps01-13-9"}
  1
  smp001-01sl1ps01-13-9
   Cannot overwrite variable Host because it is read-only or constant.
   At line:27 char:9 + foreach($Host in $ValidatedHostServers) + CategoryInfo : WriteError: (Host:String) [],      SessionStateUnauthorizedAccessException + FullyQualifiedErrorId : VariableNotWritable

 CN=1626-APPCONF,OU=Role Groups- 
 Computer,OU=Centrify,OU=Operations,DC=qateradatacloud,DC=com

Upvotes: 1

Views: 397

Answers (2)

mclayton
mclayton

Reputation: 10170

If you reduce your code to a minimum script that reproduces the behaviour you'll get something like this:

$ValidatedHostServers = @( "xxx" );

function ValidateHost
{
    write-host "inside before = '$($ValidatedHostServers | ConvertTo-Json)'";
    $ValidatedHostServers += "smp001-01";
    $ValidatedHostServers += "sl1ps01-13-9";
    write-host "inside after = '$($ValidatedHostServers | ConvertTo-Json)'";
    write-host ($ValidatedHostServers | ConvertTo-Json)
}

write-host "value before = '$($ValidatedHostServers | ConvertTo-Json)'";
ValidateHost;
write-host "value after = '$($ValidatedHostServers | ConvertTo-Json)'";

which outputs the following:

value before = '"xxx"'
inside before = '"xxx"'
inside after = '"smp001-01sl1ps01-13-9"'
value after = '"xxx"'

I think there's two things happening here:

  • Your function is executing in a child scope so the changes made inside your function to variables defined in the parent scope (i.e. $ValidatedHostServer) don't survive when the function exits - see quote from child scope link:

items that you create and change in the child scope do not affect the parent scope, unless you explicitly specify the scope when you create the items.

  • There's possibly a bug in PowerShell in the assignment by addition operator (i.e. '+=') when it first assigns a value to a parent variable in a child scope - it's basically treating the $ValidatedHostServers as if it were undefined and converting it into a string, which then causes the second += to do string concatenation instead of array appending.

You can work around both of these issues like this:

#$ValidatedHostServers = @( "xxx" ); <- replace this with the next line

Set-Variable -Name "ValidatedHostServers" -Option AllScope -Value @( "xxx" );

The "AllScope" option allows the child scope (i.e. the function) to modify variables in the parent scope so your changes will persist after the function exists. It also side-steps the potential bug in the "assignment by addition operator".

Set-Variable -Name "ValidatedHostServers" -Option AllScope -Value @( "xxx" );

function ValidateHost
{
    write-host "inside before = '$($ValidatedHostServers | ConvertTo-Json)'";
    $ValidatedHostServers += "smp001-01";
    $ValidatedHostServers += "sl1ps01-13-9";
    write-host "inside after = '$($ValidatedHostServers | ConvertTo-Json)'";
    write-host ($ValidatedHostServers | ConvertTo-Json)
}

write-host "value before = '$($ValidatedHostServers | ConvertTo-Json)'";
ValidateHost;
write-host "value after = '$($ValidatedHostServers | ConvertTo-Json)'";

and then we get this output

value before = '"xxx"'
inside before = '"xxx"'
inside after = '[
    "xxx",
    "smp001-01",
    "sl1ps01-13-9"
]'
[
    "xxx",
    "smp001-01",
    "sl1ps01-13-9"
]
value after = '[
    "xxx",
    "smp001-01",
    "sl1ps01-13-9"
]'

UPDATE

Related issues in PowerShell github repo:

Upvotes: 2

Scepticalist
Scepticalist

Reputation: 3923

Try it this way, get the list then iterate over it:

function ValidateHost{
    Param($HostServers)
#
    $GroupName = Get-ADGroup -Identity $computerrole
#
# Get server validation list
    $CheckedServers = $HostServers | ForEach-Object {
        Try {
            $HostOutput = Get-ADComputer -Identity $_ -ErrorAction Stop
            $Validated = $true
        }
        Catch {
            $Validated = $false
        }
        [pscustomobject]@{HostName = $_;Validated = $Validated}
    }

    # Add-HostComputer
# Iterate over list of validated servers
    $CheckedServers | Where-Object { $_.Validated -eq $true } | ForEach-Object { 
        Try {
            $DistinguishedName = Get-ADComputer -Identity $_.HostName -ErrorAction Stop
            $Output = Add-ADGroupMember -Identity $GroupName -Members $DistinguishedName -Credential $Cred -ErrorAction Stop
        }
        Catch {
            $Output = $_
        }
        [pscustomobject]@{HostName = $_.HostName;DN = $DistinguishedName;Result = $output}
    }
}

Upvotes: 0

Related Questions