PowerShell: Cannot find an overload for IndexOf and the argument count: 2 - powershell-remoting

I am attempting to use a portion of a script I wrote to return a list of Local Groups a specified user may be part of on a remote server so they can be quickly removed from said Local Group. Everything seems to work fine until the groups a person may be part of goes below two. When there is only one group I get the following error:
Cannot find an overload for "IndexOf" and the argument count: "2".
At line:177 char:30
+ [string]([array]::IndexOf <<<< ($localGroups, $_)+1) + ". " + $_
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Here are the Functions I wrote for this particular script...
This function will return a list of groups the given user is part of:
function Get-LocalGroupAccess
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$fqdn,
[Parameter(Mandatory=$true)]
[string]$userName
)
Process
{
$serverPath = [ADSI]"WinNT://$fqdn,computer"
$serverPath.PSBase.Children | where {$_.PSBase.SchemaClassName -eq 'group'} | foreach {
$lGroup = [ADSI]$_.PSBase.Path
$lGroup.PSBase.Invoke("Members") | foreach {
$lMember = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null).Replace("WinNT://","")
if ($lMember -like "$userName")
{
$localAccess += #($lGroup.Name)
}
}
}
return($localAccess)
}
}
This function sets the User Object (I am not sure this is the technical term):
function Set-UserObj($userDomain, $userName)
{
$userObj = [ADSI]"WinNT://$userDomain/$userName"
return ($userObj)
}
This function set the FQDN (checks to see if it is pingable):
function Set-FQDN($fqdn)
{
do{
$fqdn = Read-Host "Enter the FQDN"
} while (-not(Test-Connection $fqdn -quiet))
return($fqdn)
}
This function will take the selection for the group you want to remove the given user from, change it to the proper place in the array, and return the group:
function Set-LocalGroup($localGroups, $selectGroup)
{
$selectGroup = Read-Host "What group would you like to add $userDomain/$userName to?"
$selectGroup = [int]$selectGroup -= 1
$setGroup = $localGroups[$selectGroup]
return($setGroup)
}
This function sets the Group object (not sure if this is the technical term):
function Set-GroupObj($fqdn, $group)
{
$groupObj = [ADSI]"WinNT://$fqdn/$group"
return($groupObj)
}
This function removes the given user from the group selected:
function Remove-UserAccess ($gObj, $uObj)
{
$gObj.PSBase.Invoke("Remove",$uObj.PSBase.Path)
}
In the script the user name, domain and FQDN are requested. After these are provided the script will return a list of groups the given user is part of. Everything works fine until the user is part of one group. Once that takes place, it throws the error I pasted above.
Please note, this is my first time posting and I am not sure what information is needed here. I hope I provided the proper and correct information. if not, please let me know if there is something else that you require.
Thanks!

I went back and was looking at the difference, if there were any, in the variable $localGroups that I was creating (I used Get-Member -InputObject $localGroups). I noticed that when $localGroups had only one item it was a System.String type but when it had more than one item in it, it was a System.Object[] type. I decided to do the following and it addressed the issue I was having:
$localGroups = #(Get-LocalGroupAccess $fqdn $userName)
previous code:
$localGroups = Get-LocalGroupAccess $fqdn $userName
Everything is working as it should because I forced the variable to a static type instead of allowing it to create whatever type it wanted.

Related

Retrieving A Function From A WebhookScript Global Variable

In WebhookScript, I can store a function in a variable with:
sub = function(a, b) {
return a - b
}
I'd like to store a function in a Global Variable so that I can use it in multiple Custom Actions. But if I've saved the above function as $sub$ then
sub2 = var('$sub$')
subX = sub(1,2)
causes an error:
Trying to invoke a non-function 'string' # line...
And
function subX(a,b){
var('$sub$')
}
when sub only contains return a - b, doesn't work either.
Obviously I need to convert the string to a function but I'm not sure whether that's possible.
I know this is a bit of an obscure language but if anyone knows how this can be done in similar languages like JavaScript and PHP, I'm happy to test out any guesses...
The solution here is to remove the function section and just enter the script, which inherits the execution scope so if my global variable $script$ is:
return 'hello ' + a
Then I can execute the function with:
a = 'world'
value = exec(var('$script$'))
echo(value)
(credit to Webhook.Site's support team for explaining this)

Powershell hashtable in function not accessible

I created a function to import HR records so they're easily accessible by indexing the employeeID.
function hrDataImport {
$HRFile=Import-Csv $rawHRFile #$rawHRFile path previously defined
Write-Host "Creating HR hash table..."
$hrData = #{}
$HRFile | ForEach-Object { $hrData[$_.employeeID] = $_ }
Write-Host "Done. " $hrData.Count
}
I'd like to use that $hrData variable outside the function. I can't reference $hrData['employeeID'] as I've read that variables in functions don't exist outside the function. One thing I tried is creating another function to pass an employeeID into the $hrData hashtable:
function showHRData {
param ([string]$hrUser)
$hrData[$hrUser]
}
I put the functions in a module and imported successfully. I'm able to execute importHRData fine but when I try showHRData -hrUser $employeeID I get "Cannot index into a null array". Seems like the function does not see the hashtable variable from the previous function. What am I doing wrong or what would you suggest so I can access that $hrData hashtable across various scripts?
You might create a Hashtable within your function using a $global (or $script) scope like this:
function hrDataImport {
$HRFile = Import-Csv $rawHRFile #$rawHRFile path previously defined
Write-Host "Creating HR hash table..."
if (!$hrData) { $Global:hrData = #{} }
$HRFile | ForEach-Object { $Global:hrData[$_.employeeID] = $_ }
Write-Host "Done. " $hrData.Count
}
But in general it is advised to avoid the global (or script) scope mainly because it might conflict with variables in the current scope.
To minimize this possibility, I would consider to change the responsibility of your function to not only loading the information but also to receiving the concerned employeeID, e.g.:
function Get-EmployeeData ($EmployeeID) {
if (!$StaticEmployeeData) {
Write-Host "Creating HR hash table..."
$HRFile = Import-Csv $rawHRFile #$rawHRFile path previously defined
$Global:StaticEmployeeData = #{}
$HRFile | ForEach-Object { $Global:StaticEmployeeData[$_.employeeID] = $_ }
Write-Host "Done. " $StaticEmployeeData.Count
}
$Global:StaticEmployeeData[$employeeID]
}
function showHRData {
param ([string]$hrUser)
Get-EmployeeData $hrUser
}
In that case you might choose for a more specific global variable name (e.g. $StaticEmployeeData, knowing that PowerShell doesn't support something like a static variable) and as an extra bonus the data is only loaded the first time you really need it (lazy evaluation).

Return variable from one function to another in powershell

I am inside function #1 and triggering function #2. Function #2 has a variable that I want to get back into function #1 and use it.
My output ends up being:
hey there
var is:
What I want is the output to be:
var is: hey there
Why is it that I can feed a function a variable, and it uses it, but when I change that variable in the #2 function, it does not change the variable after it returns?
$var = $null
function one() {
two($var)
write-host "var is:" $var
}
function two($var){
$var = "hey there"
return($var)
}
clear
one
First, change your two function to actually return a value:
function two {
$var = "hey there"
return $var
}
and then update the one function to actually "capture" the output value by assigning it to a variable:
function one {
# PowerShell doesn't use parentheses when calling functions
$var = two
Write-Host "var is:" $var
}
If you really do want to reach up and change a variable in the scope of your caller, you can...
Set-Variable -Name $Name -Value $Value -Scope 1
It's a little meta/quirky but I've run into cases where it seemed proper. For example, I once wrote a function called RestoreState that I called at the beginning of several functions exported from a module to handle some messy reentrant cases; one of the things it did is reach up and set or clear $Verbose in the calling function's scope.

Function calling itself to enumerate nested group memberships

So I've been working on a new logon script for our users. It's bascially finished and it works just fine. However, I've coded a function to enumerate nested group memberships and I don't see how it can work the way I wrote it (although it does!).
Code:
$Groups = New-Object System.Collections.ArrayList #Array for all groups incl. nested
$MaxIndex = 0
#---Adds every group to the array
Function AddGroup($P1)
{
do
{
#---Param
[String]$ADObject = $P1
#---Gets all "main" groups of the current user
$AllGroups = ([ADSISEARCHER]"samaccountname=$ADObject").Findone().Properties.memberof
#---Durchlauf für jede Gruppe
ForEach($Group in $AllGroups)
{
#---Convert
[String]$GroupSTR = $Group
if($GroupSTR -ne "")
{
#---Get actual group name
$GroupSTR = $GroupSTR.SubString(3)
$IndexOfChar = $GroupSTR.IndexOf(",")
$GroupSTR = $GroupSTR.SubString(0,$IndexOfChar)
$Groups.Add($GroupSTR) | Out-Null
if($MaxIndex -le $AllGroups.Count) { AddGroup $GroupSTR }
$MaxIndex = $MaxIndex + 1
}
}
$GroupSTR = ""
}
while($GroupSTR -ne "")
}
AddGroup $LoggedOnUser
So what I do is I call the function AddGroup with the username of the current user. The script then gets all the main groups in which the user is currently in and goes into a foreach loop for every group found.
It removes the unwanted gibberish of the ouput of the ADSISEARCHER and then adds the clean group name to the array.
Now to also get the nested group memberships, I get the count of all groups of the current AD object. If my index is below this count, it means the current object is also part of other groups and then calls the function again to get those as well.
Now what I don't understand is, how can my function ever count up my index? The index will always be lower or equal the total amount of found groups. Even if it finds zero groups. Therefore it should call the function infinite times but it doesn't.
What am I missing here?
If you want to find how your code is working, you can use ISE or PowerGUI in debug mode.
In order to solve your problem why don't you use LDAP_MATCHING_RULE_IN_CHAIN have a look to Search Filter Syntax
To find all the groups that "user1" is a member of :
Set the base to the groups container DN; for example root DN (dc=dom,dc=fr)
Set the scope to subtree
Use the following filter : (member:1.2.840.113556.1.4.1941:=cn=user1,cn=users,DC=x)
Here is an sample in PowerShell :
$ADObjectDN = ([ADSISEARCHER]"samaccountname=$ADObject").Findone().Properties.distinguishedname
$AllGroups =([ADSISEARCHER]"member:1.2.840.113556.1.4.1941:=$ADObjectDN").FindAll()

Function containing a process is returning a garbled value

Spent my whole morning trying to find where my return value was getting garbled. Now that I've finally found where, I still have no idea why. Function looks like this:
function Run-RemoteCommand {
param(...) # params are $Remote (host) $Command $Credentials $Quiet (optional switch)
if($Quiet) {
$Process = New-Object System.Diagnostics.Process
$Process.StartInfo.UseShellExecute=$false
$Process.StartInfo.Domain=$Credentials.GetNetworkCredential().Domain
$Process.StartInfo.UserName=$Credentials.GetNetworkCredential().UserName
$Process.StartInfo.Password=$Credentials.Password
$Process.StartInfo.WindowStyle="Hidden"
$Process.StartInfo.FileName=$PSExec
$Process.StartInfo.Arguments=#("/acceptEULA",$Remote,"-s",$Command)
$Process.Start()
$Process.WaitForExit()
$result = $Process.ExitCode
return $result
} else {
...
}
}
What's odd is that I can step through this in a debugger and watch everything work fine. The command runs, $result is filled with the return code, but the calling function receives True appended to the return code (eg True0 on success). I even tried overriding the return value and just saying
return "false"
The calling function receives "Truefalse." All I can tell is that it's tied to $Process running. If I comment out $Process.Start(), the return code functions normally. Someone please save my sanity.
$Process.Start() returns a boolean value which is True if it succeeds. Remember that functions in PowerShell behave differently than standard programming languages. PowerShell functions "return" or more technically correct "output" any command output that isn't captured by a variable or redirected to a file or Out-Null. In this case change the Start line to:
[void]$Process.Start()
or
$Process.Start() | Out-Null
Check out this blog post for a deeper explanation.