powershell, Variable: '_' found in expression: $_ - powershell

I am fairly new to powershell and I am currently employing it to work around a few administration tasks for the Helpdesk.
I have a problem with trying to move an AD object (forgive me if the following terminology is used incorrectly) based on the property of on object from an imported CSV.
The CSV is:
UserPrincipalname,UserToAccess,DaysToLive
joe#company.com,dave#company.com,90
and so on...
I then pass the array through a ForEach loop to move the AD account:
foreach ($line in $import) {Get-ADUser -filter {userPrincipalName -eq $_.UserToAccess} -SearchBase "DistinguishedName of OU" | Move-ADObject -TargetPath 'DistinguishedName of OU'}
Subsequently I am getting the following error:
Get-ADUser : Variable: '' found in expression: $.UserToAccess is not
defined. At D:\jason\EnableArchiveAccess.ps1:17 char:29
+ foreach ($line in $import) {Get-ADUser -filter {userPrincipalName -eq $.UserToA ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADUser], ArgumentException
+ FullyQualifiedErrorId : Variable: '' found in expression: $_.UserToAccess is not defined.,Microsoft.ActiveDirec
tory.Management.Commands.GetADUser
I have been able to use the above logic to unhide users from the GAL and I have checked the array and the properties are there as noteproperties.
I assume it's because I am using not AD variables in the command but any help would be much appreciated but if I find the answer sooner I will post back.

Just looking at that, I think you need to change
$_.UserToAccess
to
$line.UserToAccess

The other alternative would be:
$import | foreach{
Get-ADUser -filter {userPrincipalName -eq $_.UserToAccess} `
-SearchBase "DistinguishedName of OU" `
| Move-ADObject -TargetPath 'DistinguishedName of OU'}

This is one of the common mixups in PowerShell. There are in fact two foreach "keywords". One is alias of the Foreach-Object cmdlet and is used as such:
$Items = 1,2,3
$Items | foreach { $_ }
The $_ in the example means the current object. That is 1 on first pass, 2 on second pass and 3 on the third.
The second foreach is keyword and is used as such
$Items = 1,2,3
foreach ($item in $items) {
$item
}
In this example the $item represents the current object.
So in your example you have to use $list instead of $_.

Related

Change Active Directory titles for all csv users

I would like to change 150 employees their job title.
I have a csvfile called Titletest.csv with columns UserPrincipalName [the user.name under it] and Title [job title under it]
The PowerShell script:
Import-Module ActiveDirectory
$users = Import-Csv -Path c:\scripts\Titlestest.csv | Foreach-Object {
$user = $_.user
$title = $_.title
#Selects the specified user and sets Job Title
Get-ADUser -Filter {(UserPrincipalName -eq $user)} | Set-ADUser -Title $title
}
I get errors saying:
Get-ADUser : Variable: 'user' found in expression: $user is not defined.
At line:14 char:1
+ Get-ADUser -Filter {(UserPrincipalName -eq $user)} | Set-ADUser -Titl ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.GetADUser
Can someone please advise?
Thank you.
The reason for your error is because $user has no assignment. You are attempting to assign $user the value of a property that does not exist. The header user apparently does not exist in your CSV file. See below for how to convert a csv into PowerShell objects and access their properties.
# Sample CSV TitleTest.csv
UserPrincipalName,Title
covid19#domain.com,Usurper
jsmith#domain.com,CEO
bossman#domain.com,CFO
Import-Csv -Path c:\scripts\TitleTest.csv | Foreach-Object {
$user = $_.UserPrincipalName
$title = $_.Title
Get-ADUser -Filter 'UserPrincipalName -eq $user' | Set-ADUser -Title $title
}
Explanation:
When using Import-Csv on a proper CSV file, the first row of delimited data will be converted to the properties of all input objects. All succeeding rows will be converted to individual objects with the header properties and output as a collection (array) of those objects. If the -Header parameter is used, then values passed into the parameter will become the properties of the objects. It is important to have the same number of delimited items on each row to ensure proper mapping.
Once you are dealing with objects, you can access their property values using the member access operator .. The syntax is object.property. So since you have headers UserPrincipalName and Title, you will need to use $_.UserPrincipalName and $_.Title to access the associated values.
$_ is the current pipeline object within your Foreach-Object {} script block.
Note that you don't technically need to define $user and $title here. You can just access the properties directly from the current object:
Import-Csv -Path c:\scripts\TitleTest.csv | Foreach-Object {
Get-ADUser -Filter "UserPrincipalName -eq '$($_.UserPrincipalName)'" |
Set-ADUser -Title $_.Title
}

Is there a cleaner way than using foreach for a single object in the pipeline?

My script is working, but I feel like I'm doing something wrong when I am using foreach-object for a single item in the pipeline. Here's the code:
$computers = ('Computer1','Computer2')
ForEach($computer in $computers){
Get-ADComputer $computer -Properties description | ForEach{$_.description -split ','} | Select -first 1 |
Get-ADUser | ForEach{$computer + ": " + $_.name}
}
The script is retrieving computers in AD and looks in the description for the owner. I only need the first part of the description so I split where the comma is.
My question: Is it correct to use ForEach like this:
ForEach{$_.description -split ','}
When it seems like something like
$_.description -split ','
Should be enough since it's only one item in the pipeline anyway. (But this obviously gives the "Expressions are only allowed as the first element of a pipeline"
Sorry is this is messy, I hope someone understands what I'm getting at.
EDIT:
After reading answers this is my final code:
$computers = ('Computer1','Computer2')
ForEach($computer in $computers){
Get-ADComputer $computer -Properties description | ForEach{$s = $_; ($s.description -split ',')[0]} | Get-ADUser | ForEach{$computer + ": " + $_.name}
}
You are correct that you don't need to code it in the way you have but there are two points worth making:
Readability could be hindered by making this more terse
Sometimes you cannot guarantee only one object will be returned
You could change the first bit to something like this
((Get-ADComputer $computer -Properties description).description -split ',')[0] | Get-ADUser | ...
That should do the same thing if only one computer object gets returned. Likely that will be the case here but not always with this type of logic. I think it reads easier the way you have it.
I might do it like this
$computers = ('Computer1','Computer2')
ForEach($computer in $computers){
Get-ADComputer $computer -Properties description |
ForEach{($_.description -split ',')[0]} |
Get-ADUser | ForEach{$computer + ": " + $_.name}
}
Here I removed the select by getting the needed element while in the foreach loop. I also changed the indentation to show the relationship of the long pipe statement. I originally made an erroneous comment as you indentation made me see Get-ADUser | ForEach{$computer + ": " + $_.name} as a unique statement.

Filtering AD property from get-ADUser

so i am writing a script,
in our company we store users home-directory on network drives,
and when they leave we rename the directory by adding .left to the folder name,
example: "name.left"
and we actually used to do so by finding the user in AD
copying the content of the home directory property and renaming it.
so i got this so far :
$name = Read-Host 'User Name: '
$path = Get-ADUser $name -Properties homedirectory
Rename-Item $path {$name+".left"}
problem is when i get the home-directorys path in get-aduser it gives it with the standart get-aduser output, it just adds it to the output so i tried using filter :
$path = Get-ADUser $name -Properties homedirectory -Filter homedirectory
and it gave me an error, but not for the filter, now it doesn't recognize the user name i gave it.
now, I'm sure that there is a way to filter the string in the property.
and i get a feeling that the third line i wrote might be wrong as-well,
but that's my python brain trying to work with powershell :) so if enyone could help me with that one, i would really appreciate it,
and if anyone can point me to good powershell guides that would be really nice.
EDIT:
so i fixed it to look like that :
$name = Read-Host 'User Name: '
$date = Read-Host 'Date Please: '
do { $path = Get-ADUser $name -Properties homedirectory | select -Expand HomeDirectory $newname = {$name + "LFT" + $date}
Rename-Item $path $newname
Write-Host $name + 'changed' }
while ($name -ne 'exit')
and i get an error that the new name is a script and not a string so it cant run, you know a way to fix it?
Rename-Item : Cannot evaluate parameter 'NewName' because its argument is specified as a script block and there is no
input. A script block cannot be evaluated without input.
At line:6 char:19
+ Rename-Item $path $newname
+ ~~~~~~~~
+ CategoryInfo : MetadataError: (:) [Rename-Item], ParameterBindingException
+ FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.RenameItemCommand
EDIT2
a fellow employee helped me out and this is the result:
it does exactly what i needed :)
$name = Read-Host 'User Name '
$homefolder = (Get-ADUser $name -Properties HomeDirectory).homedirectory
$date = Get-Date -UFormat "%d.%m.%Y"
ren $homefolder -newname ($homefolder + "_lft_" + $date)
With select-object you can filter out unwanted properties. It will still return an object tho. It has a handy argument named expand that will return single properties value:
$path = Get-ADUser $name -Properties homedirectory | select -Expand HomeDirectory

How to query ManagedBy property of ADComputer in a Foreach loop?

I'm trying to generate a list of computers owned by a particular PDL and I'm encountering some syntax issues:
$group = Get-ADGroupMember -Identity "pdl" | Select-Object -ExpandProperty DistinguishedName
Foreach($item in $group) { Get-ADComputer -Filter "ManagedBy -eq "$item"" -Property managedby | Select Name }
The second part is based on another code snippet that I found elsewhere (I think on StackOverflow as well) which worked just fine:
Get-ADComputer -Filter "ManagedBy -eq 'CN=user#company.com,OU=US,OU=Users,OU=Accounts,DC=americas,DC=company,DC=com'" -Property ManagedBy
But the difference is I could use '' in this one, but adding in $item prevents me from using that.
The syntax error I get back with the first snippet:
Get-ADComputer : A positional parameter cannot be found that accepts argument 'CN=user#company.com,OU=US,OU=Users,OU=Accounts,DC=americas,DC=company,DC=com'.
At D:\Documents\Scripts\uatgroup.ps1:2 char:31
+ Foreach($item in $UATgroup) { Get-ADComputer -Filter "ManagedBy -eq "$item"" -Pr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADComputer], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.ActiveDirectory.Management.Commands.GetADComputer
Anyone know a way to fix the syntax here? Or an alternate method of running this?
This:
"ManagedBy -eq "$item""
Is parsed as three separate strings. Only the first one (ManagedBy -eq) will be bound to the -Filter parameter, the rest will be treated as separate tokens, causing PowerShell to complain that you can't just leave the string CN=... there in the middle of everything.
You can either use single-quotes inside the double-quoted string, to avoid terminating the string early:
Get-ADComputer -Filter "ManagedBy -eq '$item'"
Escape the inline double-quotes with a backtick ( ` ):
Get-ADComputer -Filter "ManagedBy -eq `"$item`""
Or escape them by doubling them:
Get-ADComputer -Filter "ManagedBy -eq ""$item"""

Get-ADUser -Filter fails when comparing to an object's property

I have a powershell question that has to do with importing a csv file, then going through a foreach through the csv file.
I have something like the following:
$infile = "c:\temp\infile.csv"
$outfile = "c:\temp\outfile.csv"
$csv = Import-csv -path $infile
foreach ($line in $csv)
{
$data = Get-ADUser -Filter {EmailAddress -eq $line.email} -Property Mail | Select-Object -ExpandProperty SamAccountName
}
When I do something like that, I get the following error:
Get-ADUser : Property: 'email' not found in object of type: 'System.Management.Automation.PSCustomObject'.
At C:\Temp\program1.ps1:11 char:24
+ $ad_data = Get-ADUser <<<< -Filter {EmailAddress -eq $line.email} -Property Mail | Select-Object -ExpandProperty SamAccountName
+ CategoryInfo : InvalidArgument: (:) [Get-ADUser], ArgumentException
+ FullyQualifiedErrorId : Property: 'email' not found in object of type: 'System.Management.Automation.PSCustomObject'.,Microsoft.ActiveDirectory.Management.Commands.GetADUser
But if I do something like:
$var = $line.email
$data = Get-ADUser -Filter {EmailAddress -eq $var} -Property Mail | Select-Object -ExpandProperty SamAccountName
Why does the second method works but the first method throws an error?
Thanks,
Alright, I had a discussion with some of my fellow PowerShell MVPs, and the answer as to why is really quite interesting.
For the quick answer, this is how you do get the AD User while preserving the structure of your code:
Get-ADUser -Filter "Emailaddress -eq '$($line.email)'"
You can quickly test to see what is happening by just running the quoted code on its own:
"Emailaddress -eq '$($line.email)'"
>Emailaddress -eq 'Jim.Adkison#foxdeploy.com'
As to why, well, the outside set of quotes always wins in PowerShell, and the *-ADUser -Filter Cmdlets expect the value to be provided in single quotes.
According to Dave Wyatt, PowerShell MVP and all around cool guy and Mike Robbins, also MVP and well-respected around the community, the way that the ADUser Cmdlets expand variables is somewhat unstandard when compared to the rest of PowerShell's code base. They described the action of Variable expansion as 'strange voodoo', which seems about right.
If you'd like to learn a little bit more, follow up on Mike's awesomely detailed blog post on just this type of scenario PowerShell: When Best Practices and Accurate Results Collide
What if you use a subexpression in the filter?
Get-ADUser -Filter {EmailAddress -eq $($line.email)}
From the error, it isn't properly calling your object inside the filter.
For more testing, what is the object type of $line?
$line | Get-Member