Powershell Datetime null value - powershell

I am developing a powershell about the AD user account expire date. But I have occurred a problem which is unable to pass the empty parameter to set the AD user account expire date to NEVER.
Please help! Thanks.
https://i.stack.imgur.com/Zt8sv.png
Here are my script below.
import-module C:\PS\color_menu.psm1
CreateMenu -Title "AD User Account Expire Tools" -MenuItems "View the User Account Expire Date","Set the User Account Expire Date","Exit" -TitleColor Red -LineColor Cyan -menuItemColor Yellow
do {
[int]$userMenuChoice = 0
while ( $userMenuChoice -lt 1 -or $userMenuChoice -gt 3) {
Write-Host "1. View the User Account Expire Date"
Write-Host "2. Set the User Account Expire Date"
Write-Host "3. Exit"
[int]$userMenuChoice = Read-Host "Please choose an option"
switch ($userMenuChoice) {
1{$useraccount = Read-Host -prompt "Please input an user account"
Get-ADUser -Identity $useraccount -Properties AccountExpirationDate | Select-Object -Property SamAccountName, Name, AccountExpirationDate
Write-Host "";
Write-Host "";
}
2{$useraccount = Read-Host -prompt "Please input an user account"
[Datetime]$expiredatetime = Read-Host -prompt "Please input the user expire date and time (DateFormat: MM/dd/yyyy)"
Set-ADAccountExpiration -Identity $useraccount -DateTime $expiredatetime
Write-Host "";
Write-Host "";
Get-ADUser -Identity $useraccount -Properties AccountExpirationDate | Select-Object -Property SamAccountName, Name, AccountExpirationDate
Write-Host "";
Write-Host "";
}
3{Write-Host "Exit";Exit
}
default {Write-Host "Incorrect input" -ForegroundColor Red
Write-Host "";
Write-Host "";
}
}
}
} while ( $userMenuChoice -ne 3 )```

Use Clear-ADAccountExpiration to set an account to never expire.
Also, you cannot directly use a [datetime] type-constrained variable with your Read-Host call, because converting the empty string ('') to [datetime] isn't supported (the error you saw).
Here's one way to solve this:
do {
# Read the input as a string first...
$expiredatetimeStr = Read-Host -prompt "Please input the user expiration date and time (DateFormat: MM/dd/yyyy) or just press Enter to make the account non-expiring"
# ... and then try to convert it to [datetime]
if ($expiredatetime = $expiredatetimeStr -as [datetime]) {
Set-ADAccountExpiration -Identity $useraccount -DateTime $expiredatetime
}
elseif ($expiredatetimeStr.Trim() -eq '') { # empty input -> no expiration
Clear-ADAccountExpiration -Identity $useraccount
}
else { # invalid input
Write-Warning 'Please enter a valid date.'
continue
}
break
} while ($true)

Related

Powershell Script prompt user for input on validate set Function Parameter

I'm guessing this is impossible, but I figured I'd ask because it would be cool if I could.
I have a function, that has a validate set (so people don't screw up the input - obviously)
However, prompts in a script don't seem to allow for this.
I've looked all over, I can't find anyone asking this question, or providing any details that would help.
function ShowStackOverFlowCommunity-ExampleOfFunction {
PARAM (
[parameter()]
[validateset(
"Don't Input Incorrect Things",
"Stop it",
"I Swear to God..."
)]
[string]
$pplbedumb
)
write-host "Hi I'm a script. TeeHee"
write-host $pplbedumb;
}
So an example script would be
$path = read-host "input path to file"
$pathdata = gc $path;
foreach ($item in $pathdata) {
get-service $item | select name, balls, etc.
if ($item.balls) {
ShowStackOverFlowCommunity-ExampleOfFunction
}
}
I'm well aware that I can do a read-host - but that allows for the possibility of input mistakes.
The only thing i can think of is to just say at the beginning of the script "HEY, RUN THIS FUNCTION FIRST OUTSIDE OF THE SCRIPT. THEN COME BACK"
But that's just...lame.
You can run your validation after the user put his input and then return a "Please try again" or "Please follow the restrictions for the input"
When I was writing a script to check the users without opening the AD console I did the below:
Import-Module ActiveDirectory
[string[]]$GetADProps=echo Created, Name, EmailAddress, Enabled, LockedOut, LastBadPasswordAttempt, PasswordExpired, AccountExpires, PasswordLastSet, LastLogonDate, Modified, LogonCount, Office, TelephoneNumber
[string[]]$FlProps=echo Created, Modified, LogonCount, Name, EmailAddress, Enabled, LockedOut, PasswordExpired, PasswordLastSet, LastLogonDate, LastBadPasswordAttempt, Office, TelephoneNumber
do{
$Username = (Read-Host -Prompt "Please Enter Username to Lookup")
Get-ADDomainController -Filter * | Select Name, IPv4Address, Site | Sort-Object Name | Out-String
$DC = (Read-Host -Prompt "Please Enter the Domain Controler name from the list")
$ADUser= Get-ADUser -Server $DC $Username -Properties $GetADProps
if ($adUser.'LockedOut' -or $ADUser.'PasswordExpired'){
$ADUser | Format-List $FlProps | Out-String | Write-Host -ForegroundColor Red
do {
do {
write-host ""
write-host "[U] - Unlock User " -NoNewline; write-host "$Username" -ForegroundColor Red
write-host "[R] - Reset Users " -NoNewline; write-host "$Username" -ForegroundColor Red -NoNewline; write-host " Password"
write-host "[C] - Check Users " -NoNewline; write-host "$Username" -ForegroundColor Red -NoNewline; write-host " Account Info"
write-host ""
write-host "[X] - Exit"
write-host ""
write-host -NoNewline "Type your choice and press Enter: "
$Choice = read-host
write-host ""
$ok = $Choice -match '^[urcx]+$'
if ( -not $ok) { write-host "Invalid selection" }
} until ( $ok )
So the user using it had to put the right input.
If you need to search a specific path you can run a test-path and if it is not right you can prompt the user again.
Also I read about the validate set on https://adamtheautomator.com/powershell-validateset/
Hope this helps you.

POWERSHELL - Display Users AD Group That Starts With a Specific Character

This is my script it can display all the AD group that is assigned to a user account. I just don't know the syntax on how to do a wildcard search.
Write-Host "'Get AD Groups"
$userName = Read-Host -Prompt "Please enter the LDAP ID"
$ADUser = Get-ADUser -Filter "SamAccountName -eq '$userName'" | Select-Object SamAccountName
if($ADUser -eq $null) {
Write-Host "$userName does not exist in AD or account is inactive" -ForegroundColor Red
Continue
} else {
$sourceUser = Get-ADUser -Identity $userName -Properties MemberOf
$sourceGroups = $sourceUser.MemberOf
Foreach($group in $sourceGroups) {
$thisgroup = $group.split(",")[0].split("=")[1]
Write-Host "$thisgroup"
}
}

Powershell input checking

I would like to check the input option is allow 1-3 number ONLY, and input other numbers and characters will show the warning message, how to do that?
Can I check the input user account [value] from AD user is whether exist or not on Powershell? If the record not exist, just return the warning message.
Please help. Thanks!
import-module ActiveDirectory
import-module C:\PS\color_menu.psm1
CreateMenu -Title "AD User Account Expire Tools" -MenuItems "View the User Account Expire Date","Set the User Account Expire Date","Exit" -TitleColor Red -LineColor Cyan -menuItemColor Yellow
do {
[int]$userMenuChoice = 0
while ( $userMenuChoice -lt 1 -or $userMenuChoice -gt 3) {
Write-Host "1. View the User Account Expire Date"
Write-Host "2. Set the User Account Expire Date"
Write-Host "3. Exit"
[int]$userMenuChoice = Read-Host "Please choose an option"
switch ($userMenuChoice) {
1{$useraccount = Read-Host -prompt "Please input an user account"
Get-ADUser -Identity $useraccount -Properties AccountExpirationDate | Select-Object -Property SamAccountName, Name, AccountExpirationDate
Write-Host "";
Write-Host "";
}
2{$useraccount = Read-Host -prompt "Please input an user account"
do {
$expiredatetimeStr = Read-Host -prompt "Please input the user expiration date and time (DateFormat: MM/dd/yyyy) or just press Enter to make the account non-expiring"
if ($expiredatetime = $expiredatetimeStr -as [datetime]) {
Set-ADAccountExpiration -Identity $useraccount -DateTime $expiredatetime
Get-ADUser -Identity $useraccount -Properties AccountExpirationDate | Select-Object -Property SamAccountName, Name, AccountExpirationDate
Write-Host "";
Write-Host "";
}
elseif ($expiredatetimeStr.Trim() -eq '') {
Clear-ADAccountExpiration -Identity $useraccount
Get-ADUser -Identity $useraccount -Properties AccountExpirationDate | Select-Object -Property SamAccountName, Name, AccountExpirationDate
Write-Host "";
Write-Host "";
}
else {
Write-Warning 'Please enter a valid date.'
continue
}
break
} while ($true)
}
3{Write-Host "Exit";Exit
}
default {Write-Host "Incorrect input" -ForegroundColor Red
Write-Host "";
Write-Host "";
}
}
}
} while ( $userMenuChoice -ne 3 )
The problem is that you are directly casting a user input to an int:
$a = [int] Read-Host
What if the return value of Read-Host isn't an int?
The solution is to just accept whatever Read-Host gives you, and check the value after, e.g:
$a = Read-Host
try {
$intVal = [Convert]::ToInt32($a, 10)
... etc ...
}
catch {
... etc ...
}

Powershell Script to check if Active Directory User Last Logon

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)

Powershell - Order of script

I am trying to input a user using a variable and check active directory to confirm the full name of the user and pause the script before running the next command.
The script is running the pause command before the get-aduser command - see below script
#Enter Username
$username = read-host "Username"
Get-ADUser -Filter "Name -eq '$username'" | Select-Object name, samaccountname
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
#Removes user from groups
Get-ADPrincipalGroupMembership -Identity $username | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $uSername -MemberOf $_ -Confirm:$false}
write-output End
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
In my experience, Get-ADUser and similar commands can take a long time to run, possible up to 20 seconds or so. Rarely, I have found that it makes the code unusable due to some commands running before or after it. If you want to test to see if this is really the case for you, add this line in between every other line in your code:
Read-Host -Prompt "Press Enter to continue"
That way, you can test whether there is a real difference between when you put that line there, and if you don't. If there is actually a difference, you may have to look into start-sleep or wait.
I would do something like this to have the user validate, cause i think that is what you are after, before continuing to revoke the users group membership
Write-Host "`nEnter the UserName: " -NoNewline -ForegroundColor Yellow
$UserName = Read-Host
$UserName = Get-ADUser -Filter "Name -eq '$UserName'" | Select-Object Name, SamAccountName
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
While ("y","yes","n","no" -notcontains $Confirmation) {
Write-Host "`nNot a valid input! Please try again ..." -ForegroundColor Red
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
}
If ($Confirmation -eq "n" -or $Confirmation -eq "no") {
Write-Host "Aborted!" -ForegroundColor Red
Break
}
# Next step here!
# Get-ADPrincipalGroupMembership -Identity $UserName | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $UserName -MemberOf $_ -Confirm:$false}
Just another piece of code, these kind of changes needs some proper logging and error handling, while my code only logs to the console it can still be useful.
It uses confirm in place of 'pause' so the user can choose to continue or stop.
### CmdletBinding
# Alows the use of -Whatif(not used), -Confirm, -Verbose and -Debug.
# Reference: https://technet.microsoft.com/en-us/library/ff677563.aspx
# https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_functions_cmdletbindingattribute
# https://blogs.technet.microsoft.com/poshchap/2014/10/24/scripting-tips-and-tricks-cmdletbinding/
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact=’High’
)]
# Script parameters.
Param(
[parameter(HelpMessage = "Command parram, not used.")]$Command = "nothing"
#Run with PowerShell Fix, reference: https://social.technet.microsoft.com/Forums/office/en-US/fe7fb473-7ed6-4397-9c95-120201c34847/problems-with-powershell-30?forum=winserverpowershell
)
#Console clean-up.
Clear-Host
# Set error action to Stop, if something happens and it isnt inside a trap (try/catch) then stop.
$ErrorActionPreference = "Stop"
# Controls the Verbose Output
$VerbosePreference = "Continue" #Optional
#Intial message for User execution, whitespace is for the progressbars.
"
Script: Remove-ADUserGroupMembership.ps1
"
Write-Verbose "Starting main loop."
While ($true){
#White space for in between questions.
Write-Host "
"
#Retrieve username from user input.
Write-Host "Provide the ADUser for ADGroup removal here:"
$Username = read-host "Username"
#Retrieve ADUser object from AD.
Write-Verbose "Querying Active Directory for user $Username"
Try {
$ADUser = Get-ADUser $Username
Write-Verbose "User Found, $($ADUser.Name) "
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
Write-Warning "Could not find user $Username in Active Directory, check spelling and try again."
Continue #this wil reset the while loop
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve user $Username from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Retrieve GroupMembership for user.
Write-Verbose "Querying Active Directory for GroupMembership of User $($ADUser.name), exluding Domain Users"
Try {
$GroupMembership = $ADUser | Get-ADPrincipalGroupMembership | where {$_.Name -notlike "Domain Users"}
Write-Verbose "Found $($GroupMembership.count) GroupMemberships for User $($ADUser.name) (Not inluding Domain Users)"
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve GroupMembership for user $($ADUser.Name) from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Remove GroupMembership for user.
if ($pscmdlet.ShouldProcess("$($ADUser.name)", "Remove-ADPrincipalGroupMembership {$($GroupMembership.count) Groups}")) {
Write-Verbose "Entering GroupMembership removal loop for user $($ADUser.name)"
Foreach ($Group in $GroupMembership) {
Try {
$ADUser | Remove-ADPrincipalGroupMembership -MemberOf $Group -WhatIf -Confirm:$true
Write-Verbose "$Group removed from from user $($ADUser.name)"
}
catch {
Write-Warning "An Error occured, could not remove group $Group from user $($ADUser.Name)"
Continue #this will skip this group.
}
}
}
else {
Write-Warning "Action Remove-ADPrincipalGroupMembership {$($GroupMembers.count) Groups} canceled for $($ADUser.name)"
}
Read-Host "Press Enter to exit."
break #exit from while loop
}