Powershell Script to check if Active Directory User Last Logon - powershell

I'm trying to write a powershell script that accepts an username as an argument, and displays the last logon time of the user. If the user has not logged in before, the message has not logged in before should be displayed.
For example, if you run .\lastlogon -username marywong the message is displayed:
marywong last logon time 13/07/2017
If you run .\lastlogon -username guest, I get the message:
guest has not logged in before
Below is my code, however it doesn't seem to be looping into the else loop when the user has not logged in before.
param (
[string]$username
)
$user = Get-ADUser -Filter {sAMAccountName -eq $username} | Get-ADObject -Properties lastLogon
$lastlogontime = $user.lastlogon
If ($user -ne $Null) {
if($user.LastLogon -gt $time) {
$displaylastlogon = [datetime]::FromFileTime($lastlogontime)
Write-Host $username " last logon time" $displaylastlogon
}
else {
$displaylastlogon = [datetime]::FromFileTime($lastlogontime)
Write-Host $username " has not logged in before"
}
}
else {
Write-Host $username " does not exist"
}

There is information to be gained from using Get-ADUser and Get-ADObject separately. If the user has never logged in, they are still a user that exists. That is different from a user that does not exist.
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$username
)
$user = Get-ADUser -Filter {SamAccountName -eq $username}
if ($user -ne $null) {
$userlogon = $user | Get-ADObject -Properties lastLogon
if ($userlogon.LastLogon -ne $null) {
$lastlogontime = [DateTime]::FromFileTime($userlogon.LastLogon)
Write-Host $username " last logon time" $lastlogontime
} else {
Write-Host $username " has not logged in before"
}
} else {
Write-Host $username " does not exist"
}

When you use the lastLogon you get a format that AD uses...
then when the if is running you get
Could not compare "131820853335016078" to "09/24/2018 18:18:57". Error: "Cannot convert value "9/24/2018 6:18:57 PM" to type "System.Int64". Error: "Invalid cast from 'DateTime' to 'Int64'.""
so it's not getting to the else..
try using the LastLogonDate property, it will help you more.
try to use this:
$user = Get-ADUser -Filter {sAMAccountName -eq $username} -Properties LastLogonDate
$lastlogontime = $user.lastlogonDate
Edit:
you have some more issues with you code:
You need to remove the displaylastlogon
you cant use -gt because it will always be false.. the user cant log in the future.. you need to use -lt
here is the full script, working:
$user = Get-ADUser -Filter {sAMAccountName -eq $username} -Properties LastLogonDate
$lastlogontime = $user.lastlogonDate
If ($user -ne $Null) {
if($lastlogontime -lt $time)
{
Write-Host $username " last logon time" $lastlogontime
}
else
{
Write-Host $username " has not logged in before"
}
}
else
{
Write-Host $username " does not exist"
}
Another Edit:
I just notice that its not answering the case when user never logon, because you will get $null and $null is lower then the current Time. so you need to check that the lastlogontime is not null
change the if to this:
if($lastlogontime -ne $null -and $lastlogontime -lt $time)

Related

How to list users with logonhours denied

I need a list of users with the logonhours set to denied. I found another question (68558481) that partially got me there but I seem to be doing something wrong when trying to loop through my group.
$userToFind = Get-ADGroupMember -identity "AD group" | Sort-Object samAccountName | Select SamAccountName
# try and find the user also gathering its LogonHours property
Foreach ($username in $userToFind){
$user = Get-ADUser -Filter "SamAccountName -eq '$username'" -Properties LogonHours -ErrorAction SilentlyContinue
if ($user) {
# test if the property has been set. If not set, the user is allowed to login anytime of the week
if ($null -ne $user.LogonHours -and #($user.LogonHours).Count) {
# initialize a variable
$loginAllowed = $false
# loop through the 21 bytes and exit the loop if we found a non-zero byte
foreach ($byte in $user.LogonHours) {
if ($byte -ne 0) {
$loginAllowed = $true # set the flag to $true
break # and exit the loop
}
}
if (!$loginAllowed) {
"User $username - Login disabled"
}
}
}
else {
Write-Warning "User $username does not exist.."
}
}
My results for this is all users do not exist. Where am I going wrong?
|
The main issue is that $userToFind is an object or array of objects which's property is SamAccountName, so when you use it in your filter "SamAccountName -eq '$username'", the result is the stringification of each object:
$username = [pscustomobject]#{
samAccountName = 'someuser'
}
"SamAccountName -eq '$username'" # => SamAccountName -eq '#{samAccountName=someuser}'
What you want to do instead is get the value of each object, a string or array of strings in this case:
$username = [pscustomobject]#{
samAccountName = 'someuser'
} | Select-Object -ExpandProperty samAccountName
"SamAccountName -eq '$username'" # => SamAccountName -eq 'someuser'
You can also use member-access enumeration instead of Select-Object, so in your code it would be:
$userToFind = (Get-ADGroupMember -Identity "AD group" | Sort-Object samAccountName).SamAccountName
As for the current code, you could make an improvement by only searching for users member of the group which's logonHours attribute is populated. That way, there is no need to check if the user has the attribute set and no need to check if the user was found or not.
# get the DN of the group
$group = (Get-ADGroup -Identity "AD group").DistinguishedName
$param = #{
LDAPFilter = "(&(memberof=$group)(logonhours=*))"
Properties = "logonHours"
}
# enumerate all users members of this group which's attribute `LogonHours` is set
foreach($user in Get-ADUser #param) {
$loginAllowed = $false
foreach($byte in $user.LogonHours) {
if ($byte -ne 0) {
$loginAllowed = $true
break
}
}
if (-not $loginAllowed) {
"User {0} - Login disabled" -f $user.SamAccountName
}
}

Adding the next letter to user name if samaccountname is found

I am automating the new user account process from Cherwell to AD, I am stuck with duplicate samaccountname. I have successfully gotten accounts to create if the samaccountname does not already exist. I have a check in place that for now just writes the output to say whether the name is found or not.
Tinker Bell is my test user as she is my daughters favourite character.
$FirstName = “tinker”
$SurName = “bell”
for ($i = 1; $i -le $SurName.Length; ++$i) {
$Account = $null;
$Identity = $FirstName + $SurName.Substring(0,$i)
$Account = Get-ADUser -Filter {sAMAccountName -eq $Identity}
if ($Account -eq $null) {"User does not exist in AD"}
Else {"User found in AD"}
write-Output $Identity
}
I have this in place which does what it should do, I am just not sure how to stop it when the next samaccountname is created.
This is what I get as output
User found in AD
tinkerb
User does not exist in AD
tinkerbe
User does not exist in AD
tinkerbel
User does not exist in AD
tinkerbell
So it does the first part correctly, finds the account and then moves on to the next one, but then it just loops and I am not sure how to drop it out to have a variable called $newSam created when in this example 'tinkerbe' is determined.
S.
Turn your code into a simple function that returns the first available user name:
function New-Username
{
param(
[string]$FirstName,
[string]$SurName
)
for ($i = 1; $i -le $SurName.Length; ++$i) {
$Account = $null;
$Identity = $FirstName + $SurName.Substring(0,$i)
$Account = Get-ADUser -Filter {sAMAccountName -eq $Identity}
if ($Account -eq $null) {
return $Identity
}
else {
Write-Warning "User found in AD: $Identity"
}
}
throw "Unable to generate distinct username"
}
and the use it like:
$NewUsername = New-Username -FirstName tinker -SurName bell
New-ADUser -SAMAccountName $NewUserName

PowerShell script doesnt return data the first run through

I have created a PowerShell script to find the computer name from the values in the description. We put the users name in the description and computer name is an asset tag number. If you continue and put the name in a second time it works. If you look for another user you have to do it twice also.
Here is my script:
Import-Module ActiveDirectory
do {
$a = Read-Host "Enter first or last name of user"
$b = "*$a*"
# Validates if the command returns data
$searcher = $(try {
Get-ADComputer -Filter {Description -like $b} -Properties
Name,Description | Select Name,Description
} catch {
$null
})
if ($searcher -ne $null) {
Get-ADComputer -Filter {Description -like $b } -Properties Name,Description |
Select Name,Description
} else {
Write-Host Could not find: $a -ForegroundColor "yellow"
}
# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost") {
Write-Host "Press any key to continue..."
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}
$again = Read-Host 'Would you like to search again? (Y/N)'
} until ($again -eq 'No' -or $again -eq 'n')
Here is a working example using your script with a few changes.
The big change that allowed it to work was piping to Out-Host from using Get-ADComputer
# installs AD module
Import-Module ActiveDirectory
Do {
$a = Read-Host "Enter first or last name of user"
$b = "*$a*"
try {
Get-ADComputer -Filter {Description -like $b} -Properties Name, Description -ErrorVariable MyError |
Select-Object Name,Description |
Out-Host
if ($MyError){
write-host Could not find: $a -foregroundcolor Yellow
}
}
catch {
write-host Could not find: $a -foregroundcolor Red
}
$again = Read-host 'Would you like to search again? (Y/N)'
}
Until (
$again -eq 'No' -or $again -eq 'n'
)

Get-ADUser Check for conflicting proxyAddresses

Currently I have a script that creates user accounts.
Note: Not all users have the same UPN (UserPrincipalName)
User accounts are in the following format: <firstinit><lastname>.
If this conflicts, the format will be changed to: <firstinit><middleinit><lastname>
Recently I have ran into an issue where the user's proxyAddress is conflicting with existing users. This is a problem because AD will not catch this.
Issue:
Checking every AD-User's proxy address is very time consuming if not included in the filter. However, when including proxyAddresses in the filter the results are inconsistent. I am assuming this is because the proxyAddresses attribute is an array.
Inconsistent:
Import-Module ActiveDirectory
$FirstLast = "jrider#ChuckNorrisKills.com"
$conflictCheck = Get-ADUser -Properties mail, proxyAddresses -Filter "mail -eq '$FirstLast' -or UserPrincipalName -eq '$FirstLast' -or proxyAddresses -eq `"smtp:'$FirstLast'`"" | measure
if($conflictCheck.Count -gt 0)
{
Write-Host "New user conflicts with existing user" -ForegroundColor Red
}
I have come up with a solution that will resolve me issue. Unfortunately this is very slow (expected):
Import-Module ActiveDirectory
function Test-NewADUser
{
Param(
[Parameter(Mandatory=$true)][string]$firstname,
[Parameter(Mandatory=$true)][string]$lastname,
[Parameter(Mandatory=$false)][string]$middle
)
[bool]$proxExsists = $false
$domain = '#chuckNorrisKills.com'
$FirstLast = $firstname.Substring(0,1)+$lastname+$domain
Get-ADUser -Filter * -Properties proxyAddresses | foreach {
#xpand the proxy address and iterate through it
foreach($address in $_.proxyAddresses)
{
#As you can see this goes through every user
Write-Host "Address: " $address -ForegroundColor Yellow
if($address -eq "smtp:$FirstLast")
{
Write-Host "Found Conflict" -ForegroundColor Red
$proxExsists = $true
}
}
}
}
Test-NewADUser -firstname jack -lastname Rider
Question(s):
Is there a way to expand proxyAddresses and check for conflicts in the -Filter?
If not, should I bother with Jobs, or an alternate way of checking for conflicts?
Thank you in advance for any help
You don't need to expand it, as the proxyAddress filter should be reliable.
So, this should be very straightforward:
function Validate-proxyAddress($email)
{
if (Get-ADUser -Filter "proxyAddresses -eq 'smtp:$email'")
{
return $true
}
elseif (Get-ADUser -Filter "mail -eq '$email'")
{
return $true
}
elseif (Get-ADUser -Filter "UserPrincipalName -eq '$email'")
{
return $true
}
return $false
}
or you can join it all in one like your code, hasn't tested it, so if you get false, the user not exist, should be ok to continue...
Also, you can use -like instead of -eq if you need (in cases where missing the smtp prefix somehow):
"proxyAddresses -like '*$email*'"

How to have powershell go through a list of users and if they exists move them to ou mentioned if not display the username with failed.

Okay so i have the following code. It works but it returns failed for each user. The first 2 users are supposed to fail but the last one should be success but it should only show all the failed attempts and then place it in a text file. This is what I have so far besides the output to a text file.
Import-Module ActiveDirectory
#$sam = read-host "Enter username"
#$user = Get-ADUser -filter {SamAccountName -eq $sam}
$user = #("user2","user3","olduser2")
foreach($sam in $user){
if(Get-Aduser $sam){
$Name = (Get-ADUser $sam -Properties cn).name
$path = "OU=Term,OU=test,DC=patel,DC=COM"
Get-ADUser $Name | Move-ADObject -TargetPath $path
}
if(!$sam){
Write-Host "$sam failed"
}
It would return
user2 failed
with a an error message because it cant be found
user3 failed
with a an error message because it cant be found
olduser2 failed
without error message.
The iterator variable ($sam) in the ForEach goes out of scope when the ForEach loop exits. At that point, $sam -eq $null is true (equivalent to !$sam), and therefore you will get the failure message. Try
$user = #("user2","user3","olduser2")
foreach($sam in $user){
if(Get-Aduser $sam){
$Name = (Get-ADUser $sam -Properties cn).name
$path = "OU=Term,OU=test,DC=patel,DC=COM"
Get-ADUser $Name | Move-ADObject -TargetPath $path
} else {
Write-Host "$sam failed"
}
}
and see if that gives you the results you want - and if you can understand why it does. There are other improvements you can make in the script, as well - but you should get it working first, then think about optimization.
As Jeff Zeitlin mentioned in the comments of the answer. This would be better to do error checking in a try catch loop.
$user = #("user2","user3","olduser2")
foreach($sam in $user) {
try {
$path = "OU=Term,OU=test,DC=patel,DC=COM"
Get-ADUser $sam -ErrorAction Stop | Move-ADObject -TargetPath $path -ErrorAction Stop
}
catch {
Write-Host "$sam failed"
}
}