Powershell Script not recognising ForEach-Object as a valid cmdlet - powershell

I have writen a powershell script to make ammendments to Active Directory.
I am Getting a funny error.
Here is the script.
#imports the module active directory if it isn't there.
function add-ADmodule()
{
$modules = Get-Module | Where-Object{$_.Name -like "*ActiveDirectory*"}
if($modules -eq $null)
{
Import-Module ActiveDirectory
}
}
#import the data file
$user_csv = import-csv C:\temp\users.csv
#makes the ammendments to the AD object
function ammend-ADUsers($user_csv)
{#this is the loop to make ammendments to each object
$users_csv|ForEach-Object`
{
#assigns each user's AD object to a variable
$user_object = get-aduser -filter * `
-Properties mail |`
Where-Object{$_.mail -like $_."Email Address"}
#ammends the ad object in the above variable
set-aduser -Identity $user_object `
-OfficePhone $_."Office Number" `
-MobilePhone $_."Mobile Number" `
-StreetAddress $_."Street" `
-City $_."City" `
-PostalCode $_."PostCode"
}
}
#this is the main part of the code where it gets executed
add-ADmodule
Write-Verbose "Active Directory Module Added"
ammend-ADUsers($user_csv)
This is the error I am getting.
PS C:\Users\admin> C:\Scripts\ammend-aduser.ps1
ForEach-Object : The term 'ForEach-Object' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and try again.
At C:\Scripts\ammend-aduser.ps1:18 char:20
+ $users_csv|ForEach-Object`
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (ForEach-Object:String) [], Com
mandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I am not not sure what could be causing this error or why it is happening.

Your issue is because you have not put a space between the cmdlet and the backtick character, but it would be better to not use a backtick and instead just keep the opening curly brace { on the same line:
$users_csv|ForEach-Object {
You also don't need a backtick after a pipe character. You might want to also consider using splatting instead of backticks to improve your formatting (backticks are generally discouraged as they can be hard to see and easy to use improperly). I suggest the following revision:
$users_csv | ForEach-Object {
#assigns each user's AD object to a variable
$user_object = Get-ADUser -filter * -Properties mail |
Where-Object{$_.mail -like $_."Email Address"}
$Props = #{
Identity = $user_object
OfficePhone = $_."Office Number"
MobilePhone = $_."Mobile Number"
StreetAddress = $_."Street"
City = $_."City"
PostalCode = $_."PostCode"
}
#ammends the ad object in the above variable
Set-ADUser #Props
}

Related

Check and Update multiple attributes of AD users

I am trying to do an update to Active Directory from a CSV.
I want to check each value to see if the AD and CSV values match.
If the AD value and CSV values don't match, then I want to update the AD value.
finally I want to create a log of the values changed, which would eventually be exported to a CSV report.
Now there is about 30 values I want to check.
I could do an if statement for each value, but that seems like the hard way to do it.
I am try to use a function, but I cant seem to get it working.
I am getting errors like:
set-ADUser : replace
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (JDoe:ADUser) [Set-ADUser], ADInvalidOperationException
+ FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.SetADUser
set-ADUser : The specified directory service attribute or value does not exist
Parameter name: Surname
At line:94 char:9
+ set-ADUser -identity $ADUser -replace #{$ADValue = $DIAccount ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (JDoe:ADUser) [Set-ADUser], ArgumentException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft.ActiveDirectory.Management.Commands.SetADUser
Any suggestions would be welcome
Code I am using:
Function AD-Check ($ADValue, $ADUser, $ADAccount, $UpdateAccount)
{
If ($ADAccount -ne $UpdateAccount)
{
set-ADUser -identity $ADUser -replace #{$ADValue = $UpdateAccount}
$Change = "Updated"
}
Else
{
$Change = "No Change"
}
Return $Change
}
$Import = get-content C:\temp\ADUpdates.csv
Foreach ($user in $Import)
{
$Account = get-aduser $User.Samaccountname -Properties *
#First Name Check
$Test = AD-Check "GivenName" $Account.samaccountname $Account.givenname $user.givenname
$ChangeGivenName = $Test
#Initials Check
$Test = AD-Check "Initials" $Account.samaccountname $Account.Initials $user.Initials
$ChangeInitials = $Test
#Last Name Check
$Test = AD-Check "Surname" $Account.samaccountname $Account.SurnameSurname $user.Surname
$ChangeSurname = $Test
}
Reply to Theo, cant seem to add this any other way...
Thanks Theo, it seems to make sense, but getting an error.
Select-Object : Cannot convert System.Collections.Specialized.OrderedDictionary+OrderedDictionaryKeyValueCollection to one of the following types {System.String,
System.Management.Automation.ScriptBlock}.
changed the following to get all properties for testing and it works.
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
Left the following and it kicks the error
$oldProperties = $Account | Select-Object $propsToCheck
Using the following just for testing:
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
mail = 'mail'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
MobilePhone = 'mobile'
DistinguishedName = 'DistinguishedName'
}
Starting of with a WARNING:
Replacing user attributes is not something to be taken lightly and you
need to check any code that does that on a set of testusers first.
Keep the -WhatIf switch to the Set-ADUser cmdlet so you
can first run this without causing any problems to the AD.
Only once you are satisfied all goes according to plan, remove the -WhatIf switch.
Please carefully read all inline comments in the code.
In your code you use an input CSV file, apparently with properties and values to be checked/updated, but instead of using Import-Csv, you do a Get-Content on it, so you'll end up with just lines of text, not an array of parsed properties and values..
Next, as Mathias already commented, you need to use the LDAP attribute names when using either the -Add, -Remove, -Replace, or -Clear parameters of the Set-ADUser cmdlet.
To do what you intend to do, I would first create a hashtable to map the PowerShell attribute names to their LDAP equivalents.
To see which property name maps to what LDAP name, you can use the table here
# create a Hashtable to map the properties you want checked/updated
# the Keys are the PowerShell property names as they should appear in the CSV
# the Values are the LDAP AD attribute names in correct casing.
$propertiesMap = [ordered]#{
SamAccountName = 'sAMAccountName'
GivenName = 'givenName'
Initials = 'initials'
Surname = 'sn'
Office = 'physicalDeliveryOfficeName'
Organization = 'o'
MobilePhone = 'mobile'
# etcetera
}
# for convenience, store the properties in a string array
$propsToCheck = $propertiesMap.Keys | ForEach-Object { $_.ToString() }
# import your CSV file that has all the properties you need checked/updated
$Import = Import-Csv -Path 'C:\temp\ADUpdates.csv'
# loop through all items in the CSV and collect the outputted old and new values in variable $result
$result = foreach ($user in $Import) {
$sam = $user.SamAccountName
# try and find the user by its SamAccountName and retrieve the properties you really want (not ALL)
$Account = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue -Properties $propsToCheck
if (!$Account) {
Write-Warning "A user with SamAccountName '$sam' does not exist"
continue # skip this one and proceed with the next user from the CSV
}
# keep an object with the current account properties for later logging
$oldProperties = $Account | Select-Object $propsToCheck
# test all the properties and create a Hashtable for the ones that need changing
$replaceHash = #{}
foreach ($prop in $propsToCheck) {
if ($Account.$prop -ne $user.$prop) {
$ldapAttribute = $propertiesMap[$prop] # get the LDAP name from the $propertiesMap Hash
# If any of the properties have a null or empty value Set-ADUser will return an error.
if (![string]::IsNullOrWhiteSpace($($user.$prop))) {
$replaceHash[$ldapAttribute] = $user.$prop
}
else {
Write-Warning "Cannot use '-Replace' with empty value for property '$prop'"
}
}
}
if ($replaceHash.Count -eq 0) {
Write-Host "User '$sam' does not need updating"
continue # skip this one and proceed with the next user from the CSV
}
# try and do the replacements
try {
##########################################################################################################
# for safety, I have added a `-WhatIf` switch, so this wll only show what would happen if the cmdlet runs.
# No real action is performed when using '-WhatIf'
# Obviously, there won't be any difference between the 'OLD_' and 'NEW_' values then
##########################################################################################################
$Account | Set-ADUser -Replace $replaceHash -WhatIf
# refresh the account data
$Account = Get-ADUser -Identity $Account.DistinguishedName -Properties $propsToCheck
$newProperties = $Account | Select-Object $propsToCheck
# create a Hashtable with the old and new values for log output
$changes = [ordered]#{}
foreach ($prop in $propsToCheck) {
$changes["OLD_$property"] = $oldProperties.$prop
$changes["NEW_$property"] = $newProperties.$prop
}
# output this as object to be collected in variable $result
[PsCustomObject]$changes
}
catch {
Write-Warning "Error changing properties on user '$sam':`r`n$($_.Exception.Message)"
}
}
# save the result as CSV file so you can open with Excel
$result | Export-Csv -Path 'C:\temp\ADUpdates_Result.csv' -UseCulture -NoTypeInformation

POWERSHELL - Module Active Directory command New-AdGroup Problem with Spaces in value OU="Servers Of Files"

I would need your help with the use of New-AdGroup command.
The goal of my script is to verify if an Active Directory group exists and if it doesn't exist the script create the group and add the member of the group in a specific OU.
But my problem is when I use the script with a combination of OU and one of them contains some spaces in its name (ex : "OU=Servers of Files"), the script returns an error.
Example: I need to add the group in "OU=Fileserver1" which is a sub OU of "OU=Servers of Files" which is a sub OU of "OU=Groupes".
When I call New-AdGroup with a path's value : "Ou=ServerFile1,OU=Servers of Files,OU=Groupes,DC=Contoso,DC=lan", I get the following error.
New-ADGroup : Objet de l’annuaire non trouvé At line:29 char:5
+ New-ADGroup -Name "$DLGroupName" -Path "$Orga" -GroupCategory "Se ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (CN=G_GROUP1...ONTOSO,DC=lan:String) [New-ADGroup],
ADIdentityNotFoundException
+ FullyQualifiedErrorId : Objet de l’annuaire non trouvé,Microsoft.ActiveDirectory.Management.Commands.NewADGroup
The problem is caused by "OU=Servers of Files" because I tried with an OU without spaces and it was working.
Please find a part of the script below, let me know how I can manage spaces under a path :
#Variable
$CurrentDomain = Get-ADDomain | Select -Property DistinguishedName
$TargetOU = "OU=FileServer1,OU=Servers of Files,OU=Groupes" #
$OrganizationalUnitDN = $TargetOU+","+ $CurrentDomain.DistinguishedName
$Orga = $OrganizationalUnitDN
$DLGroupName = "DL_FileServer1_TEST"
$Description = "\\FileServer1\Share\Test"
New-ADGroup -Name "$DLGroupName" -Path "$Orga" -GroupCategory "Security" -GroupScope "Global" -Description "$Description" -PassThru
Note : $Orga = Ou=ServerFile1,OU=Servers of Files,OU=Groupes,DC=Contoso,DC=lan
When we encounter weird bugs like this, a good first test is to manually run the command with no variables to find the root cause of the error. Doing this shows us that we don't need to escape spaces with a \ or \20 sequence and that route won't help us.
Because I can run your command with no errors when I manually expand the variables like so:
New-ADGroup -Name "DL_FileServer1_TEST" -Path "OU=Servers Of Files,DC=FoxDeploy,DC=local" `
-GroupCategory "Security" -GroupScope "Global" -Description "Test" -PassThru
DistinguishedName : CN=DL_FileServer1_TEST1,OU=Servers Of Files,DC=FoxDeploy,DC=local
GroupCategory : Security
GroupScope : Global
Name : DL_FileServer1_TEST1
ObjectClass : group
ObjectGUID : 5889f8ea-9d80-4609-ad47-92e50a574088
SamAccountName : DL_FileServer1_TEST1
SID : S-1-5-21-3818945699-900446794-3716848007-32100
Now that I know this works, I know I can then store the values in variables to make it cleaner to read, like this:
$params = #{
Name = "DL_FileServer1_TEST1";
Path = "OU=Servers Of Files,DC=FoxDeploy,DC=local";
GroupCategory = "Security";
GroupScope = "Global";
Description = "My Test Group"
PassThru = $true
}
New-ADGroup #params
If I had to guess, I bet that you need to run the entire script to populate all of the variables, and instead you are rerunning the last command over and over but one of the variables is $null.
If that doesn't work...
If not that, then are we sure the container of Ou=ServerFile1 actually exists too?

set-aduser , update several extensionattribute variables plus other attributes at once in powershell script

So I have a basic script that works when values are hardcoded. I need some help getting it to work when values are dynamic from a file:
Here is the basic script:
set-ADUser -Identity test.hsi -replace #{extensionAttribute4="LoadedFromInterface";extensionAttribute5="2";extensionAttribute6="2"} -Manager jim.james
What I want to do is read from a file using Import-CSV, loading the important columns into variables, checking for null/empty condition and then re-setting the variables if they were null. Ultimately doing the same as above but with variables that got loaded from the file. extentionAttribute5 and extensionAttribute6 would both be values from the file (sometimes null) and manager would also be a variable that is assigned from file.
Import-CSV C:\Users\user1\Documents\WTKtoAD\WTKtoAD.csv | %{$SAM = $_.SamAccountName;If ($_.PhoneTypeMobile -eq $Null) {$PhoneMobile = "NotProvided"} Else {$PhoneMobile = $_.PhoneTypeMobile};If ($_.PhoneTypeHome -eq $Null) {$PhoneHome = "NotProvided"} Else {$PhoneHome = $_.PhoneTypeHome}} | set-ADUser -Identity $SAM -Add #{extensionAttribute4="LoadedFromKronos";extensionAttribute5=$PhoneHome;extensionAttribute6=$PhoneMobile} -Manager $_.Manager
When I run the script I get the following error in Powershell ISE (x86) 'Run As Admnistrator'.
PS C:\Users\user1> Import-CSV C:\Users\user1\Documents\WTKtoAD\WTKtoAD.csv | %{$SAM = $_.SamAccountName;If ($_.PhoneTypeMobile -eq $Null) {$PhoneMobile = "NotProvided"} Else {$PhoneMobile = $_.PhoneTypeMobile};If ($_.PhoneTypeHome -eq $Null) {$PhoneHome = "NotProvided"} Else {$PhoneHome = $_.PhoneTypeHome}} | set-ADUser -Identity $SAM -Add #{extensionAttribute4="LoadedFromKronos";extensionAttribute5=$PhoneHome;extensionAttribute6=$PhoneMobile} -Manager $_.Manager
Set-ADUser : Cannot validate argument on parameter 'Identity'. The argument is null. Provide a valid value for the argument, and then try running the command again.
At line:1 char:318
+ ... User -Identity $SAM -Add #{extensionAttribute4="LoadedFromKronos";extensionAttri ...
+ ~~~~
+ CategoryInfo : InvalidData: (:) [Set-ADUser], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.SetADUser
The file looks like this:
> SamAccountName,PhoneTypeMobile,PhoneTypeHome,Manager
> test.hsi,333-234-3433,'',bob.henst
The reason for the error is because you have put the Set-ADUser in the wrong place (AFTER the ForEach-Object).
You can hardly tell from your code because you are putting everything on the same line which makes it very hard to read. My advice is to give it some air. That way, mistakes are far easier to spot.
As for the code. I didn't test because I don't have AD available right now but i think this will work better:
# this is the content of the WTKtoAD.csv file
# SamAccountName,PhoneTypeMobile,PhoneTypeHome,Manager
# test.hsi,333-234-3433,'',bob.henst
Import-CSV C:\Users\user1\Documents\WTKtoAD\WTKtoAD.csv |
ForEach-Object {
$SAM = $_.SamAccountName
if ([string]::IsNullOrEmpty($_.PhoneTypeMobile)) { $PhoneMobile = "NotProvided" } else { $PhoneMobile = $_.PhoneTypeMobile }
if ([string]::IsNullOrEmpty($_.PhoneTypeHome)) { $PhoneHome = "NotProvided" } else { $PhoneHome = $_.PhoneTypeHome }
if ([string]::IsNullOrEmpty($_.Manager)) { $Manager = $null } else { $Manager = $_.Manager }
$props = #{
extensionAttribute4 = "LoadedFromKronos"
extensionAttribute5 = $PhoneHome
extensionAttribute6 = $PhoneMobile
}
Set-ADUser -Identity $SAM -Replace $props -Manager $Manager
}
As you can see, i have also put in a small test for the Manager. If in the CSV this is an empty string, it will now Clear the manager property for the user.

String variable in an Active Directory path not working

I'm writing a PowerShell script to create a new Active Directory group and automatically put it in the correct OU, depending on what department the user is in. The script gets the department from the user in Active Directory and then needs to use that as the name of the OU in active directory. When I don't use the variable in the AD path, this script works.
[string]$department = Get-ADUser -identity johndoe -properties department | Select department
New-ADGroup -Name NewADGroup -GroupScope Global -path “OU=($department),OU=SubDepartment,OU=MainDepartment,DC=OrgName”
However, when I try to use the variable $department as above, I get the following error:
New-ADGroup : The object name has bad syntax
At C:\Users\JohnDoe\Desktop\CreateNewGroup.ps1:7 char:1
+ New-ADGroup -Name NewADGroup -GroupScope Global -path
"OU=($department ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (CN=NewADGroup,DC=OrgName
:String) [New-ADGroup], ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:8335,Microsoft.ActiveDirec
tory.Management.Commands.NewADGroup
How can I call that variable inside the Active Directory path?
You actually have 2 issues here that are common pitfalls.
$department is not a string per se but a string representation of an object with a deparment property. You need to break the string out. That is what -ExpandProperty was for. If you looked at your department now you would see something like #{Department="IT"}
You are also having issues with variable expansion in strings.
[string]$department = Get-ADUser -identity johndoe -properties department | Select -Expandproperty department
New-ADGroup -Name NewADGroup -GroupScope Global -path "OU=$department,OU=SubDepartment,OU=MainDepartment,DC=OrgName"
If you are not calling properties or complex object then removing the brackets is sufficient. Else you can just use a sub expression "OU=$($department),OU=SubDepartment,OU=MainDepartment,DC=OrgName". Without the $ sign the brackets were considered part of the string.
try a dollar sign in front of the opening parenthesis like this:
"OU=$($department)..."

Powershell commandline with pipes error using Exchange 2010

Being new to powershell and used to oneliners in unix I find powershell strange. The below code gives me a "too many pipes" type of error. Can anyone tell me what I am doing wrong. My last step will be to add code that adds permission if not found in the else block.
[PS] C:\Windows\system32>(Get-mailbox -identity moh | select alias, distinguishedname) | foreach-object -process { if($_.distinguishedname -match "HK1|VO[1-9]") { $alias = $_.alias; get-mailboxfolderstatistics -identity $alias | Where {$_.FolderType -eq 'Calendar'} | %{ $calpath = $_.folderpath.substring(1); Get-MailboxFolderPermission -Identity $alias":\"$calpath -User iTell | %{ if($_.AccessRights -eq 'Editor') { write-host "Editor!" } else { write-host $_.AccessRights }} } } }
I get the following error.
Pipeline not executed because a pipeline is already executing. Pipelines cannot be executed concurrently.
+ CategoryInfo : OperationStopped: (Microsoft.Power...tHelperRunspace:ExecutionCmdletHelperRunspace) [], PSInvalidOperationException
+ FullyQualifiedErrorId : RemotePipelineExecutionFailed
Got it. Had to encapsulate blocks of code with parenthesis. But I thought the pipe block was just some sort of a write lock. In here I was only getting data and hence ought to be able to read from the streams.