diff --git a/.gitignore b/.gitignore
index c2e1708217..414ddb4426 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,4 +24,5 @@ project.lock.json
*DS_Store
*.ncrunchsolution
*.*sdf
-*.ipch
\ No newline at end of file
+*.ipch
+*.vs/
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Tools.PublishIIS/Program.cs b/src/Microsoft.AspNet.Tools.PublishIIS/Program.cs
new file mode 100644
index 0000000000..d3ceadffa9
--- /dev/null
+++ b/src/Microsoft.AspNet.Tools.PublishIIS/Program.cs
@@ -0,0 +1,73 @@
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Extensions.CommandLineUtils;
+
+namespace Microsoft.AspNet.Tools.PublishIIS
+{
+ public class Program
+ {
+ public static int Main(string[] args)
+ {
+ var app = new CommandLineApplication
+ {
+ // TODO: This needs to be updated once we know how this is going to be called
+ Name = "dotnet(????) publish-iis",
+ FullName = "Asp.Net IIS Publisher",
+ Description = "IIS Publisher for the Asp.Net web applications",
+ };
+ app.HelpOption("-h|--help");
+
+ var publishFolderOption = app.Option("--publish-folder", "The path to the publish output folder", CommandOptionType.SingleValue);
+ var appNameOption = app.Option("--application-name", "The name of the application", CommandOptionType.SingleValue);
+
+ app.OnExecute(() =>
+ {
+ var publishFolder = publishFolderOption.Value();
+ var appName = appNameOption.Value();
+
+ if (publishFolder == null || appName == null)
+ {
+ app.ShowHelp();
+ return 2;
+ }
+
+ XDocument webConfigXml = null;
+ var webConfigPath = Path.Combine(publishFolder, "wwwroot", "web.config");
+ if (File.Exists(webConfigPath))
+ {
+ try
+ {
+ webConfigXml = XDocument.Load(webConfigPath);
+ }
+ catch (XmlException) { }
+ }
+
+ var transformedConfig = WebConfigTransform.Transform(webConfigXml, appName);
+
+ using (var f = new FileStream(webConfigPath, FileMode.Create))
+ {
+ transformedConfig.Save(f);
+ }
+
+ return 0;
+ });
+
+ try
+ {
+ return app.Execute(args);
+ }
+ catch (Exception e)
+ {
+#if DEBUG
+ Console.Error.WriteLine(e);
+#else
+ Console.Error.WriteLine(ex.Message);
+#endif
+ }
+
+ return 1;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Tools.PublishIIS/WebConfigTransform.cs b/src/Microsoft.AspNet.Tools.PublishIIS/WebConfigTransform.cs
new file mode 100644
index 0000000000..288de27ed7
--- /dev/null
+++ b/src/Microsoft.AspNet.Tools.PublishIIS/WebConfigTransform.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Microsoft.AspNet.Tools.PublishIIS
+{
+ public static class WebConfigTransform
+ {
+ public static XDocument Transform(XDocument webConfig, string appName)
+ {
+ const string HandlersElementName = "handlers";
+ const string httpPlatformElementName = "httpPlatform";
+
+ webConfig = webConfig == null || webConfig.Root.Name.LocalName != "configuration"
+ ? XDocument.Parse("")
+ : webConfig;
+
+ var webServerSection = GetOrCreateChild(webConfig.Root, "system.webServer");
+
+ TransformHandlers(GetOrCreateChild(webServerSection, HandlersElementName));
+ TransformHttpPlatform(GetOrCreateChild(webServerSection, httpPlatformElementName), appName);
+
+ // make sure that the httpPlatform element is after handlers element
+ var httpPlatformElement = webServerSection.Element(HandlersElementName)
+ .ElementsBeforeSelf(httpPlatformElementName).SingleOrDefault();
+ if (httpPlatformElement != null)
+ {
+ httpPlatformElement.Remove();
+ webServerSection.Element(HandlersElementName).AddAfterSelf(httpPlatformElement);
+ }
+
+ return webConfig;
+ }
+
+ private static void TransformHandlers(XElement handlersElement)
+ {
+ var platformHandlerElement =
+ handlersElement.Elements("add")
+ .FirstOrDefault(e => string.Equals((string)e.Attribute("name"), "httpplatformhandler", StringComparison.OrdinalIgnoreCase));
+
+ if (platformHandlerElement == null)
+ {
+ platformHandlerElement = new XElement("add");
+ handlersElement.Add(platformHandlerElement);
+ }
+
+ platformHandlerElement.SetAttributeValue("name", "httpPlatformHandler");
+ SetAttributeValueIfEmpty(platformHandlerElement, "path", "*");
+ SetAttributeValueIfEmpty(platformHandlerElement, "verb", "*");
+ SetAttributeValueIfEmpty(platformHandlerElement, "modules", "httpPlatformHandler");
+ SetAttributeValueIfEmpty(platformHandlerElement, "resourceType", "Unspecified");
+ }
+
+ private static void TransformHttpPlatform(XElement httpPlatformElement, string appName)
+ {
+ httpPlatformElement.SetAttributeValue("processPath", $@"..\wwwroot\{appName}");
+ SetAttributeValueIfEmpty(httpPlatformElement, "stdoutLogEnabled", "false");
+ SetAttributeValueIfEmpty(httpPlatformElement, "startupTimeLimit", "3600");
+ }
+
+ private static XElement GetOrCreateChild(XElement parent, string childName)
+ {
+ var childElement = parent.Element(childName);
+ if (childElement == null)
+ {
+ childElement = new XElement(childName);
+ parent.Add(childElement);
+ }
+ return childElement;
+ }
+
+ private static void SetAttributeValueIfEmpty(XElement element, string attributeName, string value)
+ {
+ element.SetAttributeValue(attributeName, (string)element.Attribute(attributeName) ?? value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Tools.PublishIIS/project.json b/src/Microsoft.AspNet.Tools.PublishIIS/project.json
new file mode 100644
index 0000000000..cd6d70d6b3
--- /dev/null
+++ b/src/Microsoft.AspNet.Tools.PublishIIS/project.json
@@ -0,0 +1,19 @@
+{
+ "name": "publish-iis",
+ "version": "1.0.0-*",
+ "compilationOptions": {
+ "emitEntryPoint": true
+ },
+
+ "dependencies": {
+ "NETStandard.Library": "1.0.0-*",
+ "System.Xml.XDocument": "4.0.11-beta-*",
+ "Microsoft.Extensions.CommandLineUtils": "1.0.0-*"
+ },
+
+ "frameworks": {
+ "dnxcore50": {
+ "Microsoft.NETCore.Platforms": "1.0.0-*"
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Tools.PublishIIS.Tests/WebConfigTransformFacts.cs b/test/Microsoft.AspNet.Tools.PublishIIS.Tests/WebConfigTransformFacts.cs
new file mode 100644
index 0000000000..74dc5e518d
--- /dev/null
+++ b/test/Microsoft.AspNet.Tools.PublishIIS.Tests/WebConfigTransformFacts.cs
@@ -0,0 +1,111 @@
+using Xunit;
+using System.Linq;
+using System.Xml.Linq;
+using Microsoft.AspNet.Tools.PublishIIS;
+
+namespace Microsoft.AspNet.Tools.PublishIIS.Tests
+{
+ public class WebConfigTransformFacts
+ {
+ private XDocument WebConfigTemplate => XDocument.Parse(
+@"
+
+
+
+
+
+
+");
+
+ [Fact]
+ public void WebConfigTransform_creates_new_config_if_one_does_not_exist()
+ {
+ Assert.True(XNode.DeepEquals(WebConfigTemplate, WebConfigTransform.Transform(null, "test.exe")));
+ }
+
+ [Fact]
+ public void WebConfigTransform_creates_new_config_if_one_has_unexpected_format()
+ {
+ Assert.True(XNode.DeepEquals(WebConfigTemplate, WebConfigTransform.Transform(XDocument.Parse(""), "test.exe")));
+ }
+
+ [Theory]
+ [InlineData(new object[] { new[] {"system.webServer"}})]
+ [InlineData(new object[] { new[] {"add"}})]
+ [InlineData(new object[] { new[] {"handlers"}})]
+ [InlineData(new object[] { new[] {"httpPlatform"}})]
+ [InlineData(new object[] { new[] {"handlers", "httpPlatform"}})]
+ public void WebConfigTransform_adds_missing_elements(string[] elementNames)
+ {
+ var input = new XDocument(WebConfigTemplate);
+ foreach (var elementName in elementNames)
+ {
+ input.Descendants(elementName).Remove();
+ }
+
+ Assert.True(XNode.DeepEquals(WebConfigTemplate, WebConfigTransform.Transform(input, "test.exe")));
+ }
+
+ [Theory]
+ [InlineData("add", "path", "test")]
+ [InlineData("add", "verb","test")]
+ [InlineData("add", "modules", "mods")]
+ [InlineData("add", "resourceType", "Either")]
+ [InlineData("httpPlatform", "stdoutLogEnabled", "true")]
+ [InlineData("httpPlatform", "startupTimeLimit", "1200")]
+ [InlineData("httpPlatform", "arguments", "arg1")]
+ [InlineData("httpPlatform", "stdoutLogFile", "logfile.log")]
+ public void WebConfigTransform_wont_override_custom_values(string elementName, string attributeName, string attributeValue)
+ {
+ var input = new XDocument(WebConfigTemplate);
+ input.Descendants(elementName).Single().SetAttributeValue(attributeName, attributeValue);
+
+ var output = WebConfigTransform.Transform(input, "test.exe");
+ Assert.Equal(attributeValue, (string)output.Descendants(elementName).Single().Attribute(attributeName));
+ }
+
+ [Fact]
+ public void WebConfigTransform_overwrites_processPath()
+ {
+ var newProcessPath =
+ (string)WebConfigTransform.Transform(WebConfigTemplate, "app.exe")
+ .Descendants("httpPlatform").Single().Attribute("processPath");
+
+ Assert.Equal(@"..\wwwroot\app.exe", newProcessPath);
+ }
+
+ [Fact]
+ public void WebConfigTransform_fixes_httpPlatformHandler_casing()
+ {
+ var input = new XDocument(WebConfigTemplate);
+ input.Descendants("add").Single().SetAttributeValue("name", "httpplatformhandler");
+
+ Assert.True(XNode.DeepEquals(WebConfigTemplate, WebConfigTransform.Transform(input, "test.exe")));
+ }
+
+ [Fact]
+ public void WebConfigTransform_does_not_remove_children_of_httpPlatform_element()
+ {
+ var envVarsElement =
+ new XElement("environmentVariables",
+ new XElement("environmentVariable", new XAttribute("name", "ENVVAR"), new XAttribute("value", "123")));
+
+ var input = new XDocument(WebConfigTemplate);
+ input.Descendants("httpPlatform").Single().Add(envVarsElement);
+
+ Assert.True(XNode.DeepEquals(envVarsElement,
+ WebConfigTransform.Transform(input, "app.exe").Descendants("httpPlatform").Single().Elements().Single()));
+ }
+
+ private bool VerifyMissingElementCreated(params string[] elementNames)
+ {
+ var input = new XDocument(WebConfigTemplate);
+ foreach (var elementName in elementNames)
+ {
+ input.Descendants(elementName).Remove();
+ }
+
+ return XNode.DeepEquals(WebConfigTemplate, WebConfigTransform.Transform(input, "test.exe"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Tools.PublishIIS.Tests/project.json b/test/Microsoft.AspNet.Tools.PublishIIS.Tests/project.json
new file mode 100644
index 0000000000..d5060c0ace
--- /dev/null
+++ b/test/Microsoft.AspNet.Tools.PublishIIS.Tests/project.json
@@ -0,0 +1,19 @@
+{
+ "dependencies": {
+ "xunit": "2.1.0",
+ "Microsoft.AspNet.Tools.PublishIIS" : ""
+ },
+
+ "frameworks": {
+ "dnxcore50": {
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.0-*",
+ "xunit.runner.aspnet": "2.0.0-aspnet-*"
+ }
+ }
+ },
+
+ "commands": {
+ "test": "xunit.runner.aspnet"
+ }
+}
\ No newline at end of file