I am using the below code to try and pull out a list of last logon times for a group of users contained in a CSV file. I have a test domain at home and the script below works fine. However, when I try it on my work prod AD I get the following error.
Get-ADUser: Variable: 'dname' found in expression: $dname is not defined.
This is the script I have put together.
$list = Import-Csv -Path "C:\data\PowerShell\UserInfo.csv"
ForEach ($user in $list) {
$dname = $user.Displayname
Get-ADUser -Filter {displayName -eq $dname} -Properties lastLogon | Select-Object Name, SamAccountName, @{Name="LastLogon"; Expression={[DateTime]::FromFileTime($_.lastLogon)}} | Export-CSV -Path "C:\data\PowerShell\user_last_login.csv" -append -NoTypeInformation
}
tl;dr
For the reasons explained in the next section, your
Get-ADUserproxy command[1] doesn't see your local variables inside a-Filterargument. Therefore, use string interpolation instead, so as to directly incorporate the variable value into the-Filterargument (note the need to use embedded double-quoting (`")around the expanded value):As an aside:
-Filter {displayName -eq $dname}does work ifGet-ADUseris the regular (non-proxy) cmdlet from theActiveDirectorymodule that directly talks to an AD server, the script-block syntax ({ ... }) - while convenient - is conceptually problematic and can lead to misconceptions, so-Filter 'displayName -eq $name'may be preferable - see this answer for background information.However, in the case at hand (implicit remoting, see below) using string interpolation is the only solution.
Background information: The limitations of implicit remoting:
Your symptom implies that your Active Directory commands are provided via implicit remoting, where a local proxy module is used to relay calls to a remote machine behind the scenes using PowerShell's remoting feature (the proxy modules are typically created with the
Export-PSSessioncmdlet; this article provides an introduction to implicit remoting).In a nutshell, the proxy module contains proxy commands of the same name as their remote counterparts, implemented as PowerShell functions that relay the calls to their remote counterparts.[1]
While this proxying mostly works as intended, it has side effects:
References to local variables, such as in your
-Filterargument cannot work, because the remote command that ultimately executes the operation knows nothing about the local caller's variables.Note that AD module's
-Filterparameter is unusual in that it expects a string, inside of which (stand-alone only) references to PowerShell variables are recognized; while it is common to see script blocks ({ ... }) as-Filterarguments, they are actually converted to strings during parameter binding; while using script blocks is syntactically convenient, it obscures the real behavior (it can falsely suggest that the argument is a piece of PowerShell code, which it isn't) and can lead to conceptual confusion - see this answer for background information.In cases where actual script blocks are passed to remoting commands, notably in the context of
Invoke-Command, the values of local variables can be embedded in them via the$using:scope.The following side effect applies to script modules in general (script modules are those whose exported commands are implemented as PowerShell functions rather than as binary cmdlets, as is the case in implicit remoting):
$ErrorActionPreference = 'Stop'.The following side effect applies not only to PowerShell's remoting in general, but also to background jobs and "mini shells" (in-session PowerShell CLI calls with script blocks):
Redirecting, capturing, or suppressing output streams other than the success (
1) and error output stream (2) is inconsistently supported with partial inability to redirect and/or capture streams, and partial inability to effectively suppress output streams.For details, see GitHub issue #9585.
[1] Given that
Get-ADUseris normally a binary cmdlet, an easy way to test if a givenGet-ADUsercommand is a proxy command is to use(Get-Command Get-ADUser).CommandType -eq 'Function'.