C# Citrix to Powershell - c#

We have developed a new web based application which makes calls to Citrix Xenapp Powershell cmdlets through C#. The web application is hosted on the Citrix server(This is already in the farm) itself which we feel is enough to access all the servers in the Citrix farm.The code gives the output when it runs from Visual Studio IDE.
However, when I make a virtual directory on IIS and access it through Browser.
at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input, Hashtable errorResults, Boolean enumerate) at System.Management.Automation.Runspaces.LocalPipeline.InvokeHelper() at System.Management.Automation.Runspaces.LocalPipeline.InvokeThreadProc()
The following is the code
protected void Page_Load(object sender, EventArgs e)
{
string usrname = "";
string svrname = "";
string appname = "";
string clntnme = "";
string clntip="";
string farm = "";
Runspace rsp = RunspaceFactory.CreateRunspace();
//rsp.ApartmentState = System.Threading.ApartmentState.STA;
//rsp.ThreadOptions = PSThreadOptions.UseCurrentThread;
rsp.Open();
PSSnapInException exp;
rsp.RunspaceConfiguration.AddPSSnapIn("Citrix.XenApp.Commands", out exp);
try
{
PowerShell ps3 = PowerShell.Create().AddCommand("Get-XAFarm");
ps3.Runspace = rsp;
foreach (PSObject pss in ps3.Invoke())
{
farm = pss.Properties["FarmName"].Value.ToString();
}
Response.Write("<DIV align=" + "center" + " style=" + "width:100%;height:100%" + ">" + farm + "<TABLE style=" + "width:100%" + ">");
Response.Write("<TR><TH>User</TH><TH>Server</TH><TH>Application</TH><TH>Address</TH><TH>Client Name</TH></TR>");
PowerShell sessions = PowerShell.Create().AddCommand("Get-XASession").AddParameter("Full").AddCommand("where-object").AddParameter("FilterScript", ScriptBlock.Create("{$_.BrowserName -ne $null }"));
sessions.Runspace = rsp;
foreach (PSObject psr in sessions.Invoke())
{
//IPAddress.Parse(psr.Properties["ClientAddress"].Value.ToString());
usrname = psr.Properties["AccountName"].Value.ToString();
svrname = psr.Properties["ServerName"].Value.ToString();
appname = psr.Properties["BrowserName"].Value.ToString();
clntnme = psr.Properties["ClientName"].Value.ToString();
//if (psr.Properties["ClientAddress"].Value != null)
//{
clntip = psr.Properties["ClientAddress"].Value.ToString();
//}
//clntip = psr.Properties["ClientIPV4"].Value.ToString();
Response.Write("<TR><TD>" + usrname + "</TD><TD>" + svrname + "</TD><TD>" + appname + "</TD><TD>" + clntip + "</TD><TD>" + clntnme + "</TD></TR>");
}
Response.Write("</TABLE>");
}
catch (Exception ex)
{
Response.Write(ex.StackTrace.ToString());
}
finally
{
rsp.Close();
}
}**
Please let me know if any changes have to be done at IIS to enable me access the aspx page successfully.

The app needs to run as a user that has sufficient admin privileges in the XenApp farm. You could create a dedicated service account and make it an admin in the XenApp farm. To list farm state as your sample does, it only needs to be a 'View Only' XenApp administrator. Then configure IIS to run as that service account.

Related

NT Authority\Anonymous Logon throws connecting through WMI Connection from Web Server

I have Web,APP,DB Server
App Server which has a Exe it will do some operation .
when I am trying to Trigger a Exe which is in App Server through WMI Connection from Webserver please refer below code
public void RunProcessOnRemoteMachine(string inputFolder, string ProcessName, string AppRun, string isRegression, string TemplateID, string isPreProcess, string UserLoginName, string DocYear,string remoteMachine,string strPathToTheExe)
{
string usernameAndDomain = "";
string password = "";
try
{
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
if (Environment.MachineName.ToUpper() != remoteMachine.ToUpper())
{
}
ManagementScope manScope = new ManagementScope(String.Format(#"\\{0}\ROOT\CIMV2", remoteMachine), connOptions);
manScope.Connect();
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(manScope, managementPath, objectGetOptions);
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = strPathToTheExe + " " + "\"" + inputFolder + "\" \"" + ProcessName + "\" \"" + AppRun + "\" \"" + isRegression + "\" \"" + TemplateID + "\" \"" + isPreProcess + "\" \"" + UserLoginName + "\" \"" + DocYear+"\"";
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null );
Cls_ErrorLog.Writelog("Creation of the process returned: " + outParams["returnValue"]);
Cls_ErrorLog.Writelog("Process ID: " + outParams["processId"]);
}
catch (Exception ex)
{
Cls_ErrorLog.Writelog("Exception occured in method RunProcessOnRemoteMachine : " + ex.Message + " - " + ex.StackTrace);
}
finally
{
}
}
I set Integrated Security=True in my Exe Config and Web config and also i added that user in Sql Server Logins and gave the rights.
After made a Connection that EXe is Running with same Credential(what we set in Identity in IIS) in Appserver But it could not connect a database connection with same credentials
it throws Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON. but when we track the exe which is running with same credentials
when we run a Exe manually in App Server its works
So Please Suggest a Solutions
Thanks in advance

FileSystemWatcher DCOM Errors

Most would probably ignore this but I really need help. I am currently developing a Windows Service that uses FileSystemWatcher to monitor the changes on the web.config files on remote web servers.
What it generally does is upon initialization of the service, it gets the websites on IIS from a list of web servers defined in app.config, then puts a FileSystemWatcher on the the websites' web.config file and another file named web.config.orig.
When either the web.config or the web.config.orig file changes, it triggers the FileSystemWatcher then checks the LastModifiedDate of both files. If is not equal, it should generate an event log that says the the file's modified dates are not in sync.
The service successfully installs then I was able to test it one time, but it triggers the FileSystemWatcher a lot then generates a lot of this sort of error in the System Logs:
DCOM was unable to communicate with the computer Server1 using any of the configured protocols; requested by PID 4c08 (C:\Users\user1\Desktop\DynamicFSW\DynamicFSW\bin\Debug\DynamicFSW.exe).
You can download the source code here: https://www.dropbox.com/s/1pkh543g5n578fy/DynamicFSW.rar?dl=0
private void file_OnChanged(object sender, FileSystemEventArgs e)
{
FileSystemWatcher fsw = (FileSystemWatcher)sender;
try
{
fsw.EnableRaisingEvents = false;
string directory = Path.GetDirectoryName(e.FullPath);
string webconfig = directory + "\\web.config";
string webconfigorig = directory + "\\web.config.orig";
DateTime dt1 = File.GetLastWriteTime(webconfig);
DateTime dt2 = File.GetLastWriteTime(webconfigorig);
DynamicFSWEventLog.WriteEntry("" + e.FullPath);
string server = "";
for (int i = 2; i < directory.Length; i++)
{
if (directory[i] != '\\')
{
server += directory[i];
}
else
{
break;
}
}
DynamicFSWEventLog.WriteEntry("" + server);
if (!System.DateTime.Equals(dt1, dt2))
{
ServerManager IIS = Microsoft.Web.Administration.ServerManager.OpenRemote(server);
foreach (Site site in IIS.Sites)
{
if (Path.GetDirectoryName(e.FullPath).Equals("\\\\" + server + "\\" + site.Applications["/"].VirtualDirectories[0].PhysicalPath.Replace(':', '$')))
{
string message = "Server: " + server + "\n" +
"Website: " + site.Name + "\n" +
"Web.config.LastWriteTime: " + dt1 + "\n" +
"Web.config.orig.LastWriteTime: " + dt2 + "\n\n" +
"Web.config and Web.config.orig file's last write time not in sync!";
DynamicFSWEventLog.WriteEntry("" + message);
}
}
}
}
finally
{
fsw.EnableRaisingEvents = true;
}
}
I would really appreciate anyone's help on this.
Thanks!

How to keep a process alive after remote PowerShell script completion?

I'm trying to start Zookeeper and Solr remotely via PowerShell scriptblock.
Then I see that the process is created in the remote machine (by checking the port 2181 of Zookeeper). And on the script completion it is being terminated.
How do I keep this alive even after the completion?
This code below stops the remote process on script completion. The script.ps1 does a lot of things that includes starting Zookeeper and Solr asJob.
int iRemotePort = 5985;
string strShellURI = #"http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
string strAppName = #"/wsman";
WSManConnectionInfo ci = new WSManConnectionInfo(
false,
machineName,
iRemotePort,
strAppName,
strShellURI,
new PSCredential(userName, secure));
Runspace runspace = RunspaceFactory.CreateRunspace(ci);
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
session.Log(#"c:\temp\script.ps1 -serverID " + counter + " -copySolrConfig $" + status + " -currentHost \"" + machineName + "\" -IP_PORT_List \"" + String.Join(", ", machineWithPort) + "\"");
ps.Runspace = runspace;
ps.AddScript(#"c:\temp\script.ps1 -serverID " + counter + " -copySolrConfig $" + status + " -currentHost \"" + machineName + "\" -IP_PORT_List \"" + String.Join(", ", machineWithPort) + "\"");
var results = ps.Invoke();
foreach (var result in results)
{
session.Log(result.ToString());
}
}
runspace.Close();
So after a long try I came to a conclusion, Using power-shell script alive is not a appropriate way to keep zookeeper and solr running effectively. So moved it as a windows service and installed it on the remote machine via WiX.

Need help converting PowerShell to C# code

So i have this powershell code:
try{
$ComputerWMIObject = Get-WmiObject Win32_ComputerSystem -ComputerName "$oldComputerName" -Authentication 6
if ( $ComputerWMIObject ){
$result = $ComputerWMIObject.Rename("$newComputerName", $ADUserPassword , $ADUserName )
switch($result.ReturnValue)
{
0 {
if ( $Restart.IsChecked ) {
Get-WmiObject Win32_OperatingSystem -ComputerName "$oldComputerName" | ForEach-Object {$restart = $_.Win32Shutdown(6)}
$ResultText.Text = "Computer $oldComputerName was renamed to $newComputerName and restarted"
} else {
$ResultText.Text = "Computer $oldComputerName was renamed to $newComputerName restart computer to finish"
}
}
5 { $ResultText.Text = "Computer was not renamed. Please check if you have admin permissions (ReturnCode 5)" }
default { $ResultText.Text = "ReturnCode $($result.ReturnValue)"}
}
}else{
$ResultText.Text = "Couldn't create WMI Object on $oldComputerName"
}
}catch{
$ResultText.Text = $_
}
I'm trying to convert this to C# and can't find a way to do this. I just don't understand how to create WMI object.
It would be very helpful if you can post and example on how to do this.
I've read this Remotely change computer name for a Windows Server 2008 machine using C#? topic. And it throws an exception may be its because of this line:
Authentication = AuthenticationLevel.PacketPrivacy
I'm using System.Net.Security namespace and as it's stated in comment PacketPrivacy exists only there.
Since I can't ask there because I have low rating I've asked again.
Would be grateful if any one can help me.
PS: I know this can be done using NETDOM but I would prefer using WMI object.
ADDED:
I'm trying to use this:
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 = accountWithPermissions.Domain + "\\" + 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);
ManagementBaseObject outParams = remoteSystem.InvokeMethod("Rename", newRemoteSystemName, null);
And I get this error Server RPC is unavailable. (Exception HRESULT: 0x800706BA) here:
ManagementBaseObject newRemoteSystemName = remoteSystem.GetMethodParameters("Rename");
ADDED2:
Ok, I guess I found out what causes an error.
i've changed original conn User name from
Username = oldName + "\\" + accountWithPermissions.UserName,
to
Username = accountWithPermissions.Domain + "\\" + accountWithPermissions.UserName,
and error happens, if I use old code I get ACCESS_IS_DENIED and that's correct because that user doesn't have rights.
So what's wrong if I use Domain\User may be I should change NamespacePath in remoteControlObject to be able to work with domain user authentication?
I was unable to find a way resolve this issue and no one seems to have an answer.
So I've solved this by using NETDOM when Server RPC is unavailable. (Exception HRESULT: 0x800706BA) actually any error happens I will add access denied check so it won't try NETDOM when this happens;
var remoteControlObject = new ManagementPath
{
ClassName = "Win32_ComputerSystem",
Server = oldName,
Path = oldName + "\\root\\cimv2:Win32_ComputerSystem.Name='" + oldName + "'",
NamespacePath = "\\\\" + oldName + "\\root\\cimv2"
};
string domain = accountWithPermissions.Domain;
string user = accountWithPermissions.UserName;
var conn = new ConnectionOptions
{
Authentication = AuthenticationLevel.PacketPrivacy,
Username = domain + "\\" + accountWithPermissions.UserName,
Password = accountWithPermissions.Password
};
var remoteScope = new ManagementScope(remoteControlObject, conn);
var remoteSystem = new ManagementObject(remoteScope, remoteControlObject, null);
try
{
ManagementBaseObject newRemoteSystemName = remoteSystem.GetMethodParameters("Rename");
newRemoteSystemName.SetPropertyValue("Name", newName);
newRemoteSystemName.SetPropertyValue("UserName", accountWithPermissions.UserName);
newRemoteSystemName.SetPropertyValue("Password", accountWithPermissions.Password);
ManagementBaseObject outParams = remoteSystem.InvokeMethod("Rename", newRemoteSystemName, null);
}
catch (Exception e)
{
this.Res.Inlines.Add(string.Format("Ошибка:\n" + e.Message + "\n"));
this.Res.Inlines.Add(string.Format("Пробуем переименовать используя NETDOM\n"));
bool restart = false;
PowerNETDOM(oldName, newName, accountWithPermissions, restart);
}
Server RPC is unavailable. (Exception HRESULT: 0x800706BA) is happening not because my changes to the script. This error if fired only by few PC's as I found out it can happen in many cases more about this here: Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) . And changing settings on a remote PC makes me go and check the settings directly from that PC. In that case I can just rename PC not even trying to find out what this happening. That's why I do use NETDOM because for some reason it doesn't have problems renaming that PC remotely.
private static void PowerNETDOM(String oldName, String newName, NetworkCredential accountWithPermissions, bool restart)
{
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.AddScript
(
"param($Restart,$oldComputerName,$newComputerName,$ADUserPassword,$ADUserName);" +
"function ConvertTo-Encoding ([string]$From, [string]$To){" +
" Begin{ $encFrom = [System.Text.Encoding]::GetEncoding($from);$encTo = [System.Text.Encoding]::GetEncoding($to); }" +
" Process{ $bytes = $encTo.GetBytes($_);$bytes = [System.Text.Encoding]::Convert($encFrom, $encTo, $bytes);$encTo.GetString($bytes)}" +
"}" +
"$tmp = NETDOM RENAMECOMPUTER $oldComputerName /NewName:$newComputerName /ud:$ADUserName /pd:$ADUserPassword /Force $res_text | ConvertTo-Encoding \"cp866\" \"windows-1251\";" +
"$tmp > C:\\Temp\\rename.txt;$tmp;"
);
PowerShellInstance.AddParameter("restart", restart);
PowerShellInstance.AddParameter("newComputerName", newName);
PowerShellInstance.AddParameter("oldComputerName", oldName.ToString());
PowerShellInstance.AddParameter("ADUserPassword", accountWithPermissions.Password);
PowerShellInstance.AddParameter("ADUserName", accountWithPermissions.Domain + "\\" + accountWithPermissions.UserName);
PowerShellInstance.Invoke();
}
}

How to get a list of sites and SSL certificates from IIS 6.0 using C#, WMI, and/or System.Management?

I am trying to export all the SSL certificates on IIS 6.0 sites from a specificed remote server to a centralized backup server so we can migrate and/or backup our SSL certificates, however I cannot figure out how to do this with IIS 6.0 (all our servers in staging and production still run IIS 6.0). Is there a way to do with C# and System.Management for targeting IIS 6.0 web sites. I have tried everything I could think of.
Pseduo Logic:
Get a list of all IIS Web Sites on Server X
If the site has an SSL certificate binding associated with it, export the SSL certificate with the name of the IIS Web Site.
Here’s the code that is closer to what I need for for IIS 7.0:
using (ServerManager serverManager = ServerManager.OpenRemote(this.ServerName))
{
string collectionDisplay = null;
if (serverManager.Sites != null)
collectionDisplay = "There are " + serverManager.Sites.Count.ToString() + " sites:\n\n";
string siteDisplay = null;
foreach (Site site in serverManager.Sites)
{
siteDisplay = siteDisplay + site.Name + ": ID = " + site.Id + "\n";
// Display each property of each bindings.
string bindingDisplay = null;
foreach (Binding binding in site.Bindings)
{
if (binding.Protocol == "https")
{
bindingDisplay = bindingDisplay + " Binding:\n BindingInformation: " + binding.BindingInformation;
// There is a CertificateHash and CertificateStoreName for the https protocol only.
bindingDisplay = bindingDisplay + "\n CertificateHash: " +
binding.CertificateHash + ": ";
//Add the certificate hash to the collection
if (!IisCertificateHashCollection.ContainsKey(binding.CertificateHash))
{
IisCertificateHashCollection.Add(binding.CertificateHash, site.Name);
//IisCertificateHashCollection.Add(new KeyValuePair<string, byte[]>(site.Name, binding.CertificateHash));
}
// Display the hash.
foreach (System.Byte certhashbyte in binding.CertificateHash)
{
bindingDisplay = bindingDisplay + certhashbyte.ToString() + " ";
}
bindingDisplay = bindingDisplay + "\n CertificateStoreName: " +
binding.CertificateStoreName;
}
bindingDisplay = bindingDisplay + "\n EndPoint: " + binding.EndPoint;
bindingDisplay = bindingDisplay + "\n Host: " + binding.Host;
bindingDisplay = bindingDisplay + "\n IsIPPortHostBinding: " + binding.IsIPPortHostBinding;
bindingDisplay = bindingDisplay + "\n Protocol: " + binding.Protocol;
bindingDisplay = bindingDisplay + "\n ToString: " + binding.ToString();
bindingDisplay = bindingDisplay + "\n UseDsMapper: " + binding.UseDsMapper + "\n\n";
}
siteDisplay = siteDisplay + bindingDisplay;
}
collectionDisplay = collectionDisplay + siteDisplay + "\n";
}
Here’s the code I can’t quite get/don't know how to obtain the needed info from IIS 6.0, I cannot get the query correct:
// Connection succeeds, so there is no issue with that (left out code for that in sample)
ManagementScope scope = new ManagementScope(string.Format(#"\\{0}\root\cimv2", serverName, options));
//ManagementScope scope = new ManagementScope(string.Format(#"\\{0}\root\MicrosoftIISV2", serverName, options));
scope.Connect();
ObjectQuery oq = new ObjectQuery(#"SELECT * FROM Win32_NTDomain");
ManagementObjectSearcher query = new ManagementObjectSearcher(scope, oq);
ManagementObjectCollection queryCollection = query.Get();
foreach (ManagementObject mo in queryCollection)
{
foreach (PropertyData pd in mo.Properties)
{
}
}
You can use System.DirectoryServices to get the certificate hash on IIS6:
DirectoryEntry dir = new DirectoryEntry(#"IIS://Localhost/W3SVC/1"); //this is the metabase path
PropertyValueCollection vals = dir.Properties[SSLCertHash]; //this is the propertyName
The rest is the same as in IIS7.
Hope this helps,
Rotem Varon

Categories