WMI + Bitlocker + C# Get encrypted Volumes produces timeout - c#

i want to create a wrapper class for specific WMI functions that affect Bitlocker functionality. The first step is to get all the Bitlocker volumes of a machine so I created a Console Application and did this:
private static ManagementClass management;
private static ManagementObjectCollection Volumes = null;
static void Main(string[] args)
{
ManagementPath path = new ManagementPath();
path.Server = "";
path.NamespacePath = "\\ROOT\\CIMV2\\Security\\MicrosoftVolumeEncryption";
path.ClassName = "Win32_EncryptableVolume";
ConnectionOptions options = new ConnectionOptions();
options.Authentication = AuthenticationLevel.PacketPrivacy;
options.Impersonation = ImpersonationLevel.Impersonate;
ManagementScope scope = new ManagementScope(path, options);
ObjectGetOptions getOptions = new ObjectGetOptions();
management = new ManagementClass(scope, path, getOptions);
management.Get();
Volumes = management.GetInstances();
}
When I run this on a non-Bitlocker machine the Volumes Collection gets initialized OK, only that it has a Count of 0 of course. Now I copied the code over to a WinForms App and when I click a button to run this code it steps through OK but when I try to expand the collection during debugging the App hangs and I get a "Function evaluation timed out". It's the same code just in another Application. What could be the reason for this?

Hm. I got a null reference exception if I didn't run it as administrator, but when I ran it as administrator (Win 7 x64, btw), I got four Volumes back.

I just had a similar issue, I will post my code for you hopefully it helps.
ManagementObjectSearcher Encryption = new ManagementObjectSearcher(#"root\cimv2\Security\MicrosoftVolumeEncryption", "SELECT * FROM Win32_EncryptableVolume");
foreach (ManagementObject QueryObj in Encryption.Get())
{
string EncryptionStatus = QueryObj.GetPropertyValue("ProtectionStatus").ToString();
if (EncryptionStatus == "0")
{
EncryptionDialog.Text = "Unencrypted";
}
else if (EncryptionStatus == "1")
{
EncryptionDialog.Text = "Encrypted - SysPrep will not complete";
}
else if (EncryptionStatus == "2")
{
EncryptionDialog.Text = "Cannot Determine Encryption";
}
}
I'm using this to display the status for a sysprep tool i'm creating so the "EncryptionDialog.Text = ..." can be replaced with any other calls you may need. you also need to remember "which caused me issues at least" if you are using visual studio you will need to add a file to your project labeled "Application Manifest File" in the "Add New File" Dialog. The reason for this is that the application will need to be opened in Administrator mode(Just an FYI in case you haven't made it that far)

Related

Change service startup type to Delayed (Automatic) in remote machine

I am using the below code to change the service start in a remote server up type to manual/automatic using C#.
public static void ChangeServiceStartupType()
{
string query1 = "select * from Win32_Service where name = 'myservice' ";
string server = "servername";
ConnectionOptions connectoptions = new ConnectionOptions();
connectoptions.Username = #"username";
connectoptions.Password = "password";
ManagementScope scope = new ManagementScope(#"\\" + server + #"\root\cimv2");
scope.Options = connectoptions;
scope.Connect();
ObjectQuery query = new ObjectQuery(query1);
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
foreach (ManagementObject mo in managementObjectSearcher.Get())
{
string name = mo.Properties["Name"].Value.ToString().Trim().ToLower();
string state = mo.Properties["State"].Value.ToString().Trim();
string startmode = mo.Properties["StartMode"].Value.ToString().Trim();
changemode(mo, "Automatic");
}
}
Here is the changemode method
private static void changemode(ManagementObject mo, string startmode)
{
ManagementBaseObject inParams = mo.GetMethodParameters("ChangeStartMode");
inParams["startmode"] = startmode;
ManagementBaseObject outParams = mo.InvokeMethod("ChangeStartMode", inParams, null);
startmode = mo.Properties["StartMode"].Value.ToString().Trim();
}
When I pass the parameters Manual or Automatic in the changemode(object,startmode parameter) the service start up type changes from automatic to manual and vice-versa. However, I am unable to change it to Automatic(Delayed Start).
I tried Auto-Delayed , Delayed-Auto, Automatic (Delayed Start) How do I achieve this?
If there's a way through ManagementObject than I haven't found it, but it IS possible through the registry:
public void SetDalayedAutoStart(string machineName, string serviceName)
{
using (var regKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName))
{
using (RegistryKey serviceKey = regKey.OpenSubKey(#"System\CurrentControlSet\Services\" + serviceName, true))
{
serviceKey.SetValue("DelayedAutostart", 1, RegistryValueKind.DWord);
}
}
}
Note 1: This is only relevant for services with the Automatic start type
Note 2: The new state will be visible in the Services control (services.msc) only after a restart (don't ask me why)
Looking at the documentation, it doesn't seem to be possible to make the service start with a delay using the ChangeStartMode Win32 method. Doing what you want is fairly straightforward if you use ServiceInstaller, like so:
myServiceInstaller.StartType = ServiceStartMode.Automatic;
myServiceInstaller.DelayedAutoStart = true;
However I'm guessing that isn't an option, so we will have to dig deeper.
The Service class does have a DelayedAutoStart property, but it is read only. If you want to set it to delayed, you're going to have to mess around with P/Invokes. All information I could find points to ChangeServiceConfig2 and this struct.
Alternatively, you can just execute this command it it will have the same effect. However, it isn't really an answer to your question, just a workaround.
sc.exe config myService start= delayed-auto
Finally, check out this (very) long answer by user Kramii, and this by Peter Kelly. Both of them wrote helper classes to make doing this sort of thing a lot easier. I haven't tested them but they look promising.
Sty's answer lets you set a service to automatic delayed at the time of creation; not post deployment.
There is a command line that can do that
sc \\computername config *servicename* start= delayed-auto
I ran this command line in the remote server using WMI Management Class and it works fine.
More documentation on how to remote start a process here

check if a process runs on a remote machine for a certain user

I have to create an executable which checks if a certain process is running for a certain user (a service account) on a remote machine, the input parameters are 3 strings, machine name, user name and process name.
I have the idea to do this using either System.Diagnostics or WMI, just wanted to double check if anybody has another idea like powershell or even a window functionality which could make the task even easier.
since we want to make sure that process is always running on a dedicated server we will configure a scheduled task to execute a small console application which does this check. Not sure if coding it in C# is the best option or am I ignoring a builtin feature of windows server? Thanks!
I'm pretty sure you can accomplish this with tasklist cmd: tasklist /S \\<server> /V > tasklist.txt. this will give you a file you can grep through.
namespace not referenced
using System.Management;
I have ended up by implementing following solution in C#
this retrieves the username without domain name of the user running processName on machineName
public static string GetProcessOwner()
{
try
{
var resultUserName = string.Empty;
ConnectionOptions opt = new ConnectionOptions();
string path = string.Format(#"\\{0}\root\cimv2", machineName);
ManagementScope scope = new ManagementScope(path, opt);
scope.Connect();
var query = new ObjectQuery(string.Format("Select * From Win32_Process Where Name = '{0}'", processName));
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var processList = searcher.Get();
foreach (ManagementObject obj in processList)
{
string[] argList = new string[] { string.Empty, string.Empty };
int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnVal == 0)
{
// return DOMAIN\user
//return argList[1] + "\\" + argList[0];
resultUserName = argList[0];
}
}
return resultUserName;
}
catch (Exception exc)
{
Debug.WriteLine(exc.Message);
return string.Empty;
}
}
GetOwner can return empty array for remote comp, so it may not work

Remotely change computer name for a Windows Server 2008 machine using C#?

Might someone be able to point me towards a conclusive resource to learn how to remotely change a computer name on a Windows Server 2008 machine using C#
I've looked at lots of sites for help and now in day two of my task and not really any closer (other than deciding WMI is pretty much my only option) Totally out of my normal skillset so I guess pretty much any info would be nice, but especially anything having to do with changing a computer name remotely. (this would occur right after I remotely spin up a virutal from an image...and yes, i realize a reboot will be required)
thanks
Here is a nice link that discusses it in detail and also deals with active directory membership and machine naming in addition to the local machine name.
http://derricksweng.blogspot.com/2009/04/programmatically-renaming-computer.html
(Btw, should you have to deal with Active Directory naming, I would consider using the ComputerPrincipal class from the System.DirectoryServices.AccountManagement namespace vice anything from System.DirectoryServices namespace that was used in the blog post.)
Tweaked code from the blog post (you will need to add a reference to System.Management to your project):
public void RenameRemotePC(String oldName, String newName, String domain, NetworkCredential accountWithPermissions)
{
var remoteControlObject = new ManagementPath
{
ClassName = "Win32_ComputerSystem",
Server = oldName,
Path =
oldName + "\\root\\cimv2:Win32_ComputerSystem.Name='" + oldName + "'",
NamespacePath = "\\\\" + oldName + "\\root\\cimv2"
};
var conn = new ConnectionOptions
{
Authentication = AuthenticationLevel.PacketPrivacy,
Username = oldName + "\\" + accountWithPermissions.UserName,
Password = accountWithPermissions.Password
};
var remoteScope = new ManagementScope(remoteControlObject, conn);
var remoteSystem = new ManagementObject(remoteScope, remoteControlObject, null);
ManagementBaseObject newRemoteSystemName = remoteSystem.GetMethodParameters("Rename");
var methodOptions = new InvokeMethodOptions();
newRemoteSystemName.SetPropertyValue("Name", newName);
newRemoteSystemName.SetPropertyValue("UserName", accountWithPermissions.UserName);
newRemoteSystemName.SetPropertyValue("Password", accountWithPermissions.Password);
methodOptions.Timeout = new TimeSpan(0, 10, 0);
ManagementBaseObject outParams = remoteSystem.InvokeMethod("Rename", newRemoteSystemName, null);
}

Check anti-virus status in C#

I need to check a group of servers to see whether the anti virus is up-to-date and running. Tricky thing is that they are spread over Windows 2003 and 2008 servers and I need to be able to check them all.
Is there any way of doing this with C# or VB.NET?
I have briefly looked around using WMI, but it appears on 2008/win7 computers Microsoft has changed what information they give back to you.
In summary, I need the following:
AV name
AV version
AV Up-to-Date
AV Enabled/Running
Can anyone help?
Sample can be found here using WMI as you mentioned. The poster states this is being done on a Win 7 machine; so the code below should get you started...
ConnectionOptions _connectionOptions = new ConnectionOptions();
//Not required while checking it in local machine.
//For remote machines you need to provide the credentials
//options.Username = "";
//options.Password = "";
_connectionOptions.EnablePrivileges = true;
_connectionOptions.Impersonation = ImpersonationLevel.Impersonate;
//Connecting to SecurityCenter2 node for querying security details
ManagementScope _managementScope = new ManagementScope(string.Format("\\\\{0}\\root\\SecurityCenter2", ipAddress), _connectionOptions);
_managementScope.Connect();
//Querying
ObjectQuery _objectQuery = new ObjectQuery("SELECT * FROM AntivirusProduct");
ManagementObjectSearcher _managementObjectSearcher =
new ManagementObjectSearcher(_managementScope, _objectQuery);
ManagementObjectCollection _managementObjectCollection = _managementObjectSearcher.Get();
if (_managementObjectCollection.Count > 0)
{
foreach (ManagementObject item in _managementObjectCollection)
{
Console.WriteLine(item["displayName"]);
//For Kaspersky AntiVirus, I am getting a null reference here.
//Console.WriteLine(item["productUptoDate"]);
//If the value of ProductState is 266240 or 262144, its an updated one.
Console.WriteLine(item["productState"]);
}
}
Depending on how your environment is setup you may need to specify your security and permissions. You should also note that some antivirus products (like McAfee) do not make data available through WMI.
You can query the Antivirus information from WMI using this snippet:
string computer = Environment.MachineName;
string wmipath = #"\\" + computer + #"\root\SecurityCenter";
string query = #"SELECT * FROM AntivirusProduct";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmipath, query);
ManagementObjectCollection results = searcher.Get();
foreach (ManagementObject result in results)
{
// do something with `result[value]`);
}

How to read an IIS 6 Website's Directory Structure using WMI?

I need to read a website's folders using WMI and C# in IIS 6.0. I am able to read the Virtual directories and applications using the "IISWebVirtualDirSetting" class. However the physical folders located inside a website cannot be read using this class.
And for my case i need to read sub folders located within a website and later on set permission on them. For my requirement i dont need to work on Virtual Directories/Web Service Applications (which can be easily obtained using the code below..).
I have tried to use IISWebDirectory class but it has been useful.
Here is the code that reads IIS Virtual Directories...
public static ArrayList RetrieveVirtualDirList(String ServerName, String WebsiteName)
{
ConnectionOptions options = SetUpAuthorization();
ManagementScope scope = new ManagementScope(string.Format(#"\\{0}\root\MicrosoftIISV2", ServerName), options);
scope.Connect();
String SiteId = GetSiteIDFromSiteName(ServerName, WebsiteName);
ObjectQuery OQuery = new ObjectQuery(#"SELECT * FROM IISWebVirtualDirSetting");
//ObjectQuery OQuery = new ObjectQuery(#"SELECT * FROM IIsSetting");
ManagementObjectSearcher WebSiteFinder = new ManagementObjectSearcher(scope, OQuery);
ArrayList WebSiteListArray = new ArrayList();
ManagementObjectCollection WebSitesCollection = WebSiteFinder.Get();
String WebSiteName = String.Empty;
foreach (ManagementObject WebSite in WebSitesCollection)
{
WebSiteName = WebSite.Properties["Name"].Value.ToString();
WebsiteName = WebSiteName.Replace("W3SVC/", "");
String extrctedSiteId = WebsiteName.Substring(0, WebsiteName.IndexOf('/'));
String temp = WebsiteName.Substring(0, WebsiteName.IndexOf('/') + 1);
String VirtualDirName = WebsiteName.Substring(temp.Length);
WebsiteName = WebsiteName.Replace(SiteId, "");
if (extrctedSiteId.Equals(SiteId))
//if (true)
{
WebSiteListArray.Add(VirtualDirName );
//WebSiteListArray.Add(WebSiteName);
//+ "|" + WebSite.Properties["Path"].Value.ToString()
}
}
return WebSiteListArray;
}
P.S:
I need to programmatically get the sub folders of an already deployed site(s) using WMI and C# in an ASP. Net Application. I need to find out the sub folders of existing websites in a local or remote IIS 6.0 Web Server. So i require a programmatic solution. Precisely if i am pointed at the right class (like IISWebVirtualDirSetting etc ) that i may use for retrieving the list of physical folders within a website then it will be quite helpful.
I am not working in Powershell and i don't really need a solution that involves powershell or vbscripts.
Any alternative programmatic way of doing the same in C#/ASP.Net will also be highly appreciated.
EDIT:
GetSiteIdFromSiteName
The code may look like this:
public static int GetSiteIdFromSiteName(String ServerName, String WebsiteName)
{
ConnectionOptions options = SetUpAuthorization();
ManagementScope scope = new ManagementScope(string.Format(#"\\{0}\root\MicrosoftIISV2", ServerName), options);
scope.Connect();
ObjectQuery OQuery = new ObjectQuery(#"SELECT * FROM IIsSetting");
ManagementObjectSearcher WebSiteFinder = new ManagementObjectSearcher(scope, OQuery);
ArrayList WebSiteListArray = new ArrayList();
ManagementObjectCollection WebSitesCollection = WebSiteFinder.Get();
String WebSiteName = String.Empty;
foreach (ManagementObject WebSite in WebSitesCollection)
{
WebSiteName = WebSite.Properties["Name"].Value.ToString();
WebsiteName = WebSiteName.Replace("W3SVC/", "");
String extrctedSiteId = WebsiteName.Substring(0, WebsiteName.IndexOf('/'));
break;
}
return extrctedSiteId;
}
and
SetupAuthorization()
Here is the SetupAuthorization function.
public ConnectionOptions SetUpAuthorization()
{
ConnectionOptions options = new ConnectionOptions();
//options.Authentication = AuthenticationLevel.Connect;
//may need to set Packetprivacy enum
options.Authentication = AuthenticationLevel.PacketPrivacy;
options.EnablePrivileges = true;
options.Impersonation = ImpersonationLevel.Impersonate;
return options;
}
Regards
Never mind. i got it myself.
If the directory structure is on local system the System.IO.DirectoryInfo is what is needed. Using remoting the same can be used for remote server as well.
Reference:
http://msdn.microsoft.com/en-us/library/system.io.directoryinfo.aspx
If WMi has to be there then Win32_Directory is the class which should used in query:
"Select * from Win32_directory where drive ='"+ DriveLetter +":' and Path ='%" + MyPath + "%'";
Reference:
http://msdn.microsoft.com/en-us/library/aa394130(VS.85).aspx
Have you looked at the Web Deployment Tool?
http://www.iis.net/expand/WebDeploy
Ability to package ACLs, COM, GAC and registry settings.

Categories

Resources