#PowerShell, #PowerWiseScripting, #ProjectWise, PWPS_DAB

HowTo: Get LastLogin for ALL PW Users

Obtaining Last Login Information for ProjectWise Users with PowerShell

When managing users in ProjectWise, administrators often need to audit user activity, including tracking their last login dates. This script demonstrates how to retrieve and organize this information using PowerShell and custom embedded C# code for enhanced functionality.

All of the ProjectWise related cmdlets are available using the PWPS_DAB module. At the time of this post, I am using version 24.01. Take a look at the help for each of the cmdlets to become familiar with their functionality, available parameters, etc.

  • Get-PWUsersByMatch
  • Get-PWUserLists

C# Functions Used

  • aaApi_SelectUserWithLoginDataBuffer
  • aaApi_DmsDataBufferGetCount
  • aaApi_DmsDataBufferGetNumericProperty
  • aaApi_DmsDataBufferGetStringProperty
  • aaApi_DmsDataBufferFree

What the Script Does

This script retrieves the last login information for all ProjectWise users and stores it in a structured format for further analysis. Here’s how it works:

  1. Embedding C# Code: The script utilizes a C# class to interface with the dmscli.dll library, which allows access to the aaApi_SelectUserWithLoginDataBuffer function. This function retrieves a data buffer containing login information for users.
  2. Creating a DataTable: A DataTable named PWUsers is initialized to store user data, including:
    • User ID
    • User Name
    • Email
    • Last Login Date
  3. Fetching Login Data: The script calls the embedded C# function to fetch the user login data buffer, iterates through the buffer, and extracts:
    • User ID
    • Last login timestamp

    The results are stored in a SortedList for efficient lookups.

  4. Retrieving All Users: The script retrieves all ProjectWise users using the Get-PWUsersByMatch cmdlet (a custom or ProjectWise-specific cmdlet).
  5. Populating the DataTable: Each user’s details are added to the DataTable, including the last login timestamp fetched earlier.
  6. Error Handling: The script employs robust error handling to manage potential issues, such as failure to retrieve the login buffer.

Important Note

Not all users will have a last login date returned. This may occur if a user has never logged in to ProjectWise or if the last login information is unavailable for other reasons. It’s crucial to account for this when analyzing or reporting on the results.

Step-by-Step Breakdown

1. Embedding the C# Code

The script uses Add-Type to embed a C# class, defining the aaApi_SelectUserWithLoginDataBuffer function from the dmscli.dll library. This allows PowerShell to call the function directly.

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;

public class DMSFunctions
{
    [DllImport("dmscli.dll", CharSet = CharSet.Unicode)]
    public static extern IntPtr aaApi_SelectUserWithLoginDataBuffer();
}
"@

2. Creating the DataTable

A DataTable is created to store the final results. Columns are defined for UserID, UserName, Email, and LastLogin.

$dtPWUsers = [Data.Datatable]::new("PWUsers")
$dtPWUsers.Columns.AddRange(@(
    'UserID'
    'UserName'
    'Email'
    'LastLogin'
))

3. Retrieving Login Data

The script calls the aaApi_SelectUserWithLoginDataBuffer function to retrieve a buffer of user login data. This data is then processed to extract the UserID and LastLogin information into a SortedList.

if($IptrBuffer = [DMSFunctions]::aaApi_SelectUserWithLoginDataBuffer()){
    $iBufferCount = [PWWrapper]::aaApi_DmsDataBufferGetCount($IptrBuffer)
    for([int] $x = 0; $x -lt $iBufferCount; $x++){
        $iID_Temp = [PWWrapper]::aaApi_DmsDataBufferGetNumericProperty($IptrBuffer, 1, $x)
        $sLastLogin = [PWWrapper]::aaApi_DmsDataBufferGetStringProperty($IptrBuffer, 10, $x)
        $slUserLogin.Add($iID_Temp, $sLastLogin)
    }
    [PWWrapper]::aaApi_DmsDataBufferFree($IptrBuffer)
} else {
    Write-Warning -Message "Failed to get user buffer."
}

4. Fetching All Users

All ProjectWise users are retrieved using the Get-PWUsersByMatch cmdlet. This data includes basic user details such as UserID, UserName, and Email.


# Get ALL ProjectWise users
$pwUsers = Get-PWUsersByMatch

5. Populating the DataTable

The script iterates through each user and retrieves their corresponding last login timestamp from the SortedList. These details are added to the DataTable.

foreach($pwUser in $pwUsers){
    $dr = $dtPWUsers.NewRow()
    $dr.UserID    = $pwUser.UserID
    $dr.UserName  = $pwUser.UserName
    $dr.Email     = $pwUser.Email
    $dr.LastLogin = $slUserLogin[$pwUser.UserID]
    $dtPWUsers.Rows.Add($dr)
}

6. Error Handling

The try...catch block ensures any issues are logged as warnings without stopping the script abruptly.

try{
    # Main logic...
} catch {
    Write-Warning -Message $_
}

Experiment with it and have fun.

Download full script here. GetPWUserLastLoginForAllUsers

Hopefully, you find this useful. Please let me know if you have any questions or comments.  If you like this post, please click the Like button at the bottom of the page.

7 thoughts on “HowTo: Get LastLogin for ALL PW Users”

  1. I tried and I cannot get the LastLogin?

    CreationDate     : 2/9/2024 1:08:19 PMLastLogin        : Description      : Randy Heat Test UserDisabled         : FalseIsDisabled       : FalseEmail            : rheat@xxxxxx.comID               : 9UserID           : 9IsConnected      : FalseName             : Randy HeatUserName         : Randy HeatSecProvider      : Type             : FederatedIdentityIdentity         : IdentityProvider : 

    Any thoughts into what I am doing wrong?

    gheatwole@bcceng.com

    Like

  2. I tried both methods and I cannot get the LastLogin?

    CreationDate : 2/9/2024 1:08:19 PM
    LastLogin :
    Description : Randy Heat Test User
    Disabled : False
    IsDisabled : False
    Email : rheat@xxxxxx.com
    ID : 9
    UserID : 9
    IsConnected : False
    Name : Randy Heat
    UserName : Randy Heat
    SecProvider :
    Type : FederatedIdentity
    Identity :
    IdentityProvider :

    Any thoughts into what I am doing wrong?
    (I am a PWAdmin)
    Thanks Gheatwole@bcceng.com

    Like

      1. CODE:

        <#==========================================+
        | Sign in to the ProjectWise Datasource. |
        +==========================================#>
        # ProjectWise Datasource parameter was specified during PowerShell Script execution.
        If ($parProjectWiseDatasource)
        {
        # Set Datasource.
        $Datasource = $parProjectWiseDatasource

                                     # Sign in to the Datasource using CONNECTION Client credentials.
                                     $Result = New-PWLogin -DatasourceName $Datasource -BentleyIMS -NonAdminLogin
                     }
        

        ProjectWise Datasource parameter was not specified during PowerShell Script execution.

        Else
        {
        # Prompt to select and to sign in to a Datasource.
        $Result = New-PWLogin -BentleyIMS -NonAdminLogin

                                     # Get Current Datasource.
                                     $Datasource = Get-PWCurrentDatasource
        
                                     # Set Datasource.
                                     $Datasource = $Datasource.TrimStart($Datasource.Substring(0,$Datasource.LastIndexOf(':')+1))
                     }
        

        ProjectWise Datasource sign in attempt was successful.

        If ($Result -eq $True)
        {
        # Write message to Console.
        Write-Host “Status: Successfully signed in to the ‘$($Datasource)’ Datasource.”

                                     # Write message to Console.
                                     Write-Host ""
                     }
        

        ProjectWise Datasource sign in attempt was unsuccessful.

        Else
        {
        # Write warning to Console.
        Write-Warning “Unable to sign in to a Datasource. Check your CONNECTION Client Sign-in status. Exiting PowerShell Script.”

                                     # Write message to Console.
                                     Write-Host ""
        
                                     # Exit PowerShell Script.
                                     Exit
                     }
        

        Embed the C# code in PowerShell using Add-Type

        # The following is used to obtain the last login information for all users.
        # A last login date may not be returned for all users.
        Add-Type -TypeDefinition @”
        using System;
        using System.Runtime.InteropServices;

        public class DMSFunctions
        {
        [DllImport(“dmscli.dll”, CharSet = CharSet.Unicode)]
        public static extern IntPtr aaApi_SelectUserWithLoginDataBuffer();
        }

        “@

        try{

         #region CREATE DATATABLES
        
         $dtPWUsers = [Data.Datatable]::new("PWUsers")
         $dtPWUsers.Columns.AddRange(@(
             'UserID'
             'UserName'
             'Email'
             'LastLogin'
         ))
        
         #endregion CREATE DATATABLES
        
         #region GET USERS WITH LOGIN DATA
        
         $slUserLogin =  New-Object 'System.Collections.Generic.SortedList[System.Int32, System.String]'
        
         [string] $sLastLogin = [string]::Empty
         if($IptrBuffer = [DMSFunctions]::aaApi_SelectUserWithLoginDataBuffer()){
             $iBufferCount = [PWWrapper]::aaApi_DmsDataBufferGetCount($IptrBuffer)
        
             Write-Host "$iBufferCount rows returned in buffer." -ForegroundColor Cyan
        
             for([int] $x = 0; $x -lt $iBufferCount; $x++){ # break
                 $iID_Temp = [PWWrapper]::aaApi_DmsDataBufferGetNumericProperty($IptrBuffer, 1, $x)
                 $sLastLogin = [PWWrapper]::aaApi_DmsDataBufferGetStringProperty($IptrBuffer, 10, $x);
                 $slUserLogin.Add($iID_Temp, $sLastLogin)
             }
        
             #region FREE DATA BUFFER
        
             if($IptrBuffer){
                 [PWWrapper]::aaApi_DmsDataBufferFree($IptrBuffer)
             }
        
             #endregion FREE DATA BUFFER
        
         } else {
             Write-Warning -Message "Failed to get user buffer."
         }
        
         #endregion GET USERS WITH LOGIN DATA
        
         #region POPULATE DATATABLES
        
         # Get ALL ProjectWise Users.
         $pwUsers = Get-PWUsersByMatch
         Write-Host "$($pwUsers.Count) rows returned in buffer." -ForegroundColor Cyan
        
         # The following is a counter for the progress bar.
         $Counter = 1
         $itemCount = $pwUsers.Count
         # Loop through each PWUser.
         foreach($pwUser in $pwUsers){ # break
        
             #region PROGRESS SECTION
        
             $Progress = @{
                 Activity = "'$($pwUser.Name)'"
                 Status = "Processing $Counter of $ItemCount"
                 PercentComplete = $([math]::Round($(($Counter / $ItemCount) * 100 ), 2))
             }
             Write-Progress @Progress -Id 1
        
             # Increment counter.
             $Counter++
        
             #endregion PROGRESS SECTION
        
             $dr = $dtPWUsers.NewRow()
             $dr.UserID    = $pwUser.UserID
             $dr.UserName  = $pwUser.UserName
             $dr.Email     = $pwUser.Email
        
             #region GET LAST LOGIN
        
             $dr.LastLogin = $slUserLogin[$pwUser.UserID]
        
             #endregion GET LAST LOGIN
        
             $dtPWUsers.Rows.Add($dr)
         }
        
         #endregion POPULATE DATATABLES
        

        } catch {
        Write-Warning -Message $_
        }

        RESULTS:
        [cid:image001.png@01DB91D1.8ED5F2E0]

        Thanks greatly for the help!

        Gene​​​​
        Heatwole
        Highway BIM/CADD Manager
        [cid:image002.jpg@01DB91D1.8ED5F2E0]
        160 N. Westmonte Drive, Suite 2000, Altamonte Springs, FL 32714
        t. 407.951.6444
        |
        m. 702.349.7188 702.349.7188
        |
        http://www.bcceng.comhttps://www.bcceng.com/
        [cid:image003.jpg@01DB91D1.8ED5F2E0]https://www.linkedin.com/company/bccengineering
        [cid:image004.jpg@01DB91D1.8ED5F2E0]https://www.instagram.com/bcceng
        [cid:image005.jpg@01DB91D1.8ED5F2E0]https://www.facebook.com/bcceng
        [cid:image006.jpg@01DB91D1.8ED5F2E0]https://twitter.com/BccEngineering
        [cid:image007.jpg@01DB91D1.8ED5F2E0]https://www.youtube.com/channel/UCG1RnU5gtZUAtjzMYHjzjQA

        Like

      2. Unfortunately, I cannot see the error if that is one of the images sent. Images don’t come through.
        If a user has never logged in, you will not get a value. Not sure if that is the issue.

        Like

      3. I log in as an admin.
        (that is the part I added to the top of the script)

        This is the results:
        PS C:\WINDOWS\system32> $dtPWUsers

        UserID UserName Email LastLogin

        Like

      4. Brian,
        I am able to get LastLogin using this: 😊

        Get-PWUserByLastLogin -DaysAgo 30
        OR
        Get-PWUserByLastLogin

        Thanks for the guidance and all of the help you provide everyone!
        Gene​​​​
        Heatwole
        Highway BIM/CADD Manager
        [cid:image001.jpg@01DB91E7.D9DE50A0]
        160 N. Westmonte Drive, Suite 2000, Altamonte Springs, FL 32714
        t. 407.951.6444
        |
        m. 702.349.7188 702.349.7188
        |
        http://www.bcceng.comhttps://www.bcceng.com/
        [cid:image002.jpg@01DB91E7.D9DE50A0]https://www.linkedin.com/company/bccengineering
        [cid:image003.jpg@01DB91E7.D9DE50A0]https://www.instagram.com/bcceng
        [cid:image004.jpg@01DB91E7.D9DE50A0]https://www.facebook.com/bcceng
        [cid:image005.jpg@01DB91E7.D9DE50A0]https://twitter.com/BccEngineering
        [cid:image006.jpg@01DB91E7.D9DE50A0]https://www.youtube.com/channel/UCG1RnU5gtZUAtjzMYHjzjQA

        Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.