Closed anycard94 closed 1 month ago
Hi @anycard94 - thanks for the report.
This is the first time hearing of the value -62135578800
in relation to the expiry date for a user account in CyberArk.
To remove an expiry date one would usually provide a UnixTime value of 0 (or a datetime of 1/1/1970).
If you create a local PAS User using New-PASUser without specifying expiry date it creates user and has that has the value -62135578800.
So, without any changes to Format-PASUserObject.ps1 it sends expiry date as pipeline input to ConvertTo-UnixTime.ps1. Which, then just returns value as $null but with error described.
It appears the -62135578800 value comes back from API when sending $null as expiry date??
Tying to specify with -expirydate of 0 gives an error -> Invoke-PASRestMethod : [500] Value was either too large or too small for an Int32.
Results down below:
PS C:\temp> New-PASUser -UserName pas.test -userType epvuser -authenticationMethod AuthTypeRADIUS -ExpiryDate 0
Invoke-PASRestMethod : [500] Value was either too large or too small for an Int32.
At line:474 char:14
+ ... $result = Invoke-PASRestMethod -Uri $URI -Method POST -Body $Body
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: ({"ErrorCode":"C...for an Int32."}:ErrorRecord) [Invoke-PASRestMethod], Ex
ception
+ FullyQualifiedErrorId : **CAWS00001E,Invoke-PASRestMethod**
PS C:\temp> New-PASUser -UserName pas.test -userType epvuser -authenticationMethod AuthTypeRADIUS
ID UserName Source UserType Suspended enableUser ExpiryDate Location
-- -------- ------ -------- --------- ---------- ---------- --------
201 pas.test CyberArk EPVUser False True \
PS C:\temp> get-pasuser -id 201 | select *
enableUser : True
changePassOnNextLogon : True
expiryDate : -62135578800
suspended : False
lastSuccessfulLoginDate : 1715173918
unAuthorizedInterfaces : {}
authenticationMethod : {AuthTypeRadius}
passwordNeverExpires : False
distinguishedName :
description :
businessAddress : @{workStreet=; workCity=; workState=; workZip=; workCountry=}
internet : @{homePage=; homeEmail=; businessEmail=; otherEmail=}
phones : @{homeNumber=; businessNumber=; cellularNumber=; faxNumber=; pagerNumber=}
personalDetails : @{street=; city=; state=; zip=; country=; title=; organization=; department=; profession=;
firstName=; middleName=; lastName=}
id : 201
username : pas.test
source : CyberArk
userType : EPVUser
componentUser : False
groupsMembership : {}
vaultAuthorization : {}
location : \
its not something we see when creating a user nor are able to replicate
Expiry date is not set on creation when no value is specified.
ExpiryDate
parameter of New-PASUser
accepts a DateTime object type, and sets the expected value when specified
Providing a DateTime of 1/1/1970 clears the expirydate when specified for Set-PASUser
psPAS converts the date to unix time and provides a value of 0 in the request
I must have something different going on with date time on my end. If I set expiry date to 1/1/1970 it returns value 18000 instead of 0.
Creating user with expire date of tomorrow
Changing expiry date to 1/1/1970
The time I'm getting is UTC - I wrote a function long ago to convert:
Maybe something to do with timezone offset then - presume you are UTC-5?
Potentially due to the ToUniversalTime()
conversion here....
https://github.com/pspete/psPAS/blob/b8fba0fbf3c1acab2e958f275f39a73647a4b129/psPAS/Private/ConvertTo-UnixTime.ps1#L37
but - removing this would impact times used/returned by other functions in the module.
does (Get-Date 1/1/1970).AddHours(-5)
land you at 0 (when updating expirydate)?
I am UTC -5 and came across the following KB "PVWA - Requests REST API converts the request's timeframe value to LocalTime instead of UTC time". This article is marked as a known issue and no workaround.
If I do .AddHours(-5) it does get the expiry date to be 0 and no error. I could use this as workaround when I update other properties on the PAS User accounts.
But then when I get the user the value is converted back to -62135578800.
@pspete - I tried some modifications but probably not in the preferred way. It appears to give the correct results. Please feel free to copy / modify as needed.
Created a new file ConvertTo-UtcTime.ps1
Function ConvertTo-UtcTime {
[CmdletBinding()]
[OutputType('System.Integer')]
Param(
[Parameter(
Mandatory = $true,
ValueFromPipeline = $true
)]
[int64]$time
)
begin {
$currentCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
}
process {
[System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US'
#Convert EPOCH Time from local
$UTCTime = (Get-Date 01/01/1970)+([System.TimeSpan]::fromseconds($time))
$strCurrentTimeZone = (Get-WmiObject win32_timezone).StandardName
$TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone)
$UTCTimeFinal = [System.TimeZoneInfo]::ConvertTimeFromUtc($UTCTime, $TZ)
#If the time is before 1/1/1970 set final UTC to 1/1/1970
if($UTCTimeFinal -le (Get-Date 1/1/1970)){
$UTCTimeFinal = (Get-Date 1/1/1970)
}
# Do UTC Offset
$UtcOffSetHours = $TZ.BaseUtcOffset.Hours
$UtcOffSetMinutes = $TZ.BaseUtcOffset.Minutes
# Are we currently in Daylight Savings??
If($tz.IsDaylightSavingTime((get-date))){$UtcOffSetHours += 1}
# Return Value
Return $UTCTimeFinal.AddHours($UtcOffSetHours).AddMinutes($UtcOffSetMinutes)
}
end {
[System.Threading.Thread]::CurrentThread.CurrentCulture = $currentCulture
}
}
Modified Format-PASUserObject.ps1 - I added the switch as my expire date of -62135578800 comes back as Int64 type instead of DateTime.
switch ($UserProperties.keys) {
'ExpiryDate' {
#Include date string in required format
switch ($UserProperties['ExpiryDate'].GetType().Name){
'DateTime'{$UserObject['ExpiryDate'] = $UserProperties['ExpiryDate'] | ConvertTo-UnixTime}
'Int64' {$UserObject['ExpiryDate'] = ($UserProperties['ExpiryDate'] | ConvertTo-UtcTime) | ConvertTo-UnixTime}
}
}
Modified ConvertTo-UnixTime.ps1 to modify date on offset.
process {
[System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US'
$strCurrentTimeZone = (Get-WmiObject win32_timezone).StandardName
$TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone)
# Do UTC Offset
$UtcOffSetHours = $TZ.BaseUtcOffset.Hours
$UtcOffSetMinutes = $TZ.BaseUtcOffset.Minutes
# Are we currently in Daylight Savings??
If($tz.IsDaylightSavingTime((get-date))){$UtcOffSetHours += 1}
# Return Value
$Date = $Date.AddHours($UtcOffSetHours).AddMinutes($UtcOffSetMinutes)
$UnixTime = [math]::Round($(Get-Date $Date.ToUniversalTime() -UFormat %s))
If ($Milliseconds) {
$UnixTime = $UnixTime * 1000
}
$UnixTime
Results
Before ConvertTo-UnixTime.ps1 fix for TZ
PS C:\> date
Tuesday, May 14, 2024 2:45:24 PM
PS C:\> Set-PASUser -id 144 -username cybtest -ExpiryDate (get-date).addhours(4)
ID UserName Source UserType Suspended enableUser ExpiryDate Location
-- -------- ------ -------- --------- ---------- ---------- --------
144 CybTest CyberArk EPVUser False True 5/14/2024 10:45:29 PM \
PS C:\> date
Tuesday, May 14, 2024 2:46:17 PM
After modifications ConvertTo-UnixTime.ps1 for TZ
PS C:\> date
Tuesday, May 14, 2024 3:10:08 PM
PS C:\> Set-PASUser -id 144 -username cybtest -description 'test' -ExpiryDate (Get-Date).AddHours(4)
ID UserName Source UserType Suspended enableUser ExpiryDate Location
-- -------- ------ -------- --------- ---------- ---------- --------
144 CybTest CyberArk EPVUser False True 5/14/2024 7:10:11 PM \
PS C:\> date
Tuesday, May 14, 2024 3:10:14 PM
Nice - will take a look next time I have some dev time 👍
Should now be resolved in 6.4.85. Set-PASUser now does not attempt to convert in-situ property values. Unix epoch time is also not converted to universal time if specified as an expirydate.
Set-PASUser in psPAS 6.4.80 using CyberArk PAM 12.6 on accounts without expiryDate shows the following error:
This error doesn't prevent the PAS user changes but is thrown for parameter changed using Set-PASUser.
Environment:
The accounts in question all have expiryDate of -62135578800 which is a nullable date. If we set an account with a valid expiration date it does not throw this error.
I was able to modify Format-PASUserObject.ps1 to do a check of negative numbers to remove this error:
This removes the error and updates the account. However not sure if this is a proper fix but should be a good starting point.
I tried other fixes such as changing from piping to ConvertTo-UnixTime to specifying ConvertTo-UnixTime -Date UserProperties['ExpiryDate'] but that throws an error about DateTime.MinValue.Ticks / Max.Ticks and doesn't update PAS user.