UblionConnect.psm1
function Install-LeftConnectRemoteControllerSQL { [CmdletBinding()] param( [Parameter(Mandatory)] [String]$Tenant, [System.Management.Automation.PSCredential] $Credential = $(Get-Credential), [Parameter(Mandatory)] [String]$SqlConnectionString ) $folder = Create-LeftConnectConfigurationFolder Log("Install LeftConnect configuration to $folder") $secureSqlConnectionString = ConvertTo-SecureString -Force -AsPlainText $SqlConnectionString | ConvertFrom-SecureString Set-Content "$folder\sql.cfg" ( #connectionString $secureSqlConnectionString + "`n" ) Install-LeftConnectionBase -Tenant $Tenant -Credential $Credential Log("LeftConnect installed") } ### Storing the remote connection credentials on a save way function Install-LeftConnectionBase{ param( [Parameter(Mandatory)] [String]$Tenant, $Credential ) $folder = Create-LeftConnectConfigurationFolder $secureStringText = $Credential.Password | ConvertFrom-SecureString Set-Content "$folder\baseConnection.cfg" ( #password $secureStringText + "`n" + #username $Credential.UserName + "`n" + #Tenant $Tenant + "`n" ) } Function Log { param( [Parameter(Mandatory=$true)][String]$msg ) $folder = Get-LeftConnectConfigurationFolder $logFile = Join-Path $folder "logfile.txt" $data = (Get-date).ToString() + " " + $msg Add-Content $logFile $data } function Get-LeftConnectLog { param( [switch]$watching ) $folder = Get-LeftConnectConfigurationFolder $logFile = Join-Path $folder "logfile.txt" if ($watching) { Get-Content $logFile -Wait } else { get-content $logFile } } function Create-LeftConnectConfigurationFolder{ $folder = $env:LOCALAPPDATA + "\LeftConnect" if( -not (Test-path $folder)){ Install-AppData -DirectoryToCreate $folder -ErrorAction Ignore } $folder } function Get-LeftConnectConfigurationFolder{ $folder = $env:LOCALAPPDATA + "\LeftConnect" $folder } function Get-LeftConnectSqlConfiguration { $folder = Get-LeftConnectConfigurationFolder $data = (Get-Content "$folder\sql.cfg").split("`n") $secureString = $data[0] | ConvertTo-SecureString $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString) $UnsecureConnectionString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) @{ connectionString = $UnsecureConnectionString } } function Get-LeftConnectBaseConfiguration { $folder = Get-LeftConnectConfigurationFolder $data = (Get-Content "$folder\baseConnection.cfg").split("`n") $secureString = $data[0] | ConvertTo-SecureString $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString) $UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) @{ user = $data[1] pass = $UnsecurePassword tenant = $data[2] } } function Get-UblionFile { [CmdletBinding()] param($id, $filename, $path) #get the pdf as a file base 64 stream $apiGetPDF = "api/services/app/Document/GetDocumentPDF?id="+$id $result = Get-LeftConnectResult -request $apiGetPDF $b64 = $result.result.fileBase64 # construct the filename for the pdf $filename = $path+"\"+$filename $fileOutputPdf = $filename+".pdf" # convert the file from base 64 to byte and write it to disk $bytes = [Convert]::FromBase64String($b64) [IO.File]::WriteAllBytes($fileOutputPdf, $bytes) $apiGetUbl = "api/services/app/Ubl/DownloadUbl" $body = "{""id"": ""$id""}" $result = Get-LeftConnectResult -request $apiGetUbl -body $body $fileOutputUbl = $filename+".xml" $downloadFile = "/File/DownloadTempFile?FileType="+$result.result.fileType+"&FileToken="+$result.result.fileToken+"&FileName="+$result.result.filename log -msg ("Download file to $fileOutputPdf") Invoke-RestMethod ((Get-LeftConnectUrlHost)+$downloadFile) -OutFile $fileOutputUbl } <# .SYNOPSIS Get modified documents .DESCRIPTION A document is deleiered when it is changing the state and it's not send before with GetModifiedDocuments, It's a infinitely loop .EXAMPLE An example .NOTES General notes #> function Get-LeftConnectResultFiles { [CmdletBinding()] param() $DocumentType = @{Outgoing = 25} $ublionConfiguration = Get-UblionConfiguration While ($true) { if( -not (Test-path $resultFolder)) { mkdir $resultFolder } try { # Check for incoming documents $apiGetModifiedFiles = "api/services/app/Document/GetModifiedDocuments" $result = Get-LeftConnectResult -request $apiGetModifiedFiles if ($result.result.GetType().Name -eq "Object[]") { #foreach available document foreach ($document in $result.result){ Write-Host $document.type.id $DocumentType.Outgoing if ($document.type.id -eq $DocumentType.Outgoing) { $outbresultFolderound = Join-Path $ublionConfiguration.baseFolder $ublionConfiguration.incomingFolderOrders } else { $outbresultFolderound = Join-Path $ublionConfiguration.baseFolder $ublionConfiguration.incomingFolderInvoices } Get-UblionFile -id ($document.id) -filename ($document.name) -path ($outbresultFolderound) } } } catch { write-host error $_ } Start-Sleep -Seconds 10 } } <# .SYNOPSIS Get the tenant url .DESCRIPTION Returns the url host .EXAMPLE An example .NOTES General notes #># function Get-LeftConnectUrlHost { $tenant = (Get-LeftConnectBaseConfiguration).tenant if (-not $tenant.Contains("-connectapi")) { $tenant = $tenant -replace ".apprx.eu", "-connectapi.apprx.eu" } "https://$tenant/" } <# .SYNOPSIS Create bearer token .DESCRIPTION Create a bearer token based on the login credentials .EXAMPLE An example .NOTES General notes #># function Get-LeftConnectToken{ $token = $global:token if (-not [string]::IsNullOrEmpty($token)){ $token } else { write-host -backgroundcolor green new token required $data = Get-LeftConnectBaseConfiguration $loginBody = "{""userNameOrEmailAddress"": """+$data.user+""",""password"": """+$data.pass+"""}" $connectionApiString = "api/TokenAuth/Authenticate" $authorizationUrl = (Get-LeftConnectUrlHost) + $connectionApiString Log("Trying to connect to $authorizationUrl") $login = Invoke-RestMethod $authorizationUrl -Method Post -Body $loginBody -ContentType "application/json" $global:token = $login.result.accessToken $global:token } } function Get-LeftConnectResult{ [CmdletBinding()] param($request, $body) $token = Get-LeftConnectToken $headers = @{"Authorization"="Bearer "+$token;} $requestUrl = (Get-LeftConnectUrlHost) + $request Log("Requesting url: $requestUrl") try { if ($body) { $answer = Invoke-RestMethod $requestUrl -Method Post -Body $body -ContentType "application/json; charset=utf-8" -Headers $headers } else { $answer = Invoke-RestMethod $requestUrl -Headers $headers } } catch { write-host "Token not accepected. Try renewing" if ($global:token) { Remove-Variable token -Scope Global } $token = Get-LeftConnectToken $headers = @{"Authorization"="Bearer "+$token;} if ($body) { $answer = Invoke-RestMethod $requestUrl -Method Post -Body $body -ContentType "application/json; charset=utf-8" -Headers $headers } else { $answer = Invoke-RestMethod $requestUrl -Headers $headers } } return $answer } function Start-Ublion { [CmdletBinding()] Param ( [Parameter()] [String]$WatchFolder, [Parameter()] [String]$DestinationFolder ) # start watcher for outgoing invoices $ublionConfiguration = Get-UblionConfiguration $filter = '*.JSON' $outbound = Join-Path $ublionConfiguration.baseFolder $ublionConfiguration.outgoingFolderInvoices $resultFolder = Join-Path $ublionConfiguration.baseFolder $ublionConfiguration.outgoingFolderInvoices $fsw = New-Object IO.FileSystemWatcher $outbound, $filter -Property @{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'} $action = { try{ import-module UblionConnect $ublionConfiguration = Get-UblionConfiguration $copyFolder1 = ($ublionConfiguration.copyLocation1) $copyFolder2 = ($ublionConfiguration.copyLocation2) Start-Sleep -Seconds 1 $documentCreateApi = "api/services/app/Document/Create" $path = $Event.SourceEventArgs.FullPath $FileName = $Event.SourceEventArgs.Name $hashfunction = '[IO.File]::ReadAllLines($path)' $base64 = (Invoke-Expression $hashfunction).Replace("""","\""") $sendFile = "{""name"": """+$FileName.Replace(".JSON","")+""",""type"": ""invoiceJson"", ""description"": ""PSUpload"",""file"": """+$base64+"""}" write-host ("Sending: " + "{""name"": """+$FileName.Replace(".JSON","")+""",""type"": ""invoiceJson"", ""file"": fileContent}") Get-LeftConnectResult -request $documentCreateApi -body $sendFile if (-not ([string]::IsNullOrEmpty($copyFolder1))) { $newPath = Join-Path $copyFolder1 "invoice" $newPath = Join-Path $newPath $Event.SourceEventArgs.Name $newPath = Join-Path $newPath (Get-Date).ToString("yyyyMMddHHmmss") write-host Copy file to $newPath mkdir $newPath Copy-Item $Event.SourceEventArgs.FullPath $newPath write-host Copy file to $newPath } else { Write-host "copyFolder1 1 not known $copyFolder1" } if (-not ([string]::IsNullOrEmpty($copyFolder2))) { mkdir $copyFolder2 Copy-Item $Event.SourceEventArgs.FullPath $copyFolder2 write-host Copy file to $copyFolder2 } else { Write-host "copyFolder2 1 not known $copyFolder2" } Remove-Item $Event.SourceEventArgs.FullPath } catch { write-host -ForegroundColor Red $_.Exception.Message write-host -ForegroundColor Red $_.Exception.ItemName write-host "ERROR in file "$Event.SourceEventArgs.FullPath -ForegroundColor Red } } $backupscript = Register-ObjectEvent -EventName "Created" -InputObject $fsw -Action $action -MessageData $resultFolder Write-Host "WatchFolder: `"Invoice To Ublion $($baseFolder)`" DestinationFolder: `"$($resultFolder)`" started. Job is in: $backupscript" -ForegroundColor Green #start watcher for incoming invoices if (-not [string]::IsNullOrEmpty($ublionConfiguration.outgoingFolderOrders)){ $filter = '*.JSON' $outbound = Join-Path $ublionConfiguration.baseFolder $ublionConfiguration.outgoingFolderOrders $resultFolder = Join-Path $ublionConfiguration.baseFolder $ublionConfiguration.outgoingFolderOrders $fsw = New-Object IO.FileSystemWatcher $outbound, $filter -Property @{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'} $action = { try{ import-module UblionConnect $ublionConfiguration = Get-UblionConfiguration $copyFolder1 = ($ublionConfiguration.copyLocation1) $copyFolder2 = ($ublionConfiguration.copyLocation2) Start-Sleep -Seconds 1 $documentCreateApi = "api/services/app/Document/Create" $path = $Event.SourceEventArgs.FullPath $FileName = $Event.SourceEventArgs.Name $hashfunction = '[IO.File]::ReadAllLines($path)' $base64 = (Invoke-Expression $hashfunction).Replace("""","\""") $sendFile = "{""name"": """+$FileName.Replace(".JSON","")+""",""type"": ""order"", ""file"": """+$base64+"""}" write-host ("Sending: " + "{""name"": """+$FileName.Replace(".JSON","")+""",""type"": ""order"", ""file"": fileContent}") Get-LeftConnectResult -request $documentCreateApi -body $sendFile if (-not ([string]::IsNullOrEmpty($copyFolder1))) { $newPath = Join-Path $copyFolder1 "order" $newPath = Join-Path $newPath $Event.SourceEventArgs.Name $newPath = Join-Path $newPath (Get-Date).ToString("yyyyMMddHHmmss") write-host Copy file to $newPath mkdir $newPath Copy-Item $Event.SourceEventArgs.FullPath $newPath } else { Write-host "copyFolder1 1 not known $copyFolder1" } if (-not ([string]::IsNullOrEmpty($copyFolder2))) { mkdir $copyFolder2 Copy-Item $Event.SourceEventArgs.FullPath $copyFolder2 write-host Copy file to $copyFolder2 } else { Write-host "copyFolder2 1 not known $copyFolder2" } Remove-Item $Event.SourceEventArgs.FullPath } catch { write-host -ForegroundColor Red $_.Exception.Message write-host -ForegroundColor Red $_.Exception.ItemName write-host "ERROR in file "$Event.SourceEventArgs.FullPath -ForegroundColor Red } } $backupscript = Register-ObjectEvent -EventName "Created" -InputObject $fsw -Action $action -MessageData $resultFolder Write-Host "WatchFolder: `"Order to Ublion$($baseFolder)`" DestinationFolder: `"$($resultFolder)`" started. Job is in: $backupscript" -ForegroundColor Green } Log("Start ublion watchers") Get-LeftConnectResultFiles Log("Start downloading items") } function Install-Ublion{ [CmdletBinding()] param( [Parameter(Mandatory)] [String]$Tenant, # The base folder [Parameter(Mandatory)] [String]$BaseFolder, [System.Management.Automation.PSCredential] $Credential = $(Get-Credential), # The incoming folder for invoices from ublion [String]$IncomingFolder = "FromUblion", # The folder which contains the document to upload [String]$OutgoingFolder = "ToUblion", #the order folder [String]$OrderPrefix = "Order", #the order folder [String]$InvoicePrefix = "Invoice", ##the template folder [String]$TemplateFolder = "Templates", ##CopyLocation [String]$CopyLocation1 = "", ##CopyLocation [String]$CopyLocation2 = "" ) Install-LeftConnectionBase -Tenant $Tenant -Credential $Credential $folder = Get-LeftConnectConfigurationFolder Install-AppData -DirectoryToCreate ($BaseFolder + "\" + $InvoicePrefix + $OutgoingFolder) Install-AppData -DirectoryToCreate ($BaseFolder + "\"+ $InvoicePrefix + $IncomingFolder) Install-AppData -DirectoryToCreate ($BaseFolder + "\"+ $InvoicePrefix + $TemplateFolder) if (-not [string]::IsNullOrEmpty($OrderPrefix)) { $OutgoingFolderOrder = $OrderPrefix + $OutgoingFolder $IncomingFolderOrder = $OrderPrefix + $IncomingFolder Install-AppData -DirectoryToCreate ($BaseFolder + "\" + $OutgoingFolderOrder) Install-AppData -DirectoryToCreate ($BaseFolder + "\"+ $IncomingFolderOrder) } Set-Content "$folder\ublion.cfg" ( #Root folder for data $BaseFolder + "`n" + # Outgoing folder invoices $InvoicePrefix + $OutgoingFolder + "`n" + # Incoming folder invoices $InvoicePrefix + $IncomingFolder + "`n" + # Outgoing folder orders $OutgoingFolderOrder + "`n" + # Incoming folder orders $IncomingFolderOrder + "`n" + # Incoming template folder $InvoicePrefix + $TemplateFolder + "`n"+ # Copy Location 1 $CopyLocation1 + "`n" + # Copy Location 2 $CopyLocation2 + "`n" ) Log("Install ublion configuration to $folder") } function Remove-LeftConnectInstallation { $folder = Get-LeftConnectConfigurationFolder if (Test-Path -LiteralPath $folder) { Remove-Item $folder "LeftConnect configuration removed" } } function Get-UblionConfiguration { $folder = Get-LeftConnectConfigurationFolder $data = (Get-Content "$folder\ublion.cfg").split("`n") @{ baseFolder = $data[0] outgoingFolderInvoices = $data[1] incomingFolderInvoices = $data[2] outgoingFolderOrders = $data[3] incomingFolderOrders = $data[4] incomingFolderTemplate = $data[5] copyLocation1 = $data[6] copyLocation2 = $data[7] } } function Get-LeftConnectSqlConfiguration { $folder = Get-LeftConnectConfigurationFolder $data = (Get-Content "$folder\sql.cfg").split("`n") $secureString = $data[0] | ConvertTo-SecureString $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString) $UnsecureConnectionString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) @{ connectionString = $UnsecureConnectionString } } <# .SYNOPSIS Create an folder .DESCRIPTION Long description .PARAMETER DirectoryToCreate Parameter description .EXAMPLE An example .NOTES General notes #># function Install-AppData{ [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [String] $DirectoryToCreate) if (-not (Test-Path -LiteralPath $DirectoryToCreate)) { try { New-Item -Path $DirectoryToCreate -ItemType Directory -ErrorAction Stop | Out-Null #-Force } catch { Write-Error -Message "Unable to create directory '$DirectoryToCreate'. Error was: $_" -ErrorAction Stop } "Successfully saved credentials" $DirectoryToCreate } else { "Directory already existed" $DirectoryToCreate } } <# .SYNOPSIS Upload invoice templates .DESCRIPTION Upload invoice template for footer, main and header. The template is a html file with complete styling. .EXAMPLE An example .NOTES General notes #># function Start-UploadTemplate{ [CmdletBinding()] param() $ublionConfiguration = Get-UblionConfiguration $filter = '*.JSO' $templateFolder = Join-Path $ublionConfiguration.baseFolder $ublionConfiguration.incomingFolderTemplate $filter = '*.html' $fsw = New-Object IO.FileSystemWatcher $templateFolder, $filter -Property @{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'} $action = { write-host Start uploading $Event.SourceEventArgs.FullPath try{ import-module UblionConnect Start-Sleep -Seconds 2 $documentCreateApi = "api/services/app/EmailTemplate/CreateOrEdit" $path = $Event.SourceEventArgs.FullPath $FileName = $Event.SourceEventArgs.Name $hashfunction = '[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([IO.File]::ReadAllLines($path)))' $base64 = (Invoke-Expression $hashfunction).Replace("""","\""") $sendFile = "{""name"": """+$FileName.replace(".html","")+""",""type"": ""INVOICE"",""description"": ""PSUpload"", ""template"": """+$base64+"""}" write-host request file Get-LeftConnectResult -request $documentCreateApi -body $sendFile } catch { write-host -ForegroundColor Red $_.Exception.Message write-host -ForegroundColor Red $_.Exception.ItemName write-host "ERROR in file "$Event.SourceEventArgs.FullPath -ForegroundColor Red } } $backupscript = Register-ObjectEvent -EventName "Changed" -InputObject $fsw -Action $action -MessageData $resultFolder Write-Host "Template watcher is started. WatchFolder: `"$($templateFolder)`" Job is in: $ backupscript" -ForegroundColor Green } function Get-LeftConnectRemoteJob{ $deliverResult = "api/services/app/RemoteControl/SetDeliveries" $getTask= "api/services/app/RemoteControl/GetAssignment" #Get the job from the server $job = (Get-LeftConnectResult -request $getTask).result if (-not $job){ return } Log("Receive the following job: $job") if ($job.Action -eq "SQL") { $result = Start-LeftConnectJobSql -taskInformation $job $result | Add-Member -MemberType NoteProperty -Name "Id" -value $job.Id -Force -ErrorAction SilentlyContinue } elseif ($job.Action -eq "FILE") { $result = Start-LeftConnectJobFile -taskInformation $job $result | Add-Member -MemberType NoteProperty -Name "Id" -value $job.Id -Force -ErrorAction SilentlyContinue } elseif ($job.Action -eq "SELFUPDATE") { $result = = [PSCustomObject]@{ success = $false message = Start-LeftConnectSelfUpdate } }else { $result = [PSCustomObject]@{ success = $false message = ("There is no handler known for given action: " + $job.Action) } } # Send result to server $result | Add-Member -MemberType NoteProperty -Name "CreationDate" -value (Get-Date).ToString("o") Get-LeftConnectResult -request $deliverResult -body ($result | ConvertTo-Json) } function Start-LeftConnectSqlQuery{ param ($query) $fakeStart = Start-LeftConnectJobSql -taskInformation @{ Action = "SQL" Command = $query } $fakeStart } function Start-LeftConnectRemoteListener{ param($interval = 10) while ($true) { Get-LeftConnectRemoteJob Start-Sleep -Seconds $interval } } function Start-LeftConnectJobSql { param ($taskInformation) if (-not $taskInformation.command) { return @{ message= "No command found" sucess= $false } } $sqlConfiguration = Get-LeftConnectSqlConfiguration $sqlConn = New-Object System.Data.SqlClient.SqlConnection $sqlConn.ConnectionString = $sqlConfiguration.connectionString $ErrorActionPreference = "Stop" try { $sqlConn.Open() $sqlcmd = $sqlConn.CreateCommand() $sqlcmd.Connection = $sqlConn $query = $taskInformation.Command $sqlcmd.CommandText = $query $sqlresult = $sqlcmd.ExecuteReader() $table = new-object System.Data.DataTable $table.Load($sqlresult) $returnValue = @{ data= $table | select $table.Columns.ColumnName success= $true } } catch { Log("Connection issue with getting the data. $_") $returnValue = @{ message= $Error[0].Exception success= $false } } $ErrorActionPreference = "Continue" $sqlConn.Close() return $returnValue } function Start-LeftConnectJobFile{ param ($taskInformation) } function Get-LeftConnectReleaseNotes { $logData = @( "24/10/2020: Updating template service", "20/08/2020: Fix logic to route order to outgoing folder", "22/07/2020: Add order ", "15/07/2020: Added order functionality, remove connection code, make order folder ", "24/02/2022: Create Remote Controller" ) Write-host $logData } function Start-LeftConnectSelfUpdate{ param() Update-Module ublionconnect Import-Module ublionconnect } Export-ModuleMember -function Install-Ublion Export-ModuleMember -function Install-LeftConnectRemoteControllerSQL Export-ModuleMember -function Get-UblionFile Export-ModuleMember -function Get-LeftConnectResultFiles Export-ModuleMember -function Get-LeftConnectUrlHost Export-ModuleMember -function Get-LeftConnectToken Export-ModuleMember -function Get-LeftConnectResult Export-ModuleMember -function Start-Ublion Export-ModuleMember -function Start-UploadTemplate Export-ModuleMember -function Get-LeftConnectReleaseNotes Export-ModuleMember -function Remove-LeftConnectInstallation Export-ModuleMember -function Get-LeftConnectLog Export-ModuleMember -function Start-LeftConnectJobSql Export-ModuleMember -function Get-LeftConnectRemoteJob Export-ModuleMember -function Start-LeftConnectSqlQuery Export-ModuleMember -function Start-LeftConnectSelfUpdate Export-ModuleMember -function Start-LeftConnectRemoteListener #Export-ModuleMember -function Get-LeftConnectSqlConfiguration #Export-ModuleMember -function Get-UblionConfiguration |