<?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: Rahimah Sulayman</title>
    <description>The latest articles on DEV Community by Rahimah Sulayman (@rahimah_dev).</description>
    <link>https://dev.to/rahimah_dev</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3673037%2Fd084c1b1-6c80-43de-8b42-c48bd0cad2f8.png</url>
      <title>DEV Community: Rahimah Sulayman</title>
      <link>https://dev.to/rahimah_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rahimah_dev"/>
    <language>en</language>
    <item>
      <title>YAML Learning Lab</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Tue, 04 Aug 2026 15:45:17 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/yaml-learning-lab-1lh7</link>
      <guid>https://dev.to/rahimah_dev/yaml-learning-lab-1lh7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;YAML is one of the most widely used configuration and data serialization formats across modern cloud engineering, DevOps, automation, and CI/CD environments.&lt;/p&gt;

&lt;p&gt;From GitHub Actions workflows to Kubernetes manifests and infrastructure configuration, YAML provides a human-readable way to represent structured data while remaining easy for machines to process.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;YAML Learning Lab&lt;/strong&gt; is a practical technical repository focused on YAML syntax, structured data representation, configuration patterns, validation, and its application within modern DevOps workflows.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/rahimahisah17/yaml-learning-lab" rel="noopener noreferrer"&gt;YAML Learning Lab&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A Brief History of YAML
&lt;/h2&gt;

&lt;p&gt;YAML was created in &lt;strong&gt;2001&lt;/strong&gt; by &lt;strong&gt;Clark Evans, Ingy döt Net, and Oren Ben-Kiki&lt;/strong&gt; as a human-friendly data serialization language.&lt;/p&gt;

&lt;p&gt;The name &lt;strong&gt;YAML&lt;/strong&gt; originally stood for &lt;strong&gt;"Yet Another Markup Language."&lt;/strong&gt; It was later reinterpreted as &lt;strong&gt;"YAML Ain't Markup Language"&lt;/strong&gt;, emphasizing that YAML is a &lt;strong&gt;data serialization language&lt;/strong&gt;, rather than a markup language.&lt;/p&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;Markup languages such as HTML are primarily concerned with describing the structure and presentation of documents, while YAML focuses on representing structured information and configuration in a format that is readable by both humans and machines.&lt;/p&gt;

&lt;p&gt;Today, YAML is widely used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud configuration&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;Application configuration&lt;/li&gt;
&lt;li&gt;Automation&lt;/li&gt;
&lt;li&gt;DevOps tooling&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;The repository brings together practical YAML examples covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key-value pairs&lt;/li&gt;
&lt;li&gt;Mappings&lt;/li&gt;
&lt;li&gt;Nested structures&lt;/li&gt;
&lt;li&gt;Sequences&lt;/li&gt;
&lt;li&gt;Lists of objects&lt;/li&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Quoted strings&lt;/li&gt;
&lt;li&gt;Multiline strings&lt;/li&gt;
&lt;li&gt;Anchors and aliases&lt;/li&gt;
&lt;li&gt;Merge keys&lt;/li&gt;
&lt;li&gt;Multi-document YAML&lt;/li&gt;
&lt;li&gt;GitHub Actions workflows&lt;/li&gt;
&lt;li&gt;YAML validation with Python and PyYAML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project demonstrates how YAML syntax translates into structured configuration used by modern engineering tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;The project was organized as a Git repository with individual YAML files representing different concepts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh36b3u7ncri3ljadvmaa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh36b3u7ncri3ljadvmaa.png" alt="Project setup" width="800" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository structure keeps each concept isolated while also providing a final configuration challenge that combines multiple YAML features.&lt;/p&gt;




&lt;h2&gt;
  
  
  YAML Fundamentals
&lt;/h2&gt;

&lt;p&gt;The first examples introduce YAML's basic key-value structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Raphael&lt;/span&gt;
&lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloud Engineer&lt;/span&gt;
&lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nigeria&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A YAML mapping associates each key with a value:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Raphael&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;role&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Cloud Engineer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;country&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Nigeria&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This simple structure is the foundation for more complex YAML configurations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd7mrmkgbfpqts0ns36sp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd7mrmkgbfpqts0ns36sp.png" alt="YAML basics" width="799" height="185"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Mappings and Nested Structures
&lt;/h2&gt;

&lt;p&gt;YAML uses indentation to represent relationships between data elements.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Raphael&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloud Engineer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;role&lt;/code&gt; are properties nested under &lt;code&gt;person&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nested structures allow configuration to represent relationships between related pieces of information without requiring complex syntax.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6avg3oivlyeue3ofhzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6avg3oivlyeue3ofhzb.png" alt="Mappings" width="798" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nested mappings extend this idea by allowing multiple levels of configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Raphael&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
  &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nigeria&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgvqxqfq8pacz0drzy61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgvqxqfq8pacz0drzy61.png" alt="Nested mappings" width="799" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One important YAML rule becomes obvious here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Indentation defines structure.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;YAML configuration should use spaces for indentation rather than tabs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sequences
&lt;/h2&gt;

&lt;p&gt;YAML sequences represent ordered collections.&lt;/p&gt;

&lt;p&gt;They are identified using a hyphen (&lt;code&gt;-&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Azure&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Docker&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Kubernetes&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a list of four values under the &lt;code&gt;skills&lt;/code&gt; key.&lt;/p&gt;

&lt;p&gt;Sequences are common in configuration files for representing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packages&lt;/li&gt;
&lt;li&gt;Ports&lt;/li&gt;
&lt;li&gt;Hosts&lt;/li&gt;
&lt;li&gt;Skills&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;li&gt;Configuration options&lt;/li&gt;
&lt;/ul&gt;

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




&lt;h2&gt;
  
  
  Lists of Objects
&lt;/h2&gt;

&lt;p&gt;YAML sequences can contain mappings, making it possible to represent collections of structured objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;employees&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Raphael&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloud Engineer&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sarah&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DevOps Engineer&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;David&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Solution Architect&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each list item contains multiple properties.&lt;/p&gt;

&lt;p&gt;This pattern is particularly useful when configuration describes multiple resources that share a common structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbltgh6bfvhlmmp1d65v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbltgh6bfvhlmmp1d65v.png" alt="Lists of objects" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  YAML Data Types
&lt;/h2&gt;

&lt;p&gt;YAML supports several common data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Raphael&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
  &lt;span class="na"&gt;salary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4500.50&lt;/span&gt;
  &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;verified&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values represent different data types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Raphael&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;4500.50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Floating-point number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Null&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Understanding how YAML interprets values is important when configuration is consumed by automation tools or programming languages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuid0zvuml89dgfrilpw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuid0zvuml89dgfrilpw1.png" alt="YAML data types" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Comments and Quoted Strings
&lt;/h2&gt;

&lt;p&gt;Comments begin with &lt;code&gt;#&lt;/code&gt; and are ignored by YAML parsers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Employee Information Configuration&lt;/span&gt;
&lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lagos&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comments are useful for documenting configuration and explaining why a particular value or section exists.&lt;/p&gt;

&lt;p&gt;YAML also supports quoted strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Welcome:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Cloud&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Engineers!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quoting can make the intended value explicit, particularly when a value contains characters that could otherwise be interpreted specially.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Multiline Strings
&lt;/h2&gt;

&lt;p&gt;YAML provides block scalar syntax for storing multiline text.&lt;/p&gt;

&lt;p&gt;There are two important styles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;|&lt;/code&gt; — literal block&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;&lt;/code&gt; — folded block&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Literal Block
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;|&lt;/code&gt; character preserves line breaks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;bio_literal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;I enjoy learning Azure.&lt;/span&gt;
  &lt;span class="s"&gt;I build cloud solutions.&lt;/span&gt;
  &lt;span class="s"&gt;I teach DevOps.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Folded Block
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;&amp;gt;&lt;/code&gt; character folds consecutive lines into a paragraph-like value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;bio_folded&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;I enjoy learning Azure.&lt;/span&gt;
  &lt;span class="s"&gt;I build cloud solutions.&lt;/span&gt;
  &lt;span class="s"&gt;I teach DevOps.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference matters when YAML is being used to store scripts, documentation, certificates, configuration blocks, or other multiline content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwsbl14z2i6a0vcpnknn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwsbl14z2i6a0vcpnknn.png" alt="Multiline strings" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Anchors, Aliases, and Merge Keys
&lt;/h2&gt;

&lt;p&gt;YAML provides anchors and aliases for reusing configuration without repeatedly defining the same values.&lt;/p&gt;

&lt;p&gt;An anchor is created using &lt;code&gt;&amp;amp;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nigeria&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Student&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration can then be reused using an alias:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;student1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Raphael&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; merge key incorporates the values from the anchored mapping.&lt;/p&gt;

&lt;p&gt;Another object can reuse the same configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;student2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sarah&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach reduces duplication and makes shared configuration easier to maintain.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Multi-Document YAML
&lt;/h2&gt;

&lt;p&gt;A YAML file can contain multiple independent documents.&lt;/p&gt;

&lt;p&gt;The document separator is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;student1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Raphael&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Third Document Stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The separator marks the beginning of another YAML document.&lt;/p&gt;

&lt;p&gt;This becomes important during validation because a file containing multiple documents must be parsed accordingly.&lt;/p&gt;

&lt;p&gt;The repository demonstrates this concept in &lt;code&gt;anchors.yaml&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  YAML in GitHub Actions
&lt;/h2&gt;

&lt;p&gt;YAML is heavily used in CI/CD systems.&lt;/p&gt;

&lt;p&gt;The repository includes a GitHub Actions workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Project&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Building project..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow demonstrates several important GitHub Actions concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow Name
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Project&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Defines the workflow's name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trigger
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifies that the workflow runs when changes are pushed to the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Job
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Defines a job named &lt;code&gt;build&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runner
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifies the GitHub-hosted runner that executes the job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Building project..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step checks out the repository contents.&lt;/p&gt;

&lt;p&gt;The second executes a shell command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sg2ocjibza2jmnjxo02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sg2ocjibza2jmnjxo02.png" alt="GitHub Actions workflow" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Configuration Challenge
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;student-profile.yaml&lt;/code&gt; configuration combines several YAML concepts into one structured document.&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mappings&lt;/li&gt;
&lt;li&gt;Nested mappings&lt;/li&gt;
&lt;li&gt;Sequences&lt;/li&gt;
&lt;li&gt;Strings&lt;/li&gt;
&lt;li&gt;Integers&lt;/li&gt;
&lt;li&gt;Floating-point numbers&lt;/li&gt;
&lt;li&gt;Booleans&lt;/li&gt;
&lt;li&gt;Multiline strings&lt;/li&gt;
&lt;li&gt;Folded strings&lt;/li&gt;
&lt;li&gt;Anchors&lt;/li&gt;
&lt;li&gt;Aliases&lt;/li&gt;
&lt;li&gt;Merge keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The shared configuration begins with an anchor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default_profile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Student&lt;/span&gt;
  &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nigeria&lt;/span&gt;
  &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;95.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration is then reused:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;user1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sarah&lt;/span&gt;

&lt;span class="na"&gt;user2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;David&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file also contains sequences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Azure&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Docker&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Kubernetes&lt;/span&gt;

&lt;span class="na"&gt;hobbies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Coding&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Reading&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nested mappings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12 Cloud Way&lt;/span&gt;
  &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Abuja&lt;/span&gt;
  &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nigeria&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And both multiline string styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;bio_literal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;I enjoy learning Azure.&lt;/span&gt;
  &lt;span class="s"&gt;I build cloud solutions.&lt;/span&gt;
  &lt;span class="s"&gt;I teach DevOps.&lt;/span&gt;

&lt;span class="na"&gt;bio_folded&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;I enjoy learning Azure.&lt;/span&gt;
  &lt;span class="s"&gt;I build cloud solutions.&lt;/span&gt;
  &lt;span class="s"&gt;I teach DevOps.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fab26vcb3uhixs140qxcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fab26vcb3uhixs140qxcm.png" alt="Student profile configuration" width="633" height="861"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This configuration demonstrates how multiple YAML structures can be combined to represent realistic configuration data.&lt;/p&gt;




&lt;h2&gt;
  
  
  YAML Validation
&lt;/h2&gt;

&lt;p&gt;Configuration should be validated before being used by automation systems.&lt;/p&gt;

&lt;p&gt;This repository uses &lt;strong&gt;Python&lt;/strong&gt;, &lt;strong&gt;PyYAML&lt;/strong&gt;, and Linux command-line tools to validate the YAML files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate All YAML Documents
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import yaml, glob; [list(yaml.safe_load_all(open(f))) for f in glob.glob("*.yaml")]; print("All YAML files valid!")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Starts Python 3 using &lt;code&gt;python3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;-c&lt;/code&gt; to execute Python code directly from the command line.&lt;/li&gt;
&lt;li&gt;Imports the PyYAML library with &lt;code&gt;import yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Imports Python's &lt;code&gt;glob&lt;/code&gt; module with &lt;code&gt;import glob&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;glob.glob("*.yaml")&lt;/code&gt; to find every &lt;code&gt;.yaml&lt;/code&gt; file in the current directory.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;open(f)&lt;/code&gt; to open each YAML file.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;yaml.safe_load_all()&lt;/code&gt; to parse all YAML documents within each file.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;list()&lt;/code&gt; to force all documents to be parsed.&lt;/li&gt;
&lt;li&gt;Uses a list comprehension to apply the validation to every YAML file.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;print()&lt;/code&gt; to display a success message after all files are parsed successfully.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The use of &lt;code&gt;safe_load_all()&lt;/code&gt; is important because &lt;code&gt;anchors.yaml&lt;/code&gt; contains multiple YAML documents separated by &lt;code&gt;---&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check for Illegal Tab Characters
&lt;/h3&gt;

&lt;p&gt;YAML indentation should use spaces rather than tabs.&lt;/p&gt;

&lt;p&gt;The repository also checks for tab characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.yaml &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'No illegal tab characters found!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking down the command:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Searches files for a specified pattern.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Displays the line number where a match is found.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$'\t'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Represents a tab character in Bash.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Targets all &lt;code&gt;.yaml&lt;/code&gt; files in the current directory.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;echo&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Prints text to the terminal.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If no tabs are detected, the command prints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No illegal tab characters found!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsi9rcqydb3zu619x16u2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsi9rcqydb3zu619x16u2.png" alt="YAML validation" width="798" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These checks provide basic validation for both YAML syntax and indentation consistency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Git and Repository Management
&lt;/h2&gt;

&lt;p&gt;The project was maintained using Git and published to GitHub.&lt;/p&gt;

&lt;p&gt;Git was used to track changes, create commits, maintain the &lt;code&gt;main&lt;/code&gt; branch, and synchronize the repository with GitHub.&lt;/p&gt;

&lt;p&gt;The final repository includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yaml-learning-lab/
├── docs/
│   └── lessons.md
├── screenshots/
├── README.md
├── anchors.yaml
├── lesson1.yaml
├── lesson2.yaml
├── lesson3.yaml
├── lesson4.yaml
├── lesson5.yaml
├── lesson6.yaml
├── lesson7.yaml
├── multiline.yaml
├── student-profile.yaml
└── workflow.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ficyhtvz02vmoh0otiyx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ficyhtvz02vmoh0otiyx6.png" alt="Git history and status" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The complete implementation, documentation, YAML files, and supporting evidence are available in the repository:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/rahimahisah17/yaml-learning-lab" rel="noopener noreferrer"&gt;View the SKILL.SCH YAML Learning Lab on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why YAML Matters in Cloud and DevOps
&lt;/h2&gt;

&lt;p&gt;YAML is more than a configuration format. It is an important component of modern engineering workflows.&lt;/p&gt;

&lt;p&gt;The concepts demonstrated in this repository appear in technologies such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;Ansible&lt;/li&gt;
&lt;li&gt;Azure DevOps&lt;/li&gt;
&lt;li&gt;Infrastructure as Code tools&lt;/li&gt;
&lt;li&gt;Application configuration systems&lt;/li&gt;
&lt;li&gt;Cloud automation platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same fundamental ideas—mappings, sequences, nesting, data types, multiline values, and reusable configuration—appear repeatedly across these technologies.&lt;/p&gt;

&lt;p&gt;Understanding YAML therefore makes it easier to read, write, troubleshoot, and maintain configuration-driven systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;YAML Learning Lab&lt;/strong&gt; demonstrates how YAML progresses from simple key-value pairs to structured configuration used in real-world DevOps environments.&lt;/p&gt;

&lt;p&gt;The repository covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YAML syntax and structure&lt;/li&gt;
&lt;li&gt;Mappings and nested mappings&lt;/li&gt;
&lt;li&gt;Sequences and lists of objects&lt;/li&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Comments and quoted strings&lt;/li&gt;
&lt;li&gt;Literal and folded multiline strings&lt;/li&gt;
&lt;li&gt;Anchors, aliases, and merge keys&lt;/li&gt;
&lt;li&gt;Multi-document YAML streams&lt;/li&gt;
&lt;li&gt;GitHub Actions workflow configuration&lt;/li&gt;
&lt;li&gt;YAML validation using Python and PyYAML&lt;/li&gt;
&lt;li&gt;Git-based project management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project also demonstrates an important engineering principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Configuration should be readable, structured, reusable, and validated before it becomes part of an automation workflow.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;YAML Learning Lab&lt;/strong&gt; provides a practical technical reference for YAML and its role in cloud engineering and DevOps.&lt;/p&gt;

&lt;p&gt;From basic mappings and sequences to anchors, multiline strings, GitHub Actions, and automated validation, the repository brings together core YAML patterns required for working with configuration-driven tooling.&lt;/p&gt;

&lt;p&gt;More importantly, the project demonstrates YAML in context—not simply as a file format, but as a foundational component of modern &lt;strong&gt;cloud, DevOps, automation, and CI/CD workflows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the complete implementation, configuration files, technical documentation, and supporting evidence:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/rahimahisah17/yaml-learning-lab" rel="noopener noreferrer"&gt;YAML Learning Lab — GitHub Repository&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rahimah Sulayman&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cloud &amp;amp; DevOps Engineer | Data Analyst&lt;/p&gt;

</description>
      <category>python</category>
      <category>pyyaml</category>
      <category>yaml</category>
      <category>git</category>
    </item>
    <item>
      <title>Dockerfiles: Building and Running a Node.js Application in a Container</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Wed, 29 Jul 2026 12:02:53 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/dockerfiles-building-and-running-a-nodejs-application-in-a-container-bfk</link>
      <guid>https://dev.to/rahimah_dev/dockerfiles-building-and-running-a-nodejs-application-in-a-container-bfk</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Containers provide a consistent way to package applications together with the dependencies and configuration required to run them.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Dockerfile&lt;/strong&gt; defines how that container image is built.&lt;/p&gt;

&lt;p&gt;In this project, a Node.js application was containerized using a custom Dockerfile, with the image built, tested, inspected, modified, and rebuilt throughout the implementation.&lt;/p&gt;

&lt;p&gt;The project demonstrates how Dockerfile instructions influence the resulting image and, ultimately, the behavior of the running container.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Dockerfile?
&lt;/h2&gt;

&lt;p&gt;A Dockerfile is a text file containing a sequence of instructions Docker uses to build an image.&lt;/p&gt;

&lt;p&gt;A simplified workflow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application Source Code
        │
        ▼
    Dockerfile
        │
        │ docker build
        ▼
   Docker Image
        │
        │ docker run
        ▼
 Docker Container
        │
        ▼
 Running Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dockerfile describes the environment and instructions required to turn application source code into a runnable container image.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dockerfile vs Docker Image vs Container
&lt;/h2&gt;

&lt;p&gt;These three concepts are closely related but serve different purposes.&lt;/p&gt;

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

&lt;p&gt;The &lt;strong&gt;Dockerfile&lt;/strong&gt; is the build blueprint.&lt;/p&gt;

&lt;p&gt;It defines instructions such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;
&lt;span class="k"&gt;RUN
&lt;/span&gt;EXPOSE
&lt;span class="k"&gt;CMD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker Image
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;image&lt;/strong&gt; is the built artifact produced from the Dockerfile.&lt;/p&gt;

&lt;p&gt;It contains the application, dependencies, filesystem layers, and configuration required to create a container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Container
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;container&lt;/strong&gt; is a running instance of an image.&lt;/p&gt;

&lt;p&gt;In simple terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dockerfile
    │
    │ docker build
    ▼
Docker Image
    │
    │ docker run
    ▼
Docker Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;The application used for this project is a simple Node.js web application.&lt;/p&gt;

&lt;p&gt;The containerized application consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Node.js application&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;server.js&lt;/code&gt; entry point&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;package.json&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Installed Node.js dependencies&lt;/li&gt;
&lt;li&gt;A Dockerfile defining the image build process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Dockerfile is responsible for packaging these components into a reproducible container image.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dockerfiles-lab/
├── screenshots/
├── .gitignore
├── Dockerfile
├── package.json
├── package-lock.json
├── README.md
└── server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository also contains implementation screenshots documenting the different stages of the Dockerfile and container lifecycle.&lt;/p&gt;

&lt;p&gt;The complete implementation is available in the GitHub repository linked at the end of this article.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Node.js Application
&lt;/h1&gt;

&lt;p&gt;Before building the container, there needs to be an application for Docker to package.&lt;/p&gt;

&lt;p&gt;The application entry point is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;A simplified Node.js server can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from Docker!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server running on port 3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application listens on port &lt;code&gt;3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This gives Docker something concrete to package and run inside the container.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why &lt;code&gt;package.json&lt;/code&gt; Matters
&lt;/h1&gt;

&lt;p&gt;The project also contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Node.js applications, &lt;code&gt;package.json&lt;/code&gt; provides project metadata and dependency information.&lt;/p&gt;

&lt;p&gt;It can also define scripts used to start or manage the application.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node server.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The associated lock file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package-lock.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;records the resolved dependency versions.&lt;/p&gt;

&lt;p&gt;Together, these files make the Node.js application's dependencies and runtime configuration reproducible.&lt;/p&gt;




&lt;h1&gt;
  
  
  Building the First Docker Image
&lt;/h1&gt;

&lt;p&gt;The first stage of the implementation involved building a Docker image from the Dockerfile.&lt;/p&gt;

&lt;p&gt;The basic command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docker-node-app:1.0 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The components of the command are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build
     │
     ├── -t docker-node-app:1.0
     │       └── image name and tag
     │
     └── .
         └── current directory as build context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.&lt;/code&gt; tells Docker to use the current directory as the &lt;strong&gt;build context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This makes files in the project directory available to Docker during the build, subject to &lt;code&gt;.dockerignore&lt;/code&gt; rules where applicable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding the Dockerfile
&lt;/h1&gt;

&lt;p&gt;A Dockerfile is processed from top to bottom.&lt;/p&gt;

&lt;p&gt;A typical Node.js Dockerfile can contain instructions such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each instruction has a specific purpose.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;FROM&lt;/code&gt;: Choosing the Base Image
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FROM&lt;/code&gt; specifies the base image from which the new image is built.&lt;/p&gt;

&lt;p&gt;For a Node.js application, using a Node.js base image provides the Node.js runtime and supporting environment required to execute the application.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;alpine&lt;/code&gt; variant is based on Alpine Linux and is commonly used when a smaller image footprint is desirable.&lt;/p&gt;

&lt;p&gt;The base image is therefore the starting point for the container environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;WORKDIR&lt;/code&gt;: Setting the Working Directory
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;WORKDIR&lt;/code&gt; establishes the working directory for subsequent Dockerfile instructions.&lt;/p&gt;

&lt;p&gt;Instead of placing application files throughout the container filesystem, the application can be organized under:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It also determines the default working directory when the container starts unless another directory is explicitly configured.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;COPY&lt;/code&gt;: Adding Application Files
&lt;/h2&gt;

&lt;p&gt;The application files need to be transferred from the build context into the image.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This copies the Node.js package definition files into the working directory.&lt;/p&gt;

&lt;p&gt;Later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;copies the remaining application files into the image.&lt;/p&gt;

&lt;p&gt;This separation is useful because dependency installation and application source code can be handled as separate build layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;RUN&lt;/code&gt;: Installing Dependencies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt; executes a command while the image is being built.&lt;/p&gt;

&lt;p&gt;Here, npm installs the dependencies defined by the Node.js project.&lt;/p&gt;

&lt;p&gt;The resulting files become part of the image layer produced by this instruction.&lt;/p&gt;

&lt;p&gt;This is different from &lt;code&gt;CMD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt; happens during &lt;strong&gt;image build time&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;code&gt;EXPOSE&lt;/code&gt;: Documenting the Application Port
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Node.js application listens on port &lt;code&gt;3000&lt;/code&gt;, so the Dockerfile declares:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to understand that &lt;code&gt;EXPOSE&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; publish the port to the host by itself.&lt;/p&gt;

&lt;p&gt;It documents the port the application expects to use inside the container.&lt;/p&gt;

&lt;p&gt;Host-to-container port publishing is configured when the container is started.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--name&lt;/span&gt; docker-node-container docker-node-app:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mapping is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host                  Container
 3000       ───────►    3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  &lt;code&gt;CMD&lt;/code&gt;: Defining the Default Container Command
&lt;/h1&gt;

&lt;p&gt;One of the most important Dockerfile instructions in this project was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; defines the default command executed when a container is started from the image.&lt;/p&gt;

&lt;p&gt;In this case, Docker starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside the container.&lt;/p&gt;

&lt;p&gt;This is what launches the Node.js application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why &lt;code&gt;CMD&lt;/code&gt; Matters
&lt;/h1&gt;

&lt;p&gt;During the implementation, the application command was changed between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This demonstrated an important container behavior.&lt;/p&gt;

&lt;p&gt;If the file referenced by &lt;code&gt;CMD&lt;/code&gt; does not exist in the image, the container cannot successfully start the application.&lt;/p&gt;

&lt;p&gt;This can result in a container that starts and immediately exits.&lt;/p&gt;

&lt;p&gt;For example, if the Dockerfile contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the image does not contain:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Node.js cannot execute the requested entry point.&lt;/p&gt;

&lt;p&gt;This is why examining container logs is an important troubleshooting step.&lt;/p&gt;




&lt;h1&gt;
  
  
  Building After a Dockerfile Change
&lt;/h1&gt;

&lt;p&gt;A Docker image is a built artifact.&lt;/p&gt;

&lt;p&gt;Changing the Dockerfile does not automatically modify an existing image.&lt;/p&gt;

&lt;p&gt;After making changes, the image must be rebuilt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docker-node-app:2.0 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new image version containing the updated Dockerfile instructions.&lt;/p&gt;

&lt;p&gt;The general workflow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Modify Dockerfile
       │
       ▼
docker build
       │
       ▼
New Image
       │
       ▼
docker run
       │
       ▼
Test Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This build-test cycle was repeated during the project to validate changes to the container configuration.&lt;/p&gt;




&lt;h1&gt;
  
  
  Inspecting Docker Images
&lt;/h1&gt;

&lt;p&gt;After building an image, available images can be inspected with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository&lt;/li&gt;
&lt;li&gt;Tag&lt;/li&gt;
&lt;li&gt;Image ID&lt;/li&gt;
&lt;li&gt;Creation time&lt;/li&gt;
&lt;li&gt;Image size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY       TAG       IMAGE ID       CREATED        SIZE
docker-node-app  1.0       abc123...      ...            ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tags such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.0
2.0
final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be used to distinguish different image versions during development and testing.&lt;/p&gt;




&lt;h1&gt;
  
  
  Running the Container
&lt;/h1&gt;

&lt;p&gt;Once the image has been built, it can be started with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; docker-node-container &lt;span class="se"&gt;\&lt;/span&gt;
  docker-node-app:final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important options are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs the container in detached mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 3000:3000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maps host port 3000 to container port 3000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Assigns a name to the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-node-app:final&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Specifies the image to run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The application can then be accessed from the host through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Verifying a Running Container
&lt;/h1&gt;

&lt;p&gt;After starting the container, its status can be checked with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This displays currently running containers.&lt;/p&gt;

&lt;p&gt;Typical information includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container ID&lt;/li&gt;
&lt;li&gt;Image&lt;/li&gt;
&lt;li&gt;Command&lt;/li&gt;
&lt;li&gt;Creation time&lt;/li&gt;
&lt;li&gt;Status&lt;/li&gt;
&lt;li&gt;Port mappings&lt;/li&gt;
&lt;li&gt;Container name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The port mapping should show that host port &lt;code&gt;3000&lt;/code&gt; is forwarded to port &lt;code&gt;3000&lt;/code&gt; inside the container.&lt;/p&gt;




&lt;h1&gt;
  
  
  Testing the Application
&lt;/h1&gt;

&lt;p&gt;Once the container is running, the application can be accessed through a browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the request flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
   │
   │ localhost:3000
   ▼
Host Port 3000
   │
   │ Docker port mapping
   ▼
Container Port 3000
   │
   ▼
Node.js
   │
   ▼
server.js
   │
   ▼
HTTP Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This demonstrates the relationship between the host, container networking, and the application process.&lt;/p&gt;




&lt;h1&gt;
  
  
  Troubleshooting a Failed Container
&lt;/h1&gt;

&lt;p&gt;Not every container starts successfully on the first attempt.&lt;/p&gt;

&lt;p&gt;During the implementation, an invalid application entry point was used to demonstrate container troubleshooting.&lt;/p&gt;

&lt;p&gt;A failed run can be identified using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike &lt;code&gt;docker ps&lt;/code&gt;, the &lt;code&gt;-a&lt;/code&gt; option also displays stopped containers.&lt;/p&gt;

&lt;p&gt;This is particularly useful when a container starts and then exits immediately.&lt;/p&gt;




&lt;h1&gt;
  
  
  Using Docker Logs for Troubleshooting
&lt;/h1&gt;

&lt;p&gt;When a container fails, logs are often the fastest way to determine why.&lt;/p&gt;

&lt;p&gt;The logs can be viewed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs docker-node-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if the Dockerfile attempts to execute a file that does not exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js may report that it cannot find the specified module.&lt;/p&gt;

&lt;p&gt;The important troubleshooting pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Container exits
      │
      ▼
docker ps -a
      │
      ▼
Identify exited container
      │
      ▼
docker logs &amp;lt;container&amp;gt;
      │
      ▼
Identify application/configuration error
      │
      ▼
Fix Dockerfile or application
      │
      ▼
Rebuild image
      │
      ▼
Run container again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow is useful well beyond simple Docker labs.&lt;/p&gt;




&lt;h1&gt;
  
  
  Stopping and Removing a Container
&lt;/h1&gt;

&lt;p&gt;Once a container is no longer needed, it can be stopped and removed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop docker-node-container &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm &lt;/span&gt;docker-node-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop docker-node-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;stops the running container.&lt;/p&gt;

&lt;p&gt;The second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;docker-node-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;removes the stopped container.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; operator ensures the second command runs only if the first command succeeds.&lt;/p&gt;




&lt;h1&gt;
  
  
  Rebuilding and Retesting
&lt;/h1&gt;

&lt;p&gt;After correcting the Dockerfile, the image was rebuilt and the container started again.&lt;/p&gt;

&lt;p&gt;A typical sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docker-node-app:final &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;followed by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; docker-node-container &lt;span class="se"&gt;\&lt;/span&gt;
  docker-node-app:final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and finally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be used to verify the result.&lt;/p&gt;

&lt;p&gt;This build → run → inspect → troubleshoot → rebuild cycle is fundamental to working effectively with Dockerfiles.&lt;/p&gt;




&lt;h1&gt;
  
  
  Changing Application Output
&lt;/h1&gt;

&lt;p&gt;The application response was also modified during testing.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome to my Docker Node.js application!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After changing application source code, the image must be rebuilt if the source code is copied into the image during the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docker-node-app:final &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The updated image then needs to be used when starting a new container.&lt;/p&gt;

&lt;p&gt;This demonstrates an important principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A running container does not automatically become a new image when the source code or Dockerfile changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The image must be rebuilt when the application contents packaged into it need to change.&lt;/p&gt;




&lt;h1&gt;
  
  
  Docker Build Cache
&lt;/h1&gt;

&lt;p&gt;Docker builds are made up of layers.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each instruction can contribute to the image's layer structure.&lt;/p&gt;

&lt;p&gt;Docker can reuse unchanged layers during subsequent builds, which can significantly improve build performance.&lt;/p&gt;

&lt;p&gt;This is one reason Dockerfiles are commonly structured to copy dependency definition files before copying the rest of the application source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If only application source code changes, Docker may be able to reuse the dependency installation layer.&lt;/p&gt;




&lt;h1&gt;
  
  
  Dockerfile Best Practices
&lt;/h1&gt;

&lt;p&gt;A production-oriented Dockerfile should be designed with efficiency, security, and reproducibility in mind.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Use an appropriate base image
&lt;/h3&gt;

&lt;p&gt;Choose a maintained base image that provides only what the application needs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A smaller base image can reduce the image footprint and attack surface, although compatibility should always be considered.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Keep the image focused
&lt;/h3&gt;

&lt;p&gt;Only copy files required by the application.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.dockerignore&lt;/code&gt; file can prevent unnecessary files from being included in the build context.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
.git
.env
npm-debug.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Take advantage of layer caching
&lt;/h3&gt;

&lt;p&gt;Copy dependency files before application source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can prevent unnecessary dependency reinstallation when only source files change.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Avoid embedding secrets
&lt;/h3&gt;

&lt;p&gt;Do not place passwords, API keys, tokens, or other sensitive values directly into a Dockerfile.&lt;/p&gt;

&lt;p&gt;Use appropriate runtime configuration and secrets-management mechanisms instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Run as a non-root user
&lt;/h3&gt;

&lt;p&gt;Where appropriate, configure the container to run the application as a non-root user.&lt;/p&gt;

&lt;p&gt;This reduces the impact of a potential application compromise.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Use explicit image tags
&lt;/h3&gt;

&lt;p&gt;Rather than relying blindly on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;consider using an explicit version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes builds more predictable and reproducible.&lt;/p&gt;




&lt;h1&gt;
  
  
  Dockerfile Instructions at a Glance
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instruction&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines the base image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WORKDIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets the working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copies files into the image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RUN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes commands during image build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPOSE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Documents a container port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CMD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines the default runtime command&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A useful distinction is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN
└── Build time

CMD
└── Container runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding this difference is essential when diagnosing why a container behaves differently from the image build process.&lt;/p&gt;




&lt;h1&gt;
  
  
  Dockerfile Workflow
&lt;/h1&gt;

&lt;p&gt;The complete workflow demonstrated in this project can be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application Source
       │
       ▼
   Dockerfile
       │
       │ docker build
       ▼
   Docker Image
       │
       │ docker images
       ▼
   Image Inspection
       │
       │ docker run
       ▼
 Docker Container
       │
       ├── docker ps
       │
       ├── Browser Test
       │
       └── docker logs
               │
               ▼
          Troubleshooting
               │
               ▼
        Modify Application
               │
               ▼
          Rebuild Image
               │
               ▼
        Run &amp;amp; Test Again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow provides a repeatable approach to packaging and validating containerized applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Dockerfile vs Docker Compose
&lt;/h1&gt;

&lt;p&gt;Dockerfiles and Docker Compose solve different problems, but they complement each other.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dockerfile&lt;/th&gt;
&lt;th&gt;Docker Compose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Defines how an image is built&lt;/td&gt;
&lt;td&gt;Defines how multiple services run together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creates a custom image&lt;/td&gt;
&lt;td&gt;Orchestrates application services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on image construction&lt;/td&gt;
&lt;td&gt;Focuses on service configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses Dockerfile instructions&lt;/td&gt;
&lt;td&gt;Uses YAML configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker compose up&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;docker run&lt;/code&gt; starts the resulting image&lt;/td&gt;
&lt;td&gt;Compose manages the defined application stack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A Dockerfile might create the image for an application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dockerfile
     │
     ▼
Application Image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker Compose can then use that image as part of a larger application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Docker Compose
                     │
          ┌──────────┴──────────┐
          ▼                     ▼
    Application             Database
     Container              Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding both technologies provides a stronger foundation for building and operating containerized applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key Docker Commands
&lt;/h1&gt;

&lt;p&gt;The primary commands used throughout the project include:&lt;/p&gt;

&lt;h3&gt;
  
  
  Build an image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docker-node-app:final &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run a container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; docker-node-container &lt;span class="se"&gt;\&lt;/span&gt;
  docker-node-app:final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List running containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List all containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  View container logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs docker-node-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stop and remove a container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop docker-node-container &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm &lt;/span&gt;docker-node-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Skills Demonstrated
&lt;/h1&gt;

&lt;p&gt;This implementation demonstrates practical experience with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dockerfile authoring&lt;/li&gt;
&lt;li&gt;Docker image creation&lt;/li&gt;
&lt;li&gt;Docker image tagging&lt;/li&gt;
&lt;li&gt;Docker build context&lt;/li&gt;
&lt;li&gt;Docker build layers and caching&lt;/li&gt;
&lt;li&gt;Node.js containerization&lt;/li&gt;
&lt;li&gt;Base image selection&lt;/li&gt;
&lt;li&gt;Working directory configuration&lt;/li&gt;
&lt;li&gt;Application file management with &lt;code&gt;COPY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dependency installation with &lt;code&gt;RUN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Container port configuration&lt;/li&gt;
&lt;li&gt;Runtime commands with &lt;code&gt;CMD&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Docker container lifecycle management&lt;/li&gt;
&lt;li&gt;Port mapping&lt;/li&gt;
&lt;li&gt;Container inspection&lt;/li&gt;
&lt;li&gt;Container log analysis&lt;/li&gt;
&lt;li&gt;Troubleshooting failed containers&lt;/li&gt;
&lt;li&gt;Image rebuild and validation workflows&lt;/li&gt;
&lt;li&gt;Linux command-line operations&lt;/li&gt;
&lt;li&gt;Git and GitHub&lt;/li&gt;
&lt;li&gt;Technical documentation&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;A Dockerfile provides the foundation for turning application source code into a portable, reproducible container image.&lt;/p&gt;

&lt;p&gt;By defining the base runtime, working directory, application files, dependencies, exposed port, and startup command, the Dockerfile establishes how the application is packaged and executed.&lt;/p&gt;

&lt;p&gt;The implementation also demonstrates that containerization is more than simply building an image. A reliable workflow includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build
  ↓
Run
  ↓
Inspect
  ↓
Test
  ↓
Troubleshoot
  ↓
Rebuild
  ↓
Validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These concepts form a practical foundation for container-based application development and provide the building blocks for more advanced workflows involving Docker Compose, CI/CD pipelines, container registries, orchestration platforms, and cloud-native deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Repository
&lt;/h2&gt;

&lt;p&gt;The complete implementation, Dockerfile iterations, application files, and supporting screenshots are available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rahimahisah17/dockerfiles-lab" rel="noopener noreferrer"&gt;https://github.com/rahimahisah17/dockerfiles-lab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository provides the complete implementation and evidence of the build, run, troubleshooting, and validation process documented in this article.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Work
&lt;/h2&gt;

&lt;p&gt;This Dockerfile implementation pairs naturally with a multi-container deployment workflow using &lt;strong&gt;Docker Compose&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Docker Compose can be used to take individually containerized applications and combine them with supporting services such as databases, caches, and other dependencies into a complete application stack.&lt;/p&gt;

</description>
      <category>dockerfile</category>
      <category>node</category>
      <category>container</category>
      <category>dockerimages</category>
    </item>
    <item>
      <title>Docker Compose: Running WordPress and MySQL as a Multi-Container Application</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Wed, 29 Jul 2026 11:30:19 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/docker-compose-running-wordpress-and-mysql-as-a-multi-container-application-4mbf</link>
      <guid>https://dev.to/rahimah_dev/docker-compose-running-wordpress-and-mysql-as-a-multi-container-application-4mbf</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Managing containers individually with &lt;code&gt;docker run&lt;/code&gt; works well for simple applications, but once an application depends on multiple services, managing each container separately quickly becomes inefficient.&lt;/p&gt;

&lt;p&gt;A typical WordPress deployment, for example, requires more than the WordPress application itself. It also needs a database service, networking between the services, configuration, and persistent storage.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Docker Compose&lt;/strong&gt; becomes useful.&lt;/p&gt;

&lt;p&gt;In this project, I configured a multi-container WordPress environment using Docker Compose, with WordPress and MySQL defined as separate services and managed through a single &lt;code&gt;compose.yml&lt;/code&gt; file.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Docker Compose?
&lt;/h2&gt;

&lt;p&gt;Docker Compose is a tool for defining and managing multi-container applications using a YAML configuration file.&lt;/p&gt;

&lt;p&gt;Instead of manually creating and configuring each container with multiple &lt;code&gt;docker run&lt;/code&gt; commands, the required services and their configuration can be declared in one file.&lt;/p&gt;

&lt;p&gt;For this project, the architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Docker Compose
                          │
              ┌───────────┴───────────┐
              │                       │
              ▼                       ▼
        WordPress Service       MySQL Service
        wordpress:latest          mysql:8.0
              │                       │
              └─────── Network ───────┘
                         │
                  Persistent Volumes
                   ┌─────┴─────┐
                   ▼           ▼
                wp_data     db_data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two services work together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WordPress&lt;/strong&gt; provides the web application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL&lt;/strong&gt; provides the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Compose networking&lt;/strong&gt; allows WordPress to communicate with MySQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Named volumes&lt;/strong&gt; provide persistent storage.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Architecture
&lt;/h2&gt;

&lt;p&gt;The environment consists of two services:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wordpress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wordpress:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Web application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mysql:8.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The WordPress container exposes port &lt;code&gt;80&lt;/code&gt; internally, which is mapped to port &lt;code&gt;8080&lt;/code&gt; on the host.&lt;/p&gt;

&lt;p&gt;The MySQL service is not exposed directly to the host because WordPress communicates with it through the internal Docker network.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-wordpress-site/
├── screenshots/
├── compose.yml
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core configuration is contained in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Creating the Project Workspace
&lt;/h2&gt;

&lt;p&gt;The project begins with a dedicated directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-wordpress-site
&lt;span class="nb"&gt;cd &lt;/span&gt;my-wordpress-site
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the Compose configuration and project documentation organized in a single workspace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furi1dm9jo2fkfmzo17uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furi1dm9jo2fkfmzo17uy.png" alt="Project workspace" width="798" height="66"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Defining the Docker Compose Configuration
&lt;/h2&gt;

&lt;p&gt;The environment is defined in &lt;code&gt;compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The file contains two services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;

  &lt;span class="na"&gt;wordpress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;services&lt;/code&gt; section is the foundation of the Compose configuration. Each service represents a containerized component of the application.&lt;/p&gt;

&lt;p&gt;The complete configuration defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container images&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Service dependencies&lt;/li&gt;
&lt;li&gt;Port mapping&lt;/li&gt;
&lt;li&gt;Persistent volumes&lt;/li&gt;
&lt;li&gt;Service-to-service communication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fobwudmbhfv2jcda2wvdm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fobwudmbhfv2jcda2wvdm.png" alt="Compose file created" width="800" height="670"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Inspecting &lt;code&gt;compose.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before starting the application, the Compose configuration can be inspected directly from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyc96dy7dnw6hhgrjji06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyc96dy7dnw6hhgrjji06.png" alt="Compose file content" width="800" height="652"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Configuring the MySQL Service
&lt;/h1&gt;

&lt;p&gt;The database service is defined as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
  &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;supersecretpassword&lt;/span&gt;
    &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
    &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpressuser&lt;/span&gt;
    &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpresspassword&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_data:/var/lib/mysql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker Compose uses the official MySQL 8.0 image rather than building a custom database image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restart Policy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs Docker to automatically restart the container when appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;The database configuration is supplied through environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;supersecretpassword&lt;/span&gt;
&lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
&lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpressuser&lt;/span&gt;
&lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpresspassword&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These variables configure the initial MySQL database, user, and credentials.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Production note:&lt;/strong&gt; Credentials should not be committed directly into a public repository. In production environments, use environment files, Docker secrets, or an appropriate secrets-management solution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Persistent Database Storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_data:/var/lib/mysql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The named volume &lt;code&gt;db_data&lt;/code&gt; is mounted to MySQL's data directory.&lt;/p&gt;

&lt;p&gt;This allows database data to persist independently of the container lifecycle.&lt;/p&gt;




&lt;h1&gt;
  
  
  Configuring the WordPress Service
&lt;/h1&gt;

&lt;p&gt;The WordPress service is defined as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;wordpress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress:latest&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
  &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db:3306&lt;/span&gt;
    &lt;span class="na"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpressuser&lt;/span&gt;
    &lt;span class="na"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpresspassword&lt;/span&gt;
    &lt;span class="na"&gt;WORDPRESS_DB_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wp_data:/var/www/html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The official WordPress image is used as the application container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Port Mapping
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This maps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host port 8080 → Container port 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the WordPress application to be accessed through port &lt;code&gt;8080&lt;/code&gt; on the host.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database Connection
&lt;/h3&gt;

&lt;p&gt;The WordPress container is configured to connect to MySQL using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db:3306&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an important Docker Compose concept.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;db&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; a hostname from the host machine. It is the &lt;strong&gt;service name&lt;/strong&gt; defined in the Compose file.&lt;/p&gt;

&lt;p&gt;Docker Compose creates a network for the application, allowing services to communicate with each other using their service names.&lt;/p&gt;

&lt;p&gt;The connection therefore 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;WordPress
    │
    │ db:3306
    ▼
MySQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service Dependency
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This establishes a dependency relationship between WordPress and the database service.&lt;/p&gt;

&lt;h3&gt;
  
  
  WordPress Persistent Storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wp_data:/var/www/html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;wp_data&lt;/code&gt; named volume stores WordPress application data outside the container's writable layer.&lt;/p&gt;




&lt;h1&gt;
  
  
  Starting the Multi-Container Application
&lt;/h1&gt;

&lt;p&gt;Once the Compose configuration is ready, the complete environment can be started with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;up&lt;/code&gt; command creates and starts the services defined in &lt;code&gt;compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-d&lt;/code&gt; option starts them in detached mode, allowing the terminal to remain available while the containers run in the background.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose
       │
       └── up
            │
            └── -d
                 │
                 ├── MySQL container
                 └── WordPress container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffooi5713uty5cgwvojzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffooi5713uty5cgwvojzr.png" alt="Docker Compose up" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Verifying the Services
&lt;/h1&gt;

&lt;p&gt;After starting the environment, the service status can be checked with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides a quick overview of the containers managed by the Compose project.&lt;/p&gt;

&lt;p&gt;It can be used to verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service names&lt;/li&gt;
&lt;li&gt;Container status&lt;/li&gt;
&lt;li&gt;Ports&lt;/li&gt;
&lt;li&gt;Running state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjyp0gb6csa4mxoh1exb1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjyp0gb6csa4mxoh1exb1.png" alt="Compose services status" width="800" height="60"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Inspecting Container Logs
&lt;/h1&gt;

&lt;p&gt;Logs are essential when troubleshooting containerized applications.&lt;/p&gt;

&lt;p&gt;Docker Compose provides a convenient way to inspect logs from all services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--tail 20&lt;/code&gt; option limits the output to the most recent 20 log lines.&lt;/p&gt;

&lt;p&gt;This is useful for identifying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Startup issues&lt;/li&gt;
&lt;li&gt;Configuration problems&lt;/li&gt;
&lt;li&gt;Database connection errors&lt;/li&gt;
&lt;li&gt;Application errors&lt;/li&gt;
&lt;li&gt;Service initialization messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8r9ughph8ehmacumcig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8r9ughph8ehmacumcig.png" alt="Compose logs" width="799" height="236"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Managing Persistent Volumes
&lt;/h1&gt;

&lt;p&gt;The Compose configuration defines two named volumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;wp_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are mounted into the respective services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db_data
   │
   └── /var/lib/mysql

wp_data
   │
   └── /var/www/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Named volumes are useful because container storage is otherwise tied to the container's lifecycle.&lt;/p&gt;

&lt;p&gt;For a database-backed application, separating persistent data from the container itself is particularly important.&lt;/p&gt;




&lt;h1&gt;
  
  
  Cleaning Up the Environment
&lt;/h1&gt;

&lt;p&gt;Once the deployment has been verified, the entire Compose environment can be removed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;down&lt;/code&gt; command stops and removes the containers and the Compose network.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; option additionally removes the named volumes created by the project.&lt;/p&gt;

&lt;p&gt;In this lab, that means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WordPress container   → removed
MySQL container       → removed
Compose network       → removed
wp_data volume        → removed
db_data volume        → removed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; &lt;code&gt;docker compose down -v&lt;/code&gt; removes the project's named volumes and therefore deletes the persistent data stored in them. Use this carefully when working with real application data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4tkhgmsrdem1gebd8bob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4tkhgmsrdem1gebd8bob.png" alt="Docker Compose down" width="798" height="166"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Verifying the Cleanup
&lt;/h1&gt;

&lt;p&gt;The cleanup can be verified using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
docker volume &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verification confirms that the project's containers and named volumes have been removed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwpxmjwwktzmrsjwxmbdt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwpxmjwwktzmrsjwxmbdt.png" alt="Cleanup verification" width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Docker Compose vs. Individual &lt;code&gt;docker run&lt;/code&gt; Commands
&lt;/h1&gt;

&lt;p&gt;Without Compose, running this type of environment manually would require separate commands for the database and WordPress containers, along with their networking, environment variables, volumes, and port configuration.&lt;/p&gt;

&lt;p&gt;Docker Compose consolidates those requirements into a single declarative configuration.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Manual Docker&lt;/th&gt;
&lt;th&gt;Docker Compose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multiple &lt;code&gt;docker run&lt;/code&gt; commands&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;compose.yml&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration spread across commands&lt;/td&gt;
&lt;td&gt;Configuration centralized in YAML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual container relationships&lt;/td&gt;
&lt;td&gt;Services defined together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual volume configuration&lt;/td&gt;
&lt;td&gt;Named volumes declared in Compose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple commands to start services&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Individual cleanup commands&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker compose down&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This becomes increasingly valuable as application architectures grow.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key Docker Compose Commands
&lt;/h1&gt;

&lt;p&gt;The main commands used in this project were:&lt;/p&gt;

&lt;h3&gt;
  
  
  Start services
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check service status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inspect logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stop and remove services
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Skills Demonstrated
&lt;/h1&gt;

&lt;p&gt;This project demonstrates practical experience with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;Multi-container application architecture&lt;/li&gt;
&lt;li&gt;YAML configuration&lt;/li&gt;
&lt;li&gt;WordPress containerization&lt;/li&gt;
&lt;li&gt;MySQL containerization&lt;/li&gt;
&lt;li&gt;Docker networking and service discovery&lt;/li&gt;
&lt;li&gt;Docker named volumes&lt;/li&gt;
&lt;li&gt;Persistent application storage&lt;/li&gt;
&lt;li&gt;Environment-based service configuration&lt;/li&gt;
&lt;li&gt;Container lifecycle management&lt;/li&gt;
&lt;li&gt;Container log inspection&lt;/li&gt;
&lt;li&gt;Docker CLI&lt;/li&gt;
&lt;li&gt;Linux command-line operations&lt;/li&gt;
&lt;li&gt;Technical documentation&lt;/li&gt;
&lt;li&gt;Git and GitHub project management&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Docker Compose provides a practical way to define, configure, and manage applications composed of multiple containers.&lt;/p&gt;

&lt;p&gt;By defining WordPress and MySQL as services in a single &lt;code&gt;compose.yml&lt;/code&gt;, the application environment becomes easier to deploy, inspect, manage, and remove.&lt;/p&gt;

&lt;p&gt;The key concepts demonstrated in this implementation include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declarative multi-container configuration&lt;/li&gt;
&lt;li&gt;Service-to-service communication&lt;/li&gt;
&lt;li&gt;Docker Compose networking&lt;/li&gt;
&lt;li&gt;Persistent volumes&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Container lifecycle management&lt;/li&gt;
&lt;li&gt;Log-based troubleshooting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same principles can be extended to more complex architectures involving application servers, databases, caches, message brokers, reverse proxies, and other supporting services.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Repository
&lt;/h2&gt;

&lt;p&gt;The complete project, including the &lt;code&gt;compose.yml&lt;/code&gt; configuration and implementation screenshots, is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rahimahisah17/my-wordpress-site" rel="noopener noreferrer"&gt;https://github.com/rahimahisah17/my-wordpress-site&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>dockercompose</category>
      <category>wordpress</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Building a Production-Style Azure Infrastructure with Azure CLI: Lessons from a Real Deployment</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Tue, 21 Jul 2026 11:11:27 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/building-a-production-style-azure-infrastructure-with-azure-cli-lessons-from-a-real-deployment-2fdl</link>
      <guid>https://dev.to/rahimah_dev/building-a-production-style-azure-infrastructure-with-azure-cli-lessons-from-a-real-deployment-2fdl</guid>
      <description>&lt;p&gt;&lt;em&gt;How I deployed Linux and Windows virtual machines, configured networking, secured public access, and troubleshot real Azure deployment issues using only Azure CLI.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;One of the best ways to learn cloud engineering is to build infrastructure the same way it is deployed in real environments.&lt;/p&gt;

&lt;p&gt;Instead of clicking through the Azure Portal, I challenged myself to provision an entire production-style environment using &lt;strong&gt;Azure CLI&lt;/strong&gt;. The objective wasn't simply to deploy virtual machines: it was to understand the deployment process, solve real-world problems, validate every resource, and document the entire journey.&lt;/p&gt;

&lt;p&gt;The final environment included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu Server running Nginx&lt;/li&gt;
&lt;li&gt;Windows Server running IIS&lt;/li&gt;
&lt;li&gt;Windows 11 client VM&lt;/li&gt;
&lt;li&gt;Shared Virtual Network&lt;/li&gt;
&lt;li&gt;Network Security Groups&lt;/li&gt;
&lt;li&gt;Public connectivity validation&lt;/li&gt;
&lt;li&gt;Complete cleanup after deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More importantly, the project involved several real Azure deployment challenges that required investigation and troubleshooting before the infrastructure was fully operational.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Objectives
&lt;/h2&gt;

&lt;p&gt;The goals of this lab were to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provision Azure infrastructure entirely with Azure CLI&lt;/li&gt;
&lt;li&gt;Deploy Linux and Windows virtual machines&lt;/li&gt;
&lt;li&gt;Configure virtual networking&lt;/li&gt;
&lt;li&gt;Deploy a Windows 11 client VM&lt;/li&gt;
&lt;li&gt;Install and configure Nginx&lt;/li&gt;
&lt;li&gt;Install and configure IIS&lt;/li&gt;
&lt;li&gt;Configure Network Security Groups&lt;/li&gt;
&lt;li&gt;Validate public connectivity&lt;/li&gt;
&lt;li&gt;Practice troubleshooting real Azure deployment issues&lt;/li&gt;
&lt;li&gt;Document the entire implementation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The deployed environment consisted of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure Resource Group&lt;/li&gt;
&lt;li&gt;Virtual Network&lt;/li&gt;
&lt;li&gt;Shared Subnet&lt;/li&gt;
&lt;li&gt;Ubuntu Server VM&lt;/li&gt;
&lt;li&gt;Windows Server VM&lt;/li&gt;
&lt;li&gt;Windows 11 Client VM&lt;/li&gt;
&lt;li&gt;Network Security Groups&lt;/li&gt;
&lt;li&gt;Nginx Web Server&lt;/li&gt;
&lt;li&gt;IIS Web Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although this is a learning project, the overall deployment closely resembles the structure used in many production Azure environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Infrastructure Deployed
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Resource Group&lt;/td&gt;
&lt;td&gt;rg-globalretail-prod-001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;West Europe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu VM&lt;/td&gt;
&lt;td&gt;vm-linux-web-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows Server VM&lt;/td&gt;
&lt;td&gt;vm-win-admin01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows 11 VM&lt;/td&gt;
&lt;td&gt;vm-win11-ha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Virtual Network&lt;/td&gt;
&lt;td&gt;vm-linux-web-01VNET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subnet&lt;/td&gt;
&lt;td&gt;vm-linux-web-01Subnet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Deployment Process
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Step 1 — Creating the Resource Group
&lt;/h2&gt;

&lt;p&gt;Everything began by creating a dedicated Resource Group to logically organize every Azure resource used throughout the project.&lt;/p&gt;

&lt;p&gt;This became the container for all virtual machines, networking resources, storage, and security components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Resource Group Created&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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




&lt;h2&gt;
  
  
  Step 2 — Deploying the Ubuntu Server
&lt;/h2&gt;

&lt;p&gt;The first virtual machine deployed was an Ubuntu Server running Ubuntu 22.04 LTS.&lt;/p&gt;

&lt;p&gt;This server would later host the Nginx web server.&lt;/p&gt;

&lt;p&gt;Deployment was completed using Azure CLI without touching the Azure Portal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Linux VM Created&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1bwws16znhjwjidgxok2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1bwws16znhjwjidgxok2.png" alt="linux vm" width="799" height="308"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Deploying Windows Server
&lt;/h2&gt;

&lt;p&gt;Next came the Windows Server virtual machine.&lt;/p&gt;

&lt;p&gt;This VM would later host Internet Information Services (IIS).&lt;/p&gt;

&lt;p&gt;The deployment wasn't completely smooth, however...&lt;/p&gt;




&lt;h1&gt;
  
  
  Real-World Troubleshooting
&lt;/h1&gt;

&lt;p&gt;One thing I appreciated about this project was that it wasn't a perfect "follow-the-lab" experience.&lt;/p&gt;

&lt;p&gt;Several real deployment issues occurred along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows Computer Name Limitation
&lt;/h2&gt;

&lt;p&gt;The initial deployment failed because the Windows computer name exceeded Microsoft's 15-character limit.&lt;/p&gt;

&lt;p&gt;After shortening the VM name, deployment continued successfully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Azure CLI Returned &lt;code&gt;ResourceNotFound&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Immediately after deployment, Azure CLI displayed a &lt;code&gt;ResourceNotFound&lt;/code&gt; error for the Windows VM.&lt;/p&gt;

&lt;p&gt;Instead of assuming the deployment had failed, I verified the environment using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az vm list &lt;span class="nt"&gt;--resource-group&lt;/span&gt; rg-globalretail-prod-001 &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The VM had actually been created successfully.&lt;/p&gt;

&lt;p&gt;This reinforced an important lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Always verify the actual Azure resource state instead of relying solely on CLI output.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Windows 11 Deployment Failed
&lt;/h2&gt;

&lt;p&gt;The Windows 11 deployment initially failed because Azure attempted to create a subnet that conflicted with the existing virtual network.&lt;/p&gt;

&lt;p&gt;The solution was to query the existing subnet and explicitly specify it during deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  SSH Authentication Failed
&lt;/h2&gt;

&lt;p&gt;Connecting to Ubuntu initially produced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Permission denied (publickey)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After investigating, I discovered the local machine did not contain an SSH key pair.&lt;/p&gt;

&lt;p&gt;Generating a new RSA key pair resolved the issue immediately.&lt;/p&gt;




&lt;h1&gt;
  
  
  Configuring the Servers
&lt;/h1&gt;

&lt;p&gt;With the infrastructure successfully deployed, the next phase involved configuring the workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ubuntu
&lt;/h2&gt;

&lt;p&gt;After connecting through SSH:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updated package repositories&lt;/li&gt;
&lt;li&gt;Installed Nginx&lt;/li&gt;
&lt;li&gt;Verified the service was running
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Windows Server
&lt;/h2&gt;

&lt;p&gt;After connecting through Remote Desktop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opened PowerShell&lt;/li&gt;
&lt;li&gt;Installed IIS
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Install-WindowsFeature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Web-Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-IncludeManagementTools&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Network Security
&lt;/h1&gt;

&lt;p&gt;Neither web server was initially reachable.&lt;/p&gt;

&lt;p&gt;The reason?&lt;/p&gt;

&lt;p&gt;HTTP traffic wasn't allowed through the Network Security Groups.&lt;/p&gt;

&lt;p&gt;After creating inbound TCP port 80 rules for both virtual machines, browser access worked immediately.&lt;/p&gt;

&lt;p&gt;This served as a good reminder that:&lt;/p&gt;

&lt;p&gt;Infrastructure deployment is only one part of cloud engineering.&lt;/p&gt;

&lt;p&gt;Networking and security configuration are equally important.&lt;/p&gt;




&lt;h1&gt;
  
  
  Validation
&lt;/h1&gt;

&lt;p&gt;The environment was validated by confirming that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH access worked correctly&lt;/li&gt;
&lt;li&gt;Remote Desktop access worked correctly&lt;/li&gt;
&lt;li&gt;Nginx was running&lt;/li&gt;
&lt;li&gt;IIS was installed successfully&lt;/li&gt;
&lt;li&gt;HTTP traffic was permitted&lt;/li&gt;
&lt;li&gt;Both web servers were accessible from a web browser&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Browser showing Nginx or IIS page&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqp710f0smpcpt1qgqnkz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqp710f0smpcpt1qgqnkz.png" alt="nginx" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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




&lt;h1&gt;
  
  
  What I Learned
&lt;/h1&gt;

&lt;p&gt;This project reinforced several important Azure concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure regions don't always have capacity for every VM size.&lt;/li&gt;
&lt;li&gt;Windows virtual machines have naming constraints.&lt;/li&gt;
&lt;li&gt;Existing VNets and subnets should be reused explicitly.&lt;/li&gt;
&lt;li&gt;SSH authentication requires both server-side and client-side configuration.&lt;/li&gt;
&lt;li&gt;Azure CLI responses should always be validated.&lt;/li&gt;
&lt;li&gt;Network Security Groups are critical for application accessibility.&lt;/li&gt;
&lt;li&gt;Cleaning up cloud resources prevents unnecessary costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly...&lt;/p&gt;

&lt;p&gt;Cloud engineering isn't simply deploying resources.&lt;/p&gt;

&lt;p&gt;It's understanding how infrastructure, networking, security, authentication, and troubleshooting all work together.&lt;/p&gt;




&lt;h1&gt;
  
  
  Source Code
&lt;/h1&gt;

&lt;p&gt;The complete project — including Azure CLI commands, deployment documentation, troubleshooting notes, and screenshots is available on GitHub:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/rahimahisah17/globalretail-prod-lab" rel="noopener noreferrer"&gt;https://github.com/rahimahisah17/globalretail-prod-lab&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;This project pushed me beyond simply following documentation.&lt;/p&gt;

&lt;p&gt;It required investigating deployment failures, validating Azure resources, solving networking issues, configuring Linux and Windows servers, and documenting every stage of the deployment process.&lt;/p&gt;

&lt;p&gt;Every challenge became an opportunity to better understand how Azure behaves in real-world scenarios.&lt;/p&gt;

&lt;p&gt;I'm looking forward to building even more production - style cloud infrastructure projects as I continue developing my Azure and cloud engineering skills.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! If you have suggestions, questions, or similar experiences deploying infrastructure with Azure CLI, I'd love to hear about them.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  azure #azurecli #cloud #cloudcomputing #devops #linux #windowsserver #nginx #iis #networking #cloudengineering
&lt;/h1&gt;

</description>
      <category>azurecli</category>
      <category>infrastructure</category>
      <category>devops</category>
      <category>linux</category>
    </item>
    <item>
      <title>From Feature Branch to Container Registry: Building a Complete CI/CD Pipeline with Docker and GitHub Actions</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Fri, 17 Jul 2026 21:17:41 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/building-a-complete-cicd-pipeline-with-docker-and-github-actions-471k</link>
      <guid>https://dev.to/rahimah_dev/building-a-complete-cicd-pipeline-with-docker-and-github-actions-471k</guid>
      <description>&lt;h1&gt;
  
  
  Building a Complete CI/CD Pipeline with Docker and GitHub Actions
&lt;/h1&gt;

&lt;p&gt;Modern software delivery is about much more than writing code. Successful engineering teams rely on automation, collaboration, repeatable workflows, and reliable delivery pipelines to move software from development to deployment.&lt;/p&gt;

&lt;p&gt;To strengthen my DevOps skills, I built a &lt;strong&gt;production-inspired CI/CD project&lt;/strong&gt; that simulates the workflow used by modern engineering teams.&lt;/p&gt;

&lt;p&gt;Rather than focusing on building a complex application, I focused on building a professional software delivery pipeline—from planning and development to automated container image publishing.&lt;/p&gt;

&lt;p&gt;This project demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git workflow&lt;/li&gt;
&lt;li&gt;Feature branch development&lt;/li&gt;
&lt;li&gt;Pull Requests&lt;/li&gt;
&lt;li&gt;Docker containerization&lt;/li&gt;
&lt;li&gt;GitHub Actions automation&lt;/li&gt;
&lt;li&gt;Continuous Integration (CI)&lt;/li&gt;
&lt;li&gt;Continuous Delivery (CD)&lt;/li&gt;
&lt;li&gt;GitHub Container Registry (GHCR)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article has been updated to include Continuous Delivery with GitHub Container Registry (GHCR), along with improvements made after the initial publication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The complete project is available on GitHub:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;GitHub Repository&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/rahimahisah17/cloud-devops-ci-cd-lab" rel="noopener noreferrer"&gt;https://github.com/rahimahisah17/cloud-devops-ci-cd-lab&lt;/a&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  Project Objectives
&lt;/h1&gt;

&lt;p&gt;The objective was to simulate a realistic DevOps workflow that mirrors how software is developed, reviewed, automated, and delivered in production environments.&lt;/p&gt;

&lt;p&gt;The completed project includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Issues for task tracking&lt;/li&gt;
&lt;li&gt;Feature branch development&lt;/li&gt;
&lt;li&gt;Pull Request-based collaboration&lt;/li&gt;
&lt;li&gt;Docker containerization&lt;/li&gt;
&lt;li&gt;GitHub Actions automation&lt;/li&gt;
&lt;li&gt;Continuous Integration&lt;/li&gt;
&lt;li&gt;Continuous Delivery&lt;/li&gt;
&lt;li&gt;GitHub Container Registry (GHCR)&lt;/li&gt;
&lt;li&gt;Comprehensive project documentation&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  Technology Stack
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;GitHub Container Registry (GHCR)&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;Linux (WSL)&lt;/li&gt;
&lt;li&gt;Visual Studio Code&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  Development Workflow
&lt;/h1&gt;

&lt;p&gt;Every feature followed a structured Git workflow before reaching the main branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub Issue
      │
      ▼
Feature Branch
      │
      ▼
Development
      │
      ▼
Commit
      │
      ▼
Push
      │
      ▼
Pull Request
      │
      ▼
GitHub Actions
      │
      ▼
Build Docker Image
      │
      ▼
Publish to GHCR
      │
      ▼
Continuous Delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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




&lt;h1&gt;
  
  
  CI/CD Architecture
&lt;/h1&gt;

&lt;p&gt;👩‍💻 Developer&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
📁 GitHub Repository&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
🌿 Feature Branch&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
🔀 Pull Request&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
⚙️ GitHub Actions&lt;br&gt;
        │&lt;br&gt;
        ├── Repository Validation&lt;br&gt;
        ├── Docker Build&lt;br&gt;
        └── Push to GHCR&lt;br&gt;
                    │&lt;br&gt;
                    ▼&lt;br&gt;
📦 GitHub Container Registry&lt;br&gt;
                    │&lt;br&gt;
                    ▼&lt;br&gt;
🚀 Deployable Container Image&lt;/p&gt;


&lt;h1&gt;
  
  
  Continuous Delivery with GitHub Container Registry
&lt;/h1&gt;

&lt;p&gt;The project initially implemented Continuous Integration by automatically validating the repository and building the Docker image.&lt;/p&gt;

&lt;p&gt;To extend the workflow into Continuous Delivery, I enhanced the GitHub Actions pipeline to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticate with GitHub Container Registry (GHCR)&lt;/li&gt;
&lt;li&gt;Tag Docker images using the GitHub Container Registry naming convention&lt;/li&gt;
&lt;li&gt;Automatically publish Docker images after successful workflow execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every successful pipeline execution now produces a deployable Docker image stored in GitHub Container Registry, making the workflow suitable for downstream deployment environments.&lt;/p&gt;

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


&lt;h1&gt;
  
  
  Containerizing the Application
&lt;/h1&gt;

&lt;p&gt;The application is packaged inside a lightweight Docker container using Nginx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.html /usr/share/nginx/html/index.html&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Containerization ensures the application behaves consistently regardless of where it runs.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  Automating CI/CD with GitHub Actions
&lt;/h1&gt;

&lt;p&gt;Every push and pull request automatically triggers GitHub Actions.&lt;/p&gt;

&lt;p&gt;The workflow performs the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validates the repository&lt;/li&gt;
&lt;li&gt;Builds the Docker image&lt;/li&gt;
&lt;li&gt;Authenticates with GitHub Container Registry&lt;/li&gt;
&lt;li&gt;Publishes the Docker image to GHCR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This eliminates manual build steps while ensuring every change follows the same automated delivery process.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  Debugging the Pipeline
&lt;/h1&gt;

&lt;p&gt;One of the most valuable aspects of this project wasn't creating the workflow—it was troubleshooting it.&lt;/p&gt;

&lt;p&gt;After migrating the application from Python to an Nginx-based static website, the CI pipeline failed because it was still validating a Python file that no longer existed.&lt;/p&gt;

&lt;p&gt;Instead of simply rerunning the workflow, I:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Investigated the workflow logs&lt;/li&gt;
&lt;li&gt;Identified the obsolete validation step&lt;/li&gt;
&lt;li&gt;Updated the workflow&lt;/li&gt;
&lt;li&gt;Verified the fix by rerunning the pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Watching the workflow transition from ❌ failed to ✅ successful reinforced an important DevOps lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CI/CD pipelines evolve alongside the applications they support.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Lessons Learned
&lt;/h1&gt;

&lt;p&gt;This project strengthened my practical understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git workflows&lt;/li&gt;
&lt;li&gt;Feature branch development&lt;/li&gt;
&lt;li&gt;Pull Requests&lt;/li&gt;
&lt;li&gt;Docker containerization&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;Continuous Integration&lt;/li&gt;
&lt;li&gt;Continuous Delivery&lt;/li&gt;
&lt;li&gt;GitHub Container Registry (GHCR)&lt;/li&gt;
&lt;li&gt;Pipeline troubleshooting&lt;/li&gt;
&lt;li&gt;Technical documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More importantly, it demonstrated that successful DevOps projects are built through continuous improvement, testing, and iteration.&lt;/p&gt;




&lt;h1&gt;
  
  
  Explore the Complete Project
&lt;/h1&gt;

&lt;p&gt;The repository has continued to evolve beyond the initial publication of this article.&lt;/p&gt;

&lt;p&gt;It now includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📘 Professional README&lt;/li&gt;
&lt;li&gt;📝 Detailed implementation log&lt;/li&gt;
&lt;li&gt;📸 Step-by-step screenshots&lt;/li&gt;
&lt;li&gt;🐳 Dockerized Nginx application&lt;/li&gt;
&lt;li&gt;⚙️ GitHub Actions CI/CD workflow&lt;/li&gt;
&lt;li&gt;🚀 Continuous Delivery to GitHub Container Registry&lt;/li&gt;
&lt;li&gt;🏷️ Official &lt;strong&gt;v1.0.0&lt;/strong&gt; release&lt;/li&gt;
&lt;li&gt;📄 MIT License&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;What began as a simple Docker containerization exercise gradually evolved into a complete CI/CD implementation using GitHub Actions and GitHub Container Registry.&lt;/p&gt;

&lt;p&gt;Beyond the technical outcome, this project strengthened my understanding of Git workflows, automation, pipeline troubleshooting, Docker, and technical documentation.&lt;/p&gt;

&lt;p&gt;More importantly, it reinforced that DevOps is not just about writing automation scripts—it's about designing reliable, repeatable engineering processes that enable teams to deliver software with confidence.&lt;/p&gt;

&lt;p&gt;I hope this walkthrough helps anyone learning DevOps by showing what an end-to-end project looks like in practice.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

&lt;p&gt;Happy learning! 🚀&lt;/p&gt;




&lt;p&gt;If you found this project helpful, feel free to ⭐ the repository, share your thoughts, or connect with me here on Dev.to. Feedback and suggestions are always welcome!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>nginx</category>
      <category>git</category>
    </item>
    <item>
      <title>Strengthening Identity Security with Conditional Access Policies in Microsoft Entra ID</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Thu, 09 Jul 2026 14:49:43 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/strengthening-identity-security-with-conditional-access-policies-in-microsoft-entra-id-5bfl</link>
      <guid>https://dev.to/rahimah_dev/strengthening-identity-security-with-conditional-access-policies-in-microsoft-entra-id-5bfl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As organizations embrace cloud-first environments, &lt;strong&gt;protecting user identities requires more than just strong passwords&lt;/strong&gt;. Conditional Access in Microsoft Entra ID enables administrators to make intelligent access decisions based on user identity, device compliance, location, application, and risk, ensuring that only trusted users can access organizational resources under the right conditions.&lt;/p&gt;

&lt;p&gt;In this hands-on guide, I'll walk through creating and configuring a Conditional Access policy, targeting specific users and cloud applications, enforcing access controls, and using the &lt;strong&gt;What If&lt;/strong&gt; tool to validate policy behavior before deployment. These practical exercises demonstrate how Conditional Access helps organizations implement Zero Trust principles while &lt;strong&gt;minimizing the risk&lt;/strong&gt; of misconfigured security policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise - Perform basic Conditional Access policy tasks
&lt;/h2&gt;

&lt;p&gt;In this exercise you will learn to create and configure a Conditional Access policy and test its results with the &lt;strong&gt;What If&lt;/strong&gt; analysis tool.&lt;/p&gt;

&lt;p&gt;This exercise should take approximately &lt;strong&gt;10&lt;/strong&gt; minutes to complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 1 - Review a Conditional Access policy
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;Conditional access&lt;/strong&gt; submenu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtsntg5ogu9n59r94n9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtsntg5ogu9n59r94n9f.png" alt="conditional access" width="799" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4.Review the information provided on the Conditional Access Overview page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - You can see how many policies you have enabled, and how many users who have logged in recently that are not covered by a policy, along with other details(if you created any).&lt;br&gt;
5.Select the &lt;strong&gt;+ Create new policy&lt;/strong&gt; from the top menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1fzqz2j4lh84kyq4iq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1fzqz2j4lh84kyq4iq6.png" alt="create policy" width="799" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6.In the &lt;strong&gt;Name&lt;/strong&gt; box enter &lt;strong&gt;Block Bhogeswar from using Sway&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;7.In the &lt;strong&gt;Assignments&lt;/strong&gt; section select &lt;strong&gt;0 users and agents selected&lt;/strong&gt; text.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;Include&lt;/strong&gt; section mark the &lt;strong&gt;Select users and groups&lt;/strong&gt; item.&lt;/li&gt;
&lt;li&gt;Put a mark in the &lt;strong&gt;Users and groups&lt;/strong&gt; box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fumodvtomylquwuavvcop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fumodvtomylquwuavvcop.png" alt="policy" width="799" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the list of users and groups opens, select &lt;strong&gt;Bhogeswar Kalita&lt;/strong&gt; from the list.&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Select&lt;/strong&gt; button to add the user.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Select the word &lt;strong&gt;Exclude&lt;/strong&gt; to switch tabs.&lt;/li&gt;
&lt;li&gt;Put a mark in the &lt;strong&gt;Users and groups&lt;/strong&gt; box.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8t43261o5x650vgun1ai.png" alt="exclude" width="800" height="516"&gt;
&lt;/li&gt;
&lt;li&gt;When the list of users and groups opens, select &lt;strong&gt;the admin user you are using&lt;/strong&gt; from the list.&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Select&lt;/strong&gt; button to exclude this user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw71zf2bup5q8jwsnpxik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw71zf2bup5q8jwsnpxik.png" alt="exclude" width="799" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3e04vsfgm68cq5lkzzfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3e04vsfgm68cq5lkzzfp.png" alt="user" width="799" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lab tip&lt;/strong&gt; - We have added a user for this policy and we have added an exclude for our admin, to make sure they are &lt;em&gt;not accidentally locked out&lt;/em&gt; by a policy change.&lt;/p&gt;

&lt;p&gt;8.Find the section &lt;strong&gt;Target resources&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;9.Select the &lt;strong&gt;No target resources selected&lt;/strong&gt; text from this section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure &lt;strong&gt;Resources (formerly cloud apps)&lt;/strong&gt; is chosen in the dropdown.&lt;/li&gt;
&lt;li&gt;Put a mark in the &lt;strong&gt;Select resources&lt;/strong&gt; item.&lt;/li&gt;
&lt;li&gt;In the section titled &lt;strong&gt;Select&lt;/strong&gt; choose the word &lt;strong&gt;None&lt;/strong&gt;.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcuimu4twxjzisqy8rc3.png" alt="sway" width="800" height="725"&gt;
&lt;/li&gt;
&lt;li&gt;When the resource selection dialog opens, enter &lt;strong&gt;Sway&lt;/strong&gt; into the search bar.&lt;/li&gt;
&lt;li&gt;Put a check in the box next to &lt;strong&gt;Sway&lt;/strong&gt; then use the &lt;strong&gt;Select&lt;/strong&gt; button to approve the choice.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Lab tip&lt;/strong&gt; - We have chosen the app Sway to be the resource this policy works on. You could choose multiple apps, or you could even choose to exclude apps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Notice the name and target resource&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7kbkdfrnie7x8vlqxry4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7kbkdfrnie7x8vlqxry4.png" alt="done" width="799" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;10.Open and review the &lt;strong&gt;Network&lt;/strong&gt; section, but we are not going to use that for this lab.&lt;/p&gt;

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

&lt;p&gt;11.Open and review the &lt;strong&gt;Conditions&lt;/strong&gt; section, but we are not using it for this lab.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Lab tip&lt;/strong&gt; - Network and Conditions are powerful options in the Conditional Access policy, but they are not required for this simple policy to block access to an app. You would use them if you wanted to do things like restrict access for user working from a specific location, or block access if a user has shown risky behavior. There are many granular options that you can choose from, or even combine together.&lt;/p&gt;

&lt;p&gt;12.Find the section titled &lt;strong&gt;Access controls&lt;/strong&gt;.&lt;br&gt;
13.Under the word &lt;strong&gt;Grant&lt;/strong&gt; select the text &lt;strong&gt;0 controls selected&lt;/strong&gt;.&lt;br&gt;
14.When the Grant dialog opens, select the &lt;strong&gt;Block access&lt;/strong&gt; item.&lt;/p&gt;

&lt;p&gt;15.Use the &lt;strong&gt;Select&lt;/strong&gt; button to accept the change.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Lab tip&lt;/strong&gt; - You have not set the values needed to create a simple block access policy to the Sway app for Bhogeswar. In the Access controls section, we could have allowed access with specific conditions like MFA or a domain joined device.&lt;br&gt;
16.At the bottom of the &lt;strong&gt;Conditional Access&lt;/strong&gt; interface, find the &lt;strong&gt;Enable policy&lt;/strong&gt; item.&lt;br&gt;
17.Set the value to &lt;strong&gt;On&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;18.Select the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzvrjyd27x8w8e40zwo6d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzvrjyd27x8w8e40zwo6d.png" alt="select" width="752" height="878"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lab tip&lt;/strong&gt; - You have three options to pick from, and you can change value at any time, without having to change the policy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On = enables the policy to run&lt;/li&gt;
&lt;li&gt;Off = turn the policy off in case you need to modify it.&lt;/li&gt;
&lt;li&gt;Report-only = use this option to review the outcome of the policy before you fully turn it on.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Task 2 - Perform a What If analysis for a conditional access policy
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;Conditional access&lt;/strong&gt; submenu. &lt;br&gt;
4.From the Conditional access screen select the &lt;strong&gt;What if&lt;/strong&gt; option from the top menu.&lt;/p&gt;

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

&lt;p&gt;5.In the section &lt;strong&gt;User or Workload identity&lt;/strong&gt; select the text &lt;strong&gt;No user or service principal selected&lt;/strong&gt;.&lt;br&gt;
6.Make sure the box next to &lt;strong&gt;User&lt;/strong&gt; is marked.&lt;br&gt;
7.Select the text &lt;strong&gt;No user selected&lt;/strong&gt; or &lt;strong&gt;Edit user&lt;/strong&gt;.&lt;br&gt;
8.From the Users list, put a mark next to &lt;strong&gt;Bhogeswar Kalita&lt;/strong&gt;, then use the &lt;strong&gt;Select&lt;/strong&gt; button.&lt;/p&gt;

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

&lt;p&gt;9.In the &lt;strong&gt;Cloud apps, actions, or authentication context&lt;/strong&gt;, select the text &lt;strong&gt;Any cloud app&lt;/strong&gt;.&lt;br&gt;
10.When the &lt;strong&gt;Select target type&lt;/strong&gt; dialog opens, confirm that &lt;strong&gt;Cloud apps&lt;/strong&gt; is chosen in the dropdown.&lt;br&gt;
11.Click on the &lt;strong&gt;+ Select cloud app&lt;/strong&gt;.&lt;br&gt;
12.Use the search to find the &lt;strong&gt;Sway&lt;/strong&gt; app, then use the &lt;strong&gt;Select&lt;/strong&gt; botton to finalize.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnoq6xr63knr4bww7q5pe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnoq6xr63knr4bww7q5pe.png" alt="select" width="799" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;13.Scroll down to the bottom of the What If tool.&lt;/p&gt;

&lt;p&gt;14.Select the button &lt;strong&gt;What If&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - the results appear off the bottom of the screen, so you will need to scroll down again.&lt;br&gt;
15.You should see the &lt;strong&gt;Block Bhogeswar from using Sway&lt;/strong&gt; policy listed, and it is blocking access.&lt;/p&gt;

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

&lt;p&gt;16.You have to make selections for Device platform and Client app under &lt;strong&gt;sign-in conditions&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmye9oscqwz3jkyxcte19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmye9oscqwz3jkyxcte19.png" alt="policy" width="799" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtask 1 - Try a different app in the What If analysis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Scroll back to the top of the &lt;strong&gt;What If&lt;/strong&gt; tool.&lt;br&gt;
2.In the &lt;strong&gt;Cloud apps, actions, or authentication context&lt;/strong&gt;, select delete next to &lt;strong&gt;Sway&lt;/strong&gt; and remove it..&lt;/p&gt;

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

&lt;p&gt;3.Then select &lt;strong&gt;select cloud app&lt;/strong&gt; to pick a new app.&lt;br&gt;
4.Search for &lt;strong&gt;Office 365 Reports&lt;/strong&gt; and choose it from the list, then use the &lt;strong&gt;Select&lt;/strong&gt; button.&lt;/p&gt;

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

&lt;p&gt;5.Scroll down to the bottom again.&lt;br&gt;
6.Select the &lt;strong&gt;What If&lt;/strong&gt; button.&lt;br&gt;
7.You should see that the &lt;strong&gt;Block Bhoseswar from using Sway&lt;/strong&gt; policy did not apply.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I demonstrated how to create and configure Conditional Access policies in Microsoft Entra ID, assign policies to specific users and cloud applications, define access controls, and validate policy behavior using the What If analysis tool. By testing policies before enforcement, administrators can confidently implement secure access controls, reduce the risk of unintended user lockouts, and strengthen their organization's identity security posture through a Zero Trust approach.&lt;/p&gt;

</description>
      <category>zerotrust</category>
      <category>microsoft</category>
      <category>security</category>
      <category>azure</category>
    </item>
    <item>
      <title>Securing User Identities with Multi-Factor Authentication (MFA) in Microsoft Entra ID</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Wed, 08 Jul 2026 21:54:23 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/securing-user-identities-with-multi-factor-authentication-mfa-in-microsoft-entra-id-20m0</link>
      <guid>https://dev.to/rahimah_dev/securing-user-identities-with-multi-factor-authentication-mfa-in-microsoft-entra-id-20m0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Passwords alone are no longer enough to protect user accounts from today's evolving cyber threats. &lt;strong&gt;Multi-Factor Authentication (MFA)&lt;/strong&gt; adds an &lt;strong&gt;essential layer of security&lt;/strong&gt; by requiring users to verify their identity using multiple authentication factors, significantly reducing the risk of unauthorized access caused by compromised credentials.&lt;/p&gt;

&lt;p&gt;In this hands-on guide, I'll walk through configuring Multi-Factor Authentication in Microsoft Entra ID, including enabling and disabling per-user MFA, reviewing MFA service settings, and configuring account lockout policies. These practical exercises demonstrate how to strengthen identity security while balancing usability and administrative control in a modern cloud environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise - Perform basic Multifactor Authentication tasks
&lt;/h2&gt;

&lt;p&gt;In this exercise you will explore the MFA setup process and configure a basic MFA deployment.&lt;/p&gt;

&lt;p&gt;This exercise should take approximately &lt;strong&gt;10&lt;/strong&gt; minutes to complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 1 - Enable / Disable Per-user MFA settings
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, open the &lt;strong&gt;Identity&lt;/strong&gt; submenu&lt;br&gt;
4.Select &lt;strong&gt;Users&lt;/strong&gt; and then select &lt;strong&gt;All users&lt;/strong&gt;.&lt;br&gt;
5.Look at the menu on the top of the new screen and find &lt;strong&gt;Per-user MFA&lt;/strong&gt;.&lt;br&gt;
6.Select &lt;strong&gt;Per-user MFA&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frc04nfl63iwrednr2no2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frc04nfl63iwrednr2no2.png" alt="per-user mfa" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7.Put a mark in the box next to &lt;strong&gt;Bhogeswar Kalita&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;8.Select &lt;strong&gt;Enable MFA&lt;/strong&gt; from the top of the list.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsttf251pvo0dhh2kayul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsttf251pvo0dhh2kayul.png" alt="2mfa" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;9.Read the message box that pops up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - This is a manual URL that you can email to the user, if you want to let them know to set up MFA.&lt;/p&gt;

&lt;p&gt;10.Select &lt;strong&gt;Enable&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx9ugmvji4gjf05hsam6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx9ugmvji4gjf05hsam6q.png" alt="enable" width="799" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;11.Then select the &lt;strong&gt;Enforce&lt;/strong&gt; button.&lt;/p&gt;

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

&lt;p&gt;12.Note that after a few seconds &lt;strong&gt;enforced&lt;/strong&gt; shows up next to Bhogeswar’s name.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Task 2 - Review the Service settings for MFA
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, open the &lt;strong&gt;Identity&lt;/strong&gt; submenu.&lt;br&gt;
4.Select &lt;strong&gt;Users&lt;/strong&gt; and then select &lt;strong&gt;All users&lt;/strong&gt;.&lt;br&gt;
5.Select &lt;strong&gt;Per-user MFA&lt;/strong&gt; item.&lt;/p&gt;

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

&lt;p&gt;6.Select &lt;strong&gt;Service settings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fth5phfun5d8kty04nupx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fth5phfun5d8kty04nupx.png" alt="service setting" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7.Review the settings you can configure:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Setting&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What is it for&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;App passwords&lt;/td&gt;
&lt;td&gt;There are some legacy apps that won’t work with MFA, use this setting to allow a user to keep using them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trusted IPs&lt;/td&gt;
&lt;td&gt;If your company has a specifc set of IP-address ranges that are always known safe, you can allow users to by-pass MFA the logging in.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification options&lt;/td&gt;
&lt;td&gt;Select the methods you are willing to allow users to use for their second factor of authentication.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remember multifactor authentication on trusted device&lt;/td&gt;
&lt;td&gt;If your users are using a device that you trust, like a company managed laptop, you can allow them to retain thier MFA approved status for a specified number of days.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;8.Select the &lt;strong&gt;Discard&lt;/strong&gt; button if you made any changes. &lt;strong&gt;Save&lt;/strong&gt; if you want to deliberately make changes.&lt;br&gt;
This is one method of configuring MFA for your users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7i0mmmdem0b5odrad129.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7i0mmmdem0b5odrad129.png" alt="mfa" width="799" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 3 - View MFA account lockout settings
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, open the &lt;strong&gt;Protection&lt;/strong&gt; submenu.&lt;br&gt;
4.Scroll to the bottom of the list and select &lt;strong&gt;Show more&lt;/strong&gt;.&lt;br&gt;
5.Select &lt;strong&gt;Multifactor authentication&lt;/strong&gt; from the menu.&lt;br&gt;
6.Select &lt;strong&gt;Account lockout&lt;/strong&gt; from the menu.&lt;br&gt;
7.Set the following lockout values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Number of MFA denials to trigger account lockout&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minutes until account lockout counter is reset&lt;/td&gt;
&lt;td&gt;180&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minutes until account is automatically unblocked&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;8.Select the &lt;strong&gt;Save&lt;/strong&gt; option at the top of your screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv3zychvsjaif5x8voww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv3zychvsjaif5x8voww.png" alt="mfa setting" width="799" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Review some of the other configuration options you have on MFA like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fraud alert&lt;/li&gt;
&lt;li&gt;Block / unblock users&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I explored the core aspects of configuring Multi-Factor Authentication (MFA) in Microsoft Entra ID. I demonstrated how to enable and manage per-user MFA, review and customize MFA service settings, and configure account lockout policies to protect against repeated unauthorized sign-in attempts. By implementing these security measures, organizations can significantly improve identity protection, reduce the risk of account compromise, and build a more resilient authentication framework for their cloud environments.&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>azure</category>
      <category>identity</category>
      <category>azureadmin</category>
    </item>
    <item>
      <title>Mastering Self-Service Password Reset (SSPR) in Microsoft Entra ID</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Wed, 08 Jul 2026 18:59:21 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/mastering-self-service-password-reset-sspr-in-microsoft-entra-id-4m4g</link>
      <guid>https://dev.to/rahimah_dev/mastering-self-service-password-reset-sspr-in-microsoft-entra-id-4m4g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Forgotten passwords are one of the most common reasons users contact IT support, often resulting in lost productivity and increased administrative overhead. &lt;strong&gt;Self-Service Password Reset (SSPR)&lt;/strong&gt; in Microsoft Entra ID empowers users to securely reset or unlock their accounts without requiring help desk assistance, &lt;strong&gt;improving both user experience and organizational security&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this hands-on guide, I'll demonstrate how to configure Self-Service Password Reset in Microsoft Entra ID by enabling SSPR for specific users or groups, configuring authentication methods, managing registration settings, and customizing notification options. These configurations help organizations streamline identity management while maintaining strong security practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise - Perform basic Self-Service Password Rest (SSPR) tasks
&lt;/h2&gt;

&lt;p&gt;In this exercise you will learn where the self-service password reset feature is located in Microsoft Entra and explore the process to configure it.&lt;/p&gt;

&lt;p&gt;This exercise should take approximately &lt;strong&gt;15&lt;/strong&gt; minutes to complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 1 - View the SSPR properties for a specific group
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, open &lt;strong&gt;Password reset&lt;/strong&gt; submenu.&lt;/p&gt;

&lt;p&gt;4.Find the &lt;strong&gt;Self service password enabled&lt;/strong&gt; bar.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What it means&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;No users can use the SSPR feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selected&lt;/td&gt;
&lt;td&gt;Only members of the selected group(s) can use SSPR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;All users can use the SSPR feature&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;5.Set the value to &lt;strong&gt;Selected&lt;/strong&gt;.&lt;br&gt;
6.Select the text &lt;strong&gt;No groups selected&lt;/strong&gt;.&lt;br&gt;
7.Mark the &lt;strong&gt;Project233&lt;/strong&gt; group we created previously.&lt;br&gt;
8.Use the &lt;strong&gt;Select&lt;/strong&gt; button to complete your choice.&lt;/p&gt;

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

&lt;p&gt;9.&lt;strong&gt;Save&lt;/strong&gt; your configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsee7xtk7ejgvr0yhcss0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsee7xtk7ejgvr0yhcss0.png" alt="save" width="799" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtask 1 - View SSPR authentication methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Select &lt;strong&gt;Authentication methods&lt;/strong&gt; from the menu within &lt;strong&gt;Password reset&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2.Select &lt;strong&gt;1&lt;/strong&gt; as the value for &lt;strong&gt;Number of methods required to reset&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - this value represents how many different ways the user is required to sign-in to the password reset tool, before they are allowed to reset their password. Note that using a password is not an option.&lt;/p&gt;

&lt;p&gt;3.Select &lt;strong&gt;Email&lt;/strong&gt;, &lt;strong&gt;Mobile phone&lt;/strong&gt;, and &lt;strong&gt;Mobile app code&lt;/strong&gt; for &lt;strong&gt;Methods available to user&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - if you use Security Questions you have to specify the number of questions the user is required to create and answer.&lt;br&gt;
4.Use the button at the top to &lt;strong&gt;Save&lt;/strong&gt; your choices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmb9qd8pzcus6xe7lfojr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmb9qd8pzcus6xe7lfojr.png" alt="pwd reset" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5.You have added an Authentication method to your self-service password reset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtask 2 - View SSPR registration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Select &lt;strong&gt;Registration&lt;/strong&gt; from the menu within &lt;strong&gt;Password reset&lt;/strong&gt;.&lt;br&gt;
2.Make sure the &lt;strong&gt;Require users to register when signing in?&lt;/strong&gt; value is set to &lt;strong&gt;Yes.&lt;/strong&gt;&lt;br&gt;
3.Set the &lt;strong&gt;Number of days before users are asked to re-confirm…&lt;/strong&gt; to &lt;strong&gt;90&lt;/strong&gt; days.&lt;br&gt;
4.Select &lt;strong&gt;Save&lt;/strong&gt; to save your changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9wipnsoax4avstdu554w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9wipnsoax4avstdu554w.png" alt="reg" width="799" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtask 3 - View SSPR reset notifications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Select &lt;strong&gt;Notifications&lt;/strong&gt; from the menu within &lt;strong&gt;Password reset&lt;/strong&gt;.&lt;br&gt;
2.Leave the &lt;strong&gt;Notify users on password reset?&lt;/strong&gt; at the default of &lt;strong&gt;Yes&lt;/strong&gt;.&lt;br&gt;
3.Change the &lt;strong&gt;Notify all admins when other admins reset their password?&lt;/strong&gt; value to &lt;strong&gt;Yes&lt;/strong&gt;.&lt;br&gt;
4.Use the &lt;strong&gt;Save&lt;/strong&gt; option to save your changes.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I explored the configuration of Self-Service Password Reset (SSPR) in Microsoft Entra ID, including enabling the feature for selected groups, configuring authentication methods, enforcing user registration, and managing password reset notifications. By implementing these settings, organizations can reduce help desk workload, improve operational efficiency, and provide users with a secure and convenient way to regain access to their accounts while strengthening overall identity security.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>azureadmin</category>
      <category>security</category>
      <category>identity</category>
    </item>
    <item>
      <title>Strengthening Identity Security with Microsoft Entra ID Password Protection</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Wed, 08 Jul 2026 17:24:21 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/strengthening-identity-security-with-microsoft-entra-id-password-protection-3f5g</link>
      <guid>https://dev.to/rahimah_dev/strengthening-identity-security-with-microsoft-entra-id-password-protection-3f5g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Passwords remain one of the most common attack vectors in today's threat landscape, making strong password policies a critical component of &lt;strong&gt;identity security&lt;/strong&gt;. Microsoft Entra ID provides &lt;strong&gt;built-in password protection capabilities&lt;/strong&gt; that help organizations defend against brute-force attacks, password spraying, and the use of weak or commonly compromised passwords.&lt;/p&gt;

&lt;p&gt;In this hands-on guide, I'll walk through configuring essential password protection settings in Microsoft Entra ID, including custom smart lockout policies, banned password lists, and enforcement options. These configurations help strengthen authentication security while improving the overall resilience of your organization's identity infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise - Perform basic Password Protection tasks
&lt;/h2&gt;

&lt;p&gt;In this exercise you will explore the capabilities Microsoft Entra ID offers in protecting your password. You will see how you can automate and enforce a strong password, and use login restrictions to prevent password attacks.&lt;/p&gt;

&lt;p&gt;This exercise should take approximately &lt;strong&gt;5&lt;/strong&gt; minutes to complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 1 - View lock settings, and review duration and threshold values
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;Authentication methods&lt;/strong&gt; submenu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - You could also search on &lt;strong&gt;Password protection&lt;/strong&gt; in the search bar at the top.&lt;/p&gt;

&lt;p&gt;4.Select &lt;strong&gt;Password protection&lt;/strong&gt; from the list.&lt;br&gt;
5.Set the &lt;strong&gt;Custom smart lockout&lt;/strong&gt; with the following values.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lockout threshold&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;How many times can you fail to login with a password before your account locks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lockout duration&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;How many seconds the account should lock when the threshold is reached.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - You can also configure a custom banned password list here.&lt;/p&gt;

&lt;p&gt;6.Set &lt;strong&gt;Enforce custom list&lt;/strong&gt; to &lt;strong&gt;Yes&lt;/strong&gt;.&lt;br&gt;
7.Enter the following values:&lt;/p&gt;

&lt;p&gt;-Contoso&lt;br&gt;
 -London&lt;br&gt;
 -Widget&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lab Tip&lt;/strong&gt; - Your lab is being performed for a company called Contoso, located in London, and it makes Widgets. By enter these three words you block them from being part or a whole of a password.&lt;/p&gt;

&lt;p&gt;8.Set the value for &lt;strong&gt;Mode&lt;/strong&gt; to &lt;strong&gt;Enforced&lt;/strong&gt;.&lt;br&gt;
9.Select the &lt;strong&gt;Save&lt;/strong&gt; item at the top of the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkr0iqmmy1iwkmfvtlmbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkr0iqmmy1iwkmfvtlmbh.png" alt="pwd protection" width="799" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I demonstrated how to configure password protection in Microsoft Entra ID by reviewing &lt;em&gt;smart lockout settings, defining lockout thresholds and durations, creating a custom banned password list, and enforcing password protection policies&lt;/em&gt;. These security measures help reduce the risk of unauthorized access by preventing weak passwords and limiting password-based attacks, making them an essential part of any modern identity security strategy.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>azureadmin</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Mastering Microsoft Entra ID: Managing Users, Groups, Dynamic Membership, and Licensing</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:54:40 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/mastering-microsoft-entra-id-managing-users-groups-dynamic-membership-and-licensing-536g</link>
      <guid>https://dev.to/rahimah_dev/mastering-microsoft-entra-id-managing-users-groups-dynamic-membership-and-licensing-536g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In today's cloud-first world, identity has become the new security perimeter. Managing users is no longer just about creating accounts, &lt;strong&gt;it's about organizing identities efficiently, automating access, and simplifying administration at scale.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this hands-on guide, I'll walk through essential Microsoft Entra ID group management tasks, including creating Microsoft 365 and Security groups, configuring dynamic membership rules, adding users and licenses to groups, and exploring how group-based licensing simplifies administration. Whether you're preparing for the AZ-104 exam or building real-world Azure administration skills, these are practical tasks every cloud administrator should know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise - Perform basic Group Management tasks
&lt;/h2&gt;

&lt;p&gt;In this exercise you will explore the beginner level tasks associated with creating and using groups. You make a new group and assign members and set some basic access levels.&lt;/p&gt;

&lt;p&gt;This exercise should take approximately &lt;strong&gt;15&lt;/strong&gt; minutes to complete.&lt;br&gt;
&lt;strong&gt;Task 1 - Create a Microsoft 365 group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;Groups&lt;/strong&gt; and then &lt;strong&gt;All groups&lt;/strong&gt;.&lt;br&gt;
4.Select &lt;strong&gt;New group&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxi8rmg1a8wxvbg5bsbc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxi8rmg1a8wxvbg5bsbc.png" alt="groups" width="799" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5.Enter the requested information:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Group type&lt;/td&gt;
&lt;td&gt;Microsoft 365&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group name&lt;/td&gt;
&lt;td&gt;Project233&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group description&lt;/td&gt;
&lt;td&gt;This group consists of members of the new AI Simulation software with codename Project233&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Membership type&lt;/td&gt;
&lt;td&gt;Assigned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;6.Under the &lt;strong&gt;Members&lt;/strong&gt; heading, select &lt;strong&gt;No member selected&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrhix3m9ogjy7v7rba5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrhix3m9ogjy7v7rba5o.png" alt="group" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7.Add &lt;strong&gt;Bhogeswar Kalita&lt;/strong&gt;, by marking the box and choosing the &lt;strong&gt;Select&lt;/strong&gt; button.&lt;br&gt;
8.Select the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupc8a7bhbeo5jwmi60oc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupc8a7bhbeo5jwmi60oc.png" alt="select" width="799" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Task 2 - Create a Security group
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;Groups&lt;/strong&gt; and then &lt;strong&gt;All groups&lt;/strong&gt;.&lt;br&gt;
4.Select &lt;strong&gt;New group&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy4ywe2g40868g697n8ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy4ywe2g40868g697n8ae.png" alt="new group" width="799" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5.Enter the requested information:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Group type&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group name&lt;/td&gt;
&lt;td&gt;Guest Users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group description&lt;/td&gt;
&lt;td&gt;This group has all the Guest users&lt;br&gt;currently in the tenant.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Membership type&lt;/td&gt;
&lt;td&gt;Dynamic User&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;6.Under the &lt;strong&gt;Dynamic user members&lt;/strong&gt; heading, select &lt;strong&gt;Add dynamic query&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;7.Create a dynamic query with the following values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Property&lt;/td&gt;
&lt;td&gt;userType&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operator&lt;/td&gt;
&lt;td&gt;Equals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value&lt;/td&gt;
&lt;td&gt;Guest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;8.Select the &lt;strong&gt;Save&lt;/strong&gt; item from the top of the page.&lt;/p&gt;

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

&lt;p&gt;9.Select the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - It will take up to 2 minutes for the group to be populate.&lt;br&gt;
10.From the &lt;strong&gt;Groups&lt;/strong&gt; select the &lt;strong&gt;Refresh&lt;/strong&gt; item.&lt;br&gt;
11.Select the &lt;strong&gt;Guest Users&lt;/strong&gt; group we just created.&lt;br&gt;
12.Select &lt;strong&gt;Members&lt;/strong&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Task 3 - Add an existing user to a new group
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;Groups&lt;/strong&gt; and then &lt;strong&gt;All groups&lt;/strong&gt;.&lt;br&gt;
4.Select the &lt;strong&gt;Project233&lt;/strong&gt; group we created in Task 1.&lt;br&gt;
5.Select the &lt;strong&gt;Members&lt;/strong&gt; option from the menu.&lt;/p&gt;

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

&lt;p&gt;6.Select &lt;strong&gt;+ Add members&lt;/strong&gt; from the menu near the top.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsg5ailp5oczxo7se3sov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsg5ailp5oczxo7se3sov.png" alt="+members" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7.Mark the box next to &lt;strong&gt;External User&lt;/strong&gt; and use the &lt;strong&gt;Select&lt;/strong&gt; button.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zq4el1kdhmohrcxwlvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zq4el1kdhmohrcxwlvu.png" alt="verify" width="799" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtask 1 - Alternative method to add an existing user to a new group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Open the &lt;strong&gt;Users&lt;/strong&gt; menu, then select &lt;strong&gt;All users&lt;/strong&gt;.&lt;br&gt;
2.Find and select &lt;strong&gt;External User&lt;/strong&gt; from the list.&lt;br&gt;
3.Select the &lt;strong&gt;Groups&lt;/strong&gt; item from the menu.&lt;br&gt;
4.Select the option to &lt;strong&gt;+ Add memberships&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjy6d8d5f5h248327n1p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjy6d8d5f5h248327n1p4.png" alt="+member" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5.Mark an existing group and use the &lt;strong&gt;Select&lt;/strong&gt; button to add them.&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  Task 4 - Add licenses and owners to a group
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;Groups&lt;/strong&gt; and then &lt;strong&gt;All groups&lt;/strong&gt;.&lt;br&gt;
4.Select the &lt;strong&gt;Project23&lt;/strong&gt; group we created in Task 1.&lt;/p&gt;

&lt;p&gt;5.Choose the &lt;strong&gt;Owners&lt;/strong&gt; option from the menu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - Your tenant administrator will automatically be added as the owner of a group, if no owner is specified.&lt;br&gt;
6.Select the &lt;strong&gt;+ Add owners&lt;/strong&gt; menu item.&lt;br&gt;
7.Mark the box next to &lt;strong&gt;Bhogeswar&lt;/strong&gt; and use the &lt;strong&gt;Select&lt;/strong&gt; button.&lt;br&gt;
8.Notice the new owner has been added to the group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtask 1 - Adding a license to a group&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Open a new tab in your browser.&lt;br&gt;
2.Connect to Microsoft 365 admin cetner at &lt;a href="https://admin.microsoft.com" rel="noopener noreferrer"&gt;https://admin.microsoft.com&lt;/a&gt;.&lt;br&gt;
3.From the menu on the left, open the &lt;strong&gt;Billing&lt;/strong&gt; section and select &lt;strong&gt;Licenses&lt;/strong&gt;.&lt;br&gt;
4.Select the &lt;strong&gt;Microsoft Power Automate Free&lt;/strong&gt; license.&lt;/p&gt;

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

&lt;p&gt;5.Select the &lt;strong&gt;+ Assign licenses&lt;/strong&gt; item.&lt;/p&gt;

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

&lt;p&gt;7.Use the dropdown on the right side of the screen to select the &lt;strong&gt;Project 233&lt;/strong&gt; group created earlier.&lt;br&gt;
&lt;strong&gt;Note&lt;/strong&gt;: I I unassigned license to a group and created a security group called Project 233, which I assigned the license to. You cannot assign a Microsoft Power Automate Free license (or other Microsoft 365/Entra group-based licenses) to a Microsoft 365 group.&lt;br&gt;
8.Use the &lt;strong&gt;Assign licenses&lt;/strong&gt; button at the bottom of the screen.&lt;/p&gt;

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

&lt;p&gt;9.Close the &lt;strong&gt;Your licenses are…&lt;/strong&gt; notification.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqm57hx5dnamf9abuze9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqm57hx5dnamf9abuze9i.png" alt="notification" width="800" height="442"&gt;&lt;/a&gt;&lt;br&gt;
10.Use the &lt;strong&gt;Refresh&lt;/strong&gt; button to see the added group licenses.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, I explored the fundamentals of group management in Microsoft Entra ID through a series of practical, hands-on exercises. I demonstrated how to create both Microsoft 365 and Security groups, configure dynamic user membership using rules, add users to existing groups, and assign Microsoft licenses through group-based licensing. Along the way, I also highlighted the differences between Microsoft 365 groups and Security groups, including why only Security groups support group-based licensing. These are essential identity and access management skills for Azure administrators and anyone preparing for real-world cloud administration or the AZ-104 certification.&lt;/p&gt;

</description>
      <category>azureadmin</category>
      <category>identity</category>
      <category>security</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Identity as the Perimeter: Scaling User Provisioning, RBAC, and Bulk Automation in Microsoft Entra ID</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Wed, 08 Jul 2026 11:41:39 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/identity-as-the-perimeter-scaling-user-provisioning-rbac-and-bulk-automation-in-microsoft-entra-50bd</link>
      <guid>https://dev.to/rahimah_dev/identity-as-the-perimeter-scaling-user-provisioning-rbac-and-bulk-automation-in-microsoft-entra-50bd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In modern cloud architecture, &lt;strong&gt;identity&lt;/strong&gt; is the new security perimeter. As an infrastructure expands, managing access manually isn't just inefficient, &lt;strong&gt;it’s a massive security risk&lt;/strong&gt;. Engineers must be able to scale user provisioning while maintaining strict governance and the principle of least privilege.&lt;/p&gt;

&lt;p&gt;In this article, I break down a hands-on walkthrough of cloud identity engineering using Microsoft Entra ID. We will look past basic configurations to explore the real-world operational workflows every team needs: handling secure B2B guest invitations, mapping enterprise licensing, implementing granular administrative roles, and utilizing bulk automation pipelines to provision users at scale.&lt;/p&gt;

&lt;p&gt;Here is the blueprint for building efficient, production-ready identity infrastructure in the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise - Perform basic User Management tasks
&lt;/h2&gt;

&lt;p&gt;In this exercise you will explore the beginner tasks of an Identity and Access administrator by creating and managing a user in Microsoft Entra ID. There are five common activities you will perform in your lab from creating a new user to assigning them roles and licenses.&lt;/p&gt;

&lt;p&gt;This exercise should take approximately &lt;strong&gt;10&lt;/strong&gt; minutes to complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 1 - Create a new user
&lt;/h2&gt;

&lt;p&gt;1.Open the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.Log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, select &lt;strong&gt;User&lt;/strong&gt; and then &lt;strong&gt;All users&lt;/strong&gt;.&lt;br&gt;
4.Select &lt;strong&gt;+ New user&lt;/strong&gt; and then pick the &lt;strong&gt;Create new user&lt;/strong&gt; option.&lt;/p&gt;

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

&lt;p&gt;5.Enter the requested information:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User principal name&lt;/td&gt;
&lt;td&gt;BhogeswarK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Display name&lt;/td&gt;
&lt;td&gt;Bhogeswar Kalita&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;6.Leave the &lt;strong&gt;Mail nickname&lt;/strong&gt; as is.&lt;br&gt;
7.Make a copy of the &lt;strong&gt;Password&lt;/strong&gt; (auto-generated) and store it in &lt;strong&gt;Notepad&lt;/strong&gt;. You will need it later.&lt;br&gt;
8.Leave Accounted enabled checked.&lt;br&gt;
9.Select the Properties tab.&lt;/p&gt;

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

&lt;p&gt;10.Set the values for first and last name:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;First name&lt;/td&gt;
&lt;td&gt;Bhogeswar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last name&lt;/td&gt;
&lt;td&gt;Kalita&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;11.Note that you can set the &lt;strong&gt;User type&lt;/strong&gt; to &lt;strong&gt;Member&lt;/strong&gt;. If you pick Guest, it will restrict the users capabilities.&lt;/p&gt;

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

&lt;p&gt;12.Scroll down to the bottom of the page.&lt;br&gt;
13.Set the &lt;strong&gt;Usage location&lt;/strong&gt; to &lt;strong&gt;United States&lt;/strong&gt; or to whatever region you are in.&lt;br&gt;
14.Select the &lt;strong&gt;Review + create&lt;/strong&gt; button.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmw8ufb8wicunz4q3xmbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmw8ufb8wicunz4q3xmbn.png" alt="r n c" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
15.Select &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Subtask 1 - Log in to confirm user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Open an InPrivate browsing window.&lt;br&gt;
2.Connect to the Microsoft Entra admin center at the link &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
3.Enter the username and password you just created.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Username&lt;/td&gt;
&lt;td&gt;BhogeswarK@&lt;code&gt;your tenant name&lt;/code&gt;.onmicrosoft.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Auto generated password you saved&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdismjo0gw9u3dlw1kq2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdismjo0gw9u3dlw1kq2c.png" alt="tenant's det" width="768" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4.If prompted follow the on-screen instructions to complete MFA setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgtv4n2xh28w7sfnn782.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgtv4n2xh28w7sfnn782.png" alt="2MFA" width="713" height="761"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5.Feel free to explore the Microsoft Entra admin center for a minute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ndmyyc1t6jckywpy3lx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ndmyyc1t6jckywpy3lx.png" alt="SIGNED IN" width="799" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6.Close the InPrivate browsing window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 2 - Add a license to the user
&lt;/h2&gt;

&lt;p&gt;1.Open a new tab in your browser.&lt;br&gt;
2.Connect to the Microsoft 365 admin center at https//:admin.microsoft.com.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqd2ap6uojojivzo3rtpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqd2ap6uojojivzo3rtpd.png" alt="M365" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3.If prompted, log in using the credentials for your tenant.&lt;br&gt;
4.Find the menu on the left side of the screen.&lt;br&gt;
5.Open the &lt;strong&gt;Billing&lt;/strong&gt; item in the menu.&lt;br&gt;
6.Select &lt;strong&gt;Licenses&lt;/strong&gt; from the list.&lt;br&gt;
7.When the list of licenses appears, select &lt;strong&gt;Microsoft Power Automate Free&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;8.Select the &lt;strong&gt;+ Assign licenses&lt;/strong&gt; item.&lt;/p&gt;

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

&lt;p&gt;9.Choose &lt;strong&gt;Brogeswar&lt;/strong&gt; from the list (user created in previous task).&lt;br&gt;
10.Select the &lt;strong&gt;Assign licenses&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

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

&lt;p&gt;11.Notice the message that you have successfully assigned a license.&lt;/p&gt;

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

&lt;p&gt;12.Close the tab with &lt;strong&gt;Microsoft 365 admin center&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 3 - Invite an external user to your tenant
&lt;/h2&gt;

&lt;p&gt;1.If not already open - Open a browser and connect to the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.If prompted, log in using the credentials for your tenant.&lt;br&gt;
3.From the menu on the left, open the &lt;strong&gt;Identity&lt;/strong&gt; menu.&lt;br&gt;
4.Select &lt;strong&gt;Users&lt;/strong&gt; then select &lt;strong&gt;All users&lt;/strong&gt;.&lt;br&gt;
5.At the top of the page select &lt;strong&gt;+ New User&lt;/strong&gt;.&lt;br&gt;
6.Choose the option to &lt;strong&gt;Invite external user&lt;/strong&gt; from the dropdown.&lt;/p&gt;

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

&lt;p&gt;7.Enter the information for your new external user:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ExtUser@testemail.com"&gt;ExtUser@testemail.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Display name&lt;/td&gt;
&lt;td&gt;External User&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message&lt;/td&gt;
&lt;td&gt;Thank you for joining the company for this short work project. We look forward to building this project together.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;8.Select the &lt;strong&gt;Review + Invite&lt;/strong&gt; button.&lt;br&gt;
9.Select &lt;strong&gt;Invite&lt;/strong&gt;.&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  Task 4 - Assign a role to a user
&lt;/h2&gt;

&lt;p&gt;1.If not already open - Open a browser and connect to the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;..&lt;br&gt;
2.If prompted, log in using the credentials for your tenant.&lt;br&gt;
3.In the left menu find the &lt;strong&gt;Identity&lt;/strong&gt; section. Open it if not already opened.&lt;br&gt;
4.Select &lt;strong&gt;Users&lt;/strong&gt; then select &lt;strong&gt;All users&lt;/strong&gt;.&lt;br&gt;
5.Choose the user &lt;strong&gt;Bhogeswar Kalita&lt;/strong&gt; create in the earlier task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0zmwafm99otgza6wg8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0zmwafm99otgza6wg8d.png" alt="kalita" width="799" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6.From the newly opened menu select &lt;strong&gt;Assigned roles&lt;/strong&gt;.&lt;br&gt;
7.At the top of the screen select &lt;strong&gt;+ Add assignments&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwv4jc9wer8nbh7u6fvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwv4jc9wer8nbh7u6fvm.png" alt="assign role" width="799" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8.From the &lt;strong&gt;Select role&lt;/strong&gt; dropdown menu chose the role &lt;strong&gt;Attribute Definition Reader&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;9.Select the &lt;strong&gt;Next&lt;/strong&gt; button when it becomes available.&lt;br&gt;
10.Choose the option &lt;strong&gt;Eligible&lt;/strong&gt; for &lt;strong&gt;Assignment type&lt;/strong&gt;.&lt;br&gt;
Select &lt;strong&gt;Assign&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Subtask 1 - Alternate method to assign a role to a user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Look at the Identity menu section on the left side of the screen.&lt;br&gt;
2.Select the &lt;strong&gt;Show more&lt;/strong&gt; option to see the full &lt;strong&gt;Identity&lt;/strong&gt; menu.&lt;br&gt;
3.From the list of items in the &lt;strong&gt;Identity&lt;/strong&gt; menu, expand the &lt;strong&gt;Roles &amp;amp; admins&lt;/strong&gt; section.&lt;br&gt;
4.Select &lt;strong&gt;Roles &amp;amp; admins&lt;/strong&gt;.&lt;br&gt;
5.Select &lt;strong&gt;Attribute Log Reader&lt;/strong&gt; from the list of roles.&lt;/p&gt;

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

&lt;p&gt;6.From the top of the screen select &lt;strong&gt;+ Add assignment&lt;/strong&gt;.&lt;br&gt;
7.From the &lt;strong&gt;Select member(s)&lt;/strong&gt; select the No member selected text.&lt;br&gt;
8.From the list of user choose &lt;strong&gt;Bhogeswar Kalita&lt;/strong&gt;.&lt;br&gt;
9.Choose the &lt;strong&gt;Select&lt;/strong&gt; button at the bottom.&lt;br&gt;
10.Select the &lt;strong&gt;Next&lt;/strong&gt; button.&lt;/p&gt;

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

&lt;p&gt;11.On the Add assignments page choose &lt;strong&gt;Active&lt;/strong&gt; for the &lt;strong&gt;Assignment type&lt;/strong&gt;.&lt;br&gt;
12.Note that you have to enter a justification for this role / user assignment.&lt;br&gt;
13.Enter the justification &lt;strong&gt;User will be working as an Attribute log reader as their primary job&lt;/strong&gt;. into the box.&lt;br&gt;
14.Select &lt;strong&gt;Assign&lt;/strong&gt;.&lt;/p&gt;

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

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fls0d9ay773kcx9hzoggi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fls0d9ay773kcx9hzoggi.png" alt="verify" width="800" height="399"&gt;&lt;/a&gt;&lt;br&gt;
Notice the Remove|Update option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 5 - Bulk import users
&lt;/h2&gt;

&lt;p&gt;1.If not already open - Open a browser and connect to the Microsoft Entra admin center at &lt;a href="https://entra.microsoft.com" rel="noopener noreferrer"&gt;https://entra.microsoft.com&lt;/a&gt;.&lt;br&gt;
2.If prompted, log in using the credentials for your tenant.&lt;br&gt;
3.From the &lt;strong&gt;Identity&lt;/strong&gt; menu, select &lt;strong&gt;Users&lt;/strong&gt; and then select &lt;strong&gt;All users&lt;/strong&gt;.&lt;br&gt;
4.From the top of the page, select the &lt;strong&gt;Bulk operations&lt;/strong&gt; drop-down arrow and then select &lt;strong&gt;Bulk create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - Selecting Bulk create will open a new tile. This tile provides a Download link to a template file that you will edit to populate with your user information and upload to add the bulk creation of users.&lt;/p&gt;

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

&lt;p&gt;5.Select &lt;strong&gt;Download&lt;/strong&gt; to see a sample bulk user CSV file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - The .csv template provides you with the fields included with the user profile. This includes the required username, display name, and initial password. You can also complete optional fields, such as Department and Usage location, at this time. You do not need to fill out all the fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lab Tip&lt;/strong&gt; - A sample CSV has been provided in the Allfiles/Lab1 folder – APL0501-BulkUser.csv.&lt;br&gt;
6.Open Notepad.&lt;br&gt;
7.Open the SC300BulkUser.csv file.&lt;br&gt;
8.Search and replace the existing value &lt;strong&gt;«&amp;gt;&amp;gt;&lt;/strong&gt; with &lt;strong&gt;Your actual domain name&lt;/strong&gt;.&lt;br&gt;
9.Save the file.&lt;br&gt;
10.On the Bulk create users dialog, select the file folder icon on step 3.&lt;br&gt;
11.Path to the Allfiles/Lab1 folder and select &lt;strong&gt;APL0501-BulkUser.csv&lt;/strong&gt; file.&lt;br&gt;
12.Select &lt;strong&gt;Open&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;13.Wait for the notification that the file uploaded successfully. Select the Submit button to add the users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - After the users have been created, you will be prompted that the creation has succeeded.&lt;/p&gt;

&lt;p&gt;14.&lt;strong&gt;Close&lt;/strong&gt; the &lt;strong&gt;Bulk create users&lt;/strong&gt; dialog.&lt;br&gt;
15.Check the list of users to see your bulk created users.&lt;/p&gt;

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

&lt;p&gt;In summary, Identity is the foundation of secure cloud architecture. Managing access manually isn't just inefficient - it’s an operational risk.&lt;/p&gt;

&lt;p&gt;This article breaks down a hands-on walkthrough of cloud identity engineering using Microsoft Entra ID. From enforcing the principle of least privilege with granular administrative roles and secure B2B guest access to scaling user provisioning through bulk automation pipelines, here is the blueprint for building secure, production-ready identity infrastructure.&lt;/p&gt;

</description>
      <category>sysadmin</category>
      <category>identity</category>
      <category>automation</category>
      <category>security</category>
    </item>
    <item>
      <title>From Fork to Pull Request: A Practical GitHub Collaboration Workflow</title>
      <dc:creator>Rahimah Sulayman</dc:creator>
      <pubDate>Sat, 04 Jul 2026 14:11:19 +0000</pubDate>
      <link>https://dev.to/rahimah_dev/from-fork-to-pull-request-a-practical-github-collaboration-workflow-f9l</link>
      <guid>https://dev.to/rahimah_dev/from-fork-to-pull-request-a-practical-github-collaboration-workflow-f9l</guid>
      <description>&lt;p&gt;Modern software development is built on &lt;strong&gt;collaboration&lt;/strong&gt;, and &lt;code&gt;GitHub&lt;/code&gt; is at the center of that workflow.&lt;br&gt;
Every contribution to an &lt;code&gt;open-source project&lt;/code&gt; starts with a single &lt;code&gt;Pull Request&lt;/code&gt;. &lt;em&gt;This article walks through a practical GitHub collaboration workflow, from forking a repository to submitting production-style Pull Requests for review using the same process followed by development teams.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This project reinforced practical version control skills that are essential in Cloud and DevOps environments, including working with feature branches, synchronizing forks, writing meaningful commits, and contributing through Pull Requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Fork the Repository
&lt;/h2&gt;

&lt;p&gt;The workflow began by creating a personal &lt;strong&gt;fork of the original repository&lt;/strong&gt;. This provided an independent copy where changes could be made safely without affecting the upstream project.&lt;/p&gt;

&lt;p&gt;The fork serves as the working repository for development before changes are proposed back to the original project.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjmqurb1nzfgdl92gfrmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjmqurb1nzfgdl92gfrmp.png" alt="mine" width="799" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Clone the Repository
&lt;/h2&gt;

&lt;p&gt;After forking the project, I &lt;strong&gt;cloned&lt;/strong&gt; my fork to my local development environment using Git. This enabled me to work on the project locally while maintaining a connection to my GitHub repository.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;local repository&lt;/strong&gt; became the workspace for implementing changes, reviewing modifications, and managing version history before publishing updates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F349z6rchu8gg1ahvl0pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F349z6rchu8gg1ahvl0pr.png" alt="clone" width="799" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Configure the Upstream Repository
&lt;/h2&gt;

&lt;p&gt;To keep my fork &lt;strong&gt;synchronized&lt;/strong&gt; with the original project, I configured the upstream repository as an additional remote.&lt;/p&gt;

&lt;p&gt;This made it possible to fetch the latest changes from the original project before starting new work, ensuring my local repository stayed aligned with the latest &lt;code&gt;codebase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyaoqcjt77ersgpse5p73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyaoqcjt77ersgpse5p73.png" alt="upstream/main" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Create a Feature Branch
&lt;/h2&gt;

&lt;p&gt;A dedicated &lt;strong&gt;feature branch&lt;/strong&gt; was created to isolate the changes from the main branch, following a standard collaborative Git workflow.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Step 5: Contribute to the Project
&lt;/h2&gt;

&lt;p&gt;I updated the project documentation by adding my contributor profile and committed the changes using a &lt;strong&gt;descriptive commit message&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftkl0jzel8rneod1uw4kr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftkl0jzel8rneod1uw4kr.png" alt="contributors.md" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Push the Feature Branch
&lt;/h2&gt;

&lt;p&gt;The completed work was pushed to my GitHub fork, making it ready for review through a &lt;strong&gt;Pull Request&lt;/strong&gt;.&lt;br&gt;
The arrow points to the Pull Request link.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7kmyup3wk6cs98udkez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7kmyup3wk6cs98udkez.png" alt="pr" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The link opened a Pull Request through which the feature branch was submitted for review, thereby allowing the repository owner to &lt;strong&gt;review and merge&lt;/strong&gt; the contribution.&lt;br&gt;
The Pull Request was approved and merged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsz2j3p7soh6yuz41oeqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsz2j3p7soh6yuz41oeqp.png" alt="pr approve" width="800" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Submit a Documentation Improvement
&lt;/h2&gt;

&lt;p&gt;After the initial contribution, I created a second feature branch to improve the project's &lt;strong&gt;README&lt;/strong&gt; by clarifying the browser prerequisites and submitted another Pull Request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F81dvuuvg2s6o5x8opjkl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F81dvuuvg2s6o5x8opjkl.png" alt="pr for readme" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This repository reflects hands-on experience with collaborative software development using Git and GitHub. It showcases practical skills in branching strategies, repository synchronization, documentation contributions, and Pull Request-based collaboration—core practices used across modern engineering and DevOps teams.&lt;/p&gt;

&lt;p&gt;I look forward to applying these skills in professional environments while continuing to expand my expertise in Cloud and DevOps technologies.&lt;/p&gt;

</description>
      <category>github</category>
      <category>opensource</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
