PowerShell Pipeline Script Block inside Hashtable - powershell

Hopefully a PowerShell noob question, but how do I access the current pipeline object inside a script block which is also within a hashtable?
Here is what I'm trying to do in its entirety:
Get-ADGroupMember "Group Name" |
Where {$_.objectClass -eq "user"} |
Get-ADUser -properties extensionAttribute1 |
Where {$_.extensionAttribute1 -ne ($_.UserPrincipalName -replace "#ADdomain.com", "#GAdomain.com")} |
Set-ADUser -replace #{extensionAttribute1=&{$_.UserPrincipalName -replace "#ADdomain.com", "#GAdomain.com"}}
I have everything working except for that last line, where the new extensionAttribute1 should be generated from the current users UserPrincipalName, replacing the domain. Running this code results in an error:
+ Set-ADUser <<<< -replace #{ExtensionAttribute1=&{$_.UserPrincipalName -replace "#ADdomain.com", "#GAdomain.com"}}
+ CategoryInfo : InvalidOperation: (CN=Bar\, Fo...ADdomain,DC=com:ADUser) [Set-ADUser], ADInvalidOperationException
+ FullyQualifiedErrorId : replace,Microsoft.ActiveDirectory.Management.Commands.SetADUser
Replacing the code inside the script block with a string works ok (below), so it seems like some sort of access issue to the current pipeline object. Does $_ not work in this case?
Set-ADUser -replace #{extensionAttribute1=&{"foobar"}}

The short answer seems to be using a foreach in your pipeline, as such:
Get-ADGroupMember "Group Name" |
Where {$_.objectClass -eq "user"} |
Get-ADUser -properties extensionAttribute1 |
Where {$_.extensionAttribute1 -ne ($_.UserPrincipalName -replace "#ADdomain.com", "#GAdomain.com")} |
foreach-objct {Set-ADUser $_ -replace #{extensionAttribute1=&{$_.UserPrincipalName -replace "#ADdomain.com", "#GAdomain.com"}}}
As for why, I'm pretty sure it's because Set-ADUser only accepts one object, whether it's one ADUser or one collection of ADUsers. Since $_ represents this, the way you had it was causing Set-ADUser to see $_ as the one object you were providing in the pipeline - the group of users (instead of each individual user).
Note: The following is speculation! If I'm wrong please correct me!
Regarding Set-ADUser taking one or more objects... here's my guess. If you look at the Input Types of Set-ADUser, it gives the types of None or Microsoft.ActiveDirectory.Management.ADUser. But as you have seen, you can also pass along a collection of ADUser objects and Set-ADUser will accept that as well. From my understanding of that cmdlet, when you call it you can run the same Set command on all objects in that collection. For instance, you could do as you mentioned (assuming $users contains everything in the pipeline up until then):
$users | Set-ADUser -replace #{extensionAttribute1=&{"foobar"}}
My guess is that under the hood, Set-ADUser is accepting $users as the single parameter value (by setting the ValueFromPipeline attribute to true in the code) and applying the parameters you provided to each object therein. Since the iteration of the collection is happening within the cmdlet's code (which is no longer in PowerShell, it's the compiled .Net code), the $_ would have no use here in terms of representing each object.
I'm not sure the mechanics of why the pipeline lets you run Get-ADUser in a pseudo-foreach fashion since they have the same input types (you called that in a similar manner without using foreach) but based on the evidence, I have to assume it's under the hood. If anyone has further insights, I'm definitely curious to know. I may be totally off base!

Related

How to query the Active Directory using a list of users in a text file for a specific attribute with PowerShell

I'm somewhat basic to Powershell and use one-liner commands only to keep it short and basic.
I would like to do the following: I have a list of users in a text file in the form of UserPrincipalName. I'd like to query this list of users if their accounts are still active/enabled or not. To do so, I'm trying to run the following command, which just reveals nothing in the end (blank output):
gc .\users.txt | foreach {get-aduser -server "corp.xxx.com"
-f 'name -like "$_"' -properties *}| select displayname,enabled
As mentioned, the output is blank with no errors or whatsoever.
I read that aduser doesn't work with pipelines, but I need to find a solution.
Kindly request your support :)
Thanks
Your use of single quotes in your filter is not allowing the expansion of the variable. Double-quotes should be wrapping the filter expression so as to allow the interpolation of the automatic variable $_:
Get-ADUser -Filter "name -like '$_'" ...
Single-quoted strings:
A string enclosed in single quotation marks is a verbatim string. The string is passed to the command exactly as you type it. No substitution is performed.
Also note, you mention in your question that the file has the user's UserPrincipalName attribute, yet you're querying the Name attribute, if that's the case, the filter should be:
Get-ADUser -Filter "UserPrincipalName -eq '$_'" ...
Note the use of -eq instead of -like, for exact matches you should always use this operator, see about_ActiveDirectory_Filter for usage details and examples of each operator.
If you're only interested in DisplayName and Enabled for your output, there is no reason in querying all the user's attributes, -Properties * should be just -Properties DisplayName since Enabled is already part of the default attributes returned by Get-ADUser.
Finally, the -Identity parameter can be bound from pipeline, and this parameter accepts a UserPrincipalName as argument, hence ForEach-Object is not needed in this case:
Get-Content .\users.txt |
Get-ADUser -server "corp.xxx.com" -Properties DisplayName |
Select-Object DisplayName, Enabled

Powershell Expression gives no results

I have created an expression that should return the OU canonical name. A small outline:
#(Search-ADAccount -LockedOut -UsersOnly) | Select-Object Name,SamAccountName,#{Name="OU";Expression={((Get-ADOrganizationalUnit -Identity $($_."DistinguishedName")).CanonicalName)}}
However, this expression returns an empty OU column, the other 2 columns are filled.
My question is what is wrong with this expression? Any feedback is appreciated.
With kind regards,
TheStingPilot
A troubleshooting technique for calculated properties:
The script block stored in the Expression entry of a hashtable defining a calculated property:
runs in a child scope relative to the caller's scope.
more importantly, it quietly ignores any errors that occur.
However, such errors are recorded in the automatic $Error variable, so to diagnose your problem you can do the following:
$Error.Clear()
Search-ADAccount -LockedOut -UsersOnly |
Select-Object Name, SamAccountName, #{Name="OU";Expression={(Get-ADOrganizationalUnit -Identity $_.DistinguishedName).CanonicalName}}
$Error # Output the errors that occurred inside the calculated property's script block.
Solution to your specific problem:
As Santiago Squarzon's helpful answer explains, you need to extract the OU's distinguished name (DN) from the user's and pass the former to Get-ADOrganizationalUnit -Identity.
While $_.DistinguishedName.Split(',',2)[1], which removes the first ,-separated token from the user's DN will typically work, it can fail with DNs that use escaped , chars. (\,) that are to be treated as part of a value; e.g.:
# !! Simple splitting by the first "," is NOT enough here:
PS> 'CN=Martin Luther King\, Jr.,OU=Ministry,DC=example,DC=org'.Split(',',2)[1]
Jr.,OU=Ministry,DC=example,DC=org # !! WRONG - split at the *escaped* ","
To also handle these edge cases, i.e. to robustly extract the OU's DN from a user's, a sophisticated regex is required, in combination with the -replace operator:
# OK: The regex correctly recognizes the escaped \, as such.
PS> 'CN=Martin Luther King\, Jr.,OU=Ministry,DC=example,DC=org' -replace '^.+?((?<=[^\\])(?:\\\\)*),'
OU=Ministry,DC=example,DC=org # OK
To put it all together:
Search-ADAccount -LockedOut -UsersOnly |
Select-Object Name,
SamAccountName,
#{
Name = 'OU'
Expression = {
(Get-ADOrganizationalUnit -Identity ($_.DistinguishedName -replace '^.+?((?<=[^\\])(?:\\\\)*),')).CanonicalName
}
}
The issue with your code is that you're trying to feed Get-ADOrganizationalUnit a user's DistinguishedName instead of an OU's DistinguishedName which is not valid.
It's also worth mentioning, you're missing -Properties CanonicalName on Get-ADOrganizationalUnit.
Try this instead:
$e={(Get-ADOrganizationalUnit -Identity $_.DistinguishedName.Split(',',2)[1] -Properties CanonicalName).CanonicalName}
Search-ADAccount -LockedOut -UsersOnly |
Select-Object Name, SamAccountName, #{Name="OU";Expression=$e}

powershell script add-adgroupmember

I'm writing a script which is supposed to show me security groups by matching an input e.g. 'marketing'.
Afterwards I want to add a user to this security group. Since the exchange-powershell can search for user via -anr it's much easier to find the right person.
Here is the part of my script:
$grparray = get-adgroup -filter * | where { $_.name -match "marketing" -and $_.GroupCategory -eq 'Security' }
$potentarray = get-mailbox -anr Julia | select SamAccoutName
$grparray[1] | add-adgroupmember -members $potentarray[1]
But I get the error:
CannotConvertArgumentNoMessage,Microsoft.AcitveDirectory.Management.Commands.AddAdGroupMember
Seems like the ad-modules can't handle the Exchange input.
Does anyone know how I can solve this issue, or got another idea how to?
Ambiguous Name Resolution is available with Get-ADUser, this is preferable over Get-Mailbox as it returns an AD Object which can be used as an input for Add-ADGroupmember.
Try $potentarray = Get-ADUser -LDAPFilter "(anr=Julia)" instead of Get-Mailbox.

Active Directory PowerShell Filter

I'm having issues with a script in powershell using AD Module.
I know the general rule of thumb as to how AD commands like to receive its queries so I've wrote this script to (what I thought) would fall inline with those guidelines.
$CSV=Import-Csv "c:\temp\deleteduserlist.csv"
foreach ($entry in $CSV)
{
$filter = "{SamAccountName -like ""$($entry.username)""}"
Get-ADObject -filter $filter
}
I basically need to be able to query and restore any of the users that have been deleted however it fails with:
Error Message: 'syntax error' at position: '1'
At first I was sending through the filter with single quotations like so:
{SamAccountName -like 'xxx'"}
However I have fixed this now.
One thing that puzzles me is that I can literally show the results of $filter, copy them to Get-ADObject -Filter (paste) manually and it works. Therefore I cannot understand why Powershell does not like it..
Whole Error:
Get-ADObject : Error parsing query: '{SamAccountName -like "xxxx"}'
Error M essage: 'syntax error' at position: '1'. At
C:\temp\GetDeleted.ps1:5 char:14
+ Get-ADObject <<<< -filter $filter
+ CategoryInfo : ParserError: (:) [Get-ADObject], ADFilterParsing Exception
+ FullyQualifiedErrorId : Error parsing query: '{SamAccountName -like "xxx "}' Error Message: 'syntax error' at position: '1'.,Microsoft.ActiveD irectory.Management.Commands.GetADObject
One way to do it is this
$CSV=Import-Csv "c:\temp\deleteduserlist.csv"
foreach ($entry in $CSV) {
## Set username to entry from csv file
$directory_username = $entry.username
## Build search filter before using it, interested in username and deleted objects
$directory_filter = {(SamAccountName -like $directory_username) -and (Deleted -eq $true)}
## Search for ADObject based on filter and deleted objects explicitely included in the search
$directory_found_object = Get-ADObject -Filter $directory_filter -IncludeDeletedObjects -Properties sAMAccountName, Deleted
foreach ($directory_object in $directory_found_object) {
### Execute required action on each found $directory_object
### Perhaps pipe $directory_object | Restore-ADObject with appropriate attribute values for restore
}
}
$directory_filter can be of course modified to fit your needs better. However one challenge you will be facing still is to decide which of the found objects for given sAMAccountName should be restored. This is because any given objects can be in deleted state multiple times. Perhaps one way to solve for that is to restore object with latest WhenCreated attribute value.
Also I'm not sure what motivation you have to build filter beforehand. It might be useful in case you built in on the fly for different attribute values but it seems not to be the case in your example. So for simplicity it can be also removed and included directly in Get-ADObject call like this
$directory_found_object = Get-ADObject -Filter {(SamAccountName -like $directory_username) -and (Deleted -eq $true)} -IncludeDeletedObjects -Properties sAMAccountName, Deleted

Updating users with different descriptions in AD

I have a .csv file with a couple of users and each user has a different description. I'm trying to create a script to update the users' description with what is in the .csv file. I have searched but have been unable to find a script that does what I need it to do. Can someone take a look at my script please? I am erroring at:
Unexpected token 'in' in expression or statement. At line:6 char:35
Unexpected token 'in' in expression or statement. At line:6 char:38
ipmo ac*
$file = c:\user_file.csv
$user = $_.samaccountname
$desc = $_.description
Import-csv $file | Foreach ($user in $file) {
#(Set-aduser -identity $($file.user) -description $($file.desc))
}
Any help would be appreciated since I have been trying to do this for a week now and I'm sure it's something easy?
I would instead do something like this.
ipmo ac*
$file = "c:\user_file.csv"
Import-csv $file | ForEach-Object{
Set-aduser -identity $_.samaccountname -description $_.description
}
This is assuming you have your CSV with columns called samaccountname and description.
Not sure what you were trying to do with $user and $desc so those are just omitted. Guessing they would have actually been null since $_ is meant to be used inside a pipeline.
The ForEach you were using is not meant to have pipeline input. The way you have it usually stands on its own. So we switched it to the ForEach-Object version that allows pipeline input.