Get-ChildItem not working when running PowerShell script from C# - c#

I have a PowerShell script that is using the Get-ChildItem cmdlet to get a list of files from a remote folder (the command taking long time because it a big folder).
all working good when i am running the script on the server
The script:
$serverip = someremoteip
$mainbackuplocation= someremotelocation
$folder = someremotefolder
$remotefolder = Get-ChildItem "\\$serverip\\d$\\$mainbackuplocation\\$folder\\wwwroot" |
Out-File d:\log.txt
But if I am trying to run the script through C# there is no output at all to the file.
SecureString securepassword = String2SecureString(password);
PSCredential credential = new PSCredential(userName, securepassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(target,shell,credential);
Runspace remote = RunspaceFactory.CreateRunspace(connectionInfo);
remote.Open();
PowerShell PowerShellInstance = PowerShell.Create();
PowerShellInstance.Runspace = remote;
PowerShellInstance.AddScript("D:\\backup\\test.ps1");
var result = PowerShellInstance.Invoke();
I can see that the log file is created on the server, so the PowerShell script is running, but the dir command does nothing.
Would like to know what I am doing wrong.

If you're using exactly this code that you write above - there is a small bag.
You have no space after Get-ChildItem - and becouse of this bug - the log.txt file is not created.
Try:
$remotefolder = Get-ChildItem "\\$serverip\\d$\\$mainbackuplocation\\$folder\\wwwroot" | Out-File d:\log.txt

Related

Exchange Online Powershell with CBA now working as expected

I'm working on a project that uses Exchange Online Powershell commands to retrieve the data from Microsoft. We are connecting to PS with CBA. Here is a code(I simplified it a little for demonstrating purposes):
public async Task ConnectToExhangeOnline() {
string ExolV2Cba = #"Param($Bytes, $Password, $AppId, $Organization)
Import-Module ExchangeOnlineManagement
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($Bytes, $Password)
Connect-ExchangeOnline -Certificate $cert -AppId $AppId -Organization $Organization";
using var powerShellProcessInstance = new PowerShellProcessInstance(new Version(5, 0), null, null, false);
powerShellProcessInstance.Process.StartInfo.FileName =
#"C:\Program Files\PowerShell\7\pwsh.exe";
using var runspace = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(Enumerable.Empty<string>()), powerShellProcessInstance);
runspace.Open();
var parameters = new Dictionary<string, Object>();
parameters.Add("AppId", myClientId);
parameters.Add("Organization", myTenantDomain);
parameters.Add("Bytes", Convert.FromBase64String(myCertificate));
using PowerShell powershell = PowerShell.Create();
powershell.Runspace = runspace;
PSCommand command = new PSCommand().AddScript(ExolV2Cba);
foreach (var item in parameters)
{
command.AddParameter(item.Key, item.Value);
}
powershell.Commands = command;
var results = await Task.Factory.FromAsync(powershell.BeginInvoke(), powershell.EndInvoke);
var errors = powershell.Streams.Error.ReadAll();
}
Now, here is the problem. My organization parameter doesn't contain "onmicrosoft.com" part(and it's required due to documentation[see - https://learn.microsoft.com/en-us/powershell/exchange/app-only-auth-powershell-v2?view=exchange-ps#connection-examples%5C%5C\](https://i.stack.imgur.com/V7MKb.png))
So in production I got an error
For AppOnly flow Tenant "MyTenant.onmicrosoft.com" in token doesn't
match with Tenant "MyTenant" in request Url
However, when I run the same code locally on my pc everything works fine with no errors.
We are running all code in docker so the versions of Powershell and other libries are the same on prod and on local machine.
When I added the "onmicrosoft.com" suffix to the $Organization in production everything worked fine as well.
P.S.:
As you can see, in the script we're passing the $password parameter for building the certificate. However, we are not putting any value there so to be honest Idk how it's working. I suppose the certificate we are passing(as byte array) is enough(so Exchange Online doesn't require password). But when I try to run the same script from the Powershell Console on my PC (I fetched the certificate from debug) I'm constantly getting the prompt asking me for credentials(and I don't have ones since this is client's tenant).
I'm also wondering why in production it is failing on the moment when the actual script is executing(Get-ExoMailboxStatistics) and not on the moment when we are connecting (Connect-ExchangeOnline)

C# Powershell Pipeline Import Module not working

I am trying to import a Lync module to automatically send a message to a user. my Powershell script is pretty straight forward.
Powershell
$assemblyPath = “C:\Program Files (x86)\Microsoft Office 2013\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”
Import-Module $assemblyPath
$IMType = 1
$PlainText = 0
$cl = [Microsoft.Lync.Model.LyncClient]::GetClient()
$conv = $cl.ConversationManager.AddConversation()
$username = “USER#DOMAIN.com”
$getuser = $cl.ContactManager.GetContactByUri($username)
$null = $conv.AddParticipant($getuser)
$msg = New-Object “System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType,String]”
$msg.Add($PlainText, “Assistance needed”)
$m = $conv.Modalities[$IMType]
$null = $m.BeginSendMessage($msg, $null, $msg)
And it works flawlessly in Powershell. However when i throw it into C# its failing saying it cannot find the Module.
C#
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add("Import-Module \"C:\\Program Files(x86)\\Microsoft Office 2013\\LyncSDK\\Assemblies\\Desktop\\Microsoft.Lync.Model.dll\"");
pipeline.Commands.Add("$IMType = 1 ");
pipeline.Commands.Add("$PlainText = 0 ");
pipeline.Commands.Add("$cl = [Microsoft.Lync.Model.LyncClient]::GetClient() ");
pipeline.Commands.Add("$conv = $cl.ConversationManager.AddConversation() ");
pipeline.Commands.Add("$username = \"USER#DOMAIN.com"\" ");
pipeline.Commands.Add("$getuser = $cl.ContactManager.GetContactByUri($username) ");
pipeline.Commands.Add("$null = $conv.AddParticipant($getuser) ");
pipeline.Commands.Add("$msg = New-Object \"System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType, String]\" ");
pipeline.Commands.Add("$msg.Add($PlainText, \"Assistance needed with the Virtual Fitting Kiosk(GREEN)\") ");
pipeline.Commands.Add("$m = $conv.Modalities[$IMType] ");
pipeline.Commands.Add("$null = $m.BeginSendMessage($msg, $null, $msg) ");
pipeline.Invoke();
It throws an error
System.Management.Automation.CommandNotFoundException: 'The term 'Import-Module "C:\Program Files(x86)\Microsoft Office 2013\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.dll"' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.'
I have been all around google and i cannot find a solution, I have tried throwing it all into one string and adding it to the pipeline.commands. I have split it up line by line like above, I even Made it copy the command out to text so i can copy/paste it into powershell and it works. I must be missing something in setting up Powershell Runspace. Anyone have any ideas? Thanks in advance!
And, Neverminded I figured it out. Multiple things, mainly the way I coded it.
I needed to use Pipeline.Commands.addScript() not just Add. 2. I apparently forgot to put a space in between Files (x86). 100% My error, Enjoy the read and the laugh! :)

running a command on a remote windows using winrm in C#

I have a simple way to connect to a remote windows machine from a local windows machine using winrm.
Here is the powershell code that is working:
Set-Item WSMan:\localhost\Client\TrustedHosts -Value $ip -Force
$securePassword = ConvertTo-SecureString -AsPlainText -Force 'mypass'
$cred = New-Object System.Management.Automation.PSCredential 'Administrator', $securePassword
$cmd = {ls C:\temp}
Invoke-Command -ComputerName $ip -Credential $cred -ScriptBlock $cmd
I want to figure out how to do the exact thing in c#.
Also, it would be additionally helpful if someone tell me whether there is a method to send files in c# winrm.
Note: the is only a c# code needed on my local machine. The remote machine is already setup.
well, I figured out one way as I shall post below, but while it works fine on windows 8, it encounters the error "Strong name validation failed" on windows 7 so I should keep looking into this.
Still please feel free to post other ideas.
--> add System.Management.Automation.dll to your project.
WSManConnectionInfo connectionInfo = new WSManConnectionInfo();
connectionInfo.ComputerName = host;
SecureString securePwd = new SecureString();
pass.ToCharArray().ToList().ForEach(p => securePwd.AppendChar(p));
connectionInfo.Credential = new PSCredential(username, securePwd);
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();
Collection<PSObject> results = null;
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript(cmd);
results = ps.Invoke();
// Do something with result ...
}
runspace.Close();
foreach (var result in results)
{
txtOutput.AppendText(result.ToString() + "\r\n");
}
I've got an article that describes an easy way to run Powershell through WinRM from .NET at http://getthinktank.com/2015/06/22/naos-winrm-windows-remote-management-through-net/.
The code is in a single file if you want to just copy it and it's also a NuGet package that includes the reference to System.Management.Automation.
It auto manages trusted hosts, can run script blocks, and also send files (which isn't really supported but I created a work around). The returns are always the raw objects from Powershell.
// this is the entrypoint to interact with the system (interfaced for testing).
var machineManager = new MachineManager(
"10.0.0.1",
"Administrator",
MachineManager.ConvertStringToSecureString("xxx"),
true);
// will perform a user initiated reboot.
machineManager.Reboot();
// can run random script blocks WITH parameters.
var fileObjects = machineManager.RunScript(
"{ param($path) ls $path }",
new[] { #"C:\PathToList" });
// can transfer files to the remote server (over WinRM's protocol!).
var localFilePath = #"D:\Temp\BigFileLocal.nupkg";
var fileBytes = File.ReadAllBytes(localFilePath);
var remoteFilePath = #"D:\Temp\BigFileRemote.nupkg";
machineManager.SendFile(remoteFilePath, fileBytes);
Please mark as answer if this helps. I've been using this for a while with my automated deployments. Please leave comments if you find issues.

run powershell from c# application

Hello a have a file with 21 lines of code in power shell. I need a way to run this file with a button in c#. I use c# in Visual Studio 2010. Please let me know if there is a way to achieve.
// Powershell
$Proc = Get-Process | Sort-Object CPU -Descending
$cores = Get-WmiObject Win32_processor
$memory = Get-WmiObject Win32_OperatingSystem
$Total = 10
$num = 1
Clear-Content c:\scripts\prueba.txt
foreach ($objItm in $Proc) {
If ($num -gt $Total) {
break #break the loop
}
[string] $strID=[System.Convert]::ToString($objItm.ID)
[string] $strProcessName=[System.Convert]::ToString($objItm.ProcessName)
[string] $strCPU=[System.Convert]::ToString($objItm.CPU)
[string] $strNUM=[System.Convert]::ToString($num)
$info=$strNUM+"-"+$strID+"-"+$strProcessName+"-"+$strCPU
$num += 1
$info|Add-Content c://scripts/prueba.txt
}
//Code c#
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open(); RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(scriptFile)
Execute PowerShell script results = pipeline.Invoke();
I need for example press a button and make the script happens
You can use PS2EXE by Ingo Karstein
This creates a c# executable with it's own powershell host and with your script embedded.
I have a c# project which takes the c# code in PS2EXE, adds ability to read the contents of an encrypted powershell script, decrypt it and run it.
some tips that works for me . My application is already working
Run Visual Studio with permissions of Administrator this to avoid get a error with the Hkey_Local...
In power shell run this get-executionpolicy to view the policy and set unregistered with set-executionpolicy unregistered. This avoid any restriction by the OS.
And with the code write above the app work. Get ready and test !!!

The term ' ' is not recognized as the name of a cmdlet,

I have a PowerShell script stored in a file, MergeDocuments.ps1. When I run the script from the Windows PowerShell command prompt it runs fine
.\MergeDocuments.ps1 1.docx 2.docx merge.docx
Calling the script from a Windows console application also runs fine.
When I tried calling the script from an Asp.Net web service, I faced some issues regarding registry access. I used impersonation and gave permission to Network Service account to the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell to solve this problem
Next I faced issue about PowerShell being unable to create objects of type OpenXmlPowerTools.DocumentSource[], so I added the following to my script
Add-Type -Path "C:\Users\Administrator\Documents\WindowsPowerShell\Modules\OpenXmlPowerTools\OpenXmlPowerTools.dll"
Import-Module OpenXmlPowerTools
Now the current problem is that I am getting the error "The term 'Merge-OpenXmlDocument' is not recognized as the name of a cmdlet, ..."
How can I solve that?
PowerShell Script
Add-Type -Path "C:\Users\Administrator\Documents\WindowsPowerShell\Modules\OpenXmlPowerTools\OpenXmlPowerTools.dll"
Import-Module OpenXmlPowerTools
# The last argument is the path of the merged document
$noOfSourceDocs = ($($args.length) - 1)
# Create an array to hold all the source documents
[OpenXmlPowerTools.DocumentSource[]] $docs = New-Object OpenXmlPowerTools.DocumentSource[] $noOfSourceDocs
for ($i = 0; $i -lt $noOfSourceDocs; $i++)
{
$docs[$i] = New-Object -TypeName OpenXmlPowerTools.DocumentSource -ArgumentList $args[$i]
$docs[$i].KeepSection = 1
}
Merge-OpenXmlDocument -OutputPath $args[-1] -Sources $docs
Webservice .Net Code
using (new Impersonator(username, domain, password))
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
RunspaceInvoke invoker = new RunspaceInvoke(runspace);
invoker.Invoke("Set-ExecutionPolicy Unrestricted");
// create a pipeline and feed it the script file
Pipeline pipeline = runspace.CreatePipeline();
Command command = new Command(ConfigurationManager.AppSettings["PowerShellScript"]);
foreach (var file in filesToMerge)
{
command.Parameters.Add(null, file);
}
command.Parameters.Add(null, outputFilename);
pipeline.Commands.Add(command);
pipeline.Invoke();
runspace.Close();
}
Can you just try to install OpenXmlPowerTools module in the PowerShell System module path :
C:\Windows\system32\WindowsPowerShell\v1.0\Modules

Categories

Resources