HelperFunctions.psm1

function Save-ClearTextToEncryptedFile ($Password, $FileName) 
{
    #$secureStringPwd = $Password | ConvertTo-SecureString -AsPlainText -Force
    $secureStringPwd = New-Object PSCredential ("Dummy User", $Password) | Select-Object -ExpandProperty Password
    $secureStringText = $secureStringPwd | ConvertFrom-SecureString 
    Set-Content $FileName $secureStringText
}

function Save-SecureStringToEncryptedFile ($FileName, $Prompt) 
{
    if ($Prompt -eq $null) {$Prompt = "Enter Password:"}
    $secureStringPwd = Read-Host -Prompt $Prompt -AsSecureString
    $secureStringText = $secureStringPwd | ConvertFrom-SecureString 
    Set-Content $FileName $secureStringText
}

function Get-SecureStringFromEncryptedFile ($FileName)
{
    $pwdTxt = Get-Content $FileName
    $securePwd = $pwdTxt | ConvertTo-SecureString 
    Write-Output $securePwd
}

function Get-ClearTextFromEncryptedFile ($FileName)
{
    $pwdTxt = Get-Content $FileName
    $securePwd = $pwdTxt | ConvertTo-SecureString 
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePwd)
    $clearText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    Write-Output $clearText
}

function ConvertTo-DataTable {
    <#
        .SYNOPSIS
            Convert regular PowerShell objects to a DataTable object.
        .DESCRIPTION
            Convert regular PowerShell objects to a DataTable object.
        .EXAMPLE
            $myDataTable = $myObject | ConvertTo-DataTable
             
            # using the SqlServer PowerShell module to connect to SQL Server and query for and return data
            # returns data as an array of DataRow objects
            $drs=Invoke-Sqlcmd -ServerInstance "ServerName" -Database Databasename -Username UserName -Password Password -Query "SELECT * FROM [dbo].[DrawingValidation] where Owner='None' and UpToDate=1 order by stamptime desc"
 
            # use this function to Convert the DataRow array to a DataTable
            $dt=ConvertTo-DataTable $drs
 
            # use PWPS_DAB cmdlet to output the DataTable into a spreadsheet
            New-XLSXWorkbook -InputTables $dt -OutputFileName c:\temp\Output.xlsx
 
    #>

    [CmdletBinding()]
    param (
        # The object to convert to a DataTable
        [Parameter(ValueFromPipeline = $true)]
        [PSObject[]] $InputObject,

        # Override the default type.
        [Parameter()]
        [string] $DefaultType = 'System.String'
    )

    begin {
    
        # create an empty datatable
        try {
            $dataTable = New-Object -TypeName 'System.Data.DataTable'
            Write-Verbose -Message 'Empty DataTable created'
        }

        catch {
            Write-Warning -Message $_.Exception.Message
            break
        }
        
        # define a boolean to keep track of the first datarow
        $first = $true

        # define array of supported .NET types
        $types = @(
            'System.String',
            'System.Boolean',
            'System.Byte[]',
            'System.Byte',
            'System.Char',
            'System.DateTime',
            'System.Decimal',
            'System.Double',
            'System.Guid',
            'System.Int16',
            'System.Int32',
            'System.Int64',
            'System.Single',
            'System.UInt16',
            'System.UInt32',
            'System.UInt64'
        )
    }

    process {

        # iterate through each input object
        foreach ($object in $InputObject) {
            
            try {

                # create a new datarow
                $dataRow = $dataTable.NewRow()
                Write-Verbose -Message 'New DataRow created'

                # iterate through each object property
                foreach ($property in $object.PSObject.get_properties()) {

                    # check if we are dealing with the first row or not
                    if ($first) {
                    
                        # handle data types
                        if ($types -contains $property.TypeNameOfValue) {
                            $dataType = $property.TypeNameOfValue
                            Write-Verbose -Message "$($property.Name): Supported datatype <$($dataType)>"
                        }

                        else {
                            $dataType = $DefaultType
                            Write-Verbose -Message "$($property.Name): Unsupported datatype ($($property.TypeNameOfValue)), using default <$($DefaultType)>"
                        }

                        # create a new datacolumn
                        $dataColumn = New-Object 'System.Data.DataColumn' $property.Name, $dataType
                        Write-Verbose -Message 'Created new DataColumn'

                        # add column to DataTable
                        $dataTable.Columns.Add($dataColumn)
                        Write-Verbose -Message 'DataColumn added to DataTable'
                    }                  

                    # add values to column
                    if ($property.Value -ne $null) {

                        # handle data types
                        if ($types -contains $property.TypeNameOfValue) {
                            $dataType = $property.TypeNameOfValue
                            Write-Verbose -Message "$($property.Name): Supported datatype <$($dataType)>"
                        }

                        # if array or collection, add as XML
                        if (($property.Value.GetType().IsArray) -or ($property.TypeNameOfValue -like '*collection*')) {
                            $dataRow.Item($property.Name) = $property.Value | ConvertTo-Xml -As 'String' -NoTypeInformation -Depth 1
                            Write-Verbose -Message 'Value added to row as XML'
                        }

                        else{
                            $dataRow.Item($property.Name) = $property.Value -as $dataType
                            Write-Verbose -Message "Value ($($property.Value)) added to row as $($dataType)"
                        }
                    }
                }

                # add DataRow to DataTable
                $dataTable.Rows.Add($dataRow)
                Write-Verbose -Message 'DataRow added to DataTable'
            }

            catch {
                Write-Warning -Message $_.Exception.Message
            }

            $first = $false
        }
    }

    end { 
    #"properties" that aren't really columns when this is passed an array of or DataRows
    if ($dataTable.Columns.Contains("RowError")) { $dataTable.Columns.Remove("RowError")}
    if ($dataTable.Columns.Contains("RowState")) { $dataTable.Columns.Remove("RowState")}
    if ($dataTable.Columns.Contains("Table")) { $dataTable.Columns.Remove("Table")}
    if ($dataTable.Columns.Contains("ItemArray")) { $dataTable.Columns.Remove("ItemArray")}
    if ($dataTable.Columns.Contains("HasErrors")) { $dataTable.Columns.Remove("HasErrors")}
    
    Write-Output (,($dataTable)) 
    }
}

FUNCTION New-PWScanForReferences {
    <#
    .SYNOPSIS
      Runs the ScanRef.exe with provided parameter values.
    .DESCRIPTION
      The function runs a scan for reference job using the parameter values passed.
    .INPUTS
      None
    .OUTPUTS
      None
    .EXAMPLE
        There are many options and configurations for this function. Use the following as a template to setup your reference scanning.
        $NewScanRef = @{
            DataSourceName = '';
            UserName = '';
            Password = '';
            ScanMode = 'references';
            MasterDocuments = '';
            MasterFolders = '';
            Priority = '';
            Proximity = 'r:1';
            Order = 'priority;proximity';
            Applications = '';
            LogFilePath = '';
        }
        New-PWScanForReferences @NewScanRef -RecurseMasterFolders -RecursePriority -OpenLogFile -Verbose
    #>

    [CMDLETBINDING()]
    PARAM(
        [Parameter(
            Position=0, 
            Mandatory=$true, 
            HelpMessage = "ProjectWise Datasource to log into.")] 
        [string]$DataSourceName, 

        [PARAMETER(
            Position=1, 
            Mandatory=$false,
            HelpMessage = "If this option is absent, SSO login will be attempted.")] 
        [string]$UserName,
        
        [PARAMETER(
            Position=2, 
            Mandatory=$false,
            HelpMessage = "If this option is absent, password prompt will be shown. When username is supplied.")] 
        [string]$Password, 
        
        [PARAMETER(
            Position=3, 
            Mandatory=$false,
            HelpMessage = "Specifies a list of scanning modes to use. If not included, defaults to references;linksets.")] 
        [ValidateSet("references", "linksets", "references;linksets")]
        [string[]]$ScanMode = "references;linksets",
        
        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "A list of documents to scan for references and/or linksets.")] 
        [string[]]$MasterDocuments,
        
        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "A list of folders to scan for references and/or linksets.")] 
        [string[]]$MasterFolders,
        
        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "When included, all sub-folders within the supplied folder path will be scanned for references and/or linksets.")] 
        [switch]$RecurseMasterFolders,
        
        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "Enable priority search for reference files in the specified folders. To recurse folders, prefix with r:.")] 
        [string[]]$Priority, 
        
        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "When included, all sub-folders within the supplied folder path will be scanned for references and/or linksets.")] 
        [switch]$RecursePriority,   
        
        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "Enable proximity search for reference files <number> levels above the master file's folder. To recurse folders, prefix with r:.")] 
        [ValidateSet("0", "1", "r:1")]
        [string[]]$Proximity,    
        
        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "Order in which the proximity and priority searches will be done (if both are enabled). If this parameter is not specified, proximity search will take precedence.")] 
        [ValidateSet("proximity", "priority", "priority;proximity", "proximity;priority")]
        [string]$Order = "proximity;priority",
        
        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "Application filter for the lists of documents to scan - only the documents with matching application names will be scanned.")] 
        [string[]]$Applications,
        
        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "Log file path.")] 
        [string]$LogFilePath,
        
        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "When included, the log file will be open when process is complete.")] 
        [switch]$OpenLogFile
    )
    
    BEGIN {

    }

    PROCESS {
        # Variables:
        #$DataSourceName = $datasource #'BMF_W2K12R2:PSTraining'
        #$UserName = $user #'admin'
        ## Entered in clear text. This can be modified to encrypt or lookup password.
        #$Password = $password # 'admin'

        # The following retrieves the ProjectWise path information from the registry.
        #$execpath = # Get-PWScanRefsPath
        #
        ## Stops processing if the path information is not found.
        #if([string]::IsNullOrEmpty($ExecPath)) {
        # Write-Error("Failed to get ProjectWise path.")
        # return;
        #}

        # Verify the scanrefs executable exists on the current machine. Stops processing if not found.
        $executable = "C:\Program Files (x86)\Bentley\ProjectWise\bin\scanrefs.exe" #(Get-PWScanRefsPath) + "\scanrefs.exe"
        if(-not (Test-Path -Path $executable))
        {
            Write-Error ("Could not find scanrefs.exe file.")
            return;
        } else {
            "Found scanrefs.exe file."
        } # end if/else

        # -d datasource Datasource name to connect to.
        $dsname = $DataSourceName
        Write-Verbose "DSName = $dsname"
        # -u username Username for the datasource connection.
        # If this option is absent, SSO login will be attempted.
        $user = $UserName
        Write-Verbose "UserName = $user"
        # -p password Password of the datasource user.
        # If this option is absent, password prompt will be shown.
        $pw = $Password
        Write-Verbose "PW = $pw"

        # -mode scanmode Specifies a list of scanning modes to use.
        # If this option is absent, the tool will operate in "references;linksets" mode.
        #
        # Possible modes:
        # references - scan for references.
        # linksets - scan DGN documents for link sets.
        # urfcs - update references from current set - no attempt will be made at creating new sets,
        # only existing reference sets will be updated.
        # If this mode is enabled, priority or proximity reference search paths will be ignored.
        # This option is intended to be used after upgrading a datasource from a pre-V8.1 version.
        # Note that this mode disables other modes.
        # Modes can be combined:
        # references;linksets - scan for reference documents and for DGN link sets.
        # NOTE: Do not add spaces between mode labels. This will cause an error (Unrecognized scanning mode:)
        $scanmode1 = $ScanMode # 'references'
        Write-Verbose "ScanMode = $scanmode1"

        # -masters documentlist A list of documents to scan for references and/or linksets.
        if([string]::IsNullOrEmpty($MasterDocuments)) {
            $masterdocuments1 = -1
        } else {
            $masterdocuments1 = $MasterDocuments
            Write-Verbose "MasterDocuments = $masterdocuments1"
        } # end if/else

        # -masterfolders folderlist A list of folders to scan for references and/or linksets.
        # If RecurseMasterFolders is included, prefix master folders with r:.
        if([string]::IsNullOrEmpty($MasterFolders)) {
            $masterfolders1 = -1
        } else {
            $masterfolders1 = $MasterFolders #"r:Documents\Projects\NewFolder\BSI900 - Adelaide Tower"
            if($RecurseMasterFolders){
                $masterfolders1 = "r:$MasterFolders"
            }              
        } # end if/else
        Write-Verbose "MasterFolders = $masterfolders1"

        # -priority folderlist Enable priority search for reference files in the specified folders.
        # Prefix folder path with "r:" to recurse through the folder and sub-folders.
        # NOTE: Ensure the folder path does NOT end with a backslash "\". This will cause an error.
        # NOTE: Do not add spaces between folder paths. This will cause an error.
        if([string]::IsNullOrEmpty($Priority)) {
            $priority1 = -1
        } else {
            $priority1 = $Priority #"r:Documents\BSI900 - Adelaide Tower\05-Incoming;r:Documents\BSI900 - Adelaide Tower\03-Published"
            if($RecursePriority){
                $priority1 = "r:$Priority"
            }      
            Write-Verbose "Priority = $priority1"
        } # end if/else

        ### Proximity is required when priority is not set.
        #
        # -proximity [r:]number Enable proximity search for reference files <number> levels above the master file's folder.
        # r: switch enables recursive search (includes subfolders).
        # Examples:
        # -proximity 0 - look for references in the master's folder.
        # -proximity 1 - look for references in the parent folder of the master.
        # -proximity r:1 - look for references in the master's folder's parent folder and its subfolders.
         if([string]::IsNullOrEmpty($Proximity)) {
            if($priority1 -eq -1) {
                Write-Verbose "Setting proximity to 0 due to priority not being included."
                $proximity1 = 0
            } else {
                $proximity1 = -1
            } # end if/else
         } else {
            $proximity1 = $Proximity #"r:1"
            Write-Verbose "Proximity = $proximity1"
        } # end if/else

        # -order orderlist Order in which the proximity and priority searches will be done (if both are enabled).
        # If this parameter is not specified, proximity search will take precedence.
        # Examples:
        # -order proximity;priority - proximity first.
        # -order priority;proximity - priority first.
        # NOTE: Do not add spaces between order label. This will cause an error (Input error: Unknown search type:).
        $order1 = $Order #"priority;proximity"
        Write-Verbose "Order = $order1"

        # -af applicationlist Application filter for the lists of documents to scan - only the documents with matching application names will be scanned.
        # Example:
        # -af "MicroStation;AutoCad;Bentley Navigator"
        # NOTE: This is case sensative, so ensure the names are listed exactly as they are in ProjectWise Administrator.
        # NOTE: Do not add spaces between application names. This will cause an error (Collecting data... Failure).
        if([string]::IsNullOrEmpty($Applications)) {
            $applications1 = -1
        } else {
            $applications1 = $Applications #"AutoCAD;MicroStation"
            Write-Verbose "Applications = $applications1"
        } # end if/else

        # -l logfile Log file path.
        # If logfile path and name is not included, set default to 'c:\temp\'.
        if([string]::IsNullOrEmpty($LogFilePath)) {
            $logfilePath1 = 'c:\temp\'
        } else {
            $logfilePath1 = $LogFilePath
        } # end if/else

        # Test to determine if the log folder exists. If not, create.
        if(-not (Test-Path -Path $logfilePath1)) {
            New-Item -Path $logfilePath1 -ItemType "directory"
        }

        $logfile = "$logfilePath1\ScanRef.log"
        # -lv Use verbose logging - write more details to the log file.


        # masterfolders
        # masterdocuments
        # applications
        # priority
        # If priority is enabled, proximity is not required. If priority is not included, proximity is required.


        # Master Folders ONLY
        if(($masterfolders1 -ne -1) -and ($masterdocuments1 -eq -1)) {
            if($applications1 -eq -1) {
                # Masterfolders1, no applications, no priority, proximity
                # Masterfolders1, no applications, priority, no proximity
                # Masterfolders1, no applications, priority, proximity
                if($priority1 -eq -1) {
                    Write-Verbose "Only master folders will be scanned. Applications NOT included. Priority NOT included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } elseif ($proximity1 -eq -1) {
                    Write-Verbose "Only master folders will be scanned. Applications NOT included. Priority included. Proximity NOT included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -priority $priority1 -order $order1 -l $logfile -lv  
                } else {
                    Write-Verbose "Only master folders will be scanned. Applications NOT included. Priority included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -priority $priority1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } # end if/elseif/else

            } else {
                # Masterfolders1, applications, no priority, proximity
                # Masterfolders1, applications, priority, no proximity
                # Masterfolders1, applications, priority, proximity
                if($priority1 -eq -1) {
                    Write-Verbose "Only master folders will be scanned. Applications included. Priority NOT included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -af $applications1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } elseif ($proximity1 -eq -1) {
                    Write-Verbose "Only master folders will be scanned. Applications included. Priority included. Proximity NOT included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -af $applications1 -priority $priority1 -order $order1 -l $logfile -lv  
                } else {
                    Write-Verbose "Only master folders will be scanned. Applications included. Priority included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -af $applications1 -priority $priority1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } # end if/elseif/else
            } # end if/else
        } elseif (($masterfolders1 -eq -1) -and ($masterdocuments1 -ne -1)) {
            if($applications1 -eq -1) {
                # masterdocuments1, no applications, no priority, proximity
                # masterdocuments1, no applications, priority, no proximity
                # masterdocuments1, no applications, priority, proximity
                if($priority1 -eq -1) {
                    Write-Verbose "Only master documents will be scanned. Applications NOT included. Priority NOT included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masters $masterdocuments1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } elseif ($proximity1 -eq -1) {
                    Write-Verbose "Only master documents will be scanned. Applications NOT included. Priority included. Proximity NOT included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masters $masterdocuments1 -priority $priority1 -order $order1 -l $logfile -lv  
                } else {
                    Write-Verbose "Only master documents will be scanned. Applications NOT included. Priority included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masters $masterdocuments1 -priority $priority1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } # end if/elseif/else

            } else {
                # masterdocuments1, applications, no priority, proximity
                # masterdocuments1, applications, priority, no proximity
                # masterdocuments1, applications, priority, proximity
                if($priority1 -eq -1) {
                    Write-Verbose "Only master documents will be scanned. Applications included. Priority NOT included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masters $masterdocuments1 -af $applications1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } elseif ($proximity1 -eq -1) {
                    Write-Verbose "Only master documents will be scanned. Applications included. Priority included. Proximity NOT included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masters $masterdocuments1 -af $applications1 -priority $priority1 -order $order1 -l $logfile -lv  
                } else {
                    Write-Verbose "Only master documents will be scanned. Applications included. Priority included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masters $masterdocuments1 -af $applications1 -priority $priority1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } # end if/elseif/else
            } # end if/else
        } elseif (($masterfolders1 -ne -1) -and ($masterdocuments1 -ne -1)) {
            if($applications1 -eq -1) {
                # Masterfolders1, masterdocuments1, no applications, no priority, proximity
                # Masterfolders1, masterdocuments1, no applications, priority, no proximity
                # Masterfolders1, masterdocuments1, no applications, priority, proximity
                if($priority1 -eq -1) {
                    Write-Verbose "Both master folders and documents will be scanned. Applications NOT included. Priority NOT included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -masters $masterdocuments1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } elseif ($proximity1 -eq -1) {
                    Write-Verbose "Both master folders and documents will be scanned. Applications NOT included. Priority included. Proximity NOT included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -masters $masterdocuments1 -priority $priority1 -order $order1 -l $logfile -lv  
                } else {
                    Write-Verbose "Both master folders and documents will be scanned. Applications NOT included. Priority included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -masters $masterdocuments1 -priority $priority1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } # end if/elseif/else

            } else {
                # Masterfolders1, masterdocuments1, applications, no priority, proximity
                # Masterfolders1, masterdocuments1, applications, priority, no proximity
                # Masterfolders1, masterdocuments1, applications, priority, proximity
                if($priority1 -eq -1) {
                    Write-Verbose "Both master folders and documents will be scanned. Applications included. Priority NOT included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -masters $masterdocuments1 -af $applications1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } elseif ($proximity1 -eq -1) {
                    Write-Verbose "Both master folders and documents will be scanned. Applications included. Priority included. Proximity NOT included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -masters $masterdocuments1 -af $applications1 -priority $priority1 -order $order1 -l $logfile -lv  
                } else {
                    Write-Verbose "Both master folders and documents will be scanned. Applications included. Priority included. Proximity included."
                    & $executable -d $dsname -u $user -p $pw -mode $scanmode1 -masterfolders $masterfolders1 -masters $masterdocuments1 -af $applications1 -priority $priority1 -proximity $proximity1 -order $order1 -l $logfile -lv  
                } # end if/elseif/else
            } # end if/else
        } # end if/elseif/else
    } # end process

    END {
        # Opens log file
        if($OpenLogFile) {
            if(Test-Path $logfile) {
                explorer $logfile
            }
        } # end if
    } # end
}
#Export-ModuleMember -Function New-PWScanForReferences

FUNCTION New-PWEnumFolders {
    <#
    .SYNOPSIS
      Runs the EnumFolders.exe with provided parameter values.
    .DESCRIPTION
      The function runs the enumfolders.exe using the parameter values passed.
    .INPUTS
      None
    .OUTPUTS
      None
    .EXAMPLE
        There are many options and configurations for this function. Use the following as a template to setup your reference scanning.
        $NewEnum = @{
            DataSourceName = '';
            UserName = '';
            Password = '';
            PWFolder = '';
            OutputFilePath = '';
        }
        New-PWEnumFolders @NewEnum -IncludeDocuments -RecurseFolders-OpenLogFile -Verbose
    #>

    [CMDLETBINDING()]
    PARAM(
        [Parameter(
            Position=0, 
            Mandatory=$true, 
            HelpMessage = "ProjectWise Datasource to log into.")] 
        [string]$DataSourceName, 

        [PARAMETER(
            Position=1, 
            Mandatory=$false,
            HelpMessage = "If this option is absent, SSO login will be attempted.")] 
        [string]$UserName,
        
        [PARAMETER(
            Position=2, 
            Mandatory=$false,
            HelpMessage = "If this option is absent, password prompt will be shown. When username is supplied.")] 
        [string]$Password, 
        
        [PARAMETER(
            Position=3, 
            Mandatory=$false,
            HelpMessage = "Specifies a folder ID to enumerate. If not included, returns information for the entire datasource.")] 
        [int]$PWFolderID = 0,
        
        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "When included, all sub-folders are enumerated.")] 
        [switch]$RecurseFolders,
        
        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "Include documents within the enumeration process.")] 
        [switch]$IncludeDocuments,

        [PARAMETER(
            Mandatory=$false,
            HelpMessage = "Include deleted objects within the enumeration process.")] 
        [switch]$IncludeDeletedObjects,

        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "Uses verbose operation. (Does not appear to do anything.)")] 
        [switch]$VerboseOperation,  
              
        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "Output file path and name.")] 
        [string]$OutputFilePathName = "c:\temp\"  + (Get-Date).Year + (Get-Date).Month + (Get-Date).Day + "_enumfolders.txt",
        
        [PARAMETER( 
            Mandatory=$false,
            HelpMessage = "When included, the output file will be open when process is complete.")] 
        [switch]$OpenOutputFile
    )
    
    BEGIN {

    }

    PROCESS {
        <#
            Enumerates the objects in a folder hierarchy.
            Options:
                -d datasource hostname:datasource.
                -u user ProjectWise username (omit for domain auth).
                -p password ProjectWise password (omit for domain auth).
                -f folder # Folder to enumerate (GUID or integer folder id)
                -v Verbose operation.
                -D Include documents in object list
                -l Include deleted objects in object list
                -r Recursive enumeration operation. All subfolders are
                                    enumerated, along with any objects in those subfolders.
                -o filename Save results to the specified file.
        #>


        # Verify the enumfolders.exe executable exists on the current machine. Stops processing if not found.
        $executable = "C:\Program Files (x86)\Bentley\ProjectWise\bin\enumfolders.exe"
        if(-not (Test-Path -Path $executable))
        {
            Write-Error "Could not find enumfolders.exe file."
            return;
        } else {
            Write-Verbose "Found enumfolders.exe file." -Verbose
        } # end if/else

        # -d datasource Datasource name to connect to.
        $dsname = $DataSourceName
        Write-Verbose "DSName = $dsname"
        # -u username Username for the datasource connection.
        # If this option is absent, SSO login will be attempted.
        $user = $UserName
        Write-Verbose "UserName = $user"
        # -p password Password of the datasource user.
        # If this option is absent, password prompt will be shown.
        $pw = $Password
        Write-Verbose "PW = $pw"

        Write-Verbose "FolderID: $PWFolderID" -Verbose
        Write-Verbose "Recurse: $RecurseFolders" -Verbose
        Write-Verbose "IncludeDocuments: $IncludeDocuments" -Verbose
        Write-Verbose "IncludeDeletedObjects: $IncludeDeletedObjects" -Verbose
        Write-Verbose "VerboseOperation: $VerboseOperation" -Verbose


        # RecurseFolders
        # If RecurseFolders ONLY
        if($RecurseFolders -and (-not($IncludeDocuments)) -and (-not($IncludeDeletedObjects)) -and (-not($VerboseOperation))) {
            Write-Verbose "Recurse through all folders ONLY. Do not include documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects NOT included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -r -o $OutputFilePathName
        } elseif($RecurseFolders -and $IncludeDocuments -and (-not($IncludeDeletedObjects)) -and (-not($VerboseOperation))) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments IS included. IncludeDeletedObjects NOT included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -r -D -o $OutputFilePathName
        } elseif($RecurseFolders -and (-not($IncludeDocuments)) -and $IncludeDeletedObjects -and (-not($VerboseOperation))) {
            Write-Verbose "Recurse through all folders. Do not include documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects IS included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -r -l -o $OutputFilePathName 
        } elseif($RecurseFolders -and (-not($IncludeDocuments)) -and (-not($IncludeDeletedObjects)) -and $VerboseOperation) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects NOT included. VerboseOperation IS included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -r -v -o $OutputFilePathName 
        } elseif($RecurseFolders -and $IncludeDocuments -and $IncludeDeletedObjects -and (-not($VerboseOperation))) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments IS included. IncludeDeletedObjects IS included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -r -D -l -o $OutputFilePathName 
        } elseif($RecurseFolders -and $IncludeDocuments -and $IncludeDeletedObjects -and $VerboseOperation) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments IS included. IncludeDeletedObjects IS included. VerboseOperation IS included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -r -D -l -v -o $OutputFilePathName 
        } elseif($RecurseFolders -and (-not($IncludeDocuments)) -and $IncludeDeletedObjects -and $VerboseOperation) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects IS included. VerboseOperation IS included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -r -l -v -o $OutputFilePathName 
        }

        # Without RecurseFolders
        if((-not ($RecurseFolders)) -and (-not($IncludeDocuments)) -and (-not($IncludeDeletedObjects)) -and (-not($VerboseOperation))) {
            Write-Verbose "Current folder ONLY. Do not include documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects NOT included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -o $OutputFilePathName 
        } elseif((-not ($RecurseFolders)) -and $IncludeDocuments -and (-not($IncludeDeletedObjects)) -and (-not($VerboseOperation))) {
            Write-Verbose "Current folder and documents." -Verbose
            Write-Verbose "IncludeDocuments IS included. IncludeDeletedObjects NOT included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -D -o $OutputFilePathName 
        } elseif((-not ($RecurseFolders)) -and (-not($IncludeDocuments)) -and $IncludeDeletedObjects -and (-not($VerboseOperation))) {
            Write-Verbose "Recurse through all folders. Do not include documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects IS included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -l -o $OutputFilePathName 
        } elseif((-not ($RecurseFolders))  -and (-not($IncludeDocuments)) -and (-not($IncludeDeletedObjects)) -and $VerboseOperation) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects NOT included. VerboseOperation IS included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -v -o $OutputFilePathName 
        } elseif((-not ($RecurseFolders)) -and $IncludeDocuments -and $IncludeDeletedObjects -and (-not($VerboseOperation))) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments IS included. IncludeDeletedObjects IS included. VerboseOperation NOT included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -D -l -o $OutputFilePathName 
        } elseif((-not ($RecurseFolders)) -and $IncludeDocuments -and $IncludeDeletedObjects -and $VerboseOperation) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments IS included. IncludeDeletedObjects IS included. VerboseOperation IS included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -D -l -v -o $OutputFilePathName 
        } elseif((-not ($RecurseFolders)) -and (-not($IncludeDocuments)) -and $IncludeDeletedObjects -and $VerboseOperation) {
            Write-Verbose "Recurse through all folders and documents." -Verbose
            Write-Verbose "IncludeDocuments NOT included. IncludeDeletedObjects IS included. VerboseOperation IS included." -Verbose
            & $executable -d $dsname -u $user -p $pw -f $PWFolderID -l -v -o $OutputFilePathName 
        }
    } # end process

    END {
        # Opens log file
        if($OpenOutputFile) {
            if(Test-Path $OutputFilePathName) {
                explorer $OutputFilePathName
            }
        } # end if
    } # end
}
#Export-ModuleMember -Function New-PWEnumFolders