Powershell script to bulk extend AD account expiration dates from CSV

Solution for Powershell script to bulk extend AD account expiration dates from CSV
is Given Below:

I’m looking for help creating a script that requests the user input a date, then imports AD usernames from a CSV file that is in the same folder as the script then extends the expiration dates of all account usernames listed in the CSV, to the date entered, then reports success and/or failure.

So far the script below does most of this. I adapted it from a script Tom helped me with here – Powershell – Rolling script to change AD user expiration dates.

I’ve been trying to add additional functionality to the script below. I’m trying to get it so that the script will continue past blank and bad entries on the CSV and, more importantly, display these bad entries on the screen, so the user can see which entries failed.

The script will continue past bad entries if the try{ and }catch{ is removed and $ErrorActionPreference="SilentlyContinue" is put at the top of the script, but the script still stops at blank entries and doesn’t inform the user of failed entries.

Ideally I would like the script to say something like “failed to modify account for user_a” when it runs across an entry that it doesn’t find in AD; like a typo or an account that’s been deleted.

I had the idea that perhaps I could run the contents of the CSV through AD first with something like:

(Get-ADUser -Identity $_.user).DnsHostName
If ($null -eq $_.user)

and then have the script push all the ones that weren’t $null through the rest of the script and then spit out the ones ones that were not found in AD on the screen as failed, with the exceptions of the cells in the CSV that were blank, which could probably be done by running an initial $null check on the CSV contents.

I cannot figure out how to implement this though. I feel like it is possible. I’ve tried many iterations of this script, without success.

I was hoping that someone fancies the challenge.

$ErrorActionPreference="Continue"
write-host "Bulk AD Account Expiration Date Changer" 
Write-Host ""
Write-Host "Please add the usernames requiring account expiration date extensions to the bulk_accounts_extension.csv file"
Write-Host "and ensure that the bulk_accounts_extension.csv is in the same location as this script."
Write-Host ""

    $AD_User_date = new-object datetime
    do{
    $Entered_Date = Read-Host "Enter a new expiry date, in the format DD/MM/YYYY"
    $result = [datetime]::tryparseexact($Entered_Date,'dd/MM/yyyy',[cultureinfo]("en-GB"), [globalization.datetimestyles]("None"),[ref]$AD_User_date)
    if(!$result){write-host 'Invalid date entered!'}
    }until($result)
    try{

    Import-Csv ".bulk_accounts_extension.csv" | foreach-object {Set-ADAccountExpiration -Identity ($_.users = $_.users.Trim()) -DateTime $AD_User_date.AddHours(24)}

    Write-Host "New expiration date for all users is the end of $(($AD_User_date).toString('dd/MM/yyyy'))"
    }catch{
    Write-Host "Unable to set account expiry: $_"

    }

pause 

Thanks in advance.

Not much changed, just a few improvements and what you asked:

Ideally I would like the script to say something like failed to modify account for user_a when it runs across an entry that it doesn’t find in AD; like a typo or an account that’s been deleted.

$ErrorActionPreference="Stop" # 'Continue' => NO BAD :(
$csvPath = "$PSScriptRootbulk_accounts_extension.csv"
$status = [System.Collections.Generic.List[pscustomobject]]::new()

if(-not(Test-Path $csvPath))
{
    Write-Warning "bulk_accounts_extension.csv could not be found on $PSScriptRoot"
    Read-Host
    break
}

# Set minimum entered date here, must be above 30 days???
$dateMin = [datetime]::Now.AddDays(30)

@'
Bulk AD Account Expiration Date Changer
---------------------------------------------------------------------

Please add the usernames requiring account expiration date extensions
to the bulk_accounts_extension.csv file and ensure that the
bulk_accounts_extension.csv is in the same location as this script.

---------------------------------------------------------------------
'@ | Write-Host -ForegroundColor Green

while($true)
{
    try
    {
        # Loop until a valid Date is entered and that Date is above $dateMin

        $Entered_Date = [datetime]::ParseExact(
            (Read-Host "Enter a new expiry date, in the format DD/MM/YYYY"),
            'dd/MM/yyyy',
            [System.Globalization.CultureInfo]::new('en-GB')
        )

        if($Entered_Date -lt $dateMin)
        {
            throw
        }

        break
    }
    catch
    {
        Write-Warning "Invalid date entered! Format must be DD/MM/YYYY and higher than $dateMin"
    }
}

$users = Import-Csv $csvPath

foreach($user in $users.users)
{
    try
    {
        $user = $user.Trim()
        Set-ADAccountExpiration -Identity $user -DateTime $Entered_Date.AddHours(24)

        $status.Add(
        [pscustomobject]@{
            Time = [datetime]::Now
            User = $user
            Status="SUCCESS"
            Message="New Expiration Date: {0}" -f $Entered_Date.ToShortDateString()
        })
    }
    catch
    {
        # If Set-ADAccountExpiration throws, either because of lack of permissions or can't find
        # the account, this will output something like:
       
        # User          Error         
        # ----          -----         
        # ExampleUser01 User not found
        # ExampleUser02 User not found
        # ExampleUser03 Access Denied 
        # ExampleUser04 User not found
        
        $status.Add(
        [pscustomobject]@{
            Time = [datetime]::Now
            User = $user
            Status="FAILED"
            Message = $_.Exception.Message
        })
    }
}


$success, $failed = $status.Where({$_.Status -eq 'SUCCESS'},'Split')

$success | Out-String | Write-Host -ForegroundColor Green
$failed | Out-String | Write-Host -ForegroundColor Red