PowerShell: If statements and using blank cells in Import-CSV - powershell

I am having difficulty using an if statement with blank cells in my CSV file. I'm attempting to write an onboarding script and have it pull info from an xlsx HR fills out (IT copies needed rows into CSV that is used in script). In order to get the OU path, I use the department names. However, some users have a sub-department and others do not, these fields are left blank in the xlsx that HR sends. I have it working by inputting a N/A in those fields however if another tech doesn't know to do that in the CSV file the script will fail. So I would like to get it working with the blank fields.
This is what im trying when not using the N/A in the CSV field
foreach ($obj in $onboardcsv){
$dep = "$($obj.department)"
$sdep = "$($obj.subDepartment)"
if ($null -eq $obj.subDepartment){
$ou = Get-ADOrganizationalUnit -Filter {name -like $dep} -SearchBase "OU=User,OU=OU,DC=DOMAIN,DC=com"
}
else{
$ou = Get-ADOrganizationalUnit -Filter {name -like $sdep} -SearchBase "OU=User,OU=OU,DC=DOMAIN,DC=com"
}
Any help would be appreciated!

To rephrase your question, you just want to search where SubDepartment isn't empty?
Without modifying too much of your code, you can make use of the static method of ::IsNullOrWhiteSpace() provided in the [string] class to evaluate against the emptiness:
Using -Not reverses the result of [string]::IsNullOrWhiteSpace($obj.subDepartment).
foreach ($obj in $onboardcsv)
{
$department = if (-not [string]::IsNullOrWhiteSpace($obj.subDepartment)) {
$obj.subDepartment
}
else {
$obj.department
}
Get-ADOrganizationalUnit -Filter "Name -like '$department'" -SearchBase "OU=User,OU=OU,DC=DOMAIN,DC=com"
}
So, testing against the subDepartment first, if $obj.subDepartment is not null, assign it to $department. This will allow the use of just one variable for both properties, and no code copying necessary.
Thanks to #Santiago for a sanity check.

Something like this would work.
$ou = "searching by sub department"
$department = if (!($user.subDepartment)) {
#subdepartment is blank
#searching by department
$ou = "searching by department"
}
$ou

Related

Powershell Script to query Active Directory

I am trying to query all users in multiple OUs of the same name. Get the SamAccountName attribute and then check for a file at a specific location with that name.
Here is what I have so far:
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = Get-ADUser -Filter * -SearchBase $_.DistinguishedName |
Select SamAccountName
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$AccountName.pdf"
}
If a file is not found. I want to add the user to a group, however here is the kicker. The account has to be added to the non-compliance group for the organization that the account belongs to.
I.E an admin account found under:
OU=Admin-User-Accounts,OU=Administration,OU=ORG1,OU=ORGS,DC=domain,DC=net
would be added to the group named 'ORG1 IA - Non-Compliant Users' located under:
OU=Groups,OU=ORG1,OU=Information Assurance,OU=ORGS,DC=domain,DC=net
Well your post is a bit confusing, and no way to really validate because I have nothing setup like this.
Yet, querying for users in all OU or the enterprise is a common everyday thing.
However, an OU name, just like any other AD object name, must be unique. So, querying for the same OU name is not a thing, in a single AD forest / domain. If you meant querying every OU for the same username, then alrighty then.
By stepping thru how you are explanation for your use case, that you have laid out.
(though maybe you want to edit your post to make it's more clear, well to me anyway...)
Using pseudo code, then trying to map that out... and with no real way to determine what you mean by several things in your post/sample. So, the below is a rough first example of how I'd do approach this... again this is untested, so, I leave that homework to you.
# query all users in multiple OUs
(Get-ADOrganizationalUnit -Filter *).DistinguishedName |
ForEach{
# Collect all members of the current OU
$AccountNames = Get-ADUser -SearchBase $PSItem -Filter *
# Process each member in the current OU collection
ForEach($AccountName in $AccountNames)
{
"Processing $($AccountName.SamAccoutnName)`n"
# Initialize properties needed for processing
$UserOrg = $AccountName.DistinguishedName.split(",")[1]
$MemberCheckOU = "OU=Admin-User-Accounts,OU=Administration,OU=ORG1,OU=$UserOrg,DC=domain,DC=net"
$NonCompliantOU = "OU=Groups,OU=ORG1,OU=Information Assurance,OU=$UserOrg,DC=domain,DC=net"
# Validate user file existence for the current user
If(-Not (Test-Path -LiteralPath "\\domain.net\SYSVOL\domain.net\IA\$($AccountName.SamAccoutnName).pdf)"))
{
# if no file Process the user groupmebership modification
"Processing $($AccountName.SamAccoutnName)"
# Notify that the file was not found and processing is required
Write-Warning -Message "$($($AccountName.SamAccoutnName).pdf) not found. Process group modify actions"
# If the current user is in the MemberCheckOU, add to the NonCompliantOU
If(Get-ADPrincipalGroupMembership -Identity $($AccountName.SamAccoutnName) | Where-Object -Property DistinguishedName -Match $MemberCheckOU )
{ Add-ADGroupMember -Identity $NonCompliantOU -Members $($AccountName.SamAccoutnName) }
Else
{
# Do something else
}
}
Else
{
# Notify that the file was found and no processing required
Write-Host "$($AccountName.pdf) found. No further actions taken" -ForegroundColor Green }
}
}
It seems that one of the variables is incorrect because PowerShell is giving me the following:
Get-ADPrincipalGroupMembership : Cannot validate argument on parameter 'Identity'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command
again.
Okay, so here is what I have so far based on your post above Postanote:
# query all users in multiple OUs
(Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'") |
ForEach{
# Collect all members of the current OU
$AccountNames = Get-ADUser -SearchBase $PSItem -Filter *
# Process each member in the current OU collection
ForEach($AccountName in $AccountNames)
{
"Processing $($AccountName.SamAccoutnName)`n"
# Initialize properties needed for processing
$UserOrg = $AccountName.DistinguishedName.split(",")[1]
$MemberCheckOU = "OU=Admin-User-Accounts,OU=Administration,OU=$UserOrg,OU=ORGS,DC=domain,DC=net"
$NonCompliantOU = "OU=Groups,OU=$UserOrg,OU=Information Assurance,OU=ORGS,DC=domain,DC=net"
# Validate user file existence for the current user
If(-Not (Test-Path -LiteralPath "\\domain.net\SYSVOL\domain.net\IA\$($AccountName.SamAccoutnName).pdf)"))
{
# if no file Process the user groupmebership modification
"Processing $($AccountName.SamAccoutnName)"
# Notify that the file was not found and processing is required
Write-Warning -Message "$($($AccountName.SamAccoutnName).pdf) not found. Process group modify actions"
# If the current user is in the MemberCheckOU, add to the NonCompliantOU
If(Get-ADPrincipalGroupMembership -Identity $($AccountName.SamAccoutnName) | Where-Object -Property DistinguishedName -Match $MemberCheckOU )
{ Add-ADGroupMember -Identity "$UserOrg IA - Non-Compliant Users" -Members $($AccountName.SamAccoutnName) }
Else
{
# Do something else
}
}
Else
{
# Notify that the file was found and no processing required
Write-Host "$($AccountName.pdf) found. No further actions taken" -ForegroundColor Green }
}
}
Looking at the original script fragment:
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = Get-ADUser -Filter * -SearchBase $_.DistinguishedName |
Select SamAccountName # note 1
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$AccountName.pdf" # note 2
}
Note 1: Your going to end up with $accountname.accountname holding your value. I think your going to want to expand this instead.
Note2: Powershell may be getting confused and thinking your looking for the variable $accountname.pdf
Instead, try this...
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = $(Get-ADUser -Filter * -SearchBase $_.DistinguishedName).SamAccountName
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$($AccountName).pdf"
}
here, we save the value of just .SamAccountName for the query to the $AccountName, and by adding $($accountname) we make clear the variable we want, and that .pdf is not part of the variable name.
Now, note as well, this doesn't save the results anywhere, it will just flash them to screen.

Powershell comparison with attributes

I'm beginner in powershell and I need your help.
I need to compare the department attribute from the AD containing some text amd replacing by another value.
But it doesn't work. Do I made a mistake below? Cheers
//Find the user and save the user in the variable
$member = get-Aduser -f {GivenName -eq 'Jack'}
//check if the Departement field match with "Dep20 "
if($member.department -eq "Dep20")
{
//Set "Dep21" in department field
$member.Department = 'Dep21';
set-AdUser -f {GivenName -eq $member.givenName} -departement $member.Department;
}
Some issues with your initial script
First
Get-AdUser won't give you the property Department by default.
You could have confirmed this by actually looking at the output of your Get-AdUser statement. You do need to add it to the list of properties explicitely.
get-Aduser -f {GivenName -eq 'Jack'} -Properties Department
Also, you did make a mistake in the Set-AdUser cmdlet. The parameter name you have written, at the time of my answer, is -departement. Instead, you need to set -department.
Finally, Get-AdUser could return multiple users (or none).
Therefore, you need to account for that by checking how many $member were returned or to do a foreach to process none (if 0) or all of them the same.
At least, that part is subjective to what you need but here would be my approach.
$member = get-Aduser -Filter 'GivenName -like "Jack*"' -Properties Department
$member | foreach {
if ($member.Department -eq 'Dep20')
{
$_.Department = 'Dep21'
set-AdUser $_ -Department $_.Department;
}
}
Edit:
I modified my answer to switch the Filter parameter from a scriptblock (as your question) for a string filter as per mklement0 comment.
Because the Filter parameter is actually a string, giving it a script block will create problems on multiple occasions and you are better restrict yourself to the string type for this parameter.
See this for a more detailed explanation on the matter.

Update AD User From CSV Based on EmployeeID Attribute

I am attempting to update AD users based on their employeeID number. It is a reliable key field for our organization.
Every user in this case was created with an employeeID attribute. I am using the same csv for the initial creation (New-ADuser) of users (only setting less attributes), as I am the update (Set-ADUser) of users.
Most of this is pretty straightforward and sourced mostly from here. I successfully import my csv, and can print my variables. My resulting message indicates that when I execute my If/Else, that the "User with ID is not found, or more than one is found", both of which aren't true. I believe my issue to be in this line:
$UserID = Get-ADUser employeeID=$EmployeeId
Here is the entirety of the script. What am I doing wrong here?
# Import AD Module
Import-Module ActiveDirectory
# Import CSV into variable $userscsv
$ADUsers = Import-Csv -Path C:\Scripting\CSVs\UpdateADUsers.csv
foreach ($User in $ADUsers)
{
#Retrieve info from CSV
$Title = $User.Title
$Department = $User.department
$Office = $User.Office
$EmployeeId = $User.EmployeeId
$Manager = $User.manager
$Company = $User.Orglevel02
#get user DN
$UserDN = Get-ADUser -LDAPFilter "EmployeeId=$EmployeeId"
If ($UserDN.Count -eq 1)
{
# Use the Set-ADUser cmdlet to assign the new attribute values.
Set-ADUser -Identity $UserDN -Replace #{title=$Title;physicalDeliveryOfficeName=$Office;manager=$Manager}
}
Else {"User with ID $ID either not found, or more than one user found."}
}
$UserDN = Get-ADUser -LDAPFilter "(EmployeeId=*$EmployeeId*)"
Marks answer contains the major correction needed in your filter.
Each of your search criteria at a minimum must be in a set of parenthesis. Like in the example given on ldapexplorer.com
Equality: (attribute=abc) , e.g. (&(objectclass=user)(displayName=Foeckeler)
Your current example has bad syntax since it is missing braces but does not constitute a failure of the cmdlet, so, nothing ($null) is returned. You have a response to this in comments
I initially tried with that syntax, minus the *wildcard. Results still the same, implying User with ID 1234567 either not found, or more than one user found.
What if you hardcode an employeeID in there for testing?
Get-ADUser -LDAPFilter "(EmployeeId=12345)"
If that works then that tells me something is wrong with your source file. Leading or trailing whitespace or perhaps hidden characters? Either way look at the source to be sure and if you have to use .Trim() for testing as you might not initially see the problem.
Get-ADUser -LDAPFilter "(EmployeeId=$($EmployeeID.Trim()))"

Trying to find a way to speed up this powershell script

I have this script that works fine (output looks good), but it is taking longer than 12 hours now. There are 34220 records in the csv and it's only now on 2110. Maybe I need to load up all the user data first, then compare to the csv file? Thx for help...
import-module ActiveDirectory
$CCure = Import-csv C:\Scripts\CCure\CCure-Personnel-enabled.csv
ForEach ($Row in $CCure) {
[string]$ID = $Row.ObjectID
[string]$Name = $Row.Name
[string]$EmpID = $Row.Int5
If ($EmpID.Trim() -ne "0") {
$User = Get-ADUser -LDAPFilter "(&(&(&(objectclass=user)(objectcategory=person)(!userAccountControl:1.2.840.113556.1.4.803:=2))))((employeeId=*$EmpID))" -SearchBase 'DC=Enterprise,DC=mycompany,DC=org' -Properties SamAccountName,DisplayName,EmployeeId,enabled |
Select #{Name="CCure ObjectID";Expression={$ID}},SamAccountName,DisplayName,#{Name="CCure Name";Expression={$Name}},EmployeeId,#{Name="CCure Int5 Row";Expression={$EmpID}},enabled | Export-csv c:\scripts\ccure\EmployeeIds4-10-2016.csv -NoTypeInformation -append
}
}
Maybe I need to load up all the user data first, then compare to the csv file?
That's exactly what you need to do!
Since you want to correlate the users in the CSV by the EmployeeId attribute, I'd recommend pulling out all the (enabled) users that have the EmployeeId populated, and then store them in a hashtable where the EmployeeId is used as the key:
$ADUserTable = #{}
Get-ADUser -LDAPFilter "(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(employeeId=*))' -SearchBase 'DC=Enterprise,DC=mycompany,DC=org' -Properties SamAccountName,DisplayName,EmployeeId |ForEach-Object {
$ADUserTable[$_.EmployeeId] = $_
}
Then, as you iterate over the rows in the CSV, lookup the user in the hashtable instead of searching AD again:
$ExistingUsers = ForEach ($Row in $CCure) {
# Import-Csv always creates string properties anyways
$ID = $Row.ObjectID
$Name = $Row.Name
$EmpID = $Row.Int5.Trim()
if ($EmpID -ne "0" -and $ADUserTable.ContainsKeys($EmpID))
{
$ADUserTable[$EmpID] |Select #{Name="CCure ObjectID";Expression={$ID}},SamAccountName,DisplayName,#{Name="CCure Name";Expression={$Name}},EmployeeId,#{Name="CCure Int5 Row";Expression={$EmpID}}
}
}
Do NOT export them to Csv until AFTER you've collected all the information - otherwise you're opening, writing to and closing the same file 35000 times!
So, at the very end:
$ExistingUsers |Export-csv c:\scripts\ccure\EmployeeIds4-10-2016.csv -NoTypeInformation
This will undoubtedly speed up execution of your script
Note: I've removed the Enabled property from Get-ADUser and Select-Object. Your LDAP Filter already guarantees that only Enabled users are returned, so I don't really see any value in adding it to the CSV

Powershell Nested Hashtables - Export to file

Im pulling out information from AD to output alot of information to be handled at a later point.
I need to export this to a csv or something so i can have a rollback ( in effect import file so i can handle each object like i can with the hashtable below. Especially that inside $_.MemberOf).
The hashtable im struggling with handling is:
$logging1 = #{
MemberOf="$users.MemberOf"
OriginalOU="$Ou.DistinguishedName"
DisabledWhen="$descriptionDisabled"
}
$logging = #{$users.SamAccountname = $logging1}
what the rest of the script does is(that isnt pasted ) is:
iterate through a bunch of ou's. Getting users last logged in -90 days ago or more then passing them to the HashTable in question which i like to append to a file to be imported at a later time.
I have been googling for hours without getting somewhere
here is the complete script:
import-module ActiveDirectory
$descriptionDisabled = get-date -Format yyyyMMdd
$Loggdir = "C:\temp"
$array = #{}
$loggname = get-date -f yyyyMMdd
$90days = ((get-date).AddDays(-90))
$searchBase = 'OU=someou4,OU=someou3,OU=someou2,OU=someou1,DC=name,DC=NO'
$ExclusionList = Someexlutions
$OUlist = Get-ADOrganizationalUnit -SearchBase $searchBase
foreach ($Ou in $OUlist)
{
$ExpiredADusers = get-aduser -Filter {(LastlogonDate -le $90days)
-and (Enabled -eq $True)}
-SearchBase $ou.distinguishedname
-Properties *
#Get information about every user and their groupmembership
foreach ($Users in $ExpiredADusers)
{
$users = get-aduser -Identity $users.SamAccountName
-Properties *
| Select-Object -Property SamAccountName, MemberOf,
DistinguishedName, ObjectGUID
$logging = #{$person.SamAccountName = #{
MemberOf=$person.MemberOf;
OriginalOU=$Ou.DistinguishedName;
DisabledWhen="$descriptionDisabled"}}
$Array += $Logging
}
}
$array | export-clixml -path somepath -noclobber
EDIT SOLVED
Solved problem and original script in question has been updated to handle Nested hashtables
Original problem
I think you can understand what im trying to do.
Im making a rollback file incase needed.
So the exported file need to be easy to import. when using hashtables you can use . notations and each objects under memberof is treated as an object
What the rollback needs is so i can iterate through the imported info to move the user back to its original OU placement and restore membership
I think EBGreen really hit the nail on the head here. You need an array of custom objects, and then you can just export it to a CSV like you want to. It's a really minor change in code too.
$Array = #()
$ForEach($User in $Users){
$logging1 = New-Object PSObject -Property #{
MemberOf=$user.MemberOf
OriginalOU=$Ou.DistinguishedName
DisabledWhen=$descriptionDisabled
}
$Array += $Logging1
}
Edit: Hm, so you want to be able to export and import full objects. You don't want a CSV then because you have nested arrays, and a CSV is not designed to handle that for export and import. You need XML, so, as mentioned above, you need to use Export-Clixml and Import-Clixml since XML can handle nested arrays. Just pipe the array to it once the array has all your data and you should be all set.
Edit2: The hashtables within hashtables issue... Ok, so we had it as MemberOf="$user.MemberOf" and that's the issue. It is converting it to a string, so it is expanding the entire $User variable, and tacking .MemberOf to the end of it. We don't really want to do it in this case, but if you want to access a property of an object from within doublequotes you need to put $() around it. For example if you wanted to include the user's distinguishedname as a part of human friendly output you could do something like:
Write-Output "$($Users.Name)'s distinguished name is: $($users.distinguishedname)"
Which would output something like:
TMTech's distinguished name is: CN=TMTech,OU=Awesome,OU=Administrators,DC=Digital,DC=Ghost,DC=net