Im trying to create an SSH Tunnel via DVLS Powershell Module.
My toughts are to use the Get-DSEntry Function to get a Body from a working Template, editing the Contents and using New-DSEntryBase to create the new SSH Tunnel.
But im failing already on the Get-DSEntry Function, because i dont find important Informations like Host and Port:
Powershell Output of the Entry above (Template):
Where can i find and/or where do i need to set the specific Informations?
Thanks,
Andreas
Edit: I found out that i can get the needed Informations via Get-DSEntry -AsRDMConnection but im not able to update the Entry via Update-DSEntryBase nor via Invoke-WebRequest and the API Endpoint /connection/save
Im getting "ConnectionInfo was not provided":
But ConnectionInfo is set:
My Steps:
Do i eventually need to use other Object Types?
Here is my Example which fails:
$result = Get-DSEntry -EntryID $Global:Templ_SSHTunnel
if($result.isSuccess)
{
$TemplateBody = $result.Body.data
# modify body
$TemplateBody.repositoryId = $repositoryId
$TemplateBody.name = $name
$TemplateBody.group = $folder
$TemplateBody.PSObject.Properties.Remove('TemplateName')
# create entry
$NewEntry = New-DSEntryBase -jsonBody (ConvertTo-Json $TemplateBody -Depth 5)
if($NewEntry.isSuccess)
{
# object is cloned, now open it again as rdm connection to modify connection settings
$result = Get-DSEntry -EntryId $NewEntry.Body.data.id -AsRDMConnection
$ConnectionInfo = $result.Body.data.connectionInfo
$Data = Convert-XMLToPSCustomObject ([xml]$ConnectionInfo.data)
$TerminalSettings = New-Object PSObject -Property @{
HideOnConnect = $true;
Host = $ip;
HostPort = $port;
PrivateKeyPromptForPassPhrase = $false;
TCPKeepaliveInterval = 1;
}
$Data.Connection | Add-Member -NotePropertyName 'Terminal' -NotePropertyValue $TerminalSettings -Force
$VPNSettings = New-Object PSObject -Property @{
Application = 'SSHNative';
AutoClose = $false;
CloseMode = 'Manually';
EnableAutoDetectIsOnlineVPN = $false;
UseDynamicPort = $true;
}
$Data.Connection | Add-Member -NotePropertyName 'VPN' -NotePropertyValue $VPNSettings -Force
$DataXml = (Convert-PSCustomObjectToXML $Data.Connection)
$ConnectionInfo.data = Convert-XMLToSerializedString $DataXml
$NewBody = $NewEntry.Body.data
$NewBody | Add-Member -NotePropertyName 'ConnectionInfo' -NotePropertyValue $ConnectionInfo -Force
# this fails with 'ConnectionInfo was not provided.'
$res = Invoke-WebRequest -Uri "$Global:dvlsBaseUrl/api/connection/save" -Method "PUT" -Body (ConvertTo-Json $result.Body.data -Depth 5) -ContentType "application/json" -WebSession $Global:WebSession
return $res
# magic: this has a success, but no changes available in entry
$result = Update-DSEntryBase -jsonBody (ConvertTo-Json $NewEntry.Body.data -Depth 5)
if(!($result.isSuccess))
{
Write-Log -Text "Error: could not modify entry"
Write-Log -Text $result.ErrorMessage
}else
{
return $result.Body.data
}
}else
{
Write-Log -Text "Error: cannot create SSH Tunnel"
}
#>
}
else
{
Write-Log -Text "Error: cannot get SSH Tunnel Template Data"
}
Does anyone has an Input for me? Or a better Way to achieve this?
Many Thanks!
Hello Andreas,
Here are the steps in order to create a new SSH tunnel entry;
$SSHTunnelEntry = (Get-DSEntry -EntryId '142c0872-5ab6-440b-bc0e-d624bd84a771').Body.data #Newly created entry ID
$NewSSHTunnelEntry = $SSHTunnelEntry.PSObject.Copy() #Deep copy
#Those properties need to be removed in order to create a new entry
$NewSSHTunnelEntry.PSObject.Properties.Remove('id')
$NewSSHTunnelEntry.PSObject.Properties.Remove('metaInformation')
$NewSSHTunnelEntry.PSObject.Properties.Remove('resolvedInheritedPermissions')
$NewSSHTunnelEntry.PSObject.Properties.Remove('resolvedTimeBasedUsageSettings')
#Most of the properties are located in "data"
$NewSSHTunnelEntry.data.host = 'newHost'
$NewSSHTunnelEntry.data.hostPort = '4444'
$NewSSHTunnelEntry.data.username = 'newUsername'
$NewSSHTunnelEntry.name = 'newSSHTunnel'
$NewSSHTunnelEntry.userName = 'newUsername'
$NewEntryRes = New-DSEntryBase (ConvertTo-Json $NewSSHTunnelEntry)
The unsupported entry sample script was specifically made for entries which are not yet supported in Devolutions Server.
Let me know if you need any help with a specific property not saving properly and I'll send you an updated version of the script to fit your needs.
Best regards,
Alexandre Martigny
Thanks Alex
Youre Code clones successfully the Object, but the same problem as on my snipped: the Object is missing all needed Informations.
Template:
Cloned Object:
I set all the Informations like you mentioned.
I also removed the Property 'TemplateName'.
Here is my Function:
function New-DVLSDeviceFromTemplate
{
Param(
[Parameter(Mandatory=$true,Position=0)]
[guid]$sourceTemplate,
[Parameter(Mandatory=$true,Position=1)]
[string]$newName,
[Parameter(Mandatory=$true,Position=2)]
[string]$folder,
[Parameter(Mandatory=$true,Position=3)]
[ipaddress]$newHost,
[Parameter(Mandatory=$true,Position=4)]
[int]$newPort,
[Parameter(Mandatory=$true,Position=5)]
[guid]$repositoryId
)
$result = Get-DSEntry -EntryID $sourceTemplate
if($result.isSuccess)
{
# deep copy
$NewEntry = $result.Body.data.PSObject.Copy()
# those properties need to be removed in order to create a new entry
$NewEntry.PSObject.Properties.Remove('id')
$NewEntry.PSObject.Properties.Remove('metaInformation')
$NewEntry.PSObject.Properties.Remove('resolvedInheritedPermissions')
$NewEntry.PSObject.Properties.Remove('resolvedTimeBasedUsageSettings')
$NewEntry.PSObject.Properties.Remove('TemplateName')
# add new properties
$NewEntry.repositoryId = $repositoryId
$NewEntry.name = $newName
$NewEntry.group = $folder
$NewEntry.data.host = $newHost
$NewEntry.data.hostPort = $newPort
# create entry
$NewEntryRes = New-DSEntryBase -jsonBody (ConvertTo-Json $NewEntry -Depth 5)
if(!($NewEntryRes.isSuccess))
{
Write-Log -Text "Error: could not create new entry '$folder\$name'"
Write-Log -Text $NewEntryRes.ErrorMessage
}else
{
Write-Log -Text "Success: Successfully created new entry '$folder\$name'"
return $NewEntryRes.Body.data
}
}
else
{
Write-Log -Text "Error: cannot get Source Template Data"
}
return $false
}
Best Regards
Andreas
Hello Andreas,
From your first post, we thought that you were trying to create a "SSH Tunnel" entry but from the UI in those pictures, it seems like you are trying to create a "SSH VPN" entry and those 2 are different. "SSH VPN" entries are not yet supported in DVLS, but "SSH Tunnel" are. You can create a "SSH Tunnel" entry using my above script, but you will need to follow the unsupported entry sample script to create a "SSH VPN" entry.
I went ahead wrote a working sample to base your script upon;
$NewGuid = [Guid]::NewGuid()
$NewName = 'NewEntryName'
$SSH_VPN = (Get-DSEntry -EntryId 'cb1b097c-b2b7-44ba-a629-c3bef72c94ad' -AsRDMConnection).Body.data.connectionInfo
$ConnectionData = Convert-XMLToPSCustomObject ([xml]$SSH_VPN.Data)
$ConnectionMetaData = Convert-XMLToPSCustomObject ([xml]$SSH_VPN.metaDataString)
$ConnectionData.Connection.Id = $NewGuid
$ConnectionData.Connection.PSObject.Properties.Remove('CreationDateTime')
$ConnectionData.Connection.PSObject.Properties.Remove('Stamp')
$ConnectionData.Connection.Name = $NewName
$ConnectionData.Connection.Terminal.Host = 'newHost'
$ConnectionData.Connection.Terminal.HostPort = 4444
$ConnectionMetaData.ConnectionMetaDataEntity.Name = $NewName
$ConnectionDataXML = Convert-PSCustomObjectToXML $ConnectionData.Connection
$ConnectionMetaDataXML = Convert-PSCustomObjectToXML $ConnectionMetaData.ConnectionMetaDataEntity -RootName 'ConnectionMetaDataEntity'
$SSH_VPN.Id = $NewGuid
$SSH_VPN.name = $NewName
$SSH_VPN.data = $ConnectionDataXML.OuterXML
$SSH_VPN.metaDataString = $ConnectionMetaDataXML.OuterXML
$SSH_VPN.metaData.name = $NewName
$Body = ConvertTo-Json ($SSH_VPN) -Depth 15
$res = Invoke-WebRequest -Uri 'http://localhost/dps/api/connection/save' -Method 'PUT' -Body $Body -ContentType 'application/json' -WebSession $Global:WebSession
This script worked on my end. I am able to create a new "SSH VPN" with the required fields edited.
Let me know if you need any more assistance.
Best regards,
Alexandre Martigny
Hey Alex
Thanks for the Snipped.
With which Version of the PS Module do you work?
Im not able to get your Code running:
Im also wondering, where the Entry should be created since no repositoryId has been added.
Best Regards,
Andreas
Hello Andreas,
What DVLS version and DVLS PowerShell module version are you using?
I cannot reproduce your first error message with DVLS 2022.1.13 and DVLS PowerShell module version 2022.1.10.
About the second error you got, the one about the $NodePropert variable, a fix should be soon available.
Best regards,
Érica Poirier
Hi Erica
Im using DVLS 2022.1.10.0 and the second last Github Commit (22 Days ago) for Powershell Module.
Best Regards,
Andreas
Hi Andreas,
Thank you for your feedback.
As soon as the $NodePropert variable issue is fixed, I will post it here. That should help to fix the problem.
Best regards,
Érica Poirier
Hi All
I finaly tried it again after DVLS and DVLS Module Update (both latest) and after some modifications i finally got it working to get a fully functional Clone of a SSH Tunnel.
But it seems that there is still one Problem left:
If im using only a single Folder, everything is working. But if i try to create the SSH Tunnel within a Subfolder, then it fails with an "Unexptected exception":
Example:
$folder = "test\subfolder" <- is not working $folder = "test" <- is working
Here is my complete Code:
$newGuid = [Guid]::NewGuid()
$SSH_VPN = (Get-DSEntry -EntryId $sourceTemplate -AsRDMConnection).Body.data.connectionInfo
$ConnectionData = Convert-XMLToPSCustomObject ([xml]$SSH_VPN.Data)
$ConnectionMetaData = Convert-XMLToPSCustomObject ([xml]$SSH_VPN.metaDataString)
$ConnectionData.Connection | Add-Member -NotePropertyName 'Id' -NotePropertyValue $newGuid -Force
$ConnectionData.Connection | Add-Member -NotePropertyName 'Name' -NotePropertyValue $newName -Force
$ConnectionData.Connection | Add-Member -NotePropertyName 'Group' -NotePropertyValue $folder -Force
$ConnectionData.Connection.Terminal | Add-Member -NotePropertyName 'Host' -NotePropertyValue $newName -Force
$ConnectionData.Connection.Terminal | Add-Member -NotePropertyName 'HostPort' -NotePropertyValue $newPort -Force
$ConnectionData.Connection.PSObject.Properties.Remove('CreationDateTime')
$ConnectionData.Connection.PSObject.Properties.Remove('Stamp')
$ConnectionData.Connection.PSObject.Properties.Remove('SharedTemplate')
$ConnectionData.Connection.PSObject.Properties.Remove('TemplateName')
$ConnectionMetaData.ConnectionMetaDataEntity | Add-Member -NotePropertyName 'Name' -NotePropertyValue $newName -Force
$ConnectionMetaData.ConnectionMetaDataEntity | Add-Member -NotePropertyName 'Group' -NotePropertyValue $folder -Force
$ConnectionMetaData.ConnectionMetaDataEntity.PSObject.Properties.Remove('SharedTemplate')
$ConnectionDataXML = Convert-PSCustomObjectToXML $ConnectionData.Connection
$ConnectionMetaDataXML = Convert-PSCustomObjectToXML $ConnectionMetaData.ConnectionMetaDataEntity -RootName 'ConnectionMetaDataEntity'
# build splittedGroupMain array
$splittedGroupMain = @()
if([Regex]::Matches($folder, "\\").Count -gt 0)
{
$allFolders = @()
foreach($fold in $folder.Split("\"))
{
$allFolders += $fold
$splittedGroupMain += $allFolders -join "\"
}
}else
{
$splittedGroupMain += $folder
}
$SSH_VPN | Add-Member -NotePropertyName 'splittedGroupMain' -NotePropertyValue $splittedGroupMain -Force
$SSH_VPN | Add-Member -NotePropertyName 'Id' -NotePropertyValue $newGuid -Force
$SSH_VPN | Add-Member -NotePropertyName 'name' -NotePropertyValue $newName -Force
$SSH_VPN | Add-Member -NotePropertyName 'data' -NotePropertyValue $ConnectionDataXML.OuterXML -Force
$SSH_VPN | Add-Member -NotePropertyName 'metaDataString' -NotePropertyValue $ConnectionMetaDataXML.OuterXML -Force
$SSH_VPN | Add-Member -NotePropertyName 'group' -NotePropertyValue $folder -Force
$SSH_VPN | Add-Member -NotePropertyName 'groupMain' -NotePropertyValue $folder -Force
$SSH_VPN | Add-Member -NotePropertyName 'groups' -NotePropertyValue @($folder) -Force
$SSH_VPN | Add-Member -NotePropertyName 'repositoryId' -NotePropertyValue $repositoryId -Force
$SSH_VPN.metaData | Add-Member -NotePropertyName 'name' -NotePropertyValue $newName -Force
$SSH_VPN.metaData | Add-Member -NotePropertyName 'group' -NotePropertyValue $folder -Force
$SSH_VPN.metaData | Add-Member -NotePropertyName 'groupMain' -NotePropertyValue $folder -Force
$SSH_VPN.metaData | Add-Member -NotePropertyName 'groups' -NotePropertyValue @($folder) -Force
$SSH_VPN.metaData.PSObject.Properties.Remove('sharedTemplate')
$jsonBody = ConvertTo-Json ($SSH_VPN) -Depth 15
$res = Invoke-WebRequest -Uri "$Global:dvlsBaseUrl/api/connection/save" -Method "PUT" -Body $jsonBody -ContentType "application/json" -WebSession $Global:WebSession
return $res
Does anyone have an input how to solve my Problem?
Thanks,
Andreas
Edit:
Allright, i think there was a Problem with Umlaute.
In my specific case, im tried to create the Entry withing a Subfolder called "Zürich" which is causing a DecoderFallbackException:
DecoderFallbackException - Unable to translate bytes [FC] at index 68 from specified code page to Unicode.
charset=utf-8 did the Trick!
Here is a working Invoke Example:Invoke-WebRequest -Uri "$Global:dvlsBaseUrl/api/connection/save" -Method "PUT" -Body $jsonBody -ContentType "application/json; charset=utf-8" -WebSession $Global:WebSession
Best Regards
Andreas
Hi All
Im coming again because i think i found a Problem with the Function Convert-PSCustomObjectToXML
devolutions-server/Convert-PSCustomObjectToXML.ps1 at 8a04ccc4e0dafadfa66ed2454f7e3b00e4edefdc · Devolutions/devolutions-server · GitHub
It looks like that Convert-PSCustomObjectToXML doesnt returns the full PSObject as XML. It cuts all nested Objects out of Connection.
In my Case i want to create an SSH Tunnel. But it never safed me Parameters like Terminal.SSHGateways.SSHGateway... or Terminal.Portforwards.Portforward.
Original Data ($data -> formatted as json):
{
"Connection": {
"AllowPasswordVariable": "true",
"ConnectionType": "VPN",
"CreatedBy": "SuperMario",
"Terminal": {
"PortForwards": {
"PortForward": {
"DestinationPort": "3389",
"Mode": "Dynamic",
"Source": "127.0.0.1",
"SourcePort": "3389"
}
},
"SSHGateways": {
"SSHGateway": {
"Host": "1.2.3.4",
"ID": "260ecf3a-8539-4e87-bd4b-49b984a6bdf4",
"SafePassword": "hashstring",
"Username": "user"
}
},
"TCPKeepaliveInterval": "1",
"Username": "user",
"Host": "ssh-tunnel-name",
"HostPort": 22,
"HideOnConnect": true,
"Mode": "Dynamic",
"PrivateKeyPromptForPassPhrase": false,
"Source": "127.0.0.1",
"UseSSHGateway": true
},
"TerminalMac": "",
"VPN": {
"Application": "SSHNative",
"AutoClose": "false",
"CloseMode": "Manually",
"EnableAutoDetectIsOnlineVPN": "False",
"UseDynamicPort": "true"
},
"VPNMac": "",
"Id": "6f0dace2-2f13-424e-a6da-2fe4d105752e",
"Name": "ssh-tunnel-name",
"Group": "folder1\folder2"
},
"xml": "version=\"1.0\""
}
When im using Convert-PSCustomObjectToXML then it looks like this ((Convert-PSCustomObjectToXML $data).OuterXML):
<?xml version="1.0"?> <Connection> <Connection> <AllowPasswordVariable>true</AllowPasswordVariable> <ConnectionType>VPN</ConnectionType> <CreatedBy>SuperMario</CreatedBy> <DestinationPort>3389</DestinationPort> <Mode>Dynamic</Mode> <Source>127.0.0.1</Source> <SourcePort>3389</SourcePort> <Host>1.2.3.4</Host> <ID>260ecf3a-8539-4e87-bd4b-49b984a6bdf4</ID> <SafePassword>hashstring</SafePassword> <Username>user</Username> <TCPKeepaliveInterval>1</TCPKeepaliveInterval> <Username>user</Username> <Host>ssh-tunnel-name</Host> <HostPort>22</HostPort> <HideOnConnect>True</HideOnConnect> <Mode>Dynamic</Mode> <PrivateKeyPromptForPassPhrase>False</PrivateKeyPromptForPassPhrase> <Source>127.0.0.1</Source> <UseSSHGateway>True</UseSSHGateway> <TerminalMac></TerminalMac> <Application>SSHNative</Application> <AutoClose>false</AutoClose> <CloseMode>Manually</CloseMode> <EnableAutoDetectIsOnlineVPN>False</EnableAutoDetectIsOnlineVPN> <UseDynamicPort>true</UseDynamicPort> <VPNMac></VPNMac> <Id>6f0dace2-2f13-424e-a6da-2fe4d105752e</Id> <Name>ssh-tunnel-name</Name> <Group>folder1\folder2</Group> </Connection> <xml>version="1.0"</xml> </Connection>
I expect the Terminal Section, but its completetly missing on my side.
Do i something wrong or is there a Problem with the Function?
Best Regards,
Andreas
Edit:
It looks like that .OuterXML is responsible for the Cuts. Im trying to investigate further...
Edit2:
After several tries and researching Microsoft Websites i finally descided to write a simple Function which creates an own XML Structur from an Object, which works like a charm.
Here is my Function which works so far:
function New-XMLfromObject
{
Param(
[Parameter(Mandatory=$true)]
[PSCustomObject]$object,
[Parameter(Mandatory=$false)]
[String]$RootName = "Connection",
[Parameter(Mandatory=$false)]
[Switch]$isNode = $false
)
if(!$isNode)
{
$XMLout = '<?xml version="1.0"?>'
$XMLout += "<$RootName>"
}
foreach ($prop in $object.PSObject.Properties)
{
if($prop.TypeNameOfValue -eq "System.Management.Automation.PSCustomObject")
{
$XMLsubObject = New-XMLfromObject -object $prop.Value -isNode
$XMLout += "<$($prop.Name)>$($XMLsubObject)</$($prop.Name)>"
}
else
{
$XMLout += "<$($prop.Name)>$($prop.Value)</$($prop.Name)>"
}
}
if(!$isNode)
{
$XMLout += "</$RootName>"
}
return $XMLout
}
The Output is correct now:
<?xml version="1.0"?> <Connection> <AllowPasswordVariable>true</AllowPasswordVariable> <ConnectionType>VPN</ConnectionType> <CreatedBy>SuperMario</CreatedBy> <Terminal> <PortForwards> <PortForward> <DestinationPort>3389</DestinationPort> <Mode>Dynamic</Mode> <Source>127.0.0.1</Source> <SourcePort>3389</SourcePort> </PortForward> </PortForwards> <SSHGateways> <SSHGateway> <Host>1.2.3.4</Host> <ID>a81d5931-4796-4c72-a8f8-bfeed2a62ed0</ID> <SafePassword>hashstring</SafePassword> <Username>user</Username> </SSHGateway> </SSHGateways> <TCPKeepaliveInterval>1</TCPKeepaliveInterval> <Username>user</Username> <Host>new-ssh-tunnel</Host> <HostPort>22</HostPort> <HideOnConnect>True</HideOnConnect> <Mode>Dynamic</Mode> <PrivateKeyPromptForPassPhrase>False</PrivateKeyPromptForPassPhrase> <Source>127.0.0.1</Source> <UseSSHGateway>True</UseSSHGateway> </Terminal> <TerminalMac></TerminalMac> <VPN> <Application>SSHNative</Application> <AutoClose>false</AutoClose> <CloseMode>Manually</CloseMode> <EnableAutoDetectIsOnlineVPN>False</EnableAutoDetectIsOnlineVPN> <UseDynamicPort>true</UseDynamicPort> </VPN> <VPNMac></VPNMac> <Id>8baa9c4d-3fb2-4cca-82e6-85aafc58d8df</Id> <Name>user</Name> <Group>folder1\folder2</Group> </Connection>
I hope that helps someone too.
Best Regards,
Andreas
Hi Andreas,
Thank you for your function. That would be helpful for other customers.
I will ask an engineer to have a look on this.
Best regards,
Érica Poirier
Hi Andreas
There's a sample script available to edit/add any entry type that is yet to be supported by our PowerShell module. The script is "NonSupportedEntry".
TLDR (Basic sample provided below);
I just tested and everything works as expected. I'm curious as to how you successfully used the script you provided, since the only parts of our connection object that needs xml serialization is "Connection.data" and "Connection.metadata"
$SSHTunnelEntryID = '8e35193a-cb73-4ede-a3d1-eee5458a1d35' $SSHTunnelEntry = (Get-DSEntry -EntryId $SSHTunnelEntryID -AsRDMConnection).Body.data.connectionInfo $ConnectionData = Convert-XMLToPSCustomObject ([xml]$SSHTunnelEntry.Data) $ConnectionMetaData = Convert-XMLToPSCustomObject ([xml]$SSHTunnelEntry.MetaDataString) $ConnectionData.Connection.Terminal.PortForwards.PortForward.DestinationPort = '9999' $ConnectionDataXML = Convert-PSCustomObjectToXML $ConnectionData.Connection $SSHTunnelEntry.Data = (Convert-XMLToSerializedString $ConnectionDataXML) $Body = ConvertTo-Json ($SSHTunnelEntry) -Depth 10 $res = Invoke-WebRequest -Uri 'http://localhost/dps/api/connection/save' -Method 'PUT' -Body $Body -ContentType 'application/json' -WebSession $Global:WebSession
As stated in my sample script, due to the complex nature of our software, it is not a particularly straight forward and simple process but it is available nonetheless for our most adventurous users.
Thank you for your interest in helping the community, it is greatly appreciated.
Best regards,
Alexandre Martigny