Query Mongo Collection from Powershell script - mongodb

I need to run a powershell script which queries the MongoDB collection from pipeline. I am using latest MongoDB driver 2.9.3. I wrote the script as below -
$Assem = ("D:\PoweshellMongoDB\Drivers\MongoDB.Bson.dll", "D:\PoweshellMongoDB\Drivers\MongoDB.Driver.dll", "D:\PoweshellMongoDB\Drivers\MongoDB.Driver.Core.dll")
$Source = #"
using MongoDB.Bson;
using MongoDB.Driver;
using System;
namespace MongoDBSample
{
public static class MongoRepository
{
public static void Connect()
{
try
{
var mongoClient = new MongoClient("mongodb://localhost:27017");
var database = mongoClient.GetDatabase("ILP4");
var collection = database.GetCollection<BsonDocument>("Issues");
var count = collection.CountDocumentsAsync(new BsonDocument()).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
"#
Add-Type -ReferencedAssemblies $Assem -TypeDefinition $Source -Language CSharp
[MongoDBSample.MongoRepository]::Connect()
But when I debug the script I am getting below error -
Exception calling "Connect" with "0" argument(s): "Could not load file or assembly 'MongoDB.Driver, Version=2.9.3.0, Culture=neutral, PublicKeyToken=null' or
one of its dependencies. The system cannot find the file specified."
At D:\PoweshellMongoDB\PowerMongo.ps1:34 char:1
+ [MongoDBSample.MongoRepository]::Connect()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : FileNotFoundException
I am not sure which reference I am missing. Is there a better way of doing it? Or do I need to update the driver to older version? Please advise.

I found the solution. I created a .NET Framework class library which connects to MongoDB using MongoDB driver 2.3.1. In my pipeline I created setup as - checkout library code from source control, build and publish the dll, from my inline powershell script I am referring this dll and executing it.

Related

Callbacks from ABAP are not supported

I try to return "bills" from SAP with Powershell. The connection is working, my function is callable, but the Invoke method does not:
Ausnahme beim Aufrufen von "Invoke" mit 1 Argument(en): "Callbacks from ABAP are not supported"
In C:\Users\test\Desktop\script.ps1:75 Zeichen:10
+ $myFun.Invoke($destination)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : RfcAbapRuntimeException
Upon investigation they say that SAP .NET connector doesn't support the "invoke" call.
Is there an other function that do the similar? I found a post that is almost 5 years ago in c#, and i cant reproduce that.
my powershell script:
#load .net sap connector
Function Load-NCo {
$CurrentDir = Get-Location
[System.IO.Directory]::SetCurrentDirectory($CurrentDir.Path)
$rc = [Reflection.Assembly]::LoadFile("C:\Program Files\SAP\SAP_DotNetConnector3_Net40_x64\" + "sapnco.dll")
$rc = [Reflection.Assembly]::LoadFile("C:\Program Files\SAP\SAP_DotNetConnector3_Net40_x64\" + "sapnco_utils.dll")
}
Function Get-Destination2 {
#-Verbindungsparamter---------------------------------------------
$cfgParams = New-Object SAP.Middleware.Connector.RfcConfigParameters
$cfgParams.Add("NAME", "SAPconnect")
$cfgParams.Add("ASHOST", "xxx.xx.xxx.xx")
$cfgParams.Add("SYSNR", "25")
$cfgParams.Add("CLIENT", "111")
$cfgParams.Add("USER", "test")
$cfgParams.Add("SYSID ","T30")
$cfgParams.Add("LANG","DE")
$cfgParams.Add("PASSWD", "test")
$destination = [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($cfgParams)
$rfcRepository = [SAP.Middleware.Connector.RfcDestination]
$rfcRepository = $destination.Repository
$myFun = [SAP.Middleware.Connector.IRfcFunction]
$myFun = $rfcRepository.CreateFunction("Z_GET_ARCHIVE_FILE")
$myFun.setValue("SAP_OBJECT" , "test2020")
$myFun.setValue("OBJECT_ID" , "64562254")
$myFun.setValue("ARCHIV_TAB" , "TOAST02")
$myFun.setValue("ARCHIV_ID" , "I8")
$myFun.setValue("AR_OBJECT" , "ZIMT05")
$myFun.Invoke($destination) // error
$table = $myFun.GetTable("T_URLS")
}
The problem you encounter is the similar to what is described in this question:
Exception: RFC callback server not available while calling RFC
I don't see the callback parameter in your module interface (callbacks are not supported in NCo), but I see this is your custom-developed module written from scratch, and it supposedly downloads an archive file from the SAP backend.
My assumption is you coded this RFC function using module like GUI_DOWNLOAD or method cl_gui_frontend_services=>gui_download or something similar which works fine in GUI mode when it has access to frontend filesystem, but fails in RFC (NCo) mode.

PowerShell Unable to find type

I'm having trouble with an error when I try to run my PowerShell script from the command line:
Unable to find type [System.Windows.Forms.ListViewItem]
I have tried different ways to import the required classes:
using assembly System.Windows.Forms
using namespace System.Windows.Forms
using namespace System.Drawing
Add-Type System.Windows.Forms
In PowerShell ISE, I can run my script without any errors. But when I try to execute my script from CMD it doesn't work. It's exactly the same code and the same script files. This is the command I use to execute the script in CMD followed by the error messages:
C:\Users\Admin>powershell -File "C:\Folder\Main.ps1"
At C:\Folder\Main.ps1:35 char:30
+ ... return (([int](([System.Windows.Forms.ListViewItem]$a).Sub ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [System.Windows.Forms.ListViewItem].
At C:\Folder\Main.ps1:35 char:114
+ ... umnIndex].Text)) - ([int](([System.Windows.Forms.ListViewItem]$b).Sub ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [System.Windows.Forms.ListViewItem].
At C:\Folder\Main.ps1:37 char:41
+ ... return ([String]::Compare(([System.Windows.Forms.ListViewItem]$a).Sub ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [System.Windows.Forms.ListViewItem].
At C:\Folder\Main.ps1:37 char:115
+ ... [$this.columnIndex].Text, ([System.Windows.Forms.ListViewItem]$b).Sub ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to find type [System.Windows.Forms.ListViewItem].
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TypeNotFound
The error messages refer to the code below. This is my only class in that script:
class ListViewItemComparer : System.Collections.IComparer {
[int]$sortOrder
[String]$columnType
[int]$columnIndex
ListViewItemComparer() {
$this.sortOrder = 1
$this.columnType = "String"
$this.columnIndex = 0
}
ListViewItemComparer([int]$sortOrder, [String]$columnType, [int]$columnIndex) {
$this.sortOrder = $sortOrder
$this.columnType = $columnType
$this.columnIndex = $columnIndex
}
[void] ChangeSorting([int]$columnIndex, [String]$columnType) {
if($this.columnIndex -eq $columnIndex) {
$this.sortOrder *= -1
} else {
$this.columnIndex = $columnIndex
$this.sortOrder = 1
}
$this.columnType = $columnType
}
[int] Compare([Object]$a, [Object]$b) {
if($this.columnType -eq "Number") {
return (([int](([System.Windows.Forms.ListViewItem]$a).SubItems[$this.columnIndex].Text)) - ([int](([System.Windows.Forms.ListViewItem]$b).SubItems[$this.columnIndex].Text))) * $this.sortOrder
} else {
return ([String]::Compare(([System.Windows.Forms.ListViewItem]$a).SubItems[$this.columnIndex].Text, ([System.Windows.Forms.ListViewItem]$b).SubItems[$this.columnIndex].Text)) * $this.sortOrder
}
}
}
I'm using the type System.Windows.Forms.ListViewItem at other places too in my code, but there is no problem outside of a class. I don't even have to write the whole class path, just ``ListViewItem` works as well.
Can somebody tell me why the type System.Windows.Forms.ListViewItem is not recognized?
Why is it recognized outside the ListViewItemComparer class in the same script file?
Why does this error occur when I run the script from CMD and not in PowerShell ISE?

Calling AppDomain.DoCallback from Powershell

This is based on the Stack Overflow question: How to load an assembly as reflection-only in a new AppDomain?
I am attempting to determine the runtime version of an assembly, but that assembly could be loaded multiple times as I traverse through nested folders. Loading the assembly directly using
[Reflection.Assembly]::ReflectionOnlyLoadFrom($assembly)
will therefore not work, as the assembly can only be loaded once in the app-domain.
Given the following function to load an assembly in a separate AppDomain:
function Load-AssemblyInNewAppDomain($assembly)
{
Write-Host $assembly.FullName
$domain = [AppDomain]::CreateDomain([Guid]::NewGuid())
$domain.DoCallback
({
$loaded = [Reflection.Assembly]::Load($assembly)
$runtime = $loaded.ImageRuntimeVersion
Write-Host $runtime
})
}
This outputs the contents of the delegate to the console, rather than executing it:
OverloadDefinitions
-------------------
void DoCallBack(System.CrossAppDomainDelegate callBackDelegate)
void _AppDomain.DoCallBack(System.CrossAppDomainDelegate theDelegate)
$loaded = [Reflection.Assembly]::Load($assembly)
$runtime = $loaded.ImageRuntimeVersion
Write-Host $runtime
Note that the results are the same, whether I use PowerShell 4 or 5
Any help/guidance appreciated
First thought: don't muck around with AppDomains at all and use a completely separate process. Those are (relatively) easily launched from PowerShell, at least. The drawback is that it's potentially much slower if you're doing this for lots of files.
$myAssemblyPath = "C:\..."
$getImageRuntimeVersion = {
[Reflection.Assembly]::ReflectionOnlyLoadFrom($input).ImageRuntimeVersion
}
$encodedCommand = [Convert]::ToBase64String(
[Text.Encoding]::Unicode.GetBytes($getImageRuntimeVersion)
)
$imageRuntimeVersion = $myAssemblyPath | powershell -EncodedCommand $encodedCommand
So, is there no way at all to do this with AppDomains in PowerShell? Well, there is, but it's not pretty. You can't use AppDomain.DoCallBack because, as you've discovered, PowerShell can't remote delegates that way (because, under the covers, it produces dynamic methods).
However, it's easy to host the PowerShell runtime, and all PowerShell objects know how to serialize (a requirement for cross-domain remoting), so invoking a PowerShell script in another AppDomain is fairly simple (but still ugly):
$scriptInvokerAssembly = [System.IO.Path]::GetTempFileName() + ".dll"
Add-Type -OutputAssembly $tempAssembly -TypeDefinition #"
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Management.Automation;
public class ScriptInvoker : MarshalByRefObject {
public IEnumerable<PSObject> Invoke(ScriptBlock scriptBlock, PSObject[] parameters) {
using (var powerShell = PowerShell.Create()) {
powerShell.Commands.AddScript(scriptBlock.ToString());
if (parameters != null) {
powerShell.AddParameters(parameters);
}
return powerShell.Invoke();
}
}
}
"#
[Reflection.Assembly]::LoadFile($scriptInvokerAssembly) | Out-Null
Function Invoke-CommandInTemporaryAppDomain([ScriptBlock] $s, [object[]] $arguments) {
$setup = New-Object System.AppDomainSetup
$setup.ApplicationBase = Split-Path ([ScriptInvoker].Assembly.Location) -Parent
$domain = [AppDomain]::CreateDomain([Guid]::NewGuid(), $null, $setup)
$scriptInvoker = $domain.CreateInstanceAndUnwrap(
[ScriptInvoker].Assembly.FullName, [ScriptInvoker]
);
$scriptInvoker.Invoke($s, $arguments)
[AppDomain]::Unload($domain)
}
And now you can do
Invoke-CommandInTemporaryAppDomain {
[Reflection.Assembly]::ReflectionOnlyLoadFrom($args[0]).ImageRuntimeVersion
} $myAssemblyPath
Note that we have to generate a temporary assembly on disk and have AppDomain load it from there. This is ugly, but you can't have Add-Type produce an in-memory assembly, and even if you do end up with a byte[] getting that to load in another AppDomain is anything but trivial because you can't hook AppDomain.AssemblyResolve in PowerShell. If this command was packaged in a module, you'd compile the assembly containing the ScriptInvoker ahead of time, so I don't see working around this as a priority.
You can't run DoCallback via powershell alone. But DoCallBack does work with some inline C#. As Jeroen says it's ugly, but this works:
$assm = "C:\temp\so\bin\dynamic-assembly.dll"
Add-Type -TypeDefinition #"
using System.Reflection;
using System;
namespace Example
{
public class AppDomainUtil
{
public void LoadInAppDomain(AppDomain childDomain, string assemblyName)
{
childDomain.SetData("assemblyName", assemblyName);
childDomain.DoCallBack( new CrossAppDomainDelegate(LoadAssembly)) ;
}
public static void LoadAssembly()
{
string assemblyName = (string)AppDomain.CurrentDomain.GetData("assemblyName");
// console not available from another domain
string log = "c:\\temp\\hello.txt";
System.IO.File.WriteAllText(log, string.Format("Hello from {0}\r\n",AppDomain.CurrentDomain.FriendlyName));
System.IO.File.AppendAllText(log, string.Format("Assembly to load is {0}\r\n",assemblyName));
Assembly loaded = Assembly.Load(assemblyName);
System.IO.File.AppendAllText(log, string.Format("Assemblyloaded: {0}\r\n",loaded.FullName));
}
}
}
"# -OutputAssembly $assm -OutputType Library # must set output assembly otherwise assembly generated in-memory and it will break with Type errors.
Add-Type -Path $assm
function Load-AssemblyInNewAppDomain([string]$assembly) {
Write-Host "Parent domain: $([AppDomain]::CurrentDomain.FriendlyName)"
$util = New-Object Example.AppDomainUtil
$ads = New-Object System.AppDomainSetup
$cd = [AppDomain]::CurrentDomain
# set application base
$ads.ApplicationBase = [IO.path]::GetDirectoryName( $assm )
[System.AppDomain]$newDomain = [System.AppDomain]::CreateDomain([System.Guid]::NewGuid().ToString(), $null, $ads);
Write-Host "Created child domain: $($newDomain.FriendlyName)"
$util.LoadInAppDomain($newDomain, $assembly)
}
Testing it out:
PS C:\WINDOWS\system32> Load-AssemblyInNewAppDomain "".GetType().Assembly.FullName
Parent domain: PowerShell_ISE.exe
Created child domain: 61ab2dbb-8b33-4e7e-84db-5fabfded53aa
PS C:\WINDOWS\system32> cat C:\temp\hello.txt
Hello from 61ab2dbb-8b33-4e7e-84db-5fabfded53aa
Assembly to load is mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Assemblyloaded: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Call Windows Runtime Classes from PowerShell

Is there a way to call Windows Runtime (WinRT) classes (or objects) from a PowerShell script? I know that you can call COM objects, which WinRT classes are supposed to be "exposed" as ... but so far my attempts have failed...
This is my code I'm trying:
$lockscreen = New-Object -comObject Windows.System.UserProfile.LockScreen
Which gives me the following error:
New-Object : Retrieving the COM class factory for component with CLSID {00000000-0000-0000-0000-000000000000} failed
due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
Does anyone know the correct "COM Class" that I should be using for WinRT classes?
just reference the type, to "load" the assembly...
[Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime]
[Windows.System.UserProfile.LockScreen]::OriginalImageFile
if you don't want the type to be returned in your powershell results then
$null = [Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime]
Here is something hacky that seems to work:
PS> new-object "Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime"
new-object : Constructor not found. Cannot find an appropriate constructor for type
Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime.
At line:1 char:1
+ new-object "Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,Con ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : CannotFindAppropriateCtor,Microsoft.PowerShell.Commands.NewObjectCommand
PS> [Windows.System.UserProfile.LockScreen]::OriginalImageFile
AbsolutePath : C:/Windows/Web/Screen/img100.png
AbsoluteUri : file:///C:/Windows/Web/Screen/img100.png
LocalPath : C:\Windows\Web\Screen\img100.png
Authority :
HostNameType : Basic
IsDefaultPort : True
IsFile : True
IsLoopback : True
PathAndQuery : C:/Windows/Web/Screen/img100.png
...
Note that the first call fails because LockScreen has no constructor but that call does something to pull in the WinRT projection/metadata such that you can now call the static methods/properties on the LockScreen class.
DISCLAIMER: there isn't any documentation that I can find on this New-Object syntax so it is entirely possible that Microsoft could change it considering it is essentially a "hidden" and probably not fully developed feature.

How can I get PowerShell Added-Types to use Added Types

I'm working on a PoSh project that generates CSharp code, and then Add-Types it into memory.
The new types use existing types in an on disk DLL, which is loaded via Add-Type.
All is well and good untill I actualy try to invoke methods on the new types. Here's an example of what I'm doing:
$PWD = "."
rm -Force $PWD\TestClassOne*
$code = "
namespace TEST{
public class TestClassOne
{
public int DoNothing()
{
return 1;
}
}
}"
$code | Out-File tcone.cs
Add-Type -OutputAssembly $PWD\TestClassOne.dll -OutputType Library -Path $PWD\tcone.cs
Add-Type -Path $PWD\TestClassOne.dll
$a = New-Object TEST.TestClassOne
"Using TestClassOne"
$a.DoNothing()
"Compiling TestClassTwo"
Add-Type -Language CSharpVersion3 -TypeDefinition "
namespace TEST{
public class TestClassTwo
{
public int CallTestClassOne()
{
var a = new TEST.TestClassOne();
return a.DoNothing();
}
}
}" -ReferencedAssemblies $PWD\TestClassOne.dll
"OK"
$b = New-Object TEST.TestClassTwo
"Using TestClassTwo"
$b.CallTestClassOne()
Running the above script gives the following error on the last line:
Exception calling "CallTestClassOne" with "0" argument(s):
"Could not load file or assembly 'TestClassOne,...'
or one of its dependencies. The system cannot find the file specified."
At AddTypeTest.ps1:39 char:20
+ $b.CallTestClassOne <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
What am I doing wrong?
This happens because any assemblies are looked for by the CLR loader in the application's (PowerShell's) base directory. Of course, it doesn't find your assembly there. The best way to solve this is to hook the AssemblyResolve event as stej mentions but use it to tell the CLR where the assembly is. You can't do this with PowerShell 2.0's Register-ObjectEvent because it doesn't work with events that require a return value (ie the assembly). In this case, let's use more C# via Add-Type to do this work for us. This snippet of code works:
ri .\TestClassOne.dll -for -ea 0
$resolver = #'
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace Utils
{
public static class AssemblyResolver
{
private static Dictionary<string, string> _assemblies;
static AssemblyResolver()
{
var comparer = StringComparer.CurrentCultureIgnoreCase;
_assemblies = new Dictionary<string,string>(comparer);
AppDomain.CurrentDomain.AssemblyResolve += ResolveHandler;
}
public static void AddAssemblyLocation(string path)
{
// This should be made threadsafe for production use
string name = Path.GetFileNameWithoutExtension(path);
_assemblies.Add(name, path);
}
private static Assembly ResolveHandler(object sender,
ResolveEventArgs args)
{
var assemblyName = new AssemblyName(args.Name);
if (_assemblies.ContainsKey(assemblyName.Name))
{
return Assembly.LoadFrom(_assemblies[assemblyName.Name]);
}
return null;
}
}
}
'#
Add-Type -TypeDefinition $resolver -Language CSharpVersion3
$code = #'
namespace TEST {
public class TestClassOne {
public int DoNothing() {
return 1;
}
}
}
'#
$code | Out-File tcone.cs
Add-Type -OutputAssembly TestClassOne.dll -OutputType Library -Path tcone.cs
# This is the key, register this assembly's location with our resolver utility
[Utils.AssemblyResolver]::AddAssemblyLocation("$pwd\TestClassOne.dll")
Add-Type -Language CSharpVersion3 `
-ReferencedAssemblies "$pwd\TestClassOne.dll" `
-TypeDefinition #'
namespace TEST {
public class TestClassTwo {
public int CallTestClassOne() {
var a = new TEST.TestClassOne();
return a.DoNothing();
}
}
}
'#
$b = new-object Test.TestClassTwo
$b.CallTestClassOne()
When you output the TestClassTwo to a dll (in the same directory as TestClassOne) and Add-Type it, it works. Or at least at my machine ;) So that's the ugly workaround.
When calling $b.CallTestClassOne() PowerShell tries (from some reason I don't know) to find assembly TestClassOne.dll at these locations:
LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne.DLL
LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne/TestClassOne.DLL
LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne.EXE
LOG: Pokus o stažení nové adresy URL file:///C:/Windows/SysWOW64/WindowsPowerShell/v1.0/TestClassOne/TestClassOne.EXE
This is output from fuslogvw tool. It might be useful for you. The same list of paths can bee seen live using ProcessMonitor.
You might also try this (before calling CallTestClassOne()
[appdomain]::CurrentDomain.add_assemblyResolve({
$global:x = $args
})
$b.CallTestClassOne()
$x | fl
This will show you what assembly failed and some more info.
I agree that it should work as you expect. So that's why this looks somewhat buggy.