Public/ActiveDirectory.ps1
|
# Nebula.Tools: Active Directory ==================================================================================================================== function Find-ADAccountExpirations { <# .SYNOPSIS Finds Active Directory users with an account expiration date and optionally extends it. .DESCRIPTION Searches AD for enabled users with an account expiration date. You can filter by expiration date (TargetDate) and/or by email domain (FilterDomain), export results to CSV, and, if requested, extend the account expiration date. Use -WhatIf to simulate the extension without applying changes. .PARAMETER TargetDate Reference expiration date (string) in "yyyy-MM-dd" format or any DateTime-compatible format. If not specified and FilterDomain is not set, the function throws an error. .PARAMETER FilterDomain Filter on the user's email (e.g., "@contoso.com"). The filter is treated as a wildcard and special characters are escaped. .PARAMETER ExportCsv If present, exports results to a CSV file. .PARAMETER ExportPath Output folder for the CSV export. Defaults to the current location. .PARAMETER ExtendExpiration If present, extends the expiration for the matched accounts to the date provided in ExtendTo. .PARAMETER ExtendTo New expiration date (string) to apply when using -ExtendExpiration. .PARAMETER TargetServer Server/Domain Controller used for AD queries and updates. If omitted, the default domain controller is used. .EXAMPLE Find-ADAccountExpirations -TargetDate "2027-01-01" .EXAMPLE Find-ADAccountExpirations -FilterDomain "@contoso.com" -ExportCsv .EXAMPLE Find-ADAccountExpirations -TargetDate "2027-01-01" -ExtendExpiration -ExtendTo "2027-12-31" -WhatIf .NOTES Requirements: ActiveDirectory module, permissions to read/modify AD users. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( # Accept dates as strings to avoid PowerShell interpreting 2027-01-01 as arithmetic [string]$TargetDate, [string]$FilterDomain, [switch]$ExportCsv, [string]$ExportPath, [switch]$ExtendExpiration, [string]$ExtendTo, [string]$TargetServer ) try { Import-Module ActiveDirectory -ErrorAction Stop | Out-Null } catch { throw "ActiveDirectory module not available. Install RSAT or the AD module first." } $hasTargetDate = -not [string]::IsNullOrWhiteSpace($TargetDate) $hasExtendTo = -not [string]::IsNullOrWhiteSpace($ExtendTo) if ([string]::IsNullOrWhiteSpace($FilterDomain) -and -not $hasTargetDate) { throw "TargetDate is required when FilterDomain is not specified." } $TargetDateDT = $null if ($hasTargetDate) { try { $TargetDateDT = [DateTime]::Parse($TargetDate) } catch { throw "TargetDate is not a valid date: $TargetDate" } } $ExtendToDT = $null if ($ExtendExpiration) { if (-not $hasExtendTo) { throw "ExtendTo is required when using -ExtendExpiration." } try { $ExtendToDT = [DateTime]::Parse($ExtendTo) } catch { throw "ExtendTo is not a valid date: $ExtendTo" } } $csvLabel = if ($hasTargetDate) { $TargetDateDT.ToString('yyyy-MM-dd') } else { "ALL-DATES" } $exportRoot = if ($ExportPath) { $ExportPath } else { (Get-Location).Path } $csvPath = Join-Path $exportRoot "AD_Users_Expires_$csvLabel.csv" # Escape wildcard characters so the user can pass "@contoso.com" safely $emailPattern = $null if (-not [string]::IsNullOrWhiteSpace($FilterDomain)) { $emailPattern = '*' + [System.Management.Automation.WildcardPattern]::Escape($FilterDomain.Trim()) + '*' } $adParams = @{ Filter = 'Enabled -eq $true' Properties = 'accountExpires', 'mail', 'department', 'company' } if (-not [string]::IsNullOrWhiteSpace($TargetServer)) { $adParams['Server'] = $TargetServer } $results = Get-ADUser @adParams | Where-Object { $_.accountExpires -ne 0 -and $_.accountExpires -ne 9223372036854775807 -and ( -not $hasTargetDate -or ([DateTime]::FromFileTimeUtc($_.accountExpires)).Date -le $TargetDateDT.Date ) -and ( -not $emailPattern -or ( -not [string]::IsNullOrWhiteSpace($_.mail) -and $_.mail -like $emailPattern ) ) } | Select-Object ` Name, SamAccountName, DistinguishedName, @{ n = 'Email'; e = { $_.mail } }, @{ n = 'Department'; e = { $_.department } }, @{ n = 'Company'; e = { $_.company } }, @{ n = 'AccountExpirationUTC'; e = { [DateTime]::FromFileTimeUtc($_.accountExpires) } }, @{ n = 'AccountExpirationLocal'; e = { [DateTime]::FromFileTime($_.accountExpires) } } $results = @($results) if ($hasTargetDate) { $results | Select-Object Name, SamAccountName, Email, AccountExpirationUTC | Format-Table -AutoSize } else { $results | Select-Object Name, SamAccountName, Email, AccountExpirationUTC | Sort-Object AccountExpirationUTC | Format-Table -AutoSize } if ($ExportCsv) { if (-not (Test-Path -Path $exportRoot)) { throw "ExportPath does not exist: $exportRoot" } if (Test-Path $csvPath) { Write-Information "$csvPath already exists. Deleting it first ..." -InformationAction Continue Remove-Item -Path $csvPath -Force } $results | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8 Write-Information "CSV exported to $csvPath" -InformationAction Continue } if ($ExtendExpiration) { Write-Information "" -InformationAction Continue Write-Information "About to set account expiration for $($results.Count) user(s) to:" -InformationAction Continue Write-Information " $ExtendToDT" -InformationAction Continue Write-Information "Tip: use -WhatIf to preview safely." -InformationAction Continue Write-Information "" -InformationAction Continue foreach ($u in $results) { $label = "$($u.SamAccountName) ($($u.Email))" if ($PSCmdlet.ShouldProcess($label, "Set account expiration to $ExtendToDT")) { $setParams = @{ Identity = $u.SamAccountName DateTime = $ExtendToDT } if (-not [string]::IsNullOrWhiteSpace($TargetServer)) { $setParams['Server'] = $TargetServer } Set-ADAccountExpiration @setParams } } Write-Information "" -InformationAction Continue Write-Information "Done." -InformationAction Continue } } |