C# SCCM - Client Action - c#

I've seen many ways in powershell to force a computer to do a client action from the configuration manager.
Where I do work, it is not possible because we can't invoke commands on distant computer because it is blocked and the senior IT do not want to unlock it.
I did find a library in c# that allow me to do some action in sccm :
AdminUI.SmsTraceListener.dll
AdminUI.WqlQueryEngine.dll
I can add/remove computer to a collections, make queries and get the data, but I didn't find the way to force a computer to make an action from the configuration manager.
Is there someone here that knows if it is possible and how?
Thanks.
Edit 1: While searching in the MSDN documentation, I did find the TriggerSchedule Method in Class SMS_Client but I don't find the way to use it correctly. I think it might be the way to go, but i'm still stuck on this.

It is possible to trigger an Machine Policy Update via TriggerSchedule like this
ManagementScope scope = new ManagementScope(#"\\.\root\ccm");
ManagementClass cls = new ManagementClass(scope.Path.Path, "SMS_Client", null);
ManagementBaseObject inParams = cls.GetMethodParameters("TriggerSchedule");
inParams["sScheduleID"] = "{00000000-0000-0000-0000-000000000021}";
ManagementBaseObject outMPParams = cls.InvokeMethod("TriggerSchedule", inParams, null);
You already found the other Parameters for the sScheduleID in the link you posted. This uses standard WMI. With WqlQueryEngine you would get access to some WMI wrappers that can basically do the same thing. I do not see many advantages however.
Using the scope like this
\\.\root\ccm
makes the whole thing only work locally which is what you want if I understood you correctly. Otherwise replacing the . With a hostname or IP would make it work remotely. Only thing I found a bit strange is that it needs administrative rights, which should in theory not be necessary for a policy update request.

if someone is having the issue that nothing is happening, it is because WMI required higher rights. To leave triggering the actions also by the user, I switched to use the CPApplet:
TriggerSccmActions("Request & Evaluate", true);
private static List<string> TriggerSccmActions(string stringActions, bool boolContains)
{
List<string> actionName = new List<string>();
try {
const string ProgID = "CPApplet.CPAppletMgr";
Type foo = Type.GetTypeFromProgID(ProgID);
dynamic COMobject = Activator.CreateInstance(foo);
var oClientActions = COMobject.GetClientActions;
foreach (var oClientAction in oClientActions)
{
if (oClientAction.Name.ToString().Contains(stringActions) && boolContains)
{
var result = oClientAction.PerformAction();
actionName.Add(oClientAction.Name.ToString());
}
else if (!(oClientAction.Name.ToString().Contains(stringActions)) && !(boolContains))
{
var result = oClientAction.PerformAction();
actionName.Add(oClientAction.Name.ToString());
}
}
} catch(Exception e)
{
actionName.Add("Error: " + e.Message.ToString());
}
return actionName;
}

For me, EvaluateMachinePolicy Method in Class SMS_Client class worked. Here is the code:
public static void RefreshMachinePolicy(string machineName)
{
ManagementScope scope = new ManagementScope(string.Format(#"\\{0}\root\ccm", machineName));
ManagementClass cls = new ManagementClass(scope.Path.Path, "SMS_Client", null);
ManagementBaseObject inParams = cls.GetMethodParameters("EvaluateMachinePolicy");
ManagementBaseObject outMPParams = cls.InvokeMethod("EvaluateMachinePolicy", inParams, null);
Console.WriteLine("Policy refreshed successfully by EvaluateMachinePolicy method");
}
Here is the MSDN link for method details. Please include below namespace at the top of your source code file:
using System.Management;

Related

How to obtain IP addresses of everything on network in c#

I have a sonicwall on a network of around 100 machines.
I have tried and tried to find a way of creating 2 combobox's that will contain both the IP addresses linked to the sonicwall, as well as the currently logged in user of the machine as well.
What i am trying to create is a employee monitoring software (like Interguard/ActivTrak/etc), but am unable at present to even get near to finding this information?
I've been researching A LOT of stuff (~25 pages of google, with not a single link not clicked), and have met no conclusion as to get this information.
I'm not exactly a great programmer, but I would LOVE to be able to get this sort of information into two combobox's/arrays/other suitable object/control.
If anyone knows a way of even creating an array of IP addresses, along with corresponding Logins, that would be of great help to this project!
(PLEASE NOTE: I know there is a Networking exchange site, but I have already looked! Also, since i'm designing a piece of software for this, I thought i'd ask it here!)
Thanks for any advice/suggestions much appreciated!
What you looking for is similar to a packet sniffer application like wireshark or ethereal. But if you want to design it yourself, I think your best solution is a collection/list to store the data that is binded to a datagrid.
If you're in an Active Directory domain, you could use the code from my S/O question. Instead of using the CheckConnectionAsync() method to check for connectivity, you could just re-write it to get the IP address using System.Net.Dns.
To retrieve the user, you can use WMI. For this, you need to make a reference to the System.Management namespace.
public Task<string> GetUserName(string ComputerName)
{
return Task.Run(() =>
{
ConnectionOptions conn = ConnectionOptions()
{
EnablePrivileges = true,
Username = // string in format of #"DomainName\UserName",
Password = password,
Authentication = AuthenticationLevel.PacketPrivacy,
Impersonation = ImpersonationLevel.Impersonate
};
ManagementScope scope = new ManagementScope(#"\\" + ComputerName + #"\root\cimv2", conn);
try
{
scope.Connect();
ObjectQuery user = new ObjectQuery("Select UserName From Win32_ComputerSystem");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, user);
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject m in collection)
{
string username = m["UserName"].ToString().Trim();
if(!String.IsNullOrEmpty(username))
{
return username;
}
}
return null; // no current logged in user
}
catch (Exception) // error handling...
});
}
Then, just iterate through your collection of computers and retrieve the username like so:
private async Task RetrieveUsers()
{
Parallel.ForEach(Computers, c =>
{
string user = await GetUserName(c.Name); // or c.IP. both work.
});
}

Check service status remotely

I need to grab the service status (running, stopped) remotely using the credentials of the user running my executable(winform).
Would WMI be the best method?
I need this query to work on windows(7,2003,2008,2012).Can someone point me in the right direction.
if (RemoteOSversion.Contains("Windows 7"))
{
var Windows7Query = xdoc.Elements("OS").Elements("Windows7");
foreach (var myServices in Windows7Query)
{
var ServicesQuery = myServices.Elements("Services");
foreach (var ServiceName in ServicesQuery)
{
var ServiceOutput = ServiceName.Value;
}
}
}
ServiceOutput is the service name. I need to check if this service is running/stopped remotely using the same credentials of the user running my exe
It's REALLY straightforward with WMI
var sc = new ServiceController(ServiceName, MachineName);
string result = sc.Status.ToString();
Yes, use WMI.
WMI has a query language called WQL, which is similar to to SQL. You can execute these in C# using the System.Management classes.
To work with WMI you need to add a reference to the System.Management assembly. Then you can set up a connection (i.e. ManagementScope) to the WMI Provider as follows:
ConnectionOptions options = new ConnectionOptions();
// If we are connecting to a remote host and want to
// connect as a different user, we need to set some options
//options.Username =
//options.Password =
//options.Authority =
//options.EnablePrivileges =
// If we are connecting to a remote host, we need to specify the hostname:
//string providerPath = #"\\Hostname\root\CIMv2";
string providerPath = #"root\CIMv2";
ManagementScope scope = new ManagementScope(providerPath, options);
scope.Connect();
You can read more about WMI at Microsoft Docs and work around at working-with-windows-services-using-csharp-and-wmi.

Visual-SVN Server C# code not properly adding user permissions (version 2.7.2)

Story time! One of our more disgruntled employees decided to upgrade visual svn and modify our web interface a day before his last day. We had the old authentication set up, and it was all working fine. Give this particular applications footprint in the grand scheme we followed the 'if it ain't broke, don't fix it' mantra.
It wasn't broke, and he fixed it...
SO here we are. I found This Question regarding interfacing with Visual SVN with C#, and it looks like he had just copied and pasted the code verbatim from there.
The interface we have is very simple. There is an input box that the user types in the name of there desired repo. Below that is a text area where he/she can add users to have access to the repo. The user lookup is done based off of email address and it hits our active directory. The end result of this is I have the name of the repo I need to create, and the users/SIDs of the people I need to give read/write access to.
Testing this code he pasted in, it seems like the repositories are getting created fine (they show up when I log into the server as an admin). Here is the repo creation code:
static public bool CreateRepository(repository r)
{
ManagementClass repoClass = new ManagementClass("root\\VisualSVN", "VisualSVN_Repository", null);
// Obtain in-parameters for the method
ManagementBaseObject inParams = repoClass.GetMethodParameters("Create");
// Add the input parameters.
inParams["Name"] = r.name;
// Execute the method and obtain the return values.
ManagementBaseObject outParams =
repoClass.InvokeMethod("Create", inParams, null);
return true;
}
'repository r' in the method parameters, repository is a class with the following properties:
private int _id;
private string _name;
private System.Nullable<System.DateTime> _deleteAfter;
private EntitySet<UserRepositoryRight> _UserRepositoryRights;
with all the public getters and setters you would expect from a linq to sql generated file.
UserRepositoryRight is a table that holds the one (repo) to many (users) relationships.
Like I said, I think this code is fine, since I am seeing the repositories being created.
The next copypasta code is the UpdatePermissions method
static public void UpdatePermissions(string sid, string repository, AccessLevel level, bool isAdmin = false)
{
//Update SVN
ManagementClass userClass = new ManagementClass("root\\VisualSVN", "VisualSVN_WindowsAccount", null);
ManagementClass permClass = new ManagementClass("root\\VisualSVN", "VisualSVN_PermissionEntry", null);
ManagementClass repoClass = new ManagementClass("root\\VisualSVN", "VisualSVN_Repository", null);
ManagementObject userObject = userClass.CreateInstance();
userObject.SetPropertyValue("SID", sid);
ManagementObject permObject = permClass.CreateInstance();
permObject.SetPropertyValue("Account", userObject);
permObject.SetPropertyValue("AccessLevel", level);
ManagementObject repoObject = repoClass.CreateInstance();
repoObject.SetPropertyValue("Name", repository);
ManagementBaseObject inParams =
repoClass.GetMethodParameters("SetSecurity");
inParams["Path"] = "/trunk";
inParams["Permissions"] = new object[] { permObject };
ManagementBaseObject outParams =
repoObject.InvokeMethod("SetSecurity", inParams, null);
//Update in DB
var db = new DataMapSVNServiceDataContext();
if (level == AccessLevel.NoAccess) //If we are removing the user
{
var output = (db.repositories.Single(r => r.name == repository)).UserRepositoryRights.Single(u => u.User.Sid == sid);
if (output.isAdmin != null && !((bool)output.isAdmin)) //making sure DB owner isn't ever removed
db.UserRepositoryRights.DeleteOnSubmit(output);
}
if (level == AccessLevel.ReadWrite) //if we are adding the user
{
var add = new UserRepositoryRight
{
isAdmin = isAdmin,
User = db.Users.Single(u => u.Sid == sid),
repository = db.repositories.Single(r => r.name == repository)
};
db.UserRepositoryRights.InsertOnSubmit(add);
}
db.SubmitChanges();
}
Here everything looks ok, but it does not seem to be carrying over to the repo and adding the user to have ReadWrite (key value is 2) permissions on the created repo. The tail end of the method just saves the data to our websites databases to allow us to interface with it.
So, the root problem is if I create a repo via the web interface here, I get a 403 Forbidden error when trying to access it, but NO errors when creating and deleting it. Can anyone point me in the right direction here?
You should use VisualSVN Server PowerShell cmdlets instead of using the server's WMI provider. The WMI provider of VisualSVN Server can be considered as an internal API. It is not documented and is not intended to be used to build custom applications.
Old answer
If you don't get any errors when running the code, I guess that the access rule is set on path <repo>/trunk which simply does not exist in the youngest revision. You can try the script on some fresh testing repository which contains "/trunk" in it's root. Otherwise, you can simply change change the code string inParams["Path"] = "/trunk"; to inParams["Path"] = "/";.
VisualSVN Server allows you to setup path-based authorization rules on items (files and folders) that don't exist in youngest revision because these items can exist in earlier and newer revisions.

How to add a DNS alias programmatically?

I want to create an alias record in Microsoft's DNS server to point AliasA to ComputerA. How can I do this programmatically?
I used WMI to do this, found an example on the web, and this is what it looked like.
private ManagementScope _session = null;
public ManagementPath CreateCNameRecord(string DnsServerName, string ContainerName, string OwnerName, string PrimaryName)
{
_session = new ManagementScope("\\\\" + DnsServerName+ "\\root\\MicrosoftDNS", con);
_session.Connect();
ManagementClass zoneObj = new ManagementClass(_session, new ManagementPath("MicrosoftDNS_CNAMEType"), null);
ManagementBaseObject inParams = zoneObj.GetMethodParameters("CreateInstanceFromPropertyData");
inParams["DnsServerName"] = ((System.String)(DnsServerName));
inParams["ContainerName"] = ((System.String)(ContainerName));
inParams["OwnerName"] = ((System.String)(OwnerName));
inParams["PrimaryName"] = ((System.String)(PrimaryName));
ManagementBaseObject outParams = zoneObj.InvokeMethod("CreateInstanceFromPropertyData", inParams, null);
if ((outParams.Properties["RR"] != null))
{
return new ManagementPath(outParams["RR"].ToString());
}
return null;
}
I don't think .NET has anything to provide access to these (all I can find in a bit of quick searching is references to proprietary libraries, controls, etc.), so you'll probably have to use the Win32 API via P/Invoke (though another possibility would be to do the job via WMI).
You'd start with DnsAcquireContextHandle, then (probably) DnsQuery to get a current record set, modify its contents to add your new alias, DnsReplaceRecordSet to have the DNS server use the new set of records, and finally DnsReleaseContextHandle to shut things down.
Of course, you'll need the right permissions on the server or none of this will work at all.

what are registry settings to enable TCP on SQL Server 2005 and 2008?

I want to programatically enable TCP connections on SQL Server. I believe we can achieve this by modifying registry entries and restarting SQL Server service. What registry should I edit?
Unless you have a good reason for modifying the registry directly, I suggest you consider using WMI. WMI will provide you with a more version agnostic implementation. WMI can be accessed through the System.Management namespace. You could have code that looks something like this.
public void EnableSqlServerTcp(string serverName, string instanceName)
{
ManagementScope scope =
new ManagementScope(#"\\" + serverName +
#"\root\Microsoft\SqlServer\ComputerManagement");
ManagementClass sqlService =
new ManagementClass(scope,
new ManagementPath("SqlService"), null);
ManagementClass serverProtocol =
new ManagementClass(scope,
new ManagementPath("ServerNetworkProtocol"), null);
sqlService.Get();
serverProtocol.Get();
foreach (ManagementObject prot in serverProtocol.GetInstances())
{
prot.Get();
if ((string)prot.GetPropertyValue("ProtocolName") == "Tcp" &&
(string)prot.GetPropertyValue("InstanceName") == instanceName)
{
prot.InvokeMethod("SetEnable", null);
}
}
uint sqlServerService = 1;
uint sqlServiceStopped = 1;
foreach (ManagementObject instance in sqlService.GetInstances())
{
if ((uint)instance.GetPropertyValue("SqlServiceType") == sqlServerService &&
(string)instance.GetPropertyValue("ServiceName") == instanceName)
{
instance.Get();
if ((uint)instance.GetPropertyValue("State") != sqlServiceStopped)
{
instance.InvokeMethod("StopService", null);
}
instance.InvokeMethod("StartService", null);
}
}
}
This code assumes a project reference to System.Management.dll and the following using statement:
using System.Management;
The Sql Protocols blog has an article that goes into some detail as to what the above code is doing.
Note: If a firewall is blocking the port(s) you will still be unable to access the server via TCP.
Take a look at HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQLServer\SuperSocketNetLib\Tcp hive. There are keys like Enabled, ListenOnAllIPs and a list of IP addresses to listen on.

Categories