I write program on c#. I need set trigger, that check: if installed Azure PowerShell or not. So how it write? If exist unique path for Azure PowerShell? Or better see on register?
One way to check the installation status/version of Azure Powershell is to type this cmdlet in your PowerShell session.
Get-module azure
If you mean to check it programmatically from code, I believe registry is the right way.
Hello. You can try to do like this, maybe it help you:
try
{
var pipe = PowerShell.Create().AddScript("Get-Module -ListAvailable" +
" | Where-Object{ $_.Name -like 'Azure*' }" +
"|Select Version, Name, Author, PowerShellVersion").AddCommand("out-string");
Collection<PSObject> results = pipe.Invoke();
foreach (var result in results)
{
Console.WriteLine(result);
Console.ReadKey();
}
}
catch (Exception Ex)
{
Console.WriteLine(Ex.ToString());
Console.ReadKey();
}
Related
I am trying to get the list of virtual machine names, from a local Hyper-V server, with the following code:
string _scr = ("Get-VM | Select -ExpandProperty Name");
var _ps = PowerShell.Create();
_ps.AddScript(_scr);
Collection<PSObject> _cObj = _ps.Invoke();
foreach (PSObject _vm in _cObj)
{
Console.WriteLine(_vm);
}
The Get-VM cmdlet should return values in string format already, but I don't get any output.
I would like to get a result like the following output we get in PowerShell:
PS C:\> Get-VM | Select -ExpandProperty Name
VM-Name1
VM-Name2
VM-Name3
VM-Name4
PS C:\>
Can anyone help me with this please?
Thanks a million.
Ada
Get-VM only works in a PowerShell session run as administrator. When I try your C# code run as an administrator it does print VM names.
When I try it without administrator rights I get no output, same as you - but there is an error reported if you check _ps.HadErrors it's true. Try this version of your code:
string _scr = ("Get-VM | Select -ExpandProperty Name");
var _ps = PowerShell.Create();
_ps.AddScript(_scr);
Collection<PSObject> _cObj = _ps.Invoke();
if (_ps.HadErrors) {
Console.WriteLine(_ps.Streams.Error[0].ToString());
}
foreach (PSObject _vm in _cObj)
{
Console.WriteLine(_vm);
}
I get "You do not have the required permission to complete this task. Contact the administrator of the authorization policy for the computer {name}", you may get another error which gives you an idea what is going wrong.
(Another consideration, if the Hyper-V module is not being autoloaded you may need Import Hyper-V; at the beginning of your PowerShell code).
This script works when running in PowerShell ISE (it sets the given user's Remote Desktop Services Profile settings in Active Directory):
Get-ADUser FirstName.LastName | ForEach-Object {
$User = [ADSI]"LDAP://$($_.DistinguishedName)"
$User.psbase.invokeset("TerminalServicesProfilePath","\\Server\Share\HomeDir\Profile")
$User.psbase.invokeset("TerminalServicesHomeDrive","H:")
$User.psbase.invokeset("TerminalServicesHomeDirectory","\\Server\Share\HomeDir")
$User.setinfo()
}
But when I try running it from a C# application I get an error for each invokeset that I call:
Exception calling "InvokeSet" with "2" argument(s):
"Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))"
Here is the code, which is inside my PowerShell class:
public static List<PSObject> Execute(string args)
{
var returnList = new List<PSObject>();
using (var powerShellInstance = PowerShell.Create())
{
powerShellInstance.AddScript(args);
var psOutput = powerShellInstance.Invoke();
if (powerShellInstance.Streams.Error.Count > 0)
{
foreach (var error in powerShellInstance.Streams.Error)
{
Console.WriteLine(error);
}
}
foreach (var outputItem in psOutput)
{
if (outputItem != null)
{
returnList.Add(outputItem);
}
}
}
return returnList;
}
And I call it like this:
var script = $#"
Get-ADUser {newStarter.DotName} | ForEach-Object {{
$User = [ADSI]""LDAP://$($_.DistinguishedName)""
$User.psbase.invokeset(""TerminalServicesProfilePath"",""\\file\tsprofiles$\{newStarter.DotName}"")
$User.psbase.invokeset(""TerminalServicesHomeDrive"",""H:"")
$User.psbase.invokeset(""TerminalServicesHomeDirectory"",""\\file\home$\{newStarter.DotName}"")
$User.setinfo()
}}";
PowerShell.Execute(script);
Where newStarter.DotName contains the (already existing) AD user's account name.
I tried including Import-Module ActveDirectory at the top of the C# script, but with no effect. I also called $PSVersionTable.PSVersion in both the script running normally and the C# script and both return that version 3 is being used.
After updating the property names to
msTSProfilePath
msTSHomeDrive
msTSHomeDirectory
msTSAllowLogon
I am getting this error in C#:
Exception calling "setinfo" with "0" argument(s): "The attribute syntax specified to the directory service is invalid.
And querying those properties in PowerShell nothing (no error but also no output)
Does anyone happen to know what could cause this?
Many thanks
Updated answer: It seems that these attributes don't exist in 2008+. Try these ones instead:
msTSAllowLogon
msTSHomeDirectory
msTSHomeDrive
msTSProfilePath
See the answer in this thread for the full explanation.
Original Answer:
The comment from Abhijith pk is probably the answer. You need to run Import-Module ActiveDirectory, just like you need to do in the command line PowerShell.
If you've ever run Import-Module ActiveDirectory in the PowerShell command line, you'll know it takes a while to load. It will be the same when run in C#. So if you will be running several AD commands in your application, you would be better off keeping a Runspace object alive as a static object and reuse it, which means you only load the ActiveDirectory module once.
There is details here about how to do that in C#:
https://blogs.msdn.microsoft.com/syamp/2011/02/24/how-to-run-an-active-directory-ad-cmdlet-from-net-c/
Particularly, this is the code:
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new string[] { "activedirectory" });
Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss);
myRunSpace.Open();
I´m making a program to delete some files that I have on my PC. But when I try to do it, I get some error messages like this:
If you are attempting to access a file, make sure it is not ReadOnly.
Make Sure you have sufficient privileges to access this resource.
Get general Help for this exception.
foreach (string subFich in SubFicheiros)
{
listBox.Items.Add("- Deleting File: " + subFich.Substring(Pasta.Length + 1, subFich.Length - Pasta.Length - 1));
ficheirosEncontrador++;
}
try
{
Directory.Delete(Pasta, true);
}
catch (IOException)
{
Thread.Sleep(0);
//The Message Error appears here on this code right below:
Directory.Delete(Pasta, true);
}
catch (UnauthorizedAccessException)
{
Directory.Delete(Pasta, true);
}
}
I would like to get some help with this.
How do i ask the user, to let me get the privilegies to delete it.
Well.. what your code doing is: You're deleting the directory and if it gives any exception then you're again trying to do the same step where you got exception.
First of all error is because files are set to read only or because you dont have enough rights to delete the directory (or probably some process is using the files which you are trying to delete)
foreach (string subFich in SubFicheiros)
{
listBox.Items.Add("- Deleting File: " + subFich.Substring(Pasta.Length + 1, subFich.Length - Pasta.Length - 1));
ficheirosEncontrador++;
}
try
{
var di = new DirectoryInfo(Pasta);
di.Attributes &= ~FileAttributes.ReadOnly;
Directory.Delete(Pasta, true);
}
catch (Exception EE)
{
MessageBox.Show("Error: "+ EE.toString());
}
if this code still doesn't work check if you have admin rights to delete that folder
Sounds like your file is read-only, or you do not have the right to remove the file you want based on your user login.
I have an application which (among other things) needs to call the New-MailContact cmdlet and create contacts in Active Directory. I have followed a handful of tech articles to get as far as I have, but it is still not working.
I have verified my service account being used has the proper authentication based on this TechNet page. I am able to find and invoke the cmdlet from powershell, and I recieve no errors.
However, after running I inspect my OU and my contact was not created. I found this KB article which I think may be suspect, but since the cmdlet doesn't return any errors after the invoke, I can't be sure that this will solve my problem.
Here is a snippet of what I am doing:
public bool CreateMailContactObject(ADExchangeContact adExchangeContacts)
{
Collection<PSObject> results;
Pipeline pipeLine = null;
try
{
var runspaceConfiguration = RunspaceConfiguration.Create();
PSSnapInException snapInException;
var snapInInfo = runspaceConfiguration.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException);
using (var runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
{
var newMailBoxContact = new Command("New-MailContact");
newMailBoxContact.Parameters.Add("Name", adExchangeContacts.DisplayName);
newMailBoxContact.Parameters.Add("ExternalEmailAddress", adExchangeContacts.ExternalEmailAddress);
newMailBoxContact.Parameters.Add("OrganizationalUnit", adExchangeContacts.OrganizationalUnit);
newMailBoxContact.Parameters.Add("Alias", adExchangeContacts.Alias);
runspace.Open();
pipeLine = runspace.CreatePipeline();
pipeLine.Commands.Add(newMailBoxContact);
results = pipeLine.Invoke();
_log.DebugFormat("results.Count = {0}", results.Count);
results.ForEach(x => x.Properties.ForEach(y => _log.DebugFormat("{0}: {1}", y.Name, y.Value)));
pipeLine.Stop();
runspace.Close();
}
return true;
}
catch (Exception ex)
{
// Add log statement
_log.ErrorFormat("Creation of Mail Contact in AD Failed. Error: {0}", ex);
return false;
}
}
I do not get any exceptions, and my result list is empty from the Pipeline invoke. Is there something I am missing? If the cmdlet fails due to permissions when creating the contact in AD, wouldn't I expect to recieve some sort of error in the result set from pipeLine.Invoke() ??
I am new with running Powershell, so if there is another issue (beyond the KB article) that could be at hand, please let me know.
if (pipeline.Error != null && pipeline.Error.Count > 0)
{
StringBuilder pipelineError = new StringBuilder();
pipelineError.AppendFormat("Error calling Add-MailboxPermission.");
foreach (object item in thisPipeline.Error.ReadToEnd())
{
pipelineError.AppendFormat("{0}\n", item.ToString());
}
ErrorText = ErrorText + "Error: " + pipelineError.ToString() + Environment.NewLine;
}
Please Put this code after pipeline.Invoke() and check if there is any error in there
Update:
I think this is erro with giving right permission to user,some solutions for this:
http://boardreader.com/thread/Microsoft_Exchange_2010_wont_allow_new_M_1w69j__37ad9f8a-cdcf-4d26-9384-00ad1a3d0f91.html
http://blogs.technet.com/b/richardroddy/archive/2010/07/12/exchange-2010-and-the-exchange-trusted-subsystem.aspx
I have the following sample Powershell script that is embedded in my C# application.
Powershell Code
$MeasureProps = "AssociatedItemCount", "ItemCount", "TotalItemSize"
$Databases = Get-MailboxDatabase -Status
foreach($Database in $Databases) {
$AllMBStats = Get-MailboxStatistics -Database $Database.Name
$MBItemAssocCount = $AllMBStats | %{$_.AssociatedItemCount.value} | Measure-Object -Average -Sum
$MBItemCount = $AllMBStats | %{$_.ItemCount.value} | Measure-Object -Average -Sum
New-Object PSObject -Property #{
Server = $Database.Server.Name
DatabaseName = $Database.Name
ItemCount = $MBItemCount.Sum
}
}
Visual Studio offers me the following embedding options:
Every PowerShell sample I've seen (MSDN on Exchange, and MSFT Dev Center) required me to chop up the Powershell command into "bits" and send it through a parser.
I don't want to leave lots of PS1 files with my application, I need to have a single binary with no other "supporting" PS1 file.
How can I make it so myapp.exe is the only thing that my customer sees?
Many customers are averse to moving away from a restricted execution policy because they don't really understand it. It's not a security boundary - it's just an extra hoop to jump through so you don't shoot yourself in the foot. If you want to run ps1 scripts in your own application, simply use your own runspace and use the base authorization manager which pays no heed to system execution policy:
InitialSessionState initial = InitialSessionState.CreateDefault();
// Replace PSAuthorizationManager with a null manager which ignores execution policy
initial.AuthorizationManager = new
System.Management.Automation.AuthorizationManager("MyShellId");
// Extract psm1 from resource, save locally
// ...
// load my extracted module with my commands
initial.ImportPSModule(new[] { <path_to_psm1> });
// open runspace
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
RunspaceInvoke invoker = new RunspaceInvoke(runspace);
// execute a command from my module
Collection<PSObject> results = invoker.Invoke("my-command");
// or run a ps1 script
Collection<PSObject> results = invoker.Invoke("c:\temp\extracted\my.ps1");
By using a null authorization manager, execution policy is completed ignored. Remember - this is not some "hack" because execution policy is something for protecting users against themselves. It's not for protecting against malicious third parties.
http://www.nivot.org/nivot2/post/2012/02/10/Bypassing-Restricted-Execution-Policy-in-Code-or-in-Script.aspx
First of all you should try removing your customer's aversion To scripts. Read up about script signing, execution policy etc.
Having said that, you can have the script as a multiline string in C# code itself and execute it.Since you have only one simple script, this is the easiest approach.
You can use the AddScript ,ethos which takes the script as a string ( not script path)
http://msdn.microsoft.com/en-us/library/dd182436(v=vs.85).aspx
You can embed it as a resource and retrieve it via reflection at runtime. Here's a link from MSDN. The article is retrieving embedded images, but the principle is the same.
You sort of hovered the answer out yourself. By adding it as content, you can get access to it at runtime (see Application.GetResourceStream). Then you can either store that as a temp file and execute, or figure out a way to invoke powershell without the use of files.
Store your POSH scripts as embedded resources then run them as needed using something like the code from this MSDN thread:
public static Collection<PSObject> RunScript(string strScript)
{
HttpContext.Current.Session["ScriptError"] = "";
System.Uri serverUri = new Uri(String.Format("http://exchangsserver.contoso.com/powershell?serializationLevel=Full"));
RunspaceConfiguration rc = RunspaceConfiguration.Create();
WSManConnectionInfo wsManInfo = new WSManConnectionInfo(serverUri, SHELL_URI, (PSCredential)null);
using (Runspace runSpace = RunspaceFactory.CreateRunspace(wsManInfo))
{
runSpace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
PowerShell posh = PowerShell.Create();
posh.Runspace = runSpace;
posh.AddScript(strScript);
Collection<PSObject> results = posh.Invoke();
if (posh.Streams.Error.Count > 0)
{
bool blTesting = false;
string strType = HttpContext.Current.Session["Type"].ToString();
ErrorRecord err = posh.Streams.Error[0];
if (err.CategoryInfo.Reason == "ManagementObjectNotFoundException")
{
HttpContext.Current.Session["ScriptError"] = "Management Object Not Found Exception Error " + err + " running command " + strScript;
runSpace.Close();
return null;
}
else if (err.Exception.Message.ToString().ToLower().Contains("is of type usermailbox.") && (strType.ToLower() == "mailbox"))
{
HttpContext.Current.Session["ScriptError"] = "Mailbox already exists.";
runSpace.Close();
return null;
}
else
{
HttpContext.Current.Session["ScriptError"] = "Error " + err + "<br />Running command " + strScript;
fnWriteLog(HttpContext.Current.Session["ScriptError"].ToString(), "error", strType, blTesting);
runSpace.Close();
return null;
}
}
runSpace.Close();
runSpace.Dispose();
posh.Dispose();
posh = null;
rc = null;
if (results.Count != 0)
{
return results;
}
else
{
return null;
}
}
}
The customer just can't see the PowerShell script in what you deploy, right? You can do whatever you want at runtime. So write it to a temporary directory--even try a named pipe, if you want to get fancy and avoid files--and simply start the PowerShell process on that.
You could even try piping it directly to stdin. That's probably what I'd try first, actually. Then you don't have any record of it being anywhere on the computer. The Process class is versatile enough to do stuff like that without touching the Windows API directly.