mass .net update in iis6 - c#

We have about 200 web sites (on 3 separate servers, so 600 total) that we're going to have to update to run on .net 4 framework. They're currently set to run on .net 2. Does anyone know if there's a way to do this with a c# console program? Thanks!
Edit: I found a tool called IIS Metabase Explorer. Using that along with some code I had found a while ago for setting up a site in IIS 6, I came up with the following code. Hopefully it will be of help to others.
I would call it like
UpdateSiteToDotNet4("mytestsite", "mywebserver", #"D:\inetpub\wwwroot\", "DotNet4");
private void UpdateSiteToDotNet4(string siteName, string server, string serverPath, string newAppPoolId)
{
var service = new DirectoryEntry("IIS://" + server + "/W3SVC");
DirectoryEntries sites = service.Children;
bool found = false;
foreach (DirectoryEntry siteEntry in sites)
{
var siteId = siteEntry.Name;
DirectoryEntries siteSettings = siteEntry.Children; //Root
foreach (DirectoryEntry siteSetting in
from DirectoryEntry siteSetting in siteSettings
where siteSetting.SchemaClassName == "IIsWebVirtualDir"
let siteRoot = siteSetting.Properties["Path"][0].ToString()
where siteRoot.ToLower().Trim() == (serverPath + siteName).ToLower().Trim()
select siteSetting)
{
siteSetting.Properties["AppPoolId"].Value = newAppPoolId;
siteSetting.CommitChanges();
//Update to version v4.0.30319
var aspnet = string.Format(#"{0}\Microsoft.NET\Framework\v{1}\aspnet_regiis.exe", Environment.GetEnvironmentVariable("windir"), "4.0.30319");
var startInfo = new ProcessStartInfo(aspnet)
{
Arguments = string.Format("-sn \"{0}\"", "W3SVC/" + siteId + "/Root"),
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
CreateNoWindow = true
};
var process = new Process { StartInfo = startInfo };
process.Start();
process.WaitForExit();
process.Dispose();
found = true;
break;
}
if (found) break;
}
}

Related

How to execute cmds using HTTP Post method in C#

We have a REST endpoint installed as a windows service on a server. This server will be used for test automation. The idea is make a POST request to the endpoint and that endpoint will kick off cypress tests based off the parameters specified in the POST url.
I have an existing powershell script that will do this. Ideally I'd like to call that script and fill in the needed params.
No matter what I do the request returns OK without actually running any of the commands. Is there a way to use a POST request to execute a cmds or a powershell script?? Code below(Note: I'm trying 2 different methods here neither work):
namespace CNet.TestRunner
{
public class AddTestRun : CNetApiEndpoint
{
[HttpPost("Cypress/{testName}/{serverName}")]
public CineNetResult TestRun(string testName, string serverName)
{
string test = testName + ".js";
string testPath = $#"..\EndToEndSpecs\cypress\integration\{test}";
var path = "%PROGRAMFILES%";
var resolvedPath = Environment.ExpandEnvironmentVariables(path);
string cmd = $#"{resolvedPath}\PowerShell\7\pwsh.exe "" '..\EndToEnd.ps1' '-TestPath {testPath}' '-HostToTest {serverName}' """;
Process P = Process.Start($#"{resolvedPath}\PowerShell\7\pwsh.exe", $#""" '..\EndToEnd.ps1' '-TestPath {testPath}' '-HostToTest {serverName}'""");
P.WaitForExit();
int result = P.ExitCode;
string resStr = result.ToString();
var model = new ResponseModel
{
Message = resStr,
DataPassed = $"{testName}|{serverName}"
};
var proc1 = new ProcessStartInfo();
string anyCommand = "yarn install";
proc1.UseShellExecute = true;
proc1.WorkingDirectory = #"C:\Windows\System32";
proc1.FileName = #"C:\Windows\System32\cmd.exe";
proc1.Verb = "runas";
proc1.Arguments = "/c " + anyCommand;
proc1.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(proc1);
return Ok(model);
}
}
}

Shell:appsFolder show Windows Explorer when App is not installed

I have a code like this
Process p = new Process();
p.StartInfo = new ProcessStartInfo("explorer.exe");
p.StartInfo.Arguments = #"shell:appsFolder\AppName";
But if the App isn't installed on the machine, it only open a window explorer.
How can I prevent it from opening the Windows Explorer if the App isn't installed?
I've also tried using this code, to check for the app, it is working fine, but is there a way to just dont show the windows explorer when the app isn't installed?
var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);
List<PackageInfo> installedPackages = new List<PackageInfo>();
((IKnownFolder)appsFolder).ToList().Where(w => !w.ParsingName.Contains(".txt") && !w.ParsingName.Contains(".chm") && !w.ParsingName.Contains(".htm") && !w.ParsingName.Contains(".html") && !w.ParsingName.Contains("http://") && !w.ParsingName.Contains(".bat") && !w.ParsingName.Contains(".rtf") && !w.ParsingName.Contains(".url") && !w.ParsingName.Contains(".pdf")).ToList().ForEach(fe =>
{
try
{
installedPackages.Add(new PackageInfo
{
DisplayName = (fe.ParsingName.Replace(#"\", #"\\")),
FullName = fe.Name,
Version = fe.Properties.System.FileVersion.Value == null ? "" : fe.Properties.System.FileVersion.Value
});
Debug.WriteLine(fe.ParsingName);
}
catch { }
});
return installedPackages.OrderBy(ob => ob.FullName).ToList();
I've tried getting the output,error and exitcode of the process.
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
string errorx = p.StandardError.ReadToEnd(); //return empty string
string outputx = p.StandardOutput.ReadToEnd(); //return empty string
int exitint = p.ExitCode; //return 1
But it didn't give me anything useful.
The windows explorer doesn't show now after I've added the code
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

How to translate this PowerShell script to C#? - Get-ItemProperty

I can get a list of installed applications by using this PowerShell command:
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName
in C#, it doesn't return the exact list like I see in PowerShell, I need it to show exactly the output from PowerShell.
It shows a totally different list with other programs.
public void conf() {
process p1 = new Process();
ProcessStartInfo psi1 = new ProcessStartInfo("powershell", "Get-ItemProperty HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* | Select-Object DisplayName");
psi1.RedirectStandardOutput = true;
psi1.CreateNoWindow = true;
p1.StartInfo = psi1;
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.Verb = "runas";
p1.Start();
string output = p1.StandardOutput.ReadToEnd();
Console.WriteLine(output);
p1.WaitForExit(400);
}
What am I doing wrong?
Thanks.
If you are talking about content - if you force C# program to run as x64 in Configuration Manager, you'll get the same output. And by default (Any CPU) it was reading from and x86 registry key. Or if you will run Powershell x86, you'll get the same result, as your original C# program
If you query also HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* key and merge results you'll get the whole list. Make sure your program is x64 in that case
There is some mess with spaces in stdout from PowerShell, so I just removed it.
static void Main(string[] args)
{
var lines = GetSoft("Get-ItemProperty HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*")
.Union(GetSoft("Get-ItemProperty HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*"))
.Distinct()
.ToList();
lines.Sort();
foreach (var line in lines)
{
Console.WriteLine(line);
}
Console.WriteLine(lines.Count);
Console.ReadLine();
}
public static IEnumerable<string> GetSoft(string key)
{
Process p1 = new Process();
ProcessStartInfo psi1 = new ProcessStartInfo("powershell",
key + " | Select-Object DisplayName")
{
RedirectStandardOutput = true,
CreateNoWindow = true
};
p1.StartInfo = psi1;
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.Verb = "runas";
p1.Start();
var output = p1.StandardOutput.ReadToEnd();
var result= output.Split('\r', '\n').Select(s => s.Trim()).Where(s => !String.IsNullOrWhiteSpace(s));
p1.WaitForExit(400);
return result;
}
}

Programatically create an IIS website on button click [duplicate]

We have been able to create a web site. We did this using the information in this link:
https://msdn.microsoft.com/en-us/library/ms525598.aspx
However, we would like to use a port number other that port 80. How do we do this?
We are using IIS 6
If you're using IIS 7, there is a new managed API called Microsoft.Web.Administration
An example from the above blog post:
ServerManager iisManager = new ServerManager();
iisManager.Sites.Add("NewSite", "http", "*:8080:", "d:\\MySite");
iisManager.CommitChanges();
If you're using IIS 6 and want to do this, it's more complex unfortunately.
You will have to create a web service on every server, a web service that handles the creation of a website because direct user impersonation over the network won't work properly (If I recall this correctly).
You will have to use Interop Services and do something similar to this (This example uses two objects, server and site, which are instances of custom classes that store a server's and site's configuration):
string metabasePath = "IIS://" + server.ComputerName + "/W3SVC";
DirectoryEntry w3svc = new DirectoryEntry(metabasePath, server.Username, server.Password);
string serverBindings = ":80:" + site.HostName;
string homeDirectory = server.WWWRootPath + "\\" + site.FolderName;
object[] newSite = new object[] { site.Name, new object[] { serverBindings }, homeDirectory };
object websiteId = (object)w3svc.Invoke("CreateNewSite", newSite);
// Returns the Website ID from the Metabase
int id = (int)websiteId;
See more here
Heres the solution.
Blog article : How to add new website in IIS 7
On Button click :
try
{
ServerManager serverMgr = new ServerManager();
string strWebsitename = txtwebsitename.Text; // abc
string strApplicationPool = "DefaultAppPool"; // set your deafultpool :4.0 in IIS
string strhostname = txthostname.Text; //abc.com
string stripaddress = txtipaddress.Text;// ip address
string bindinginfo = stripaddress + ":80:" + strhostname;
//check if website name already exists in IIS
Boolean bWebsite = IsWebsiteExists(strWebsitename);
if (!bWebsite)
{
Site mySite = serverMgr.Sites.Add(strWebsitename.ToString(), "http", bindinginfo, "C:\\inetpub\\wwwroot\\yourWebsite");
mySite.ApplicationDefaults.ApplicationPoolName = strApplicationPool;
mySite.TraceFailedRequestsLogging.Enabled = true;
mySite.TraceFailedRequestsLogging.Directory = "C:\\inetpub\\customfolder\\site";
serverMgr.CommitChanges();
lblmsg.Text = "New website " + strWebsitename + " added sucessfully";
}
else
{
lblmsg.Text = "Name should be unique, " + strWebsitename + " is already exists. ";
}
}
catch (Exception ae)
{
Response.Redirect(ae.Message);
}
Looping over sites whether name already exists
public bool IsWebsiteExists(string strWebsitename)
{
Boolean flagset = false;
SiteCollection sitecollection = serverMgr.Sites;
foreach (Site site in sitecollection)
{
if (site.Name == strWebsitename.ToString())
{
flagset = true;
break;
}
else
{
flagset = false;
}
}
return flagset;
}
Try the following Code to Know the unUsed PortNo
DirectoryEntry root = new DirectoryEntry("IIS://localhost/W3SVC");
// Find unused ID PortNo for new web site
bool found_valid_port_no = false;
int random_port_no = 1;
do
{
bool regenerate_port_no = false;
System.Random random_generator = new Random();
random_port_no = random_generator.Next(9000,15000);
foreach (DirectoryEntry e in root.Children)
{
if (e.SchemaClassName == "IIsWebServer")
{
int site_id = Convert.ToInt32(e.Name);
//For each detected ID find the port Number
DirectoryEntry vRoot = new DirectoryEntry("IIS://localhost/W3SVC/" + site_id);
PropertyValueCollection pvcServerBindings = vRoot.Properties["serverbindings"];
String bindings = pvcServerBindings.Value.ToString().Replace(":", "");
int port_no = Convert.ToInt32(bindings);
if (port_no == random_port_no)
{
regenerate_port_no = true;
break;
}
}
}
found_valid_port_no = !regenerate_port_no;
} while (!found_valid_port_no);
int newportId = random_port_no;
I have gone though all answer here and also tested. Here is the most clean smarter version of answer for this question. However this still cant work on IIS 6.0. so IIS 8.0 or above is required.
string domainName = "";
string appPoolName = "";
string webFiles = "C:\\Users\\John\\Desktop\\New Folder";
if (IsWebsiteExists(domainName) == false)
{
ServerManager iisManager = new ServerManager();
iisManager.Sites.Add(domainName, "http", "*:8080:", webFiles);
iisManager.ApplicationDefaults.ApplicationPoolName = appPoolName;
iisManager.CommitChanges();
}
else
{
Console.WriteLine("Name Exists already");
}
public static bool IsWebsiteExists(string strWebsitename)
{
ServerManager serverMgr = new ServerManager();
Boolean flagset = false;
SiteCollection sitecollection = serverMgr.Sites;
flagset = sitecollection.Any(x => x.Name == strWebsitename);
return flagset;
}
This simplified method will create a site with default binding settings, and also create the application pool if needed:
public void addIISApplication(string siteName, string physicalPath, int port, string appPoolName)
{
using (var serverMgr = new ServerManager())
{
var sitecollection = serverMgr.Sites;
if (!sitecollection.Any(x => x.Name.ToLower() == siteName.ToLower()))
{
var appPools = serverMgr.ApplicationPools;
if (!appPools.Any(x => x.Name.ToLower() == appPoolName.ToLower()))
{
serverMgr.ApplicationPools.Add(appPoolName);
}
var mySite = serverMgr.Sites.Add(siteName, physicalPath, port);
mySite.ApplicationDefaults.ApplicationPoolName = appPoolName;
serverMgr.CommitChanges();
}
}
}
In properties of site select "Web Site" tab and specify TCP Port.
In studio to debug purpose specify http://localhost:<port>/<site> at tab Web for "Use Local IIS Web Server"

I am having trouble comparing two lists. The purpose of the program is to perform offline patch installation

The problem comes in the looping. It seems to perform the comparison between the wmi qfe list and the KB list. The issue I run into is taking and searching the contents of the .msu file name list for the KB and then installing it. I have made a similar program in python and was mimicking a similar construct in the C# code. Any guidance on how to make this function properly would be greatly appreciated. The code is as follows:
static void Server08r2Patches()
{
var IE9 = from a in IAVA.Worksheet<IE9>("IE9") select a;
var Server08r2 = from a in IAVA.Worksheet<Server08r2>("Server08r2") select a;
var KBlist = Server08r2.Select(s=>new {KB = s.KB}).ToList();
var ExeList = Server08r2.Select(s=>new {Executable = s.Executable}).ToList();
string path = (Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)+#"\x64\");
foreach (var item in KBlist)
{
if (OsPatches.Contains(item.KB))
{
Console.WriteLine(item.KB+" is already installed.");
}
else
{
Console.WriteLine(item.KB+" will now be installed.");
foreach (var inst in ExeList)
{
do
{
var kb_inst = new ProcessStartInfo()
{
CreateNoWindow = true,
UseShellExecute = true,
FileName =path+inst.Executable,
WindowStyle = ProcessWindowStyle.Hidden,
Arguments = (#" /quiet /norestart"),
};
try
{
Process update = new Process();
update.StartInfo = kb_inst;
update.Start();
update.WaitForExit();
Console.WriteLine(inst.Executable+" was successfully installed");
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message+" "+inst.Executable);
}
}
while (inst.Executable.Contains(item.KB) == true);
}
}
}
}

Categories