<?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: Sugumar Prabhakar</title>
    <description>The latest articles on DEV Community by Sugumar Prabhakar (@sprabha1990).</description>
    <link>https://dev.to/sprabha1990</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%2F723811%2F27018f79-65f4-4445-b3d7-d7688c803aa5.png</url>
      <title>DEV Community: Sugumar Prabhakar</title>
      <link>https://dev.to/sprabha1990</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sprabha1990"/>
    <language>en</language>
    <item>
      <title>S3 File Operations using Blazor WASM</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Tue, 21 Mar 2023 02:11:45 +0000</pubDate>
      <link>https://dev.to/sprabha1990/s3-file-operations-using-blazor-wasm-5cn5</link>
      <guid>https://dev.to/sprabha1990/s3-file-operations-using-blazor-wasm-5cn5</guid>
      <description>&lt;p&gt;In the previous chapter, we created three APIs that will list all the files available in the S3 bucket, download them, and upload a file to the S3 bucket.&lt;/p&gt;

&lt;p&gt;In this chapter, we are going to create the Blazor web assembly front-end application that will provide functionalities to the users to see all the files from the bucket, download them and upload a new file if wanted. We are going to use the APIs we created in the previous chapter as the intermediate service that handles the user request.&lt;/p&gt;

&lt;p&gt;Find the complete source code in my GitHub repository below.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/sprabha1990/S3FileOperations.Blazor.NET7" rel="noopener noreferrer"&gt;https://github.com/sprabha1990/S3FileOperations.Blazor.NET7&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will use the empty Blazor WASM project that was already in the previous chapter.&lt;/p&gt;

&lt;p&gt;Let's open the "S3FileOperations.Blazor" project inside the "S3FileOperations.NET7.sln" in VS2022. As a first step, we are going to incorporate bootstrap v5 into the project. &lt;/p&gt;

&lt;p&gt;Add the following code inside the "&lt;strong&gt;index.html&lt;/strong&gt;" available inside "&lt;strong&gt;wwwroot&lt;/strong&gt;" folder.&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;head&amp;gt;
    ...

    &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"&amp;gt;
    &amp;lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css"&amp;gt;

    ...
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    ...
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have close look at the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag, I'm adding the script to use bootstrap icons. If you want to use alternative icons, you can add/replace their script here.&lt;/p&gt;

&lt;p&gt;Now, we are going to add an entity class named &lt;code&gt;Files.cs&lt;/code&gt;. This holds the public properties of the file information that will be shown in the UI. &lt;br&gt;
Let's add the below properties into the &lt;code&gt;Files&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Files
    {
        public string ETag { get; set; } = string.Empty;
        public string Key { get; set; } = string.Empty;
        public string BucketName { get; set; } = string.Empty;
        public long Size { get; set; } = 0;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add the UI HTML code in the &lt;code&gt;Index.razor&lt;/code&gt; page available inside the Pages folder.&lt;/p&gt;

&lt;p&gt;Workflow is that,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We are going to add an HTML table with 4 columns that will show the "eTag, fileName, bucketName, and size of the files retrieved from the S3. &lt;/li&gt;
&lt;li&gt;We are going to add a download button adjacent to each row of the table to help users download the file easily.&lt;/li&gt;
&lt;li&gt;We are going to add a File upload option for the user so that the user can upload any file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add the below code in the &lt;code&gt;Index.razor&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@page "/"
@using S3FileOperations.Blazor.Entity;
@inject HttpClient _httpClient;
@using Microsoft.AspNetCore.Components.Forms;
@using System.Net.Http.Headers;

&amp;lt;table class="table table-striped mt-5"&amp;gt;
     &amp;lt;thead class="table-dark"&amp;gt;
     &amp;lt;tr&amp;gt;
          &amp;lt;th scope="col"&amp;gt;ETAG&amp;lt;/th&amp;gt;
          &amp;lt;th scope="col"&amp;gt;FileName&amp;lt;/th&amp;gt;
          &amp;lt;th scope="col"&amp;gt;BucketName&amp;lt;/th&amp;gt;
          &amp;lt;th scope="col"&amp;gt;Size (In Bytes)&amp;lt;/th&amp;gt;
          &amp;lt;th scope="col"&amp;gt;&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;
    &amp;lt;tbody class="table-light"&amp;gt;
        @foreach(var file in FilesInS3)
        {
            &amp;lt;tr&amp;gt;
                &amp;lt;th scope="col"&amp;gt;@file.ETag&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;@file.Key&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;@file.BucketName&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;@file.Size&amp;lt;/th&amp;gt;
                &amp;lt;th scope="col"&amp;gt;&amp;lt;a href="**https://localhost:7097**/
api/S3/download?Key=@file.Key" class="link-success"&amp;gt;Download&amp;lt;/a&amp;gt;&amp;lt;i class="bi bi-download ms-3 d-inline-flex align-Items-center text-danger"&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/th&amp;gt;
            &amp;lt;/tr&amp;gt;
        }
    &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;

&amp;lt;div class="card d-inline-flex flex-row bd-highlight ms-2 mt-2 w-50 p-3"&amp;gt;
    &amp;lt;div class="card-body p-2 bd-highlight"&amp;gt;
        &amp;lt;InputFile OnChange="(e) =&amp;gt; _currentFile = e.File" class="btn-light mb-2 w-100" /&amp;gt;
        @if (_currentFile != null)
        {
            &amp;lt;div class="d-grid gap-3 "&amp;gt;
                &amp;lt;div class="p-2 bg-light border mt-1"&amp;gt;&amp;lt;p&amp;gt;Name: @_currentFile.Name&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;
                &amp;lt;div class="p-2 bg-light border"&amp;gt;Size in bytes: @_currentFile.Size&amp;lt;/div&amp;gt;
                &amp;lt;div class="p-2 bg-light border"&amp;gt;Last modified date: @_currentFile.LastModified.ToString()&amp;lt;/div&amp;gt;
                &amp;lt;div class="p-2 bg-light border"&amp;gt;Content type (not always supplied by the browser): @_currentFile.ContentType&amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        }
        &amp;lt;button type="button" class="btn btn-primary mt-3" @onclick="UploadFileAsync"&amp;gt;Upload File &amp;lt;i class="bi bi-upload ms-2 d-inline-flex"&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

@code {
    public List&amp;lt;Files&amp;gt; FilesInS3 { get; set; } = new();
    public IBrowserFile? _currentFile = null;

    protected override async Task OnInitializedAsync()
    {
        FilesInS3 = await _httpClient.GetFromJsonAsync&amp;lt;List&amp;lt;Files&amp;gt;&amp;gt;("**https://localhost:7097**/api/S3/") ?? new();
    }

    public async Task UploadFileAsync()
    {
        if (_currentFile == null)
            return;

        var content = new MultipartFormDataContent();
        var fileContent = new StreamContent(_currentFile.OpenReadStream(1024000));

        content.Add(
            content: fileContent,
            name: "\"file\"",
            fileName: _currentFile.Name);

        var response = await _httpClient.PostAsync("**https://localhost:7097**/api/S3/upload", content);
        response.EnsureSuccessStatusCode();
        FilesInS3 = await _httpClient.GetFromJsonAsync&amp;lt;List&amp;lt;Files&amp;gt;&amp;gt;("https://localhost:7097/api/S3/") ?? new();
    }
}

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

&lt;/div&gt;



&lt;p&gt;If you have a look at the &lt;code&gt;@code&lt;/code&gt; part above, &lt;/p&gt;

&lt;p&gt;we are having a list of &lt;code&gt;Files&lt;/code&gt; class instances that will be assigned at the page initialization life cycle. The value of the list of files is retrieved from the S3 via the &lt;strong&gt;GET&lt;/strong&gt; API we created in the last chapter. If you see, I'm using the base address "&lt;strong&gt;&lt;a href="https://localhost:7097" rel="noopener noreferrer"&gt;https://localhost:7097&lt;/a&gt;&lt;/strong&gt;" in the code but it will be different on your pc. You have to change it accordingly.&lt;/p&gt;

&lt;p&gt;Next, we have the &lt;code&gt;UploadFileAsync()&lt;/code&gt; button that will upload the file user has chosen using the &lt;strong&gt;POST&lt;/strong&gt; API we created in the last chapter. We are passing the file as a Stream to that API body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1024000&lt;/strong&gt; is nothing but the maximum file size I can upload. For this demo, I'm setting up 1MB as the maximum file size I can upload. But it will vary based on the requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_currentFile&lt;/strong&gt; is the object that holds information about the current file chosen by the user in the UI. This is set from the HTML Code&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;InputFile **OnChange="(e) =&amp;gt; _currentFile = e.File"** class="btn-light mb-2 w-100" /&amp;gt;&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;That's it. To test the application, we need our API running first. &lt;br&gt;
After running both the APIs and the Blazor app, you should see the Blazor app like the one below,&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%2F1dariq9fzmhxr97jregi.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%2F1dariq9fzmhxr97jregi.png" alt="Running Blazor App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To upload a new file, the user will choose a file by pressing the "Choose File" button and then press the "UploadFile" button.&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%2Fcgghetmuom6d56x1od1c.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%2Fcgghetmuom6d56x1od1c.png" alt="Upload File"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please add your questions/suggestion in the comments.&lt;br&gt;
Thanks!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>webassembly</category>
      <category>aws</category>
      <category>blazor</category>
    </item>
    <item>
      <title>S3 File Operations using .NET 7 WebAPI</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Tue, 21 Mar 2023 02:11:40 +0000</pubDate>
      <link>https://dev.to/sprabha1990/s3-file-operations-using-net-7-webapi-53jj</link>
      <guid>https://dev.to/sprabha1990/s3-file-operations-using-net-7-webapi-53jj</guid>
      <description>&lt;p&gt;In this chapter, we will create an Asp Net Core Web API on top of .NET 7 that contain 3 APIs. One will upload the file into AWS, another for downloading the file from AWS and another to list the available files. &lt;/p&gt;

&lt;p&gt;Find the complete source code in my GitHub repository below.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/sprabha1990/S3FileOperations.Blazor.NET7" rel="noopener noreferrer"&gt;https://github.com/sprabha1990/S3FileOperations.Blazor.NET7&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's jump into creating a project via Dotnet CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Assumption: .NET 7 SDK and runtime installed.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running the below commands on the command prompt will create a visual studio solution file, an ASP Net Core Web API project, and an empty Blazor WASM project. &lt;/p&gt;

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

dotnet new sln --name S3FileOperations.NET7 --output S3FileOperations.Blazor.Net7
dotnet new webapi --name S3FileOperations.WebApi --output S3FileOperations.Blazor.Net7/Api
dotnet new blazorwasm-empty --name S3FileOperations.Blazor --output S3FileOperations.Blazor.Net7/WebApp


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

&lt;/div&gt;

&lt;p&gt;Now running the below commands will add the project files to the solution file. &lt;/p&gt;

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

dotnet sln S3FileOperations.Blazor.Net7\S3FileOperations.NET7.sln add S3FileOperations.Blazor.Net7/Api
dotnet sln S3FileOperations.Blazor.Net7\S3FileOperations.NET7.sln add S3FileOperations.Blazor.Net7/WebApp


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

&lt;/div&gt;

&lt;p&gt;Let's open the "&lt;strong&gt;S3FileOperations.NET7.sln&lt;/strong&gt;" file in the VS2022. You'll find two projects in it. In this chapter, We are going to work on web APIs.&lt;/p&gt;

&lt;p&gt;First up, let’s modify the appsettings.json of the Web API project. Make sure that your appsettings.json looks like the one below. Ensure that you are populating the Profile and Region fields with the values you configured earlier in the AWS CLI.&lt;/p&gt;

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

  "AWS": {
    "Profile": "default",
    "Region": "us-east-2"
  }


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

&lt;/div&gt;

&lt;p&gt;Run the following commands via Visual Studio to install the required AWS NuGet packages.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Install-Package AWSSDK.S3&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Install-Package AWSSDK.Extensions.NETCore.Setup&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With that done, let’s register the AWS Service into the .NET application’s Container. Open up the Program.cs and make the modifications as below. You might have to use the Amazon.S3 namespace while referencing the below changes.&lt;/p&gt;

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

builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions());
builder.Services.AddAWSService&amp;lt;IAmazonS3&amp;gt;();


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

&lt;/div&gt;

&lt;p&gt;Line 1 Loads the AWS Configuration from the appsettings.json into the application’s runtime.&lt;/p&gt;

&lt;p&gt;Line 2 Adds the S3 Service into the pipeline. We will be injecting this interface into our controllers to work with Bucket and Objects!&lt;/p&gt;

&lt;p&gt;Its time to create a new API controller under Controllers and name it S3Controller.cs. You would need to inject the IAmazonS3 interface into the constructor of the S3Controller.&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%2F9z7gwnecx1wmh7tfb8gd.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%2F9z7gwnecx1wmh7tfb8gd.png" alt="Controller Creation"&gt;&lt;/a&gt;&lt;/p&gt;

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

    [Route("api/[controller]")]
    [ApiController]
    public class S3Controller : ControllerBase
    {
        private readonly IAmazonS3 _s3Client;

        public S3Controller(IAmazonS3 s3Client)
        {
            _s3Client = s3Client;
        }
    }


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

&lt;/div&gt;

&lt;p&gt;Let's create an endpoint to upload a file into S3. &lt;/p&gt;

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

 [HttpPost("upload")]
    public async Task&amp;lt;IActionResult&amp;gt; UploadFileAsync(IFormFile file)
    {

    }


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

&lt;/div&gt;

&lt;p&gt;This API will get the file from the user and upload the file content into S3. &lt;/p&gt;

&lt;p&gt;Before uploading the file, we have to create the bucket in S3. Go to the S3 service in the AWS Management Console and Create a new bucket by pressing the "Create Bucket" button. &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%2Fubggg6u8pyblvlhcmdth.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%2Fubggg6u8pyblvlhcmdth.png" alt="Press Create Bucket"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide a name for the bucket and press create bucket at the bottom. &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%2F43d58bxou5flgoncgc1o.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%2F43d58bxou5flgoncgc1o.png" alt="Bucket Name"&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%2Fud3vxarlu4e0wphrrzij.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%2Fud3vxarlu4e0wphrrzij.png" alt="Press Create Bucket"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can see your bucket in the bucket list.&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%2Fkrky46ktfxd3l2hwee9i.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%2Fkrky46ktfxd3l2hwee9i.png" alt="bucket list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's go and implement the API "UploadFileAsync" in the S3Controller class.&lt;/p&gt;

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

 [HttpPost("upload")]
        public async Task&amp;lt;IActionResult&amp;gt; UploadFileAsync(IFormFile file)
        {
            var request = new PutObjectRequest()
            {
                BucketName = "blazor-file-transfer-demo",
                Key = file.FileName,
                InputStream = file.OpenReadStream()
            };

            request.Metadata.Add("Content-Type", file.ContentType);
            await _s3Client.PutObjectAsync(request);
            return Ok($"File {file.FileName} uploaded to S3 successfully!");
        }


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

&lt;/div&gt;

&lt;p&gt;The above API creates a putobjectrequest with bucket name, key, and input stream. The incoming file is converted to a stream via "OpenReadStream()". The bucket name is the one which we created just now and the key name is nothing but the file name.&lt;br&gt;
Calling the PutObjectAsync() API that is available in the S3 client SDK will upload the incoming file data into the S3 bucket we specified.&lt;/p&gt;

&lt;p&gt;Now, it's time to create an API for downloading the file. As same as above, create an API called "DownloadFileAsync" inside the S3Controller.&lt;/p&gt;

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

        [HttpPost("download")]
        public async Task&amp;lt;IActionResult&amp;gt; DownloadFileAsync(string key)
        {
            var s3Object = await _s3Client.GetObjectAsync("blazor-file-transfer-demo", key);
             return File(s3Object.ResponseStream, s3Object.Headers.ContentType, key);
        }


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

&lt;/div&gt;

&lt;p&gt;In the above implementation, we are getting the key name (filename) from the user. We will use the key name and the bucket name to fetch the object from the S3 by Calling the GetObjectAsync() API available on the S3 Client SDK. This will provide us with the S3 object as a stream. We will return the stream as a File object to the client application.&lt;/p&gt;

&lt;p&gt;At this point, we needed another API to fetch the list of files inside the bucket. &lt;/p&gt;

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

 [HttpGet]
        public async Task&amp;lt;IActionResult&amp;gt; GetAllFilesAsync()
        {
            var request = new ListObjectsV2Request()
            {
                BucketName = "blazor-file-transfer-demo",
            };
            var result = await _s3Client.ListObjectsV2Async(request);
            return Ok(result.S3Objects);
        }


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

&lt;/div&gt;

&lt;p&gt;That's it! We have created all the required APIs to list all the available files, upload a file and download a file from S3.&lt;/p&gt;

&lt;p&gt;We'll see how to upload/download files from the blazor application with the help of these APIs in the next chapter.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>dotnetcore</category>
      <category>webapi</category>
      <category>aws</category>
    </item>
    <item>
      <title>Setup AWS Credentials</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Tue, 21 Mar 2023 02:11:24 +0000</pubDate>
      <link>https://dev.to/sprabha1990/setup-aws-credentials-1igb</link>
      <guid>https://dev.to/sprabha1990/setup-aws-credentials-1igb</guid>
      <description>&lt;p&gt;First, we need to create an IAM user with programmatic access. &lt;br&gt;
Let's get into the AWS Management Console and search for IAM. Go to the IAM and click "Add User" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n97iGeP2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g7kkrjuyd4ljih7v762q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n97iGeP2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g7kkrjuyd4ljih7v762q.png" alt="Add User" width="880" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide a username and click next and next. If you want to add any tags, you can add them here and click create. Now the user will be created. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gQqcKrg2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4liumr56p9c1w1ynnv6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gQqcKrg2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4liumr56p9c1w1ynnv6.png" alt="User Created" width="880" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the user. You'll see the permission tab. For our application to access the S3 storage, we need to provide this user with S3 permission. To do that, Click on the Add permission button and &lt;br&gt;
choose the "Attach Policies Directly" option. Now choose "&lt;strong&gt;AmazonS3FullAccess&lt;/strong&gt;" and click Next. Now review the policy and press Add Permission. The permission should be added now to the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UE0DhL8f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao24gyybiokze8bfpo5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UE0DhL8f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao24gyybiokze8bfpo5t.png" alt="Add Permission Button" width="880" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aCEPPRQ8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/thsloslz3fu5qfukt95b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aCEPPRQ8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/thsloslz3fu5qfukt95b.png" alt="s3 Access Chosen" width="880" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e-CFrMnJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/onurufrxyluqq9c9lfl3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e-CFrMnJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/onurufrxyluqq9c9lfl3.png" alt="Permission List" width="880" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to configure the IAM user credentials on our pc. To do that, we need to create access key and secret for the user we created just now.&lt;/p&gt;

&lt;p&gt;Go to the "Security Credentials" inside the IAM user. Click the "&lt;strong&gt;Create access key&lt;/strong&gt;" button to create keys. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mw9bCLUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kph9j2t4b6hpkg6sofm6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mw9bCLUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kph9j2t4b6hpkg6sofm6.png" alt="Create Access Key Button" width="880" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now choose the "Application running outside AWS" option and click next.  Add any description tag value if you want and press "Create access key"&lt;/p&gt;

&lt;p&gt;You can see your access key in the console now. You can download the secrets in a CSV file by clicking "Download .csv file" at the bottom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RO3pED6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jyi7bhyypncumrtuy4u7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RO3pED6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jyi7bhyypncumrtuy4u7.png" alt="Download CSV File" width="880" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. We have created the IAM user, provided the S3 permission, and created the access keys to access S3 from our application. Let's go and configure the credentials on our pc.&lt;/p&gt;

&lt;p&gt;To configure the AWS Credentials, the latest AWS Cli must be installed on your machine. You can download the Cli on the official AWS website &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make sure the AWS Cli is installed on your machine, run the below command in your command prompt.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aws --version&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The response should be like this below except the version number might be different.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8Y8P4TmJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejejfez3e1wp7aald6ex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8Y8P4TmJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejejfez3e1wp7aald6ex.png" alt="AWS Version" width="436" height="24"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now run the below command,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aws configure&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The command prompt will ask for the access key, secret key, default regions, and output format. You can find the access key and secret key in the CSV file you downloaded just now. Provide the same. &lt;br&gt;
For the default region, I'm providing us-east-2 and "json" as the default output format. But you can provide whatever region you're gonna use for other AWS services. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vQWLwSZ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/093p4fia6bhydwo30p82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vQWLwSZ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/093p4fia6bhydwo30p82.png" alt="AWS Cred Setup" width="547" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>S3 File Upload &amp; Download using .NET7 Blazor WASM &amp; WebApi</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Tue, 21 Mar 2023 02:11:10 +0000</pubDate>
      <link>https://dev.to/sprabha1990/s3-file-upload-download-using-net7-blazor-wasm-webapi-3gbk</link>
      <guid>https://dev.to/sprabha1990/s3-file-upload-download-using-net7-blazor-wasm-webapi-3gbk</guid>
      <description>&lt;p&gt;In this series, We are going to see how to create a Blazor Web Assembly application that will connect with the AWS S3 services and do file uploads and downloads into S3 buckets. An ASP NET Core WebAPI application will be acting as an intermediate backend service that will handle the request from the Blazor application and do the appropriate actions in the AWS S3.&lt;/p&gt;

&lt;p&gt;We will use .NET 7 for this tutorial as it is the latest at this time. In the next chapter, you will find out how to set up AWS IAM users.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>webassembly</category>
      <category>aws</category>
      <category>blazor</category>
    </item>
    <item>
      <title>.NET7 Blazor Server app in AWS ECS</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Thu, 09 Mar 2023 02:18:37 +0000</pubDate>
      <link>https://dev.to/sprabha1990/creating-deploying-net7-blazorserver-app-in-aws-using-cf-templates-4h3g</link>
      <guid>https://dev.to/sprabha1990/creating-deploying-net7-blazorserver-app-in-aws-using-cf-templates-4h3g</guid>
      <description>&lt;h2&gt;
  
  
  What is Blazor?
&lt;/h2&gt;

&lt;p&gt;In simple terms, Blazor is a Single Page Application development framework provided and owned by the .NET foundation. Blazor uses the power of .NET and C# to build full-stack web apps without writing a line of JavaScript. Blazor can run anywhere such as client browsers on WASM, server-side on ASP.NET core, or in the native client apps such as mobile or desktop apps.&lt;/p&gt;

&lt;p&gt;In this post, we are going to create a sample Blazor Server SPA application, build it as a docker image and push it to AWS and deploy it there. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Blazor Server hosting model?
&lt;/h2&gt;

&lt;p&gt;Blazor server application means the web application is executed on the server side instead of in a browser. ASP.NET Core apps can be configured to use Blazor Server as the hosting model. A SignalR connection using WebSockets will be created for each tab in the client browser which will be responsible for updating UI content and handling events. Blazor allows the scaling apps to handle multiple client connections.&lt;/p&gt;

&lt;p&gt;The state on the server associated with each connected client is called a circuit. Circuits don't get tied to a specific network connection and can tolerate - temporarily - interrupted connections. In addition, clients can reconnect to the server when they want to even after going offline.&lt;/p&gt;

&lt;p&gt;To know more about Blazor hosting models, please go through the &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models?view=aspnetcore-7.0" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; from the Microsoft&lt;/p&gt;

&lt;p&gt;Let's start with work!&lt;br&gt;
What we gonna do here is,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a sample Blazor Server App in VS2022&lt;/li&gt;
&lt;li&gt;Adding a docker file&lt;/li&gt;
&lt;li&gt;Build the Blazor server app as an image and push it to AWS ECR.&lt;/li&gt;
&lt;li&gt;Create Networking Infrastructure in AWS using Cloudformation.&lt;/li&gt;
&lt;li&gt;Create ECS cluster, services, and task definitions to deploy the Blazor server app.&lt;/li&gt;
&lt;li&gt;Test the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creating Blazor Server sample app
&lt;/h3&gt;

&lt;p&gt;First, open visual studio 2022 and go to create project option. In the templates, choose the Blazor Server app.&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%2Fte74g0dibfe81iq7pxw2.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%2Fte74g0dibfe81iq7pxw2.png" alt="Project Templates"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, choose the directories to save the project and then press next. Now it's time to choose the .NET SDK version. I have .NET7 installed, so, I'm choosing the same and press next.&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%2F3ye23advln1on0edtwnb.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%2F3ye23advln1on0edtwnb.png" alt="Dotnet SDK Version"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. The sample application is created now. Press "CTRL+F5" to run the application locally. If you see the below page in your default browser, then everything is working as expected.&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%2Fmmhnr6ogs4k52pl84b2b.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%2Fmmhnr6ogs4k52pl84b2b.png" alt="Application locally running"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Dockerfile
&lt;/h3&gt;

&lt;p&gt;Now it's time to build and push the application as a docker image into AWS ECR. To do that, we need to do a couple of things. First, we need to add a Dockerfile in our project folder that helps docker to build an image step by step. &lt;/p&gt;

&lt;p&gt;I'm gonna create a folder called "buildScripts" and inside that, I create a file "Dockerfile" with the below contents on it.&lt;/p&gt;

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

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80

ENV ASPNETCORE_URLS=http://*:8000

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["BlazorSampleApp.csproj", "."]
RUN dotnet restore "./BlazorSampleApp.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "BlazorSampleApp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "BlazorSampleApp.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "BlazorSampleApp.dll"]


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

&lt;/div&gt;

&lt;p&gt;If you have noted, I'm exposing the application inside the container in port 8000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the Docker image and push it to AWS ECR
&lt;/h2&gt;

&lt;p&gt;Now, I'm gonna build and push the application as a docker container image into AWS ECR. Before that, we need to configure the AWS account on our machine. &lt;/p&gt;

&lt;p&gt;To do that, we need the latest AWS CLI installed on our PC. I do have AWS CLI V2 installed already. If you don't, get the latest version &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the installation of AWS, press &lt;code&gt;aws --version&lt;/code&gt; in the command prompt. It will tell you the correct version number if that is installed perfectly.&lt;/p&gt;

&lt;p&gt;To configure, Open the command prompt and type &lt;code&gt;aws configure&lt;/code&gt; and press enter. Now it will ask for Access ID, Key, default region, and output. Give the appropriate inputs and press enter so that it will be configured.&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%2Fwrwjqmagn4d2o1yc8rhm.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%2Fwrwjqmagn4d2o1yc8rhm.png" alt="AWS Cred"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we will create an ECR repository in the AWS by running the below command in the command prompt.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aws ecr create-repository --repository-name blazorserverapp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The successful response of this command will give you the full repository Uri. We will use the Uri to tag our docker image during the application build stage.&lt;/p&gt;

&lt;p&gt;Running the below commands from the Blazor application project root directory will login your aws cli into ECR, build &amp;amp; tag the docker image and push it.&lt;/p&gt;

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

aws ecr get-login-password --region {your-default-region} | docker login --username AWS --password-stdin {your-aws-account-id}.dkr.ecr.{your-default-region}.amazonaws.com

docker build -t {your-aws-account-id}.dkr.ecr.{your-default-region}.amazonaws.com/blazorserverapp:latest -f buildScripts\Dockerfile .

docker push {your-aws-account-id}.dkr.ecr.{your-default-region}.amazonaws.com/blazorserverapp:latest



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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Create Networking Infrastructure in AWS
&lt;/h2&gt;

&lt;p&gt;Our next step is to deploy the container image we pushed just now into Amazon ECS. To do that, We are gonna set up our own networks first such as VPC, Subnets, etc. We are going to create a VPC, 2 public subnets, 2 private subnets, a public load balancer, a private load balancer, and its associated resources.&lt;/p&gt;

&lt;p&gt;We will deploy our Blazor application container image inside the public subnets which are inside the VPC. We'll use the AWS Cloudformation service to create our resources.&lt;/p&gt;

&lt;p&gt;Add the below cloudformation template as "ecs-infra.yaml" inside the buildscripts folder&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

AWSTemplateFormatVersion: 2010-09-09
Description: This stack creates a VPC with 3 public subnets and private subnets.
  Then, it will create a public load balancer and private load balancer. Public LB is publicly accessable where as
  private LB will be accessible within the service that deployed inside private subnets.
  The docker containers will be deployed in Fargate ECS clusters. It will be deployed in either public subnets and associate
  with public LB listeres or in private subnets and associate with private LB

Parameters:
  VPCCIDR:
    Type: String
    Description: CIDR value for the VPC.
    Default: "10.0.0.0/16"
  PublicSubnet1CIDR:
    Type: String
    Description: CIDR value for the public subnet 1.
    Default: "10.0.0.0/24"
  PublicSubnet2CIDR:
    Type: String
    Description: CIDR value for the public subnet 2.
    Default: "10.0.1.0/24"
  PrivateSubnet1CIDR:
    Type: String
    Description: CIDR value for the private subnet 1.
    Default: "10.0.3.0/24"
  PrivateSubnet2CIDR:
    Type: String
    Description: CIDR value for the private subnet 2.
    Default: "10.0.4.0/24"
  PublicLoadBalancerName:
    Type: String
    Description: Name of the public application load balancer the blazorserverapp application will use for
  PrivateLoadBalancerName:
    Type: String
    Description: Name of the private application load balancer the blazorserverapp containers inside ecs taske will use for its communication with other containers
  ECSClusterName:
    Type: String
    Description: Name of the ecs cluster that borker 2 application uses to deploy its microservices

Resources:
  # VPC in which containers will be networked.
  # It has two public subnets, and two private subnets.
  # We distribute the subnets across the first two available subnets
  # for the region, for high availability.
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      CidrBlock: !Ref VPCCIDR
      Tags:
        - Key: Name
          Value: blazorserverapp-vpc

  # Two public subnets, where containers can have public IP addresses
  PublicSubnetOne:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, Fn::GetAZs: !Ref "AWS::Region"]
      VpcId: !Ref "VPC"
      CidrBlock: !Ref PublicSubnet1CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: blazorserverapp-pub-sub1

  PublicSubnetTwo:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, Fn::GetAZs: !Ref "AWS::Region"]
      VpcId: !Ref "VPC"
      CidrBlock: !Ref PublicSubnet2CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: blazorserverapp-pub-sub2

  # Theree private subnets where containers will only have private
  # IP addresses, and will only be reachable by other members of the
  # VPC
  PrivateSubnetOne:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, Fn::GetAZs: !Ref "AWS::Region"]
      VpcId: !Ref "VPC"
      CidrBlock: !Ref PrivateSubnet1CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: blazorserverapp-pri-sub1

  PrivateSubnetTwo:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, Fn::GetAZs: !Ref "AWS::Region"]
      VpcId: !Ref "VPC"
      CidrBlock: !Ref PrivateSubnet2CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: blazorserverapp-pri-sub2

  # Setup networking resources for the public subnets.
  # Containers in the public subnets have public IP addresses and the routing table
  # sends network traffic via the internet gateway.
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: blazorserverapp-vpc-igw

  GatewayAttachement:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref "VPC"
      InternetGatewayId: !Ref "InternetGateway"

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref "VPC"
      Tags:
        - Key: Name
          Value: blazorserverapp-pub-route-tbl

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: GatewayAttachement
    Properties:
      RouteTableId: !Ref "PublicRouteTable"
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref "InternetGateway"

  PublicSubnetOneRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetOne
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetTwoRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetTwo
      RouteTableId: !Ref PublicRouteTable

  # Setup networking resources for the private subnets.
  # Containers in these subnets have only private IP addresses, and must use a NAT
  # gateway to talk to the internet. We launch two NAT gateways, one for
  # each private subnet.
  NatGatewayOneAttachment:
    Type: AWS::EC2::EIP
    DependsOn: GatewayAttachement
    Properties:
      Domain: vpc
  NatGatewayTwoAttachment:
    Type: AWS::EC2::EIP
    DependsOn: GatewayAttachement
    Properties:
      Domain: vpc
  NatGatewayOne:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayOneAttachment.AllocationId
      SubnetId: !Ref PublicSubnetOne
      Tags:
        - Key: Name
          Value: blazorserverapp-nat1
  NatGatewayTwo:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayTwoAttachment.AllocationId
      SubnetId: !Ref PublicSubnetTwo
      Tags:
        - Key: Name
          Value: blazorserverapp-nat2

  PrivateRouteTableOne:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref "VPC"
  PrivateRouteOne:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableOne
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayOne
  PrivateRouteTableOneAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableOne
      SubnetId: !Ref PrivateSubnetOne
  PrivateRouteTableTwo:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref "VPC"
  PrivateRouteTwo:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableTwo
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayTwo
  PrivateRouteTableTwoAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableTwo
      SubnetId: !Ref PrivateSubnetTwo

  PublicLoadBalancerSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: public internet access to the load balancer
      VpcId: !Ref "VPC"
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          IpProtocol: "tcp"
          Description: "Allow HTTP Request from anywhere in the internet"
          FromPort: 80
          ToPort: 80
        - CidrIp: 0.0.0.0/0
          Description: "Allow HTTPS Request from anywhere in the internet"
          IpProtocol: "tcp"
          FromPort: 443
          ToPort: 443
      Tags:
        - Key: Name
          Value: blazorserverapp-pub-lb-access-sg

  PublicLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Ref PublicLoadBalancerName
      Scheme: internet-facing
      LoadBalancerAttributes:
        - Key: idle_timeout.timeout_seconds
          Value: "30"
      Subnets:
        # The load balancer is placed into the public subnets, so that traffic
        # from the internet can reach the load balancer directly via the internet gateway
        - !Ref PublicSubnetOne
        - !Ref PublicSubnetTwo
      SecurityGroups: [!Ref "PublicLoadBalancerSG"]

  # A dummy target group is used to setup the ALB to just drop traffic
  # initially, before any real service target groups have been added.
  DummyTargetGroupPublic:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 60
      HealthCheckPath: /
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      Name: blazorserverapp-default-public
      Port: 80
      Protocol: HTTP
      UnhealthyThresholdCount: 10
      VpcId: !Ref "VPC"

  PublicLoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    DependsOn:
      - PublicLoadBalancer
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref "DummyTargetGroupPublic"
          Type: "forward"
      LoadBalancerArn: !Ref "PublicLoadBalancer"
      Port: 80
      Protocol: HTTP

  # An internal load balancer, this would be used for a service that is not
  # directly accessible to the public, but instead should only receive traffic
  # from your other services.
  PrivateLoadBalancerSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Restricted access to load balancer
      VpcId: !Ref "VPC"
      Tags:
        - Key: Name
          Value: blazorserverapp-pri-lb-access-sg

  PrivateLoadBalancerIngressFromECS:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      Description: Only accept traffic from a container in the fargate container security group
      GroupId: !Ref "PrivateLoadBalancerSG"
      IpProtocol: "-1"
      SourceSecurityGroupId: !Ref "FargateContainerSecurityGroup"

  PrivateLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Ref PrivateLoadBalancerName
      Scheme: internal
      LoadBalancerAttributes:
        - Key: idle_timeout.timeout_seconds
          Value: "30"
      Subnets:
        # This load balancer is put into the private subnet, so that there is no
        # route for the public to even be able to access the private load balancer.
        - !Ref PrivateSubnetOne
        - !Ref PrivateSubnetTwo
      SecurityGroups: [!Ref "PrivateLoadBalancerSG"]

  # This dummy target group is used to setup the ALB to just drop traffic
  # initially, before any real service target groups have been added.
  DummyTargetGroupPrivate:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 60
      HealthCheckPath: /
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      Name: blazorserverapp-default-private
      Port: 80
      Protocol: HTTP
      UnhealthyThresholdCount: 10
      VpcId: !Ref "VPC"

  PrivateLoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    DependsOn:
      - PrivateLoadBalancer
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref "DummyTargetGroupPrivate"
          Type: "forward"
      LoadBalancerArn: !Ref "PrivateLoadBalancer"
      Port: 80
      Protocol: HTTP

  # A security group for the containers we will run in Fargate.
  # Two rules, allowing network traffic from a public facing load
  # balancer, a private internal load balancer, and from other members
  # of the security group.
  #
  # Remove any of the following ingress rules that are not needed.
  FargateContainerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Access to the Fargate containers
      VpcId: !Ref "VPC"
      Tags:
        - Key: Name
          Value: blazorserverapp-ecs-container-sg

  EcsSecurityGroupIngressFromPublicALB:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      Description: Ingress from the public ALB
      GroupId: !Ref "FargateContainerSecurityGroup"
      IpProtocol: "-1"
      SourceSecurityGroupId: !Ref "PublicLoadBalancerSG"

  EcsSecurityGroupIngressFromPrivateALB:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      Description: Ingress from the private ALB
      GroupId: !Ref "FargateContainerSecurityGroup"
      IpProtocol: "-1"
      SourceSecurityGroupId: !Ref "PrivateLoadBalancerSG"

  EcsSecurityGroupIngressFromSelf:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      Description: Ingress from other containers in the same security group
      GroupId: !Ref "FargateContainerSecurityGroup"
      IpProtocol: "-1"
      SourceSecurityGroupId: !Ref "FargateContainerSecurityGroup"

  ECSCluster:
    DependsOn:
      - PublicLoadBalancer
      - PrivateLoadBalancer
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Ref ECSClusterName
      ClusterSettings:
        - Name: containerInsights
          Value: enabled

Outputs:
  InternalUrl:
    Description: The url of the internal load balancer
    Value: !Join ["", ["http://", !GetAtt "PrivateLoadBalancer.DNSName"]]
    Export:
      Name: InternalUrl
  ExternalUrl:
    Description: The url of the external load balancer
    Value: !Join ["", ["http://", !GetAtt "PublicLoadBalancer.DNSName"]]
    Export:
      Name: ExternalUrl
  VPCId:
    Description: The ID of the VPC that this stack is deployed in
    Value: !Ref "VPC"
    Export:
      Name: VPC
  PublicSubnetOne:
    Description: Public subnet one
    Value: !Ref "PublicSubnetOne"
    Export:
      Name: PublicSubnetOne
  PublicSubnetTwo:
    Description: Public subnet two
    Value: !Ref "PublicSubnetTwo"
    Export:
      Name: PublicSubnetTwo
  PrivateSubnetOne:
    Description: Private subnet one
    Value: !Ref "PrivateSubnetOne"
    Export:
      Name: PrivateSubnetOne
  PrivateSubnetTwo:
    Description: Private subnet two
    Value: !Ref "PrivateSubnetTwo"
    Export:
      Name: PrivateSubnetTwo
  FargateContainerSecurityGroup:
    Description: The security group to be used by blazorserverapp containers
    Value: !Ref FargateContainerSecurityGroup
    Export:
      Name: FargateContainerSecurityGroup
  ClusterName:
    Description: The name of the ECS cluster created for blazorserverapp applications
    Value: !Ref "ECSCluster"
    Export:
      Name: ClusterName
  PublicLoadBalancerListener:
    Description: Listener for the load balancer exposed to the public internet
    Value: !Ref PublicLoadBalancerListener
    Export:
      Name: PublicListener
  PrivateLoadBalancerListener:
    Description: Listener for the load balancer exposed internally inside the VPC
    Value: !Ref PrivateLoadBalancerListener
    Export:
      Name: PrivateListener



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

&lt;/div&gt;

&lt;p&gt;In a same way, Add the below configuration template as "ecs-infra.json" inside the buildscripts folder&lt;/p&gt;

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

[
  {
    "ParameterKey": "VPCCIDR",
    "ParameterValue": "11.200.13.0/24"
  },
  {
    "ParameterKey": "PublicSubnet1CIDR",
    "ParameterValue": "11.200.13.0/26"
  },
  {
    "ParameterKey": "PublicSubnet2CIDR",
    "ParameterValue": "11.200.13.64/26"
  },
  {
    "ParameterKey": "PrivateSubnet1CIDR",
    "ParameterValue": "11.200.13.128/26"
  },
  {
    "ParameterKey": "PrivateSubnet2CIDR",
    "ParameterValue": "11.200.13.192/26"
  },
  {
    "ParameterKey": "PublicLoadBalancerName",
    "ParameterValue": "blazorserverapp"
  },
  {
    "ParameterKey": "PrivateLoadBalancerName",
    "ParameterValue": "blazorserverapp-internal"
  },
  {
    "ParameterKey": "ECSClusterName",
    "ParameterValue": "blazorserverapp"
  }
]



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

&lt;/div&gt;

&lt;p&gt;Run the below command from the blazor application project root directory&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 cloudformation create-stack --stack-name blazorserverapp-networking-infra --capabilities=CAPABILITY_NAMED_IAM --template-body file://./buildScripts/ecs-infra.yaml --parameters file://./buildScripts/ecs-infra.json

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

&lt;/div&gt;

&lt;p&gt;The above command will validate and create the needful networking things in the aws. You can see the status of the command in the AWS CloudFormation console. The status will be "Create Completed". If there is any failures, Make sure your IAM user have necessary access to create these resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy Blazor app into ECS
&lt;/h2&gt;

&lt;p&gt;Save the below Cloudformation template as "ecs-service.yaml" inside the buildScripts directory&lt;/p&gt;

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


AWSTemplateFormatVersion: 2010-09-09
Description: This stack will deploy the blazorserverapp on AWS Fargate,
  hosted in a private/public subnet, behind a private/public load balancer.

Parameters:
  ServiceName:
    Type: String
    Description: A name for the service
  ImageUrl:
    Type: String
    Description:
      The url of a docker image that contains the application process that
      will handle the traffic for this service
  ContainerPort:
    Type: Number
    Description: What port number the application inside the docker container is binding to
  DesiredCount:
    Type: Number
    Default: 1
    Description: How many copies of the service task to run
  ListenerRulePriority:
    Type: Number
    Default: 1
    Description: The priority of the listener rule defined in the load balancer
  LogGroupName:
    Type: String
    Description: Name of the log group where the logs from the service should be saved
  ASPNETCOREENV:
    Type: String
    Description: Value of the ASPNETCORE_ENVIRONMENT to be passed to the container
  HealthCheckURL:
    Type: String
    Description: Path of the health check address to be done by the load balancer

Resources:
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: [ecs-tasks.amazonaws.com]
            Action: ["sts:AssumeRole"]
      Path: /
      RoleName: BlazorServerAppECSTaskExecRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

  ECSTaskALBPermissionPolicy:
    Properties:
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - "elasticloadbalancing:DeregisterInstancesFromLoadBalancer"
              - "elasticloadbalancing:DeregisterTargets"
              - "elasticloadbalancing:Describe*"
              - "elasticloadbalancing:RegisterInstancesWithLoadBalancer"
              - "elasticloadbalancing:RegisterTargets"
            Resource:
              - !Join
                - ":"
                - - arn:aws:elasticloadbalancing
                  - !Ref "AWS::Region"
                  - !Ref "AWS::AccountId"
                  - !Sub "loadbalancer/app/blazorserverapp*"
      PolicyName: ECSTaskRole-ALB-PermissionPolicy
      Roles:
        - !Ref ECSTaskExecutionRole
    Type: AWS::IAM::Policy

  CloudWatchLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Ref "LogGroupName"
      RetentionInDays: 7
  # The task definition. This is a simple metadata description of what
  # container to run, and what resource requirements it has.
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Ref "ServiceName"
      Cpu: 256
      Memory: 512
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
      ContainerDefinitions:
        - Name: !Ref "ServiceName"
          Cpu: 256
          Memory: 512
          Image: !Ref "ImageUrl"
          PortMappings:
            - ContainerPort: !Ref "ContainerPort"
          Environment:
            - Name: "ASPNETCORE_ENVIRONMENT"
              Value: !Ref ASPNETCOREENV
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref "CloudWatchLogGroup"
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: !Ref "ASPNETCOREENV"

  # The service. The service is a resource which allows you to run multiple
  # copies of a type of task, and gather up their logs and metrics, as well
  # as monitor the number of running tasks and replace any that have crashed
  Service:
    Type: AWS::ECS::Service
    DependsOn: LoadBalancerRule
    Properties:
      ServiceName: !Ref "ServiceName"
      Cluster: !ImportValue ClusterName
      LaunchType: FARGATE
      DeploymentConfiguration:
        DeploymentCircuitBreaker:
          Enable: true
          Rollback: true
        MaximumPercent: 200
        MinimumHealthyPercent: 75
      DesiredCount: !Ref "DesiredCount"
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - !ImportValue FargateContainerSecurityGroup
          Subnets:
            - !ImportValue PublicSubnetOne
            - !ImportValue PublicSubnetTwo
      TaskDefinition: !Ref "TaskDefinition"
      LoadBalancers:
        - ContainerName: !Ref "ServiceName"
          ContainerPort: !Ref "ContainerPort"
          TargetGroupArn: !Ref "TargetGroup"

  # A target group. This is used for keeping track of all the tasks, and
  # what IP addresses / port numbers they have. You can query it yourself,
  # to use the addresses yourself, but most often this target group is just
  # connected to an application load balancer, or network load balancer, so
  # it can automatically distribute traffic across all the targets.
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 6
      HealthCheckPath: !Ref "HealthCheckURL"
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      TargetType: ip
      Name: !Ref "ServiceName"
      Port: !Ref "ContainerPort"
      Protocol: HTTP
      UnhealthyThresholdCount: 2
      VpcId: !ImportValue VPC

  # Create a rule on the load balancer for routing traffic to the target group
  LoadBalancerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - TargetGroupArn: !Ref "TargetGroup"
          Type: "forward"
      Conditions:
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - /*
      ListenerArn: !ImportValue PublicListener
      Priority: !Ref ListenerRulePriority



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

&lt;/div&gt;

&lt;p&gt;In the same way, add the below configuration template as "ecs-service.json" file inside the buildScripts directory.&lt;/p&gt;

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


[
  {
    "ParameterKey": "ServiceName",
    "ParameterValue": "blazorserverapp"
  },
  {
    "ParameterKey": "ImageUrl",
    "ParameterValue": "{your-aws-account-id}.dkr.ecr.{your-default-region}.amazonaws.com/blazorserverapp:latest"
  },
  {
    "ParameterKey": "ContainerPort",
    "ParameterValue": "8000"
  },
  {
    "ParameterKey": "DesiredCount",
    "ParameterValue": "1"
  },
  {
    "ParameterKey": "ListenerRulePriority",
    "ParameterValue": "1"
  },
  {
    "ParameterKey": "LogGroupName",
    "ParameterValue": "blazorserverapp-service"
  },
  {
    "ParameterKey": "ASPNETCOREENV",
    "ParameterValue": "Development"
  },
  {
    "ParameterKey": "HealthCheckURL",
    "ParameterValue": "/"
  }
]



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

&lt;/div&gt;

&lt;p&gt;Run the below command from the blazor application project root directory. This will create ECS clusters, Service, task definitions and deploy the container image we pushed just before into the task running inside service.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;br&gt;
 cloudformation create-stack --stack-name blazorserverapp-service-infra --capabilities=CAPABILITY_NAMED_IAM --template-body file://./buildScripts/ecs-service.yaml --parameters file://./buildScripts/ecs-service.json

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Test the application.&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;It's time to test our application. To find the URL of our application, You need to go to AWS Cloudformation in the console. Search for "blazorserverapp-networking-infra" stack. You'll find the external Url once you go to the output tab of "blazorserverapp-networking-infra" stack. Your application should open up once you click that link.&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%2Fxphka2h0g1rflmazna9m.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%2Fxphka2h0g1rflmazna9m.png" alt="CF Console"&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%2Fcnn2ky5dwfk0sdm2isbc.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%2Fcnn2ky5dwfk0sdm2isbc.png" alt="Application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. We have successfully created and deployed a Blazor Server .NET7 app in the AWS. We can see how to set up CICD for our application in a different post.&lt;/p&gt;

&lt;p&gt;Full source is available here &lt;br&gt;
&lt;a href="https://github.com/sprabha1990/BlazorServer.NET7" rel="noopener noreferrer"&gt;https://github.com/sprabha1990/BlazorServer.NET7&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aws</category>
      <category>blazor</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Self Signed RSA Certificate with .NET 7</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Tue, 21 Feb 2023 22:01:03 +0000</pubDate>
      <link>https://dev.to/sprabha1990/self-signed-rsa-certificate-with-net-7-57ga</link>
      <guid>https://dev.to/sprabha1990/self-signed-rsa-certificate-with-net-7-57ga</guid>
      <description>&lt;p&gt;Asymmetric encryption uses a public/private key pair to encrypt the data at one end and decrypt the data on the other end. Most of the times, the encryption keys are created in the form of certificates, which might have been created in advance. &lt;/p&gt;

&lt;p&gt;But for some scenarios such as IOT, certificates should be created for each device beforehand, which should be registered on the public cloud provider before using the IoT features. It will be tricky to create certificates beforehand if we have millions of devices to work with IoT and goes for production. &lt;/p&gt;

&lt;p&gt;In this case, we can create our own self-signed device certificates at runtime using RSA in .NET Core&lt;/p&gt;

&lt;p&gt;I'm gonna create a console application that will create a random RSA certificate and export the certificate and its private and public keys locally. &lt;/p&gt;

&lt;p&gt;To do that, I'm gonna create a Console App project in VS2022.&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%2Fc47ywpv8wgybyfb3jnf7.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%2Fc47ywpv8wgybyfb3jnf7.png" alt="Choosing Console App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choosing &lt;strong&gt;.NET 7&lt;/strong&gt; as a framework&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%2Ficqcyzxoicwfbhbzfawa.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%2Ficqcyzxoicwfbhbzfawa.png" alt="Choosing .NET7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the project has been created.&lt;br&gt;
Let's create a public record model that will store the certificate instance as well as its keys.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 record CertificateModel(X509Certificate2 Certificate, string PublicKeyPEM, string PrivateKeyPEM, string PublicKeyPFX = "");

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

&lt;/div&gt;

&lt;p&gt;Next, I'm gonna create a helper class to create RSA certificates and store its keys in a &lt;strong&gt;CertificateModel&lt;/strong&gt; instance&lt;/p&gt;

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


internal class RSAHelper
{
    /// &amp;lt;summary&amp;gt;
    ///     Create a random RSA certificate for asymentric encryption
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name="subjectName"&amp;gt;Subject name for the certificate. Ex, "cn=test"&amp;lt;/param&amp;gt;
    /// &amp;lt;param name="hashAlgorithmName"&amp;gt;Algorithm mode for creating hash keys&amp;lt;/param&amp;gt;
    /// &amp;lt;param name="signaturePadding"&amp;gt;Padding mode for the certificate signature&amp;lt;/param&amp;gt;
    /// &amp;lt;returns&amp;gt;Certificate model&amp;lt;/returns&amp;gt;
    public CertificateModel CreateRSACertificate(string subjectName
        , HashAlgorithmName hashAlgorithmName
        , RSASignaturePadding signaturePadding)
    {
        using var rsa = RSA.Create();
        var certRequest = new CertificateRequest(subjectName, rsa, hashAlgorithmName, signaturePadding);
        var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1));

        var privateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks);
        var publicKey = certificate.ExportCertificatePem();
        var publicKeyPfx = Convert.ToBase64String(certificate.Export(X509ContentType.Pfx), Base64FormattingOptions.InsertLineBreaks);

        return new CertificateModel(certificate, publicKey, privateKey, publicKeyPfx);
    }
}



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

&lt;/div&gt;

&lt;p&gt;That's it. The method "&lt;strong&gt;CreateRSACertificate&lt;/strong&gt;" will create random RSA Keys and create a self-signed X509Certificate2 instance using the RSA keys which we created. It exports the private key and public key in a PEM format and stores it in the CertificateModel instance which will be returned to the caller method.&lt;/p&gt;

&lt;p&gt;Now, we have to call &lt;code&gt;RSAHelper.CreateRSACertificate()&lt;/code&gt; instance from the &lt;code&gt;Program.cs&lt;/code&gt; class.&lt;/p&gt;

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

RSAHelper rsaHelperInstance = new RSAHelper();

Console.Write("Enter the subject name of the certificate : ");
var subjectName = $"CN={Console.ReadLine()?.ToUpper()}";

CertificateModel certificate = rsaHelperInstance.CreateRSACertificate(subjectName: subjectName
    , hashAlgorithmName: HashAlgorithmName.SHA256
    , signaturePadding: RSASignaturePadding.Pkcs1);

Console.WriteLine("Certificate is ready !!");
Console.Write("Do you want to save it locally? Press y or n : ");

char choice = Console.ReadKey().KeyChar;

if (choice == 'y')
{
    if (!Directory.Exists("Certificates"))
        Directory.CreateDirectory("Certificates");

    File.WriteAllText("Certificates/certificate.pem", certificate.PublicKeyPEM);
    File.WriteAllText("Certificates/certificate.private.pem", certificate.PrivateKeyPEM);
    File.WriteAllText("Certificates/certificate.pfx", certificate.PublicKeyPFX);

    Console.WriteLine();
    string filesLocation = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location)?.FullName ?? "", "Certificates");
    Console.WriteLine($"Files are saved in {filesLocation}");

    Process.Start(fileName: "explorer.exe", arguments: filesLocation);
}




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

&lt;/div&gt;

&lt;p&gt;Here, I'm getting the subject name for the certificate from the user and passing it to the &lt;code&gt;CreateRSACertificate()&lt;/code&gt; method which returns the CertificateModel Instance. Based on the user's interest, I'm saving the certificates and keys in a local file. &lt;/p&gt;

&lt;p&gt;We can also save the certificates in X509Stores for appropriate usage.&lt;/p&gt;

&lt;p&gt;_&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Full source code is available here "&lt;a href="https://github.com/sprabha1990/RSACertificateGenerator.NET7" rel="noopener noreferrer"&gt;RSACertificateGenerator.NET7&lt;/a&gt;"&lt;br&gt;
_&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>dotnet7</category>
      <category>rsa</category>
      <category>cryptography</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Data streaming in .NET 6 using Kafka</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Fri, 05 Nov 2021 02:11:14 +0000</pubDate>
      <link>https://dev.to/sprabha1990/data-streaming-in-net-6-using-kafka-3dd</link>
      <guid>https://dev.to/sprabha1990/data-streaming-in-net-6-using-kafka-3dd</guid>
      <description>&lt;p&gt;Data streaming is nothing but a flow of data from one end to another. Normally, there will be a producer who sends some event information to consumers via message queue/brokers. Here, we are going to create a data streaming framework using .NET core 6 which acts as producer and consumers while Kafka will act as message broker.&lt;/p&gt;

&lt;p&gt;Before starting to create the producer and consumers, we need to create a Kafka broker. I'm using docker and docker-compose to create a Kafka in my localhost.&lt;/p&gt;

&lt;p&gt;Here is the docker-compose.yml file.&lt;br&gt;
&lt;/p&gt;

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

services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"
    networks:
      - kafka_net
  kafka:
    image: wurstmeister/kafka
    depends_on:
      - zookeeper
    restart: on-failure
    container_name: kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: localhost
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    networks:
      - kafka_net
networks:
  kafka_net:
    driver: "bridge"

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

&lt;/div&gt;



&lt;p&gt;Running this script with 'docker-compose -d up' will create the Kafka and ZooKeeper service in my local host.&lt;/p&gt;

&lt;p&gt;This post is not about the docker, so I'm moving to the actual part. &lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;strong&gt;Kafka Producer&lt;/strong&gt;
&lt;/h5&gt;

&lt;p&gt;Kafka producer is a .NET core 6 console application with C#10.&lt;br&gt;
We need the Nuget package &lt;strong&gt;Confluent.Kafka&lt;/strong&gt; to use Kafka in .NET client application. Hence, we are installing this via Nuget package manager.&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%2Fb1j6x7d4dvt1zn9kcaf6.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%2Fb1j6x7d4dvt1zn9kcaf6.png" alt="Nuget Package Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As one of the improvements in C#10, we can use 'global using' throughout the application. We are now creating the global.cs file with default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global using System.Text;
global using static System.Console;
global using System.Text.Json;
global using Confluent.Kafka;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are going to create a Message class. This is the data class which will be serialized and published to the Kafka in this example. This is a simple class containing 3 public properties namely Id, Data and Timestamp.&lt;br&gt;
&lt;/p&gt;

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

public class Message
{
    public string Id { get; set; } = Guid.NewGuid().ToString();
    public string? Data { get; set; }
    public DateTime Timestamp { get; set; } = DateTime.UtcNow;

    public override string ToString()
    {
        return JsonSerializer.Serialize(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ID is an auto generated GUID and the timestamp will be the current time when the Message object is created.&lt;/p&gt;

&lt;p&gt;Now, we are going to create a Custom Serializer class. The reason for this class is that Kafka implicitly supports some predefined datatypes to serialize and deserialize in it which are, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Null &lt;/li&gt;
&lt;li&gt;Ignore &lt;/li&gt;
&lt;li&gt;Int32 &lt;/li&gt;
&lt;li&gt;Int64 &lt;/li&gt;
&lt;li&gt;Utf8 &lt;/li&gt;
&lt;li&gt;Single &lt;/li&gt;
&lt;li&gt;Double &lt;/li&gt;
&lt;li&gt;ByteArray &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, if we need to publish/consume different data from these, we need to tell Kafka which serializer/deserializer object it needs to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace KafkaProducer
{
    public class CustomValueSerializer&amp;lt;T&amp;gt; : **ISerializer&amp;lt;T&amp;gt;**
    {
        public byte[] Serialize(T data, SerializationContext context)
        {
            return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(data, typeof(T)));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the CustomValueSerializer is implementing the ISerialize interface from Kafka which has a single method implemented called "Serialize". This will be used by Kafka while publishing this message.&lt;/p&gt;

&lt;p&gt;Now, we are going to create a producer class which publishes the message to Kafka asynchronously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace KafkaProducer;
public class Producer&amp;lt;T&amp;gt;
{

    readonly string? _host;
    readonly int _port;
    readonly string? _topic;

    public Producer()
    {
        _host = "localhost";
        _port = 9092;
        _topic = "producer_logs";
    }

    ProducerConfig GetProducerConfig()
    {
        return new ProducerConfig
        {
            BootstrapServers = $"{_host}:{_port}"
        };
    }

    public async Task ProduceAsync(T data)
    {
        using (var producer = new ProducerBuilder&amp;lt;Null, T&amp;gt;(GetProducerConfig())
                                             .SetValueSerializer(new CustomValueSerializer&amp;lt;T&amp;gt;())
                                             .Build())
        {
            await producer.ProduceAsync(_topic, new Message&amp;lt;Null, T&amp;gt; { Value = data });
            WriteLine($"{data} published");
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class has a public method "ProduceAsync" which gets the message information and publishes it to Kafka. If you notice, while creating the ProducerBuilder object, we are letting Kafka know that the value serializer for this producer is "CustomValueSerializer" by using an api &lt;strong&gt;.SetValueSerializer(new CustomValueSerializer())&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, from the program.cs, we are creating an instance of producer class and invoke the produceasync method to publish our data to Kafka.&lt;br&gt;
&lt;/p&gt;

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

public class Program
{
    public static async Task Main(string[] args)
    {
        var producer = new Producer&amp;lt;Message&amp;gt;();

        for(int i=0; i&amp;lt; 25 ; i++)
        {
            await producer.ProduceAsync(new Message
            {
                Data = $"Pushing Data {i} !!",
            });

            await Task.Delay(1000);
        }

        WriteLine("Publish Success!");
        ReadKey();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this, the application will publish 25 times to Kafka.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;strong&gt;Kafka Consumer&lt;/strong&gt;
&lt;/h5&gt;

&lt;p&gt;Kafka consumer is again a .NET core 6 console application with C#10. We need the same Nuget package &lt;strong&gt;Confluent.Kafka&lt;/strong&gt; here too.&lt;br&gt;
After installing that, we will create a global.cs file to add global usings like what we did for producer.&lt;/p&gt;

&lt;p&gt;Once global file is added, we will create a Message class.&lt;br&gt;
&lt;/p&gt;

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

public class Message
{
    public string? Id { get; set; }
    public string? Data { get; set; }
    public DateTime Timestamp { get; set; }

    public override string ToString()
    {
        return JsonSerializer.Serialize(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are going to create Custom serializer class that will be used by Kafka while serializing the published message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace KafkaConsumer
{
    public class CustomValueDeserializer&amp;lt;T&amp;gt; : IDeserializer&amp;lt;T&amp;gt;
    {
        public T Deserialize(ReadOnlySpan&amp;lt;byte&amp;gt; data, bool isNull, SerializationContext context)
        {
            return JsonSerializer.Deserialize&amp;lt;T&amp;gt;(data);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we are going to create a generic Consumer class to start consuming the Kafka topic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace KafkaConsumer;
public class Consumer&amp;lt;T&amp;gt;
{
    readonly string? _host;
    readonly int _port;
    readonly string? _topic;

    public Consumer()
    {
        _host = "localhost";
        _port = 9092;
        _topic = "producer_logs";
    }

    ConsumerConfig GetConsumerConfig()
    {
        return new ConsumerConfig
        {
            BootstrapServers = $"{_host}:{_port}",
            GroupId = "foo",
            AutoOffsetReset = AutoOffsetReset.Earliest
        };
    }

    public async Task ConsumeAsync()
    {
        using (var consumer = new ConsumerBuilder&amp;lt;Ignore, T&amp;gt;(GetConsumerConfig())
            .SetValueDeserializer(new CustomValueDeserializer&amp;lt;T&amp;gt;())                        
            .Build())
        {
            consumer.Subscribe(_topic);

            WriteLine($"Subscribed to {_topic}");

            await Task.Run(() =&amp;gt;
            {
                while (true)
                {
                    var consumeResult = consumer.Consume(default(CancellationToken));

                    if(consumeResult?.Message?.Value is Message result)
                    {
                        WriteLine($"Data Received - {result}");
                    }
                }
            });

            consumer.Close();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this class contains a method "ConsumeAsync" which creates a consumer object and starts consuming from Kafka using "&lt;em&gt;consumer.consume()&lt;/em&gt;" method. This is not an async method, so if you call this method in a main thread, it will be blocked until you receive a message here which is why I have consumed it in a different task. Once a message is received, we are printing it here. &lt;/p&gt;

&lt;p&gt;Now the main program looks like,&lt;br&gt;
&lt;/p&gt;

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

public class Program
{
    public static async Task Main(string[] args)
    {
        var consumer = new Consumer&amp;lt;Message&amp;gt;();
        await consumer.ConsumeAsync();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run both the producer and consumer now,&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%2Fqbnbfb14h4adrxknd7fs.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%2Fqbnbfb14h4adrxknd7fs.png" alt="Result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original source is available in &lt;a href="https://github.com/sprabha1990/kafka-dotnet6" rel="noopener noreferrer"&gt;my github repo&lt;/a&gt; &lt;/p&gt;

</description>
      <category>dotnet6</category>
      <category>kafka</category>
      <category>dotnetcore</category>
      <category>csharp</category>
    </item>
    <item>
      <title>How to choose a collection in C#</title>
      <dc:creator>Sugumar Prabhakar</dc:creator>
      <pubDate>Thu, 14 Oct 2021 22:10:11 +0000</pubDate>
      <link>https://dev.to/sprabha1990/how-to-choose-a-collection-in-c-5ajk</link>
      <guid>https://dev.to/sprabha1990/how-to-choose-a-collection-in-c-5ajk</guid>
      <description>&lt;p&gt;When I started my career with .Net Framework 4.0, I always felt challenging when it comes to choose the right collection for my development. I always end up with either Generic Dictionary or List.&lt;/p&gt;

&lt;p&gt;But this article from the Microsoft can be a life-saver [&lt;a href="https://docs.microsoft.com/en-us/dotnet/standard/collections/?WT.mc_id=-blog-scottha" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/dotnet/standard/collections/?WT.mc_id=-blog-scottha&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%2Fjj1sfkll9jnsdw5w0797.JPG" 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%2Fjj1sfkll9jnsdw5w0797.JPG" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>programming</category>
      <category>dotnet</category>
      <category>datastructures</category>
    </item>
  </channel>
</rss>
