Looking at other articles and even this StackOverflow question, I still can't get this to work. The machine is contactable on the network but still produces an IO error.
The is the code I'm using:
var environmentKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "gm-1015").OpenSubKey("Environment");
TextBox1.Text = environmentKey.ToString();
When I try to run it, I get:
System.IO.IOException: 'The network path was not found.
Which from Microsoft's website indicates that the machine is not accessible.
Am I missing something?
Came to the conclusion that the remote registry service wasn't enabled or turned on for the remote computer. Instead of enabling it which would open another door into the computer for any intruders, I use the below code:
PowerShell ps = PowerShell.Create()
.AddCommand("Invoke-Command")
.AddParameter("ComputerName", SearcherClass.CurrentComputer)
.AddParameter("Scriptblock", ScriptBlock.Create("Get-Tpm | Select-Object -ExpandProperty TPMPresent"));
This starts a new PowerShell instance and runs the Invoke-Command to eventually run the Get-Tpm command which ultimately returns the TPMPresent property
This is the actual code block that I used to evaluate and return a usable result to my form:
public static string GetTPM()
{
string output = "N/A";
try
{
PowerShell ps = PowerShell.Create()
.AddCommand("Invoke-Command")
.AddParameter("ComputerName", SearcherClass.CurrentComputer)
.AddParameter("Scriptblock", ScriptBlock.Create("Get-Tpm | Select-Object -ExpandProperty TPMPresent"));
foreach(PSObject i in ps.Invoke())
{
output = i.BaseObject.ToString();
}
if (output == "True")
{
output = "Present";
}
else
{
output = "None";
}
return output;
}
catch
{
return "Failed";
}
}
Related
I am trying to get informations about MFA in my C# application. I already achieved to get satisfying results in Powershell but i'm struggling to make the same thing in C#.
My code in Powershell :
Get-MsolUser -SearchString c.test#mytenant.com |
Where-Object {$_.StrongAuthenticationRequirements -like “*”} |
Select-Object UserPrincipalName, DisplayName, #{n='MFA';e=
{$_.StrongAuthenticationRequirements.State}}, #{n='Methods'; e=
{($_.StrongAuthenticationMethods).MethodType}}, #{n='Default Method'; e=
{($_.StrongAuthenticationMethods).IsDefault}}
UserPrincipalName : c.test#mytenant.com
DisplayName : Cyril test
MFA : Enforced
Methods : {OneWaySMS, TwoWayVoiceMobile, PhoneAppOTP,
PhoneAppNotification}
Default Method : {False, False, False, True}
As you can see, i get the MFA status and methods used.
Now, i want to do the same in C#.
My function :
public static List<string> GetMFA(Runspace runspace, string nom)
{
List<string> listResult = new List<string>();
try
{
Command getLicenseCommand = new Command("Get-MsolUser");
getLicenseCommand.Parameters.Add(new CommandParameter("SearchString", nom));
var pipe = runspace.CreatePipeline();
pipe.Commands.Add(getLicenseCommand);
var props = new string[] { "displayname", "userprincipalname", "StrongAuthenticationRequirements" };
Command CommandSelect = new Command("Select-Object");
CommandSelect.Parameters.Add("Property", props);
pipe.Commands.Add(CommandSelect);
// Execute command and generate results and errors (if any).
Collection<PSObject> results = pipe.Invoke();
if (results.Count != 0)
{
var error = pipe.Error.ReadToEnd();
if (error.Count > 0)
{
throw new Exception(error[0].ToString());
}
foreach (PSObject resultat in results)
{
string dn = resultat.Properties["displayname"].Value.ToString();
string upn = resultat.Properties["userprincipalname"].Value.ToString();
string mfa = resultat.Properties["StrongAuthenticationRequirements"].Value.ToString();
string res = dn + '/' + upn + '/' + mfa;
listResult.Add(res);
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return listResult;
}
The "StrongAuthenticationRequirements" property isn't returning something like "Enforced" but
System.Collections.Generic.List`1[Microsoft.Online.Administration.StrongAuthenticationRequirement]
What am i missing here ?
I'm guessing the OP doesn't care any more, but for the next person who struggled with this like I have for the last few hours, I'm posting an answer.
In order to call these items, you must include the Microsoft.Online.Administration references which are included as DLLs within the MSOL PowerShell module package.
In Visual Studio:
Right Click References
Select "Add Reference"
Open the "Browse.."
Navigate to the installation location of the MSOL module. This may vary depending on if it was installed on the user/machine scope.
I added Microsoft.Online.Administration.PSModule
You will now be able to parse the results in c#.
Note: It was my experience that I needed to explicitly cast the items into a variable before I could use them.
Edit:
I removed the unnecessary reference to the resources.dll after further testing.
Attempting to pull the automatic update settings from the registry of a remote server. For some reason, it's returning a 0 even though a manual check of the key is 1-4. What am I overlooking? Snippet below:
ManagementScope msAutoUpdateReg = new ManagementScope(#"\\" + remoteServer + #"\root\DEFAULT:StdRegProv", connection);
msAutoUpdateReg.Connect();
ManagementClass ci = new ManagementClass(msAutoUpdateReg, new ManagementPath(#"DEFAULT:StdRegProv"), new ObjectGetOptions());
ManagementBaseObject inParams = ci.GetMethodParameters("GetDWORDValue");
inParams["hDefKey"] = 0x80000002; //HKLM
inParams["sSubKeyName"] = #"Software\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update";
inParams["sValueName"] = "AUOptions";
ManagementBaseObject outParams = ci.InvokeMethod("GetDWORDValue", inParams, null);
UInt32 auValue = (UInt32)outParams["uValue"];
if (auValue.ToString() != "0")
{
if (auValue == 1)
{
string currentSetting = "Keep my computer up to date has been disabled in Automatic Updates.";
}
if (auValue == 2)
{
string currentSetting = "Notify of download and installation.";
}
if (auValue == 3)
{
string currentSetting = "Automatically download and notify of installation.";
}
if (auValue == 4)
{
string currentSetting = "Automatically download and scheduled installation.";
}
}
else
{
string currentSetting = "Unknown";
}
I guess a process of elimination might help here...
1) Is this happening on just one server or are you getting this on all servers? How about on your own local machine? Is it a Windows version thing? For example it seems my Windows 10 box doesn't show the SubKey name you are looking for.
2) Do you also get zero if you change the sValueName to "foo"? Is a value of 0 representing an error?
3) Can you put a watch on outParams and check to see what values have been returned?
4) Are you being blocked by UAC, firewall or other permission issues? Can you execute other WMI commands against this server without any problems? Do you need to Run As Administrator to get this to work?
5) Are you getting an other exceptions or return values? I'm guessing you've posted just a portion of the code here so is this code inside a try/catch block?
Sorry if this sounds either vague or simplistic but I think you may need to look at what does work and what doesn't to see if you can identify a pattern.
I want to uninstall a software by using my code, I have already tried wmic approach to perform uninstallation but it can't able to find my Software in the system. Is it possible to uninstall without using msi file or any setup file. I found this code but it doesn't work---
public string GetUninstallCommandFor(string productDisplayName)
{
RegistryKey localMachine = Registry.LocalMachine;
string productsRoot = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products";
RegistryKey products = localMachine.OpenSubKey(productsRoot);
string[] productFolders = products.GetSubKeyNames();
foreach (string p in productFolders)
{
RegistryKey installProperties = products.OpenSubKey(p + #"\InstallProperties");
if (installProperties != null)
{
string displayName = (string)installProperties.GetValue("DisplayName");
if ((displayName != null) && (displayName.Contains(productDisplayName)))
{
string uninstallCommand = (string)installProperties.GetValue("UninstallString");
return uninstallCommand;
}
}
}
return "";
}
The most reliable way would be to programmatically execute the following shell command:
msiexec.exe /x {PRODUCT-GUID}
If you made the original MSI you will have access to your PRODUCT-GUID, and that is all you need. No need for the actual MSI file as Windows stashes a copy of this away for exactly this purpose.
Just FYI:
Windows ® Installer. V 5.0.14393.0
msiexec /Option <Required Parameter> [Optional Parameter]
Install Options
</package | /i> <Product.msi>
Installs or configures a product
/a <Product.msi>
Administrative install - Installs a product on the network
/j<u|m> <Product.msi> [/t <Transform List>] [/g <Language ID>]
Advertises a product - m to all users, u to current user
</uninstall | /x> <Product.msi | ProductCode>
Uninstalls the product
Display Options
/quiet
Quiet mode, no user interaction
/passive
Unattended mode - progress bar only
/q[n|b|r|f]
Sets user interface level
n - No UI
b - Basic UI
r - Reduced UI
f - Full UI (default)
/help
Help information
Restart Options
/norestart
Do not restart after the installation is complete
/promptrestart
Prompts the user for restart if necessary
/forcerestart
Always restart the computer after installation
Logging Options
/l[i|w|e|a|r|u|c|m|o|p|v|x|+|!|*] <LogFile>
i - Status messages
w - Nonfatal warnings
e - All error messages
a - Start up of actions
r - Action-specific records
u - User requests
c - Initial UI parameters
m - Out-of-memory or fatal exit information
o - Out-of-disk-space messages
p - Terminal properties
v - Verbose output
x - Extra debugging information
+ - Append to existing log file
! - Flush each line to the log
* - Log all information, except for v and x options
/log <LogFile>
Equivalent of /l* <LogFile>
Update Options
/update <Update1.msp>[;Update2.msp]
Applies update(s)
/uninstall <PatchCodeGuid>[;Update2.msp] /package <Product.msi | ProductCode>
Remove update(s) for a product
Repair Options
/f[p|e|c|m|s|o|d|a|u|v] <Product.msi | ProductCode>
Repairs a product
p - only if file is missing
o - if file is missing or an older version is installed (default)
e - if file is missing or an equal or older version is installed
d - if file is missing or a different version is installed
c - if file is missing or checksum does not match the calculated value
a - forces all files to be reinstalled
u - all required user-specific registry entries (default)
m - all required computer-specific registry entries (default)
s - all existing shortcuts (default)
v - runs from source and recaches local package
Setting Public Properties
[PROPERTY=PropertyValue]
try this
We get a ManagementObject property by using the following format:
The full block of code to list installed applications:
using System.Management
private List<string> ListPrograms()
{
List<string> programs = new List<string>();
try
{
ManagementObjectSearcher mos =
new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach (ManagementObject mo in mos.Get())
{
try
{
//more properties:
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa394378(v=vs.85).aspx
programs.Add(mo["Name"].ToString());
}
catch (Exception ex)
{
//this program may not have a name property
}
}
return programs;
}
catch (Exception ex)
{
return programs;
}
}
Now that we have a list of installed applications we should be able to pass the [Name] property to our uninstall method.
we now need to Invoke the Win32_Product method to “Uninstall”
Here is the entire block to uninstall an application, I'll get in detail after you take a look.
private bool UninstallProgram(string ProgramName)
{
try
{
ManagementObjectSearcher mos = new ManagementObjectSearcher(
"SELECT * FROM Win32_Product WHERE Name = '" + ProgramName + "'");
foreach (ManagementObject mo in mos.Get())
{
try
{
if (mo["Name"].ToString() == ProgramName)
{
object hr = mo.InvokeMethod("Uninstall", null);
return (bool)hr;
}
}
catch (Exception ex)
{
//this program may not have a name property, so an exception will be thrown
}
}
//was not found...
return false;
}
catch (Exception ex)
{
return false;
}
}
It's an old question and maybe too late to answer but here is my code anyway
public static void UninstallApplication(string uninstallString)
{
if (string.IsNullOrEmpty(uninstallString))
{
throw new ArgumentNullException(nameof(uninstallString));
}
ProcessStartInfo startInfo = new ProcessStartInfo();
int indexofexe = uninstallString.IndexOf(".exe");
//Check for executable existence
if (indexofexe > 0)
{
uninstallString = uninstallString.Replace(#"""", string.Empty);
//Get exe path
string uninstallerPath = uninstallString.Substring(0, indexofexe + 4);
startInfo.FileName = uninstallerPath;
//Check for arguments
if (uninstallerPath.Length != uninstallString.Length)
{
string args = uninstallString.Substring(uninstallerPath.Length);
if (!string.IsNullOrEmpty(args))
{
/*If not set to false You will get InvalidOperationException :
*The Process object must have the UseShellExecute property set to false in order to use environment variables.*/
startInfo.UseShellExecute = false;
startInfo.Arguments = args;
}
}
}
//Not tested
else
{
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/c " + uninstallString;
}
//Start the process
Process.Start(startInfo).WaitForExit();
}
This worked on most of my applications
I have recently began using C# to invoke powershell scripts, with some success :)
$spWeb = Get-SPWeb -Identity http://127.0.0.1
$listTemplate = [Microsoft.SharePoint.SPListTemplateType]::DocumentLibrary
$spWeb.Lists.Add($args, "", $listTemplate) > $myDirectory
$spWeb.Lists.Add returns a SharePoint GUID.
Question:
I simply wish to pass in the directory/filename in which the GUID will
be written. How could that be done?
I have posted on the MSDN forums here: http://goo.gl/5p0oz but have continued my search on stackoverflow due to a seemingly dead end thread. This post is a cumulative gathering of the information found through MSDN responses.
You can try using the Set-Content cmdlet instead, like this. You need to pass the $myFile as a string and Set-Content will do the rest for you -
Inside your script i.e. MyScript.ps1 here, you will have this piece of code -
param([string]$parameter, [string]$myFile)
try
{
$spWeb = Get-SPWeb -Identity http://127.0.0.1
$listTemplate = [Microsoft.SharePoint.SPListTemplateType]::DocumentLibrary
$guid = $spWeb.Lists.Add($parameter, "", $listTemplate)
$guid | Set-Content $myFile
}
catch
{
throw ("Failed. The error was: '{0}'." -f $_ )
}
How to Run:
Open Powershell Prompt and type this
.\MyScript.ps1 "someparam1" "D:\Output.txt"
C# Bit -
private PowerShell _ps;
_ps = PowerShell.Create();
var parameter = new List<string>
{
parameter,
myFile
};
var sr = new StreamReader(#"C:\MyScript.ps1");
_ps.AddScript(sr.ReadToEnd()).AddParameters(parameter);
_ps.Invoke();
I have some PowerShell hosts in C# from where I execute PowerShell script code. The code below is from a host in an Add0In for Visual Studio. Problem is that if an error occurs in the PowerShell script code that I don't know the file and line number of the PowerShell script file where the error occured.
My hosting code looks like:
public Exception Execute(string scriptcode, Hashtable variables)
{
Runspace runspace = null;
Pipeline pipeline = null;
PipelineStateInfo info = null;
try
{
// Make our output window active
Logger.ShowOutputWindow();
// Create the runspace and stuff.
runspace = RunspaceFactory.CreateRunspace(host);
pipeline = runspace.CreatePipeline();
runspace.Open();
// Add our scriptcode to the pipeline
pipeline.Commands.Add(new Command(scriptcode, true));
// We don't want to get PSObjects out of the pipeline, output result as string in default way
pipeline.Commands.Add(new Command("Out-Default", true));
// Set up global variables
FillVariables(runspace, variables);
SetUpOutputStreams(pipeline);
// Run the scriptcode
Collection<PSObject> psOutput = pipeline.Invoke();
// Did it complete ok?
info = pipeline.PipelineStateInfo;
if (info.State != PipelineState.Completed)
{
return info.Reason;
}
else
{
return null; // succesful!
}
}
catch (Exception ex)
{
return ex;
}
}
First I had my script in the scriptcode variable, I now write the code to a temporary .ps1 file first so I could report on linenumbers in that file. But I can't find how to execute code in a file so it is possible to retrieve filename and line numbers in case of errors.
Any ideas?
This should get you in the right place:
//invoke pipeline
collection = pipeline.Invoke();
// check for errors (non-terminating)
if (pipeline.Error.Count > 0)
{
//iterate over Error PipeLine until end
while (!pipeline.Error.EndOfPipeline)
{
//read one PSObject off the pipeline
var value = pipeline.Error.Read() as PSObject;
if (value != null)
{
//get the ErrorRecord
var r = value.BaseObject as ErrorRecord;
if (r != null)
{
//build whatever kind of message your want
builder.AppendLine(r.InvocationInfo.MyCommand.Name + " : " + r.Exception.Message);
builder.AppendLine(r.InvocationInfo.PositionMessage);
builder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo));
builder.AppendLine(
string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId));
}
}
}
return builder.ToString();
}
UPDATE:
As well as the information I wrote in the comment, please also look at this book: Professional PowerShell Programming
I found this book invaluable when I first started programming a host for the PowerShell runtime. It was written by some of the PowerShell devs.