Having an issue that I can’t seem to figure out. When running a dynamic form, one of my API calls is causing duplicate logging entries (which I assume means the actual API code is running 2x). However, it doesn’t happen if I call the API manually outside of PSU. I use the PSFramework module for my logging needs, and I’m 99% sure it’s not an issue with that module as it only occurs within PSU. I’ve noticed the following:
- Calling API from local PowerShell console results in singular the expected singular logging entry
Invoke-RestMethod "http://localhost/GetUserHealth?TargetUser=$TargetUser" -Method GET
- Placing the API call outside of the function at page load (with a statically assigned username) works as expected as well
- Making the API call within a New-UDDynamic command within the page causes 2x entries to be recorded. For example, this should be one API call, but it’s resulting in 2x logging:
"ComputerName","FunctionName","Level","Line","Message","Runspace","Tags","TargetObject","Timestamp","Type","Username"
"PC01","API-GetUserHealth","Verbose","7","Starting API","3436a345-4502-46c2-aa39-d1f0ff46675a","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
"PC01","API-GetUserHealth","Verbose","8","Getting AD info for [Username]","3436a345-4502-46c2-aa39-d1f0ff46675a","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
"PC01","API-GetUserHealth","Verbose","12","Successfully retrieved AD info for [Username]","3436a345-4502-46c2-aa39-d1f0ff46675a","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
"PC01","API-GetUserHealth","Verbose","16","API call complete","3436a345-4502-46c2-aa39-d1f0ff46675a","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
"PC01","API-GetUserHealth","Verbose","7","Starting API","6c02d743-be4a-4292-87dd-5446ccb819fb","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
"PC01","API-GetUserHealth","Verbose","8","Getting AD info for [Username]","6c02d743-be4a-4292-87dd-5446ccb819fb","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
"PC01","API-GetUserHealth","Verbose","12","Successfully retrieved AD info for [Username]","6c02d743-be4a-4292-87dd-5446ccb819fb","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
"PC01","API-GetUserHealth","Verbose","16","API call complete","6c02d743-be4a-4292-87dd-5446ccb819fb","",,"1/17/2022 2:29:01 PM","Verbose, Debug","AD\Svc-PSU"
Really just looking for another pair of eyes in case I’m just missing something obvious. Here is each relevant section of code.
API
Param($TargetUser)
# Setup PSFramework logging
$FilePath = 'C:\Logs\GetUserHealth.log'
Set-PSFLoggingProvider -Name LogFile -FilePath $FilePath -Enabled $true
Write-PSFMessage -Message "Starting API" -FunctionName 'API-GetUserHealth'
Write-PSFMessage -Message "Getting AD info for [$TargetUser]" -FunctionName 'API-GetUserHealth'
$Properties = 'Enabled','LockedOut','AccountExpirationDate','PasswordExpired','DistinguishedName','MemberOf'
Try{
Get-ADUser $TargetUser -Properties $Properties -ErrorAction Stop | Select-Object $Properties
Write-PSFMessage -Message "Successfully retrieved AD info for [$TargetUser]" -FunctionName 'API-GetUserHealth'
} Catch {
"Failed"
}
Write-PSFMessage -Message "API call complete" -FunctionName 'API-GetUserHealth'
Dashboard
$PageUserHealthCheck = New-UDPage -Name 'User Health Check' -Content {
<# Example purposes only. Calling function outside of New-UDDynamic returns expected singular log entries #
$TargetUser = '<SomeUsername>'
Get-UserHealth -TargetUser $TargetUser
# End of example
#>
New-UDStepper -Steps {
New-UDStep -OnLoad {
New-UDElement -tag 'div' -Content { "Enter Username" }
New-UDTextbox -Id 'txtStep1' -Value $EventData.Context.txtStep1
} -Label "AD Username"
New-UDStep -OnLoad {
$TargetUser = ($EventData.Context.txtStep1).Trim()
Get-UserHealth -TargetUser $TargetUser
} -Label "Check User Login Health"
} -OnFinish {
Invoke-UDRedirect -Url '/UserHealthCheck'
} -Orientation 'vertical'
} -Url '/UserHealthCheck'
Function Get-UserHealth {
[CmdletBinding()]
param(
$TargetUser
)
$UserObj = Invoke-RestMethod "http://localhost/GetUserHealth?TargetUser=$TargetUser" -Method GET
If($UserObj -ne 'Failed'){
$Now = Get-Date
New-UDHtml "Checking health of <i><strong>$(($TargetUser).ToUpper())</i></strong>"
New-UDDynamic -Content {
Switch ($UserObj.Enabled) {
$false {
New-UDAlert -Severity 'error' -Text 'User account is disabled'
New-UDButton -Text 'Enable Account' -OnClick {
$Result = Invoke-RestMethod -Uri 'http://localhost/UpdateUserHealth' -Method 'PUT' -Body (@{
Caller = $User
TargetUser = $TargetUser
Action = 'Enable'
} | ConvertTo-Json) -ContentType 'application/json'
If($Result -eq "Success"){
Show-UDToast -Message "Enabled [$TargetUser] account successfully"
Get-UserHealth -TargetUser $TargetUser
Sync-UDElement -Id 'DynEnabled'
} Elseif ($Result -eq "Failed") {
Show-UDToast -Message "Failed to enable [$TargetUser]" -Position center -Duration 5000
}
}
}
$true {
New-UDAlert -Severity 'success' -Text 'User account is enabled'
}
}
Switch ($UserObj.PasswordExpired){
$false {
New-UDAlert -Severity 'success' -Text "User's password is not expired"
}
$true {
New-UDAlert -Severity 'warning' -Text 'User must change password at next login'
}
}
Switch ($UserObj.LockedOut){
$false {
New-UDAlert -Severity 'success' -Text 'User account is not locked out'
}
$true {
New-UDAlert -Severity 'error' -Text 'User account is locked out'
New-UDButton -Text 'Unlock Account' -OnClick {
$Result = Invoke-RestMethod -Uri 'http://localhost/UpdateUserHealth' -Method 'PUT' -Body (@{
Caller = $User
TargetUser = $TargetUser
Action = 'Unlock'
} | ConvertTo-Json) -ContentType 'application/json'
If($Result -eq "Success"){
Show-UDToast -Message "Unlocked [$TargetUser] account successfully"
Get-UserHealth -TargetUser $TargetUser
Sync-UDElement -Id 'DynEnabled'
} Elseif ($Result -eq "Failed") {
Show-UDToast -Message "Failed to unlock [$TargetUser]" -Position center -Duration 5000
}
}
}
}
Switch ($UserObj.AccountExpirationDate){
{$null -eq $_} {
New-UDAlert -Severity 'success' -Text 'User account is not expired'
}
{$_ -gt $Now} {
New-UDAlert -Severity 'success' -Text "User account expires [$($UserObj.AccountExpirationDate)]"
}
{$_ -lt $Now -and $null -ne $_} {
New-UDAlert -Severity 'error' -Text "User account is expired. Expired $($UserObj.AccountExpirationDate)"
New-UDButton -Text 'Extend Account' -OnClick {
$Result = Invoke-RestMethod -Uri 'http://localhost/UpdateUserHealth' -Method 'PUT' -Body (@{
Caller = $User
TargetUser = $TargetUser
Action = 'Extend'
} | ConvertTo-Json) -ContentType 'application/json'
If($Result -eq "Success"){
Show-UDToast -Message "Extended [$TargetUser] account successfully"
Get-UserHealth -TargetUser $TargetUser
Sync-UDElement -Id 'DynEnabled'
} Elseif ($Result -eq "Failed") {
Show-UDToast -Message "Failed to extend [$TargetUser]" -Position center -Duration 5000
}
}
}
}
Switch -Regex ($UserObj.DistinguishedName){
{$_ -match "OU=People"} {
New-UDAlert -Severity 'success' -Text "User is properly located within 'People' OU"
}
{$_ -match 'OU=TerminatedUsers'} {
New-UDAlert -Severity 'error' -Text 'User is located under "TerminatedUsers" OU'
}
{$_ -notmatch 'OU=People'} {
New-UDAlert -Severity 'error' -Text "User account is not located within 'People' OU [$_]"
}
}
} -id 'DynEnabled'
} Else {
New-UDAlert -Severity 'error' -Content { New-UDHtml "Unable to locate <i><strong>$UserNameString</i></strong> in Active Directory" }
}
}
# Add pages to array
$Pages = [System.Collections.Generic.List[PSObject]]::new()
$Pages.Add($PageUserHealthCheck)
New-UDDashboard -Title 'Dev Dashboard' -Pages $Pages
Product: PowerShell Universal
Version: 2.6.2