Trying to pipe into Set-ADAccountExpiration - powershell

I'm taking a list of email addresses and future leave dates from a database and trying to populate the account expiry date in AD.
$ds contains email and leave date.
foreach ($mail in $ds[0].rows) {
$ms = $mail.mail;
Get-ADUser -Filter {(emailaddress -eq $ms) -And (Enabled -eq $True)} |
Set-ADAccountExpiration -Datetime $mail.leavedate
}
The Get-ADUser part is working fine but the problem is the $mail.leavedate is not available to the command, only the results from Get-ADUser.
I've tried using $_$mail.leavedate but this passes through empty results.
Perhaps I need to create a new object with the result from Get-ADUser and the leave date, I'm just not sure how to do that or how to then use a new object with the Set-ADAccountExpiration command.

Related

Exporting last logon date for inactive users via PowerShell

I have a command that will export a list of users who have logged in for 12 months but I am struggling to export the last login date and time.
The command is as follows:
Search-ADAccount –AccountInActive -UsersOnly –TimeSpan 365:00:00:00 –ResultPageSize 2000 –ResultSetSize $null |?{$_.Enabled –eq $True} | Select-Object Name, SamAccountName, DistinguishedName, lastLogon| Export-CSV “C:\Users\Me\Desktop\InactiveUsers.CSV” –NoTypeInformation
But lastLogon is showing a blank in the CSV file.
I am new to PowerShell I understand the command can be made much smoother.
Any help on this is much appreciated.
Search-ADAccount doesn't have an option to pull other attributes from the AD Objects than the default ones, you can use Get-ADUser with an elaborate filter to query the users who haven't logged on for the past year. One option is to query the user's lastLogonTimeStamp attribute however by doing so you're risking not getting accurate results because this attribute is not replicated in real time. To get accurate one must query the user's lastLogon attribute but, since this attribute is not replicated across the Domain, one must query all Domain Controllers to get the latest logon from the user.
For more information on this topic, please check this excellent TechNet Article: Understanding the AD Account attributes - LastLogon, LastLogonTimeStamp and LastLogonDate.
$dateLimit = [datetime]::UtcNow.AddYears(-1).ToFileTimeUtc()
$AllDCs = Get-ADDomainController -Filter *
$logons = #{}
$params = #{
LDAPFilter = -join #(
"(&" # AND, all conditions must be met
"(!samAccountName=krbtgt)" # exclude krbtgt from this query
"(!samAccountName=Guest)" # exclude Guest from this query
"(userAccountControl:1.2.840.113556.1.4.803:=2)" # object is Disabled
"(lastLogon<=$dateLimit)" # lastLogon is below the limit
")" # close AND clause
)
Properties = 'lastLogon'
}
foreach($DC in $AllDCs) {
$params['Server'] = $DC
foreach($user in Get-ADUser #params) {
# this condition is always met on first loop iteration due to ldap filtering condition
if($logons[$user.samAccountName].LastLogon -lt $user.LastLogon) {
$logons[$user.samAccountName] = $user
}
}
}
$logons.Values | ForEach-Object {
[PSCustomObject]#{
Name = $_.Name
SamAccountName = $_.SamAccountName
DistinguishedName = $_.DistinguishedName
lastLogon = [datetime]::FromFileTimeUtc($_.lastLogon).ToString('u')
}
} | Export-CSV "C:\Users\Me\Desktop\InactiveUsers.CSV" -NoTypeInformation

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

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

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 get-aduser specific date range

Hello and thank you for taking the time to read this.
I am writing a program that will look back at a certain date range and then return the values that I specify in a get-aduser cmdlet
The code I have thus far is here:
$grabDate = Read-Host 'enter how many days back you want to search'
$date = Get-Date
$desiredDate = $date.AddDays(- $grabDate)
Write-Host $desiredDate 'to' $date 'is your search range'
Pause
Get-ADUser -Filter * -Properties Name, LastLogonDate | Where-Object { $date.AddDays(- $grabDate) } | Select-Object name, LastLogonDate
I know that this is not the cleanest code and there are some redundant steps my main focus is on the line:
Get-ADUser -Filter * -Properties Name, LastLogonDate | Where-Object { $date.AddDays(- $grabDate) } | Select-Object name, LastLogonDate
when I enter 30 days to search back, I am getting strange entries from 2016, does anyone see anything strange with my code?
No need for the pipeline here - just use a little simple math, a conversion from string to int, and use the -Filter parameter as it was designed.
Set $grabDate like this, so you get an actual int value, not a string
# Convert Read-Host input to an integer, and multiply by -1 to get the negative value
$grabDate = [Convert]::ToInt32( (Read-Host 'enter how many days back you want to search' ) ) * -1
Then call Get-ADUser with the following -Filter parameter:
# Use the filter to return only the users who haven't logged on in the
# last $grabDate days
Get-ADUser -Filter "LastLogonDate -ge '$((Get-Date).AddDays( $grabDate ))'"
This way, you only return the ADUsers you care about and you don't have to process the list of users a second time. Using -Filter * can be a costly operation, especially in larger AD environments.
Import-Module ActiveDirectory
# Set the number of days since last logon
$DaysInactive = 90
$InactiveDate = (Get-Date).Adddays(-($DaysInactive))
#-------------------------------
# FIND INACTIVE USERS
#-------------------------------
# Below are four options to find inactive users. Select the one that is most appropriate for your requirements:
# Get AD Users that haven't logged on in xx days
$Users = Get-ADUser -Filter { LastLogonDate -lt $InactiveDate -and Enabled -eq $true } -Properties LastLogonDate | Select-Object #{ Name="Username"; Expression={$_.SamAccountName} }, Name, LastLogonDate, DistinguishedName

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.