I'm having a strange problem. I'm trying to create an Azure site by running PowerShell script from my C# code. I open the script file, modify it to insert the necessary data, save it and then run it. This is the script:
$azureUserName = "myusername"
$azurePass = "mypass"
$siteName = "myurl"
$clientID = "*******"
$clientSecret = "********"
$subscriptionID = "*******"
$securePassword = ConvertTo-SecureString -String $azurePass -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($azureUserName, $securePassword)
Add-AzureAccount -Credential $cred
Select-AzureSubscription -SubscriptionId $subscriptionID
New-AzureWebsite $siteName | Set-AzureWebsite -WebSocketsEnabled $true -AppSettings #{"ClientID"=$clientID;"ClientSecret"=$clientSecret}
#Get the website's propeties.
$website = Get-AzureWebSite -Name $siteName
$siteProperties = $website.SiteProperties.Properties
#extract url, username and password
$url = ($siteProperties | ?{ $_.Name -eq "RepositoryURI" }).Value.ToString() + "/MsDeploy.axd"
$userName = ($siteProperties | ?{ $_.Name -eq "PublishingUsername" }).Value
$pw = ($siteProperties | ?{ $_.Name -eq "PublishingPassword" }).Value
#build the command line argument for the deploy.cmd :
$argFormat = ' /y /m:"{0}" -allowUntrusted /u:"{1}" /p:"{2}" /a:Basic'
$arguments = [string]::Format($argFormat, $url, $userName, $pw)
The thing is. If I modify the data manually and then run it from PowerShell ISE the site is being created and is running perfectly. If I modify the data from code and run it, the site is also being created and seems to be working ok but when I try navigating to it it is not being found.
This is how I run the script:
using (PowerShell powershell = PowerShell.Create())
{
String file = System.IO.File.ReadAllText(System.IO.Path.Combine(filesPath, "CreateAzureSite.ps1"),Encoding.UTF8);
powershell.AddScript(file);
Collection<PSObject> results = powershell.Invoke();
}
Use the .NET management libraries:
Documentation: https://msdn.microsoft.com/en-us/library/azure/microsoft.windowsazure.management.websites(v=azure.11).aspx
Nuget: https://www.nuget.org/packages/Microsoft.WindowsAzure.Management.WebSites/4.4.1-prerelease
Related
I have a PowerShell script that closes opened shared files on remote server
$User = "user"
$PWord = ConvertTo-SecureString -String "pas" -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
$cim = New-CimSession -ComputerName epicorlive -Credential $Credential
Get-SmbOpenFile -CimSession $cim | Where-Object Path -like "*dll\Form.dll" | Close-SmbOpenFile -Force
Running this script from native PowerShell window works fine.
but when i try to run from C# code there is an error:
The term 'Get-SmbOpenFile' 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
string scriptText = //"\r\n" +
"$User = \"user\"\r\n" +
"$PWord = ConvertTo-SecureString -String \"pas\" -AsPlainText -Force\r\n" +
"$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord\r\n" +
"$cim = New-CimSession -ComputerName epicorlive -Credential $Credential\r\n" +
"Get-SmbOpenFile -CimSession $cim | Where-Object Path -like \"*dll\\" + file + "\" | Close-SmbOpenFile -Force";
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Why is this command not recognized running from C#?
How can i fix it?
If there is some other options to close opened shared files on remote computer from C# code, I will be glad to hear about them :)
I want to open powershell from C# code like this. However, i want to set a credential to powershell when it comes up.
Process myProcess = new Process();
try
{
myProcess.StartInfo.UseShellExecute = true;
myProcess.StartInfo.FileName = #"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
myProcess.StartInfo.CreateNoWindow = false;
myProcess.Start();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Essentially after powershell is running i want to do a Enter-PSSession to connect to remote computer but i don't want to prompt for username and password there.
I know this can be done.
PS C:\> $C = Get-Credential
PS C:\> Enter-PsSession -ComputerName "Server01" -Credential $C
I don't want to ask the user for credentials, instead i want to set it in the variable when invoking powershell. How can i achieve this ?
Current approach is two fold,
Dump the password via C#
string cmd = "\"" + mypwd+ "\"" + #" | ConvertTo-SecureString -AsPlainText -Force | convertfrom-securestring | out-file output";
PowerShellInstance.AddScript(cmd);
Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
read it in powershell
$pwd = Get-Content .\output | ConvertTo-SecureString
$localcred = New-Object -typename System.Management.Automation.PSCredential -argumentlist "myusername",$pwd
Enter-PSSession -ComputerName 192.168.2.51 -Credential $localcred
Is there a neater way ?
you can use credential manager and avoid the hard coding or interactive get-credential : Accessing Windows Credential Manager from PowerShell or https://gallery.technet.microsoft.com/scriptcenter/how-to-add-credentials-to-c8e9bd5f
Is there a way to get text of executed powershell script in C#.
For example:
using (PowerShell powerShellInstance = PowerShell.Create())
{
Collection<PSObject> result;
// psScript object is script text loaded from some .ps1 file
powerShellInstance.AddScript(psScript);
//parameters object is list of passed parameters
powerShellInstance.AddParameters(parameters);
result = powerShellInstance.Invoke();
}
Can we get executed script text anywhere in above code so we could just copy it and try to execute it directly in powershell.
EDIT:
My .ps1 script file looks like this:
param($CustomerPrimaryO365Domain, $AdminUsername, $AdminPassword, $Domain)
#####################################################################################################################################################################
function Get-CustomerDomain()
#####################################################################################################################################################################
{
$O365Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AdminUsername,(ConvertTo-SecureString -AsPlainText -Force -String $AdminPassword)
Connect-MsolService -Credential $O365Cred
$tenID=(Get-MSOLPartnerContract -DomainĀ $CustomerPrimaryO365Domain).tenantId.guid
Get-MsolDomain -DomainName $Domain -TenantId $tenID
}
Import-Module MSOnline
Get-CustomerDomain
But when it is executed it needs to look something like this:
#####################################################################################################################################################################
function Get-CustomerDomain()
#####################################################################################################################################################################
{
$O365Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "MyAdmin#test.test",(ConvertTo-SecureString -AsPlainText -Force -String "testpassword")
Connect-MsolService -Credential $O365Cred
$tenID=(Get-MSOLPartnerContract -DomainĀ "mydomain.onmicrosoft.com").tenantId.guid
Get-MsolDomain -DomainName "testdomain.info" -TenantId $tenID
}
Import-Module MSOnline
Get-CustomerDomain
Is there a way to get "executed" script from powerShellInstance object.
I am new to PowerShell. I am trying to execute PowerShell script from C#. PS Script that I have written transfer xml file from host computer (running PS script) to a remote computer. Script is as follows
$Username = "User"
$Password = "Pass"
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force"
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass"
$contents = [IO.File]::ReadAllBytes("D:\MyFile.xml")
Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes("D:\MyFile.xml",$using:contents)}
I store username and password of remote computer, to which I want to transfer my file, in variables. Convert password to secure string and create a credential object containing username and password. Use credential to copy the contents of file from my computer to remote computer.
This code works fine if I execute these commands one by one from my powershell or by running these lines from a .ps1 powershell script from my computer.
I used following code in C# to execute above script and transfer the file.
Runspace runSpace = RunspaceFactory.CreateRunspace();
Pipeline pipeline = runSpace.CreatePipeline();
runSpace.Open();
string script = "";
script = "$Username = \"User\"\n";
script = script + "$Password = \"Pass\"\n";
script = script + "$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force\n";
script = script + "$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass\n";
script = script + "$contents = [IO.File]::ReadAllBytes(\"D:\\MyFile.xml\")\n";
script = script + "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\\MyFile.xml\",$using:contents)}\n";
// pipeline.Runspace.SessionStateProxy.SetVariable
pipeline.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
However, it did not transfer the file. So, I thought I need to Add each line in script one by one to the pipeline and then execute it. So I changed it to following.
string[] commands = script.Split('\n');
foreach (string command in commands)
{
pipeline.AddScript(command);
}
Collection<PSObject> results = pipeline.Invoke();
It did not work either. I executed single Invoke-Command from C# code on my computer and it worked fine. Also, if I try to execute other single PS commands they work. So I assessed that setting variable values like this $User = "user" is not working through C#.
I used SessionStateSetProcy.SetVariable to Store variable values in powershell session as follows.
pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");
var value = pipeline.Runspace.SessionStateProxy.GetVariable("Password");
Console.WriteLine(value);
So, I was able to set value and retrieve as well. But, since I was using Other cmdlets like ConvertTo-SecureString, I realized I need to execute these cmdlets separately in my code.
I finally I came up with following piece of code.
pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");
Command command = new Command("ConvertTo-SecureString");
string pass_value = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("Password").ToString();
command.Parameters.Add("AsPlainText",pass_value);
command.Parameters.Add("Force");
pipeline.Runspace.SessionStateProxy.SetVariable("SecurePass",command);
/*Build a command2*/
Command command1 = new Command("New-Object");
string user_name = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("User").ToString();
command1.Parameters.Add("TypeName", "System.Management.Automation.PSCredential");
string args = user_name + " , "+pass_value;
command1.Parameters.Add("-ArgumentList", args);
pipeline.Runspace.SessionStateProxy.SetVariable("Cred", command1);
byte[] wifiXML = System.IO.File.ReadAllBytes(#"D:\MyFile.xml");
pipeline.Runspace.SessionStateProxy.SetVariable("contents", wifiXML);
Object a = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("contents");
string script = "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\MyFile.xml\",$using:contents)}";
pipeline.Commands.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
It gave some weird error in System.Management.Automation. I know in SetVariable I am doing it wrong by passing command variable.
I have tried PowerShell ps = PowerShell.Create() instead of pipeline as well and tried ps.AddCommand and ps.AddParameters followed by ps.AddArgument as well. But I am not sure that once I run cmdlet ConvertTo-SecureString through ps.AddCommad, ps.AddArgument etc., how should I set it's output value to $Cred variable?
I am aware that solution to this won't be this complicated and someone must have executed multiple line powershell script through C#. All I want to accomplish is execute the .ps1 script lines mentioned in very start from C# code.I have tried bunch of methods including mentioned above to no use, so any help related to it would be much appreciated?
Try in this way
string script = #"
$Username = 'User'
$Password = 'Password'
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass
$contents = [IO.File]::ReadAllBytes('D:\MyFile.xml')
Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes('D:\\MyFile.xml',$using:contents)}
";
pipeline.Commands.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
I'm currently trying to run a powershell script on my web service, which maps a drive to a shared folder on another system and copies a folder to it. The strange problem I currently have is that the script seems to be executed just fine, however the job in which the script is running doesn't seem to finish properly. Therefore I have to set a timeout for the task to be finished by force, otherwise it won't finish at all. However this is not really what I want, since it can have some nasty side effects if the script takes longer than expected etc. On the other hand I want to have the execution as fast as possible in the given scenario, so I'd like to let the script finished "naturally".
This is my current setup
The C# web service calls the powershell script like this:
public Collection<PSObject> executeCommand(String pCommand, String pName)
{
// Call the script
var runspaceConfiguration = RunspaceConfiguration.Create();
var runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
var pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(pCommand);
pipeline.Commands.AddScript("Wait-Job -Name " + pName + " -Timeout 60");
pipeline.Commands.AddScript("Receive-Job -Name " + pName);
return pipeline.Invoke();
}
String shareArguments = "some stuff here";
String shareCommandName = "copyFolder";
String shareCommand = "Start-Job -filepath " + currentDirectory + "\\scripts\\copyFolder.ps1 -ArgumentList " + shareArguments + " -Name " + shareCommandName + " -RunAs32";
Collection<PSObject> results1 = executeCommand(shareCommand, shareCommandName);
StreamWriter sharestream = new StreamWriter("D:\\shareoutput.txt");
foreach (PSObject obj in results1)
{
sharestream.WriteLine(obj.ToString());
}
sharestream.Close();
The script itself:
param($sharepath,$shareuser,$sharepassword,$hostname,$sourcefolder)
# create a credentials object
$secpasswd = ConvertTo-SecureString $sharepassword -AsPlainText -Force
Write-Output "0"
$cred = New-Object System.Management.Automation.PSCredential ($shareuser, $secpasswd)
Write-Output "1"
# Access the share
New-PSDrive -Name J -PSProvider FileSystem -Root $sharepath -Credential $cred
Write-Output "2"
# Copy the folder including the file
Copy-Item $sourcefolder "J:\" -Recurse -Force
Write-Output "3"
# Unmap drive
Remove-PSDrive -Name J
When I retrieve the debug output of the job, the output looks like this. So it seems that the New-PSDrive call seems to block here somehow:
0
1
Any idea what is the cause for this and how I can fix it?
Thanks in advance for any hint
You have problem with New-PSDrive. First Check your PSCredential object.
then update the script with
New-PSDrive -Name J -PSProvider FileSystem -Root $sharepath -Credential $cred -Persist
Perfect article:
https://technet.microsoft.com/en-us/library/hh849829.aspx