<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahmed Hesham</title>
    <description>The latest articles on DEV Community by Ahmed Hesham (@mark0960).</description>
    <link>https://dev.to/mark0960</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F839429%2F71790072-2235-4dfe-bf4a-720519b053d6.png</url>
      <title>DEV Community: Ahmed Hesham</title>
      <link>https://dev.to/mark0960</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mark0960"/>
    <language>en</language>
    <item>
      <title>Full Duplex Communication Between Web App and Desktop Application Using WebSocket</title>
      <dc:creator>Ahmed Hesham</dc:creator>
      <pubDate>Tue, 10 May 2022 17:38:39 +0000</pubDate>
      <link>https://dev.to/mark0960/full-duplex-communication-between-web-app-and-desktop-application-using-websocket-51gd</link>
      <guid>https://dev.to/mark0960/full-duplex-communication-between-web-app-and-desktop-application-using-websocket-51gd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;One of the limitations of web apps is hardware access. If you use some special hardware, communication might be difficult if not impossible.&lt;br&gt;
In this case, the client will need to install a software to handle the communication between the web app and the hardware.&lt;/p&gt;

&lt;p&gt;An example of this is when you get a new laptop and then visit the manufacturer website to check for driver updates. Most often, you will be asked to install a software that will check your laptop; then, the results will be displayed on the website.&lt;/p&gt;

&lt;p&gt;In our case, this software acts as a WebSocket server, and the browser is the WebSocket client that initiates the session.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskadpd2gxitdst6xwqn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskadpd2gxitdst6xwqn2.png" alt="WebSocket communication between frontend and desktop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depending on your design, the software might also send HTTP requests to the backend server.&lt;/p&gt;
&lt;h2&gt;
  
  
  Do You Really Need It?
&lt;/h2&gt;

&lt;p&gt;This design should be used only when necessary; we are adding a component to our system which increases the complexity, and the users need to install a software to use our web app which raises the question whether we should use a web app in the first place.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://web.dev/devices-introduction/" rel="noopener noreferrer"&gt;post&lt;/a&gt; covers most JavaScript APIs for communicating with hardware devices, check them first before attempting the WebSocket approach.&lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;We will create a simple web page that will connect to a WebSocket server running on the client Windows machine. When a session is open, we can exchange messages between the web page and the server.&lt;/p&gt;

&lt;p&gt;The WebSocket client is implemented with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;WebSocket API&lt;/a&gt;.&lt;br&gt;
The WebSocket server is a console application written in .Net Framework, and we use &lt;a href="https://www.supersocket.net/" rel="noopener noreferrer"&gt;SuperSocket&lt;/a&gt; for the WebSocket server implementation.&lt;/p&gt;
&lt;h2&gt;
  
  
  WebSocket Client
&lt;/h2&gt;

&lt;p&gt;We make an &lt;code&gt;input&lt;/code&gt; to write messages, and a &lt;code&gt;div&lt;/code&gt; to display received messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input id="input" type="text"&amp;gt;
&amp;lt;div id="log"&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple function that creates the WebSocket client instance, and assigns event listeners.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function createWebSocket() {
    const webSocket = new WebSocket('ws://127.0.0.1:85'); // In the constructor we use the loopback address and a free port

    webSocket.onopen = () =&amp;gt; {
        webSocket.send('Connected');

        const input = document.getElementById('input');
        input.onkeydown = e =&amp;gt; {
            if (e.code === 'Enter') {
                webSocket.send(input.value);
                input.value = null;
            }
        }
    }

    webSocket.onmessage = msg =&amp;gt; {
        document.getElementById('log').innerHTML += `Server: ${msg.data} &amp;lt;br/&amp;gt;`;
    }

    return webSocket;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the following code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We keep attempting to connect to the WebSocket server.&lt;/li&gt;
&lt;li&gt;There is no way to reconnect to the server when a session is closed, instead we create a new client instance.&lt;/li&gt;
&lt;li&gt;A session gets closed for several reasons, but here it happens because the server is unreachable, most likely because the server hasn't started yet.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.onload = async () =&amp;gt; {
    var webSocket = createWebSocket();

    while (webSocket.readyState !== webSocket.OPEN) {
        await new Promise(res =&amp;gt; setTimeout(res, 1000)); // wait 1 second

        if (webSocket.readyState === webSocket.CLOSED)
            webSocket = createWebSocket();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  WebSocket Server
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a Console Application&lt;/li&gt;
&lt;li&gt;Install both &lt;code&gt;SuperSocket.Engine&lt;/code&gt; and &lt;code&gt;SuperSocket.WebSocket&lt;/code&gt;; you can get them from NuGet.&lt;/li&gt;
&lt;li&gt;Create a server instance and add event listeners. Notice how similar it is to the client (JavaScript) code.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static void Main()
{
    using WebSocketServer server = new();
    WebSocketSession currSession = null;

    server.NewSessionConnected += session =&amp;gt;
    {
        currSession = session;
        currSession.Send("Connected");
    };

    server.NewMessageReceived += (session, message) =&amp;gt;
    {
        Console.WriteLine($"Client: {message}");

        if (message == "close")
            session.Close();
    };

    server.SessionClosed += (_, reason) =&amp;gt; Console.WriteLine($"Server: Connection closed. Reason: {reason}, press 'Enter' to exit.");

    // In the ServerConfig, the default IP value is the localhost
    ServerConfig config = new()
    {
        Port = 85, // same port number as in client
        MaxConnectionNumber = 1 // depending on you design, there might be more than one connection
    };

    server.Setup(config);
    server.Start();

    while (true)
    {
        string input = Console.ReadLine();

        if (currSession != null &amp;amp;&amp;amp; !currSession.Connected)
            break;

        if (currSession != null)
            currSession.Send(input);
        else
            Console.WriteLine("Server: No session");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's Test It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo22vx09p1ffvhp7700xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo22vx09p1ffvhp7700xg.png" alt="Test over HTTPS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works great!&lt;/p&gt;

&lt;p&gt;Even though we are using non-secure protocol (ws) instead of the secure protocol (wss), and we are connecting from a secure context (https), the connection is not blocked.&lt;br&gt;
That's because &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure" rel="noopener noreferrer"&gt;locally-delivered resources are considered secure&lt;/a&gt;, however &lt;code&gt;http://localhost&lt;/code&gt; is not considered secure by older browsers, because it is not guaranteed to map to loopback address.&lt;br&gt;
That's why although &lt;code&gt;ws://localhost&lt;/code&gt; works fine with newer browsers, it's recommended to use &lt;code&gt;ws://127.0.0.1&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  See Also
&lt;/h2&gt;

&lt;p&gt;Some information is outdated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://textslashplain.com/2019/08/28/browser-architecture-web-to-app-communication-overview/" rel="noopener noreferrer"&gt;Browser Architecture: Web-to-App Communication Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://emilymstark.com/2021/12/24/when-a-web-pki-certificate-wont-cut-it.html" rel="noopener noreferrer"&gt;When a web PKI certificate won't cut it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://words.filippo.io/how-plex-is-doing-https-for-all-its-users/" rel="noopener noreferrer"&gt;HOW PLEX IS DOING HTTPS FOR ALL ITS USERS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-let-localhost-be-localhost-02" rel="noopener noreferrer"&gt;Let 'localhost' be localhost&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.blog/2019/12/18/websockets-for-fun-and-profit/" rel="noopener noreferrer"&gt;WebSockets for fun and profit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>dotnet</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Deploy Web App to IIS with Zero Downtime in Visual Studio</title>
      <dc:creator>Ahmed Hesham</dc:creator>
      <pubDate>Sun, 17 Apr 2022 09:26:01 +0000</pubDate>
      <link>https://dev.to/mark0960/deploy-web-app-to-iis-with-zero-downtime-in-visual-studio-3f5k</link>
      <guid>https://dev.to/mark0960/deploy-web-app-to-iis-with-zero-downtime-in-visual-studio-3f5k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my previous &lt;a href="https://dev.to/mark0960/publish-web-app-from-visual-studio-to-iis-using-web-deploy-56m6"&gt;post&lt;/a&gt;, I wrote about how to deploy a web app from Visual Studio to IIS using Web Deploy tool. One problem you might have noticed, is the downtime that occurs during deployment.&lt;br&gt;
So in this post, I'll write about how to use &lt;a href="https://en.wikipedia.org/wiki/Blue-green_deployment" rel="noopener noreferrer"&gt;Blue-Green Deployment&lt;/a&gt; in IIS to achieve zero downtime deployment.&lt;/p&gt;

&lt;p&gt;We will need at least IIS 7, in order to install Application Request Routing (ARR).&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic Idea&lt;/li&gt;
&lt;li&gt;Steps&lt;/li&gt;
&lt;li&gt;Scripts&lt;/li&gt;
&lt;li&gt;Remote Access&lt;/li&gt;
&lt;li&gt;Integration with Visual Studio&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Basic Idea &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We will create two server, blue and green. One of them will be accessible through public network for our clients, and the other only through the private network for testing purposes.&lt;/p&gt;

&lt;p&gt;Deployment is done in these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We will deploy our new web app to the private network server.&lt;/li&gt;
&lt;li&gt;Make sure it is working as intended.&lt;/li&gt;
&lt;li&gt;Make the private network server accessible to public network, now both servers can receive clients requests.&lt;/li&gt;
&lt;li&gt;Gracefully shutdown the original public network server; now it can't receive new requests but will finish its current requests.&lt;/li&gt;
&lt;li&gt;Make the original public network server accessible only to private network, then turn it on.&lt;/li&gt;
&lt;li&gt;Now the green and blue servers are switched, and in each deployment they will get switched again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Steps &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Download and Install Web Platform Installer (WebPI)
&lt;/h3&gt;

&lt;p&gt;It's recommended to use WebPI instead of directly downloading tools; because WebPI also gets the dependencies and configures these tools depending on the current system settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.microsoft.com/web/downloads/platform.aspx" rel="noopener noreferrer"&gt;Web Platform Installer&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Get Application Request Routing (ARR)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open WebPI directly (start menu for example), not through IIS Manager.&lt;/li&gt;
&lt;li&gt;Search for ARR, then install.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flswqi2ofdkf1yk70kqpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flswqi2ofdkf1yk70kqpu.png" alt="Install ARR"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Create the Blue and Green Servers
&lt;/h3&gt;

&lt;p&gt;Make three websites, one on port 80 that will receive client requests, we will call it server-farm. The other two are bound to other ports, say 8001 and 8002, and in this example they are called blue and green respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkplk8urekc6iry9ncem8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkplk8urekc6iry9ncem8.png" alt="Create Websites"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two websites contain one file &lt;code&gt;content.html&lt;/code&gt; which either has &lt;code&gt;blue&lt;/code&gt; or &lt;code&gt;green&lt;/code&gt; written in it.&lt;/p&gt;

&lt;p&gt;We will also add those websites to the hosts file and make them point to localhost.&lt;br&gt;
In Windows, the hosts file is in: &lt;code&gt;C:\Windows\System32\drivers\etc\hosts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdst2qc4mhtd4vutcrn7s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdst2qc4mhtd4vutcrn7s.png" alt="hosts File"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's test it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11pxot83jn52tn9nvg2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11pxot83jn52tn9nvg2z.png" alt="Test Website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Configure the Server Farm
&lt;/h3&gt;

&lt;p&gt;Create new &lt;a href="https://en.wikipedia.org/wiki/Server_farm" rel="noopener noreferrer"&gt;server farm&lt;/a&gt;, and add two servers to it with the port numbers we just assigned to the blue and green websites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwf29gczytr3d0qinf38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwf29gczytr3d0qinf38.png" alt="Create Server Farm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkey1147b1c1konxmfuzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkey1147b1c1konxmfuzi.png" alt="Add Two Servers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will get a prompt suggesting to add rewrite rules, select no; we will add our own rewrite rules later.&lt;/p&gt;

&lt;p&gt;Things should look like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa92zrxbfh8ncxwfjek8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa92zrxbfh8ncxwfjek8w.png" alt="Server Farm Set Up"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Configure Health Test
&lt;/h3&gt;

&lt;p&gt;The health test will decide which server receives the clients requests.&lt;br&gt;
First create an html file &lt;code&gt;is-up.html&lt;/code&gt; in each website, beside the &lt;code&gt;content.html&lt;/code&gt; file. One of them will contain &lt;code&gt;1&lt;/code&gt;, and the other &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's test it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37b240irdvup09bhobmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37b240irdvup09bhobmm.png" alt="Test is-up.html"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to our server farm, and configure the health test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URL will point to the &lt;code&gt;is-up.html&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Interval will check for response every second.&lt;/li&gt;
&lt;li&gt;Response Match is what we consider healthy—i.e., which server receives the requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez84jrycpz3gcsjo8cmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez84jrycpz3gcsjo8cmi.png" alt="Add Health Test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verify.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiisnvh1lc5fza5jz0yk5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiisnvh1lc5fza5jz0yk5.png" alt="Verify Health Test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check Monitor and Management in the server farm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh78gqcwgswmz9ud85hea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh78gqcwgswmz9ud85hea.png" alt="Monitor and Management"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Logging Problem
&lt;/h3&gt;

&lt;p&gt;The health test makes a request to each server every second, and these requests are logged, which is not desirable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pb3g4i23elmundoynfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pb3g4i23elmundoynfp.png" alt="Health Test Logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To solve this problem, we will need to add an entry excluding the &lt;code&gt;is-up.html&lt;/code&gt; file from the logs in the &lt;code&gt;applicationHost.config&lt;/code&gt; file of the IIS. As far as I know, this is not possible through the IIS Manager.&lt;/p&gt;

&lt;p&gt;In Windows, the &lt;code&gt;applicationHost.config&lt;/code&gt; file is located in: &lt;code&gt;C:\Windows\System32\inetsrv\config\applicationHost.config&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add this code to the end of the &lt;code&gt;applicationHost.config&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

    &amp;lt;location path="blue/is-up.html"&amp;gt;
        &amp;lt;system.webServer&amp;gt;
            &amp;lt;httpLogging dontLog="true" /&amp;gt;
        &amp;lt;/system.webServer&amp;gt;
    &amp;lt;/location&amp;gt;
    &amp;lt;location path="green/is-up.html"&amp;gt;
        &amp;lt;system.webServer&amp;gt;
            &amp;lt;httpLogging dontLog="true" /&amp;gt;
        &amp;lt;/system.webServer&amp;gt;
    &amp;lt;/location&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is how the end of the &lt;code&gt;applicationHost.config&lt;/code&gt; file should look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhr21w1acb81xy1s7p89n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhr21w1acb81xy1s7p89n.png" alt="End of applicationHost.config"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Add URL Rewrite Rules
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/using-the-url-rewrite-module" rel="noopener noreferrer"&gt;URL Rewrite&lt;/a&gt; is used to change the request URL according to the rules you specify.&lt;/p&gt;

&lt;p&gt;We will use this module to route requests to our server farm.&lt;br&gt;
If you installed ARR using WebPI, URL Rewrite also gets installed.&lt;/p&gt;

&lt;p&gt;From the main node in the IIS Manager, select URL Rewrite, then add a Blank rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z7lpcmpmzvt1mh2cqmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z7lpcmpmzvt1mh2cqmr.png" alt="Add Blank Rule"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pattern will match any URL: &lt;code&gt;.*&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevd6w3qcz3q7gapn18ju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevd6w3qcz3q7gapn18ju.png" alt="Match Any URL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a condition for the port to be &lt;code&gt;80&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc0gbxc32lyj02ae3fcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc0gbxc32lyj02ae3fcr.png" alt="Only Port 80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Action type will be: Route to Server Farm&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkzh3mwbguxdb6hc1etl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkzh3mwbguxdb6hc1etl.png" alt="Action Type"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the end, things will look like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnzflwlb1l0k7ghd0a63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnzflwlb1l0k7ghd0a63.png" alt="URL Rewrite Rule"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's test it.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejjzho4u9ejczmkmu570.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejjzho4u9ejczmkmu570.png" alt="Test Server-Farm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we switch the &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; in the &lt;code&gt;is-up.html&lt;/code&gt; files&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscz0p2z1e52093cruyzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscz0p2z1e52093cruyzd.png" alt="Switch 0 and 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now thigs are set up. Next, we will write some scripts to automate the deployment process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scripts &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We can use PowerShell to automate some processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Server Farm Object
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function get-webfarm {
    param($webFarmName)

    $assembly = [System.Reflection.Assembly]::LoadFrom("$env:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll")
    $mgr = new-object Microsoft.Web.Administration.ServerManager "$env:systemroot\system32\inetsrv\config\applicationhost.config"
    $conf = $mgr.GetApplicationHostConfiguration()
    $section = $conf.GetSection("webFarms")
    $webFarms = $section.GetCollection()
    $webFarms | Where {
        $_.GetAttributeValue("name") -eq $webFarmName
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  To check which server is unhealthy
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$webFarmName = "server-farm"

$webFarm = get-webfarm $webFarmName
$unhealthyServer = $webFarm.GetCollection() | where {
    !$_.GetChildElement("applicationRequestRouting").GetChildElement("counters").GetAttributeValue("isHealthy")
}

$unhealthyServer.GetAttributeValue("address")


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Switch Blue and Green
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$webFarmName = "server-farm"
$addressBlue = "blue"
$addressGreen = "green"
$siteBlue = "http://blue:8001/"
$siteGreen = "http://green:8002/"
$pathBlue = "C:\inetpub\sites\blue"
$pathGreen = "C:\inetpub\sites\green"
$warmUpRequestURL = "content.html"
$healthCheckFileName = "is-up.html"
$healthyResponse = "1"
$unhealthyResponse = "0"

$webFarm = get-webfarm $webFarmName

# Get health check interval
$healthCheckTimeoutS = $webFarm.GetChildElement("applicationRequestRouting").GetChildElement("healthCheck").GetAttributeValue("interval").TotalSeconds

$healthyServer = $webFarm.GetCollection() | where {
    $_.GetChildElement("applicationRequestRouting").GetChildElement("counters").GetAttributeValue("isHealthy")
}

$siteToWarm = $siteBlue
$serverAddressToBringDown = $addressGreen
$pathToBringDown = $pathGreen
$pathToBringUp = $pathBlue

if($healthyServer.GetAttributeValue("address") -eq $addressBlue) {
    $siteToWarm = $siteGreen
    $serverAddressToBringDown = $addressBlue
    $pathToBringUp = $pathGreen
    $pathToBringDown = $pathBlue
}

# Initializing unhealthy server, and making sure it works as intended
Write-Host "Warming up $($siteToWarm)"
Do {
    $time = Measure-Command {
        $res = Invoke-WebRequest "$($siteToWarm)$($warmUpRequestURL)"
    }
    $ms = $time.TotalMilliSeconds
    If ($ms -ge 400) {
        Write-Host "$($res.StatusCode) from $($siteToWarm) in $($ms)ms"
    }
} While ($ms -ge 400)

Write-Host "$($res.StatusCode) from $($siteToWarm) in $($ms)ms"

# If the unhealthy server is fine, start the switching operation
if ($res.StatusCode -eq 200) {
    Write-Host "Bringing $($pathToBringUp) up"
    (Get-Content $pathToBringUp\$healthCheckFileName).replace($unhealthyResponse, $healthyResponse) | Set-Content $pathToBringUp\$healthCheckFileName

    # Wait for health check to mark the server healthy, afterwards we can bring down the other server
    Write-Host "Waiting for health check to pass in $($healthCheckTimeoutS) seconds..."
    Start-Sleep -s $healthCheckTimeoutS

    Write-Host "Draining $($pathToBringDown)"

    $serverToBringDown = $webFarm.GetCollection() | Where {
        $_.GetAttributeValue("address") -eq $serverAddressToBringDown
    }
    $arrToBringDown = $serverToBringDown.GetChildElement("applicationRequestRouting")

    $method = $arrToBringDown.Methods["SetState"]
    $methodInstance = $method.CreateInstance()

    # 0 = Available
    # 1 = Drain
    # 2 = Unavailable
    # 3 = Unavailable Gracefully
    $methodInstance.Input.Attributes[0].Value = 1
    $methodInstance.Execute()

    # loop till there is no requests, then bring the server down
    $currentRequests = $arrToBringDown.GetChildElement("counters").GetAttributeValue("currentRequests")
    While($currentRequests -gt 0) {
        Start-Sleep -s 1
        $currentRequests = $arrToBringDown.GetChildElement("counters").GetAttributeValue("currentRequests")
    }

    Write-Host "Bringing $($pathToBringDown) down"
    (Get-Content $pathToBringDown\$healthCheckFileName).replace($healthyResponse, $unhealthyResponse) | Set-Content $pathToBringDown\$healthCheckFileName

    $methodInstance.Input.Attributes[0].Value = 0
    $methodInstance.Execute()
} else {
    Write-Host "Cannot warm up $($siteToWarm)"
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Remote Access &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To run these scripts remotely, we will use &lt;a href="https://docs.microsoft.com/en-us/windows/win32/winrm/portal" rel="noopener noreferrer"&gt;Windows Remote Management (WinRM)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To configure WinRM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make sure port &lt;code&gt;5985&lt;/code&gt; is open through the network.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;winrm qc&lt;/code&gt; at remote server, select &lt;code&gt;y&lt;/code&gt; when prompted.&lt;/li&gt;
&lt;li&gt;Make sure WinRM service is running locally.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;winrm set winrm/config/client '@{TrustedHosts="your.server.IP"}'&lt;/code&gt; locally.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you can run scripts remotely using &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-5.1" rel="noopener noreferrer"&gt;Invoke-Command&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Invoke-Command -ComputerName your.server.ip -ScriptBlock {

    $webFarmName = "server-farm"

    $assembly = [System.Reflection.Assembly]::LoadFrom("$env:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll")
    $mgr = new-object Microsoft.Web.Administration.ServerManager "$env:systemroot\system32\inetsrv\config\applicationhost.config"
    $conf = $mgr.GetApplicationHostConfiguration()
    $section = $conf.GetSection("webFarms")
    $webFarms = $section.GetCollection()
    $webFarm = $webFarms | Where {
        $_.GetAttributeValue("name") -eq $webFarmName
    }

    $unhealthyServer = $webFarm.GetCollection() | where {
        !$_.GetChildElement("applicationRequestRouting").GetChildElement("counters").GetAttributeValue("isHealthy")
    }

    $unhealthyServer.GetAttributeValue("address")
} -Credential (New-Object System.Management.Automation.PSCredential ("user", (ConvertTo-SecureString 'password' -AsPlainText -Force)))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is simple authentication, for more options check this &lt;a href="https://docs.microsoft.com/en-us/windows/win32/winrm/authentication-for-remote-connections" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration with Visual Studio &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This builds on my previous &lt;a href="https://dev.to/mark0960/publish-web-app-from-visual-studio-to-iis-using-web-deploy-56m6"&gt;post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will use &lt;a href="https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-targets?view=vs-2022" rel="noopener noreferrer"&gt;Targets&lt;/a&gt; in MSBuild to control which server we will deploy to, and to switch Blue and Green servers after deployment.&lt;/p&gt;

&lt;p&gt;Add the PowerShell scripts in the project directory:&lt;br&gt;
&lt;code&gt;\Properties\Scripts\&lt;/code&gt;&lt;br&gt;
In the publish profile, add these lines of code in the project tag:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;Target Name="DetermineUnhealthy" BeforeTargets="BeforePublish"&amp;gt;
    &amp;lt;Exec Command="powershell -executionpolicy unrestricted &amp;amp;quot;&amp;amp;amp; &amp;amp;quot;&amp;amp;quot;$(MSBuildProjectDirectory)\Properties\Scripts\GetUnhealthy.ps1&amp;amp;quot;&amp;amp;quot;&amp;amp;quot;"
            ConsoleToMSBuild="true"&amp;gt;
        &amp;lt;Output TaskParameter="ConsoleOutput" PropertyName="DeployIisAppPath"/&amp;gt;
    &amp;lt;/Exec&amp;gt;
&amp;lt;/Target&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This code determines the unhealthy server before the start of the deployment and assigns the value to &lt;code&gt;DeployIisAppPath&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;In case of an application under the site in IIS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;Target Name="DetermineUnhealthy" BeforeTargets="BeforePublish"&amp;gt;
    &amp;lt;Exec Command="powershell -executionpolicy unrestricted &amp;amp;quot;&amp;amp;amp; &amp;amp;quot;&amp;amp;quot;$(MSBuildProjectDirectory)\Properties\Scripts\GetUnhealthy.ps1&amp;amp;quot;&amp;amp;quot;&amp;amp;quot;"
            ConsoleToMSBuild="true"&amp;gt;
        &amp;lt;Output TaskParameter="ConsoleOutput" PropertyName="UnhealthySite"/&amp;gt;
    &amp;lt;/Exec&amp;gt;
    &amp;lt;CreateProperty Value="$(UnhealthySite)/AppName"&amp;gt;
        &amp;lt;Output TaskParameter="Value" PropertyName="DeployIisAppPath"/&amp;gt;
    &amp;lt;/CreateProperty&amp;gt;
&amp;lt;/Target&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you want to switch directly after deployment, add this code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;Target Name="SwitchBlueGreen" AfterTargets="AfterPublish"&amp;gt;
    &amp;lt;Exec Command="powershell -executionpolicy unrestricted &amp;amp;quot;&amp;amp;amp; &amp;amp;quot;&amp;amp;quot;$(MSBuildProjectDirectory)\Properties\Scripts\SwitchBlueGreen.ps1&amp;amp;quot;&amp;amp;quot;&amp;amp;quot;" /&amp;gt;
&amp;lt;/Target&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In my case, I have two profiles: one with switching, and another without.&lt;/p&gt;

&lt;p&gt;Now you can deploy to IIS with zero downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  See Also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kevinareed.com/2015/11/07/how-to-deploy-anything-in-iis-with-zero-downtime-on-a-single-server/" rel="noopener noreferrer"&gt;How to Deploy Anything in IIS with Zero Downtime on a Single Server&lt;/a&gt;&lt;br&gt;
Taught me most of the stuff I wrote here.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild" rel="noopener noreferrer"&gt;MSBuild&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
    <item>
      <title>Publish Web App from Visual Studio to IIS Using Web Deploy</title>
      <dc:creator>Ahmed Hesham</dc:creator>
      <pubDate>Mon, 04 Apr 2022 20:12:14 +0000</pubDate>
      <link>https://dev.to/mark0960/publish-web-app-from-visual-studio-to-iis-using-web-deploy-56m6</link>
      <guid>https://dev.to/mark0960/publish-web-app-from-visual-studio-to-iis-using-web-deploy-56m6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There are several ways you could publish your Web App from Visual Studio. The simplest way is to publish to a folder, then copy paste to the hosting server, IIS for example.&lt;/p&gt;

&lt;p&gt;But it's always better to automate these operations; it makes them faster (no need to copy all files, just the changed ones), and less likely to cause mistakes (replace appsettings.json or web.config file).&lt;br&gt;
It also opens the door to more advanced operations, like zero downtime deployment, which I plan to write about in the next post.&lt;/p&gt;

&lt;p&gt;In this post I'll write about how to publish a Web App in Visual Studio to IIS using Web Deploy. When things are set up, you will only need to click the publish button in Visual Studio to publish your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/iis/publish/using-web-deploy/introduction-to-web-deploy" rel="noopener noreferrer"&gt;What is Web Deploy?&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;IIS 7&lt;/li&gt;
&lt;li&gt;Visual Studio 2010&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Note: The order of steps is important.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install Web Management Service (WMSVC)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In remote server, open Control Panel -&amp;gt; Turn Windows features on or off.&lt;/li&gt;
&lt;li&gt;Look for Internet Information Services (IIS) -&amp;gt; Management Tools -&amp;gt; check Management Service.&lt;/li&gt;
&lt;li&gt;Proceed to install.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fveu44d12yy13snavfjgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fveu44d12yy13snavfjgt.png" alt="Check Management Service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Download and Install Web Platform Installer (WebPI)
&lt;/h3&gt;

&lt;p&gt;It's recommended to use WebPI instead of directly downloading tools; because WebPI also gets the dependencies and configures these tools depending on the current system settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.microsoft.com/web/downloads/platform.aspx" rel="noopener noreferrer"&gt;Web Platform Installer&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Get Web Deploy
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open WebPI directly (start menu for example), not through IIS Manager.&lt;/li&gt;
&lt;li&gt;Search for Web Deploy, then install.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy01g8w89r99nc988s53h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy01g8w89r99nc988s53h.png" alt="Install Web Deploy from WebPI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If Web Deploy is already installed, uninstall it (from Control Panel, not repair), then repeat above steps.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Add Port 8172 to Exceptions
&lt;/h3&gt;

&lt;p&gt;This port gets added automatically to Windows Firewall exceptions if you followed the above steps.&lt;/p&gt;

&lt;p&gt;Make sure the port is open through the path from the development environment to the hosting server, for example if you are using EC2, add the port to the Inbound Rules of the Security Group.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Create New Publish Profile in Visual Studio
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the project you want to publish, then create a new publish profile.&lt;/li&gt;
&lt;li&gt;Select Web Server (IIS) -&amp;gt; Web Deploy.&lt;/li&gt;
&lt;li&gt;Write the server details as follow:

&lt;ul&gt;
&lt;li&gt;Server: &lt;code&gt;https://(ip or name):8172/msdeploy.axd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Site name: &lt;code&gt;site/application&lt;/code&gt; as in IIS&lt;/li&gt;
&lt;li&gt;Username and Password are windows credentials.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click Finish.&lt;/li&gt;
&lt;li&gt;You might need to add &lt;code&gt;&amp;lt;AllowUntrustedCertificate&amp;gt;True&amp;lt;/AllowUntrustedCertificate&amp;gt;&lt;/code&gt; to the publish profile found under solution tree: YourProject -&amp;gt; Properties -&amp;gt; PublishProfiles.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zl4rx7ph8vl7nqi8g8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zl4rx7ph8vl7nqi8g8x.png" alt="Publish Profile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Publish Your Project
&lt;/h3&gt;

&lt;p&gt;We finished setting up Web Deploy, now you just need to click publish, and Web Deploy will do all the work for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  See Also
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd568996(v=ws.10)" rel="noopener noreferrer"&gt;Web Deploy with command line&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
