'Error "Cannot bind argument to parameter 'Name' because it is null" when running PowerShell script from C#
AFAIK, the only way to change the type of an Office 365 mailbox to Shared is via Exchange Online Powershell, and seeing that I have to do so from an ASP.NET Core 3.1 web app, I've implemented the following:
Method to run a PowerShell script from C#:
public async Task<List<string>> RunScript(string script, Dictionary<string, object> parameters)
{
var result = new List<string>();
PowerShell ps = null;
try
{
ps = PowerShell.Create();
ps.AddScript(script);
ps.AddParameters(parameters);
var pipeline_objs = await ps.InvokeAsync().ConfigureAwait(false);
foreach (var item in pipeline_objs)
{
result.Add(item.BaseObject.ToString());
}
}
catch (Exception ex)
{
throw;
}
finally
{
if (ps != null)
{
ps.Dispose();
}
}
return result;
}
Method is invoked as follows:
async Task GetMailboxDataAsync()
{
var script = //Script content
var parameters = new Dictionary<string, object>
{
{ "$username", "[email protected]" },
{ "$password", "password" },
{ "$principal", "[email protected]" },
{ "$organization", "domain.onmicrosoft.com" }
};
var result = await RunScript(script, parameters);
result.Dump();
}
PowerShell script I'm trying to run (be aware that for testing purposes I'm not actually using Set-EXOMailbox
, just running Get-EXOMailbox
for the time being):
Param($username,$password,$organization,$principal)
Import-Module ExchangeOnlineManagement
$secured_password = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($username, $secured_password)
Connect-ExchangeOnline -UserPrincipalName $username -DelegatedOrganization $organization -Credential $cred -ShowBanner:$false
Get-EXOMailbox -Identity $principal
Disconnect-ExchangeOnline -Confirm:$false
Hardcoding the values in the PowerShell script and running it in the command line works as expected, but invoking it from C# (even with hardcoded parameters) returns me the following RuntimeException
: Cannot bind argument to parameter 'Name' because it is null.
. The ScriptStackTrace property says:
at Disconnect-ExchangeOnline<Process>, C:\Users\[OMITTED]\Documents\PowerShell\Modules\ExchangeOnlineManagement\2.0.5\netCore\ExchangeOnlineManagement.psm1: line 666
at <ScriptBlock>, <No file>: line 7
What am I missing?
Solution 1:[1]
The fact that it runs on the command line suggests that it might be an issue with the .NET version of the host app (3.1). You could try wrapping the script with Invoke-Command, which might leverage the most updated versions of Powershell and .NET. Here's the pattern I've used recently to call EXO V2 cmdlets from a .NET host app. I've added some extra error handling and multi-threading so that PS remoting exceptions don't escape to the host app.
$result = $null
# Outer try-catch.
try {
# Run the script asynchronously. Please note that script blocks must be self-contained,
# meaning you must pass parameters to them and define all functions inside them.
Start-Job -Name "$i" -ArgumentList @($username,$password,$organization,$principal) -ScriptBlock {
param($username,$password,$organization,$principal)
$result = $null
# Try-catch inside async script.
try {
# Another self-contained script block inside Invoke-Command.
$result = Invoke-Command -ComputerName localhost -ErrorAction Stop `
-ArgumentList @($username,$password,$organization,$principal) `
-ScriptBlock {
<# BEGIN Original script, with edits. #>
Param($username,$password,$organization,$principal)
Import-Module ExchangeOnlineManagement
$secured_password = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($username, $secured_password)
Connect-ExchangeOnline -UserPrincipalName $username -DelegatedOrganization $organization -Credential $cred -ShowBanner:$false
$result = Get-EXOMailbox -Identity $principal
# This should allow the script to continue without interruption,
# but any remoting exceptions could still escape to the .NET host app.
try { Disconnect-ExchangeOnline -Confirm:$false -ErrorAction Stop } catch { $Error.Clear() }
<# END Original script, with edits. #>
return $result
}
} catch {
Write-Warning ($_ | Out-String) # Do NOT use Write-Error!
$Error.Clear()
}
return $result
} | Out-Null
# Wait for the job to finish and get the result.
$jobs = Get-Job
while($jobs) {
$job = $jobs | Wait-Job -Any
$result = $job | Receive-Job
$job | Remove-Job
$jobs = Get-Job
}
} catch {
Write-Warning ($_ | Out-String) # Do NOT use Write-Error (unless you want certain exceptions to bubble up, of course).
$Error.Clear()
}
return $result
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Joe Zamora |