<?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: David Omokhodion</title>
    <description>The latest articles on DEV Community by David Omokhodion (@nobleman97).</description>
    <link>https://dev.to/nobleman97</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F947671%2Ff917abd8-dcdf-46d4-887a-ec89b4aff68f.png</url>
      <title>DEV Community: David Omokhodion</title>
      <link>https://dev.to/nobleman97</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nobleman97"/>
    <language>en</language>
    <item>
      <title>Building Out Your Active Directory Homelab - A Hands-on Guide</title>
      <dc:creator>David Omokhodion</dc:creator>
      <pubDate>Tue, 19 May 2026 11:30:49 +0000</pubDate>
      <link>https://dev.to/nobleman97/building-out-your-active-directory-a-hands-on-guide-286</link>
      <guid>https://dev.to/nobleman97/building-out-your-active-directory-a-hands-on-guide-286</guid>
      <description>&lt;p&gt;Setting up an Active Directory domain from scratch is one of the most valuable hands-on projects a Windows sysadmin can have in their portfolio. In this guide, you will build a fully functional simulated enterprise environment — complete with a Domain Controller, Group Policy, DNS, DHCP, and domain-joined workstation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skill level:&lt;/strong&gt; Intermediate&lt;br&gt;
&lt;strong&gt;What you need:&lt;/strong&gt; 2 Windows VMs with at least 4 GB RAM each&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;p&gt;Before touching a single command, here is the mental model you need.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Active Directory Domain Services (AD DS)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microsoft's directory service. Stores information about every user, computer, and resource on a network, and controls who can access what.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Domain Controller (DC)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The server running AD DS. It authenticates logins, enforces policies, and is the authority for the entire domain.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Organisational Units (OUs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Folders inside AD. You group users and computers into OUs so different policies can be applied to different departments cleanly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Group Policy Objects (GPOs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rules linked to OUs that control security settings, drive mappings, restrictions, and more — applied automatically to every machine in scope.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AD DS relies entirely on DNS to locate the Domain Controller. Without correct DNS, domain joins fail and logins fail.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DHCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatically assigns IP addresses and can push the correct DNS server address to every device on the network.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How they fit together:&lt;/strong&gt; AD DS is the database, the Domain Controller hosts it, OUs organise the records inside it, and GPOs are the rules attached to those records. DNS is the glue that lets every machine find the DC in the first place.&lt;/p&gt;


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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Proxmox Host]
   ├── DC01  —  Windows Server 2022  —  192.168.100.27  (Domain Controller)
   └── WS01  —  Windows 11           —  192.168.100.28  (Domain Member)

Gateway: 192.168.100.1
Domain:  contoso.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  VM Specs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VM&lt;/th&gt;
&lt;th&gt;OS&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;Disk&lt;/th&gt;
&lt;th&gt;IP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DC01&lt;/td&gt;
&lt;td&gt;Windows Server 2022 (Desktop Experience)&lt;/td&gt;
&lt;td&gt;Domain Controller&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;60 GB&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;192.168.100.27&lt;/code&gt; (static)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WS01&lt;/td&gt;
&lt;td&gt;Windows 10 or 11&lt;/td&gt;
&lt;td&gt;Client workstation&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;60 GB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.100.28&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Phase 1 — Install &amp;amp; Configure DC01
&lt;/h2&gt;

&lt;p&gt;Boot DC01 from the Windows Server 2022 ISO. Select &lt;strong&gt;Windows Server 2022 Standard (Desktop Experience)&lt;/strong&gt; when prompted. Complete the installation and set a strong Administrator password.&lt;/p&gt;
&lt;h3&gt;
  
  
  Set a static IP address
&lt;/h3&gt;

&lt;p&gt;A Domain Controller must always be reachable at the same IP. Dynamic addresses will break DNS and domain joins. Open PowerShell as Administrator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set static IP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;New-NetIPAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-InterfaceAlias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ethernet"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-IPAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.27"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PrefixLength&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-DefaultGateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.1"&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="c"&gt;### Rename the computer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Rename-Computer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-NewName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DC01"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Restart&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;p&gt;The VM reboots. Log back in as Administrator before continuing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 2 — Promote DC01 to Domain Controller
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install the AD DS and DNS roles
&lt;/h3&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;AD-Domain-Services&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DNS&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;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Common mistake:&lt;/strong&gt; &lt;code&gt;Install-WindowsFeature&lt;/code&gt; only exists on Windows Server. If you see &lt;em&gt;"term not recognised"&lt;/em&gt;, you are running this on the wrong machine — confirm you are on DC01 running Server 2022, not your Windows 10/11 workstation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Promote to Domain Controller
&lt;/h3&gt;

&lt;p&gt;This command creates a new forest and domain called &lt;code&gt;contoso.local&lt;/code&gt;. DC01 reboots automatically when complete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Install-ADDSForest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-DomainName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contoso.local"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-DomainNetBiosName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CONTOSO"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SafeModeAdministratorPassword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ConvertTo-SecureString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P@ssw0rd123!"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AsPlainText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-InstallDns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the reboot, log in as &lt;code&gt;CONTOSO\Administrator&lt;/code&gt;. The login screen now shows the domain name — promotion was successful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect via RDP from from your machine
&lt;/h3&gt;

&lt;p&gt;First, go into the DC01 and enable remote desktop.&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.amazonaws.com%2Fuploads%2Farticles%2F8bsc32rzetgxatsrt1vm.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.amazonaws.com%2Fuploads%2Farticles%2F8bsc32rzetgxatsrt1vm.png" alt=" " width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, if you are managing DC01 from a Linux machine using &lt;code&gt;xfreerdp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xfreerdp /v:192.168.100.27 /u:Administrator /d:CONTOSO /p:&lt;span class="s1"&gt;'YourPassword'&lt;/span&gt; /cert:ignore /dynamic-resolution /clipboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you could connect using a GUI RDP client like Remmina (On Linux) or Remote Desktop. Remember that "domain" is &lt;em&gt;CONTOSO&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3 — Build the OU Structure
&lt;/h2&gt;

&lt;p&gt;Organisational Units are the filing system of Active Directory. A well-designed OU structure makes GPO targeting clean and mirrors how real enterprises are organised.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contoso.local
└── Contoso Corp
   ├── IT
   ├── HR
   ├── Finance
   ├── Sales
   ├── Servers
   ├── Service Accounts
   └── _Admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DC=contoso,DC=local"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Top-level OU&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;New-ADOrganizationalUnit&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="s2"&gt;"Contoso Corp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OU=Contoso Corp,&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Department OUs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ou&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s2"&gt;"IT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Finance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Sales"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Servers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Service Accounts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"_Admin"&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="n"&gt;New-ADOrganizationalUnit&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="nv"&gt;$ou&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$base&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;blockquote&gt;
&lt;p&gt;✅ The &lt;code&gt;_Admin&lt;/code&gt; OU uses an underscore prefix so it sorts to the top alphabetically — a common real-world convention for keeping privileged accounts visually separated from standard department OUs.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Phase 4 — Create Users &amp;amp; Groups
&lt;/h2&gt;

&lt;p&gt;Rather than creating users manually through the GUI, use PowerShell (exactly how sysadmins handle bulk provisioning in production).&lt;/p&gt;

&lt;h3&gt;
  
  
  Create security groups
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$groups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s2"&gt;"IT-Team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"HR-Team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Finance-Team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Sales-Team"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$groups&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="n"&gt;New-ADGroup&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="nv"&gt;$g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-GroupScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-GroupCategory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OU=IT,OU=Contoso Corp,DC=contoso,DC=local"&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;h3&gt;
  
  
  Bulk-create users
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DC=contoso,DC=local"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&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="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;First&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Johnson"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Dept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"IT"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;OU&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"IT"&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="nx"&gt;First&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Smith"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Dept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;OU&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"HR"&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="nx"&gt;First&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Carol"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Davis"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Dept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Finance"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;OU&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Finance"&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="nx"&gt;First&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Dan"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Lee"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;Dept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Sales"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;OU&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Sales"&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="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$users&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="nv"&gt;$username&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;First&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$upn&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="s2"&gt;@contoso.local"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$ouPath&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OU=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OU&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;,OU=Contoso Corp,&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertTo-SecureString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome1!"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AsPlainText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;

   &lt;/span&gt;&lt;span class="n"&gt;New-ADUser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-GivenName&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-Surname&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Last&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&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="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Last&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-SamAccountName&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-UserPrincipalName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$upn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-Department&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nv"&gt;$u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dept&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nv"&gt;$ouPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-AccountPassword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-Enabled&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-PasswordNeverExpires&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nt"&gt;-ChangePasswordAtLogon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&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;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;RDP gotcha:&lt;/strong&gt; Setting &lt;code&gt;-ChangePasswordAtLogon $true&lt;/code&gt; blocks RDP logins entirely — the protocol cannot prompt for a password change mid-connection. Keep it &lt;code&gt;$false&lt;/code&gt; for lab use. In production, set it to &lt;code&gt;$true&lt;/code&gt; so users choose their own password on first login.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Phase 5 — Configure Group Policy Objects (GPOs)
&lt;/h2&gt;

&lt;p&gt;Group Policy Objects (GPOs) are rules you create and link to Organisational Units. Every user and computer inside that OU automatically receives and enforces those rules — no manual configuration needed on each machine. You manage GPOs through the &lt;strong&gt;Group Policy Management Console (GPMC)&lt;/strong&gt;, which was installed alongside AD DS earlier.&lt;/p&gt;

&lt;p&gt;Open GPMC from DC01 via: &lt;strong&gt;Server Manager → Tools → Group Policy Management&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.amazonaws.com%2Fuploads%2Farticles%2F5k23fnmdpl674dxnuru6.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.amazonaws.com%2Fuploads%2Farticles%2F5k23fnmdpl674dxnuru6.png" alt=" " width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  GPO 1 — Security Baseline (linked to Contoso Corp)
&lt;/h3&gt;

&lt;p&gt;This GPO applies to every user and computer under the Contoso Corp OU — think of it as your organisation-wide minimum security standard.&lt;/p&gt;

&lt;p&gt;First, create and link the GPO via PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-GPO&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="s2"&gt;"Security Baseline"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;New-GPLink&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="s2"&gt;"Security Baseline"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OU=Contoso Corp,DC=contoso,DC=local"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then configure the settings inside GPMC.&lt;/p&gt;

&lt;p&gt;To find it, in the left panel, expand:&lt;br&gt;
Forest: contoso.local → Domains → contoso.local → Group Policy Objects&lt;br&gt;
You should see Security Baseline listed there.&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.amazonaws.com%2Fuploads%2Farticles%2Fwezz0ow6fwdmhk61ga1i.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.amazonaws.com%2Fuploads%2Farticles%2Fwezz0ow6fwdmhk61ga1i.png" alt=" " width="799" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right-click &lt;strong&gt;Security Baseline → Edit&lt;/strong&gt; to open the Group Policy Management Editor, then follow each path below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enforce UAC (User Account Control)&lt;/strong&gt;
UAC is a Windows security feature that prevents unauthorised changes to the system by prompting for administrator approval. Enforcing it via GPO ensures users cannot disable it locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Computer Configuration → Policies → Windows Settings → Security Settings → Local Policies → Security Options&lt;/p&gt;

&lt;p&gt;Set &lt;strong&gt;"User Account Control: Run all administrators in Admin Approval Mode"&lt;/strong&gt; → &lt;strong&gt;Enabled&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minimum Password Length&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enforces that no account in the domain can have a password shorter than 12 characters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Computer Configuration → Policies → Windows Settings → Security Settings → Account Policies → Password Policy&lt;/p&gt;

&lt;p&gt;Set &lt;strong&gt;"Minimum password length"&lt;/strong&gt; → &lt;strong&gt;12 characters&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Account Lockout Policy&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Locks an account after repeated failed login attempts, protecting against brute-force attacks. After 15 minutes the account unlocks automatically.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Computer Configuration → Policies → Windows Settings → Security Settings → Account Policies → Account Lockout Policy&lt;/p&gt;

&lt;p&gt;Set &lt;strong&gt;"Account lockout threshold"&lt;/strong&gt; → &lt;strong&gt;5 invalid logon attempts&lt;/strong&gt;&lt;br&gt;
Set &lt;strong&gt;"Account lockout duration"&lt;/strong&gt; → &lt;strong&gt;15 minutes&lt;/strong&gt;&lt;br&gt;
Set &lt;strong&gt;"Reset account lockout counter after"&lt;/strong&gt; → &lt;strong&gt;15 minutes&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  GPO 2 — Workstation Lockdown (linked to non-IT OUs)
&lt;/h3&gt;

&lt;p&gt;This GPO restricts what standard users can do on their workstations. You will link it to HR, Finance, and Sales — but &lt;strong&gt;not&lt;/strong&gt; IT, since your IT team needs unrestricted access to manage systems.&lt;/p&gt;

&lt;p&gt;First, create and link the GPO to each non-IT department OU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-GPO&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="s2"&gt;"Workstation Lockdown"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Link to each non-IT OU&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ou&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Finance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Sales"&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="n"&gt;New-GPLink&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="s2"&gt;"Workstation Lockdown"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nt"&gt;-Target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OU=&lt;/span&gt;&lt;span class="nv"&gt;$ou&lt;/span&gt;&lt;span class="s2"&gt;,OU=Contoso Corp,DC=contoso,DC=local"&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;Then in GPMC, right-click &lt;strong&gt;Workstation Lockdown → Edit&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.amazonaws.com%2Fuploads%2Farticles%2Feqb262ajn7s1bff6el2n.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.amazonaws.com%2Fuploads%2Farticles%2Feqb262ajn7s1bff6el2n.png" alt="Workstation lockdown" width="723" height="978"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;... then configure the following:&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Access to Control Panel and PC Settings&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prevents standard users from changing system settings, uninstalling software, or modifying network configuration — common restrictions in managed enterprise environments.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User Configuration → Policies → Administrative Templates → Control Panel (Click on it)&lt;/p&gt;

&lt;p&gt;Double-click &lt;strong&gt;"Prohibit access to Control Panel and PC Settings"&lt;/strong&gt; → Set to &lt;strong&gt;Enabled&lt;/strong&gt; → Click &lt;strong&gt;OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prevent Access to Command Prompt&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Command Prompt (cmd.exe) can be used to bypass desktop restrictions. Disabling it for non-IT users reduces the risk of accidental or deliberate system changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User Configuration → Policies → Administrative Templates → System&lt;/p&gt;

&lt;p&gt;Double-click &lt;strong&gt;"Prevent access to the command prompt"&lt;/strong&gt; → Set to &lt;strong&gt;Enabled&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When prompted &lt;em&gt;"Disable the command prompt script processing also?"&lt;/em&gt; → Select &lt;strong&gt;No&lt;/strong&gt; (selecting Yes would also break logon scripts, which you don't want)&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Remove Run Dialog from Start Menu&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Run dialog (Win + R) gives users a quick way to launch programs and access network paths, which can be used to circumvent other restrictions. Removing it closes that gap.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User Configuration → Policies → Administrative Templates → Start Menu and Taskbar&lt;/p&gt;

&lt;p&gt;Double-click &lt;strong&gt;"Remove Run menu from Start Menu"&lt;/strong&gt; → Set to &lt;strong&gt;Enabled&lt;/strong&gt; → Click &lt;strong&gt;OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Verify the GPO is linked correctly&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ou&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s2"&gt;"HR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Finance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Sales"&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="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;--- &lt;/span&gt;&lt;span class="nv"&gt;$ou&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;Get-GPInheritance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OU=&lt;/span&gt;&lt;span class="nv"&gt;$ou&lt;/span&gt;&lt;span class="s2"&gt;,OU=Contoso Corp,DC=contoso,DC=local"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExpandProperty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GpoLinks&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;You should see &lt;strong&gt;Workstation Lockdown&lt;/strong&gt; listed under each of the three OUs.&lt;/p&gt;




&lt;h3&gt;
  
  
  GPO 3 — IT Drive Mapping (linked to IT OU)
&lt;/h3&gt;

&lt;p&gt;First create the share on DC01:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Shares\ITShare"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;New-SmbShare&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="s2"&gt;"ITShare"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Shares\ITShare"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FullAccess&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CONTOSO\IT-Team"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ReadAccess&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CONTOSO\Domain Users"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, Create the Drive Map in GPMC manually...&lt;br&gt;
This part is done entirely through the GUI. In GPMC, right-click Workstation Lockdown (or whichever GPO is linked to the IT OU) → Edit, then navigate to:&lt;/p&gt;

&lt;p&gt;User Configuration → Preferences → Windows Settings → Drive Maps&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.amazonaws.com%2Fuploads%2Farticles%2Fskn5zmtsx7ae4r0s1rq8.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.amazonaws.com%2Fuploads%2Farticles%2Fskn5zmtsx7ae4r0s1rq8.png" alt="IT FIle Share" width="800" height="520"&gt;&lt;/a&gt;&lt;br&gt;
Right-click in the right-hand pane → New → Mapped Drive&lt;/p&gt;



&lt;p&gt;Then in GPMC: User Configuration → Preferences → Windows Settings → Drive Maps → map &lt;code&gt;\\DC01\ITShare&lt;/code&gt; to &lt;code&gt;Z:&lt;/code&gt; for the IT OU.&lt;/p&gt;


&lt;h2&gt;
  
  
  Phase 6 — Join WS01 to the Domain
&lt;/h2&gt;

&lt;p&gt;On WS01, first point DNS at DC01. Without this, WS01 cannot resolve &lt;code&gt;contoso.local&lt;/code&gt; and the domain join will fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Open Network Settings → Change adapter options → IPv4 Properties. Set Preferred DNS to &lt;code&gt;192.168.100.27&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Join the domain — run this on WS01 as Administrator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Add-Computer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-DomainName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contoso.local"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Credential&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Credential&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OUPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OU=IT,OU=Contoso Corp,DC=contoso,DC=local"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Restart&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2.5:&lt;/strong&gt; Allow ajohnson to use RDP&lt;br&gt;
Domain users are not automatically allowed to RDP into machines — you need to add them to the local Remote Desktop Users group on WS01:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;powershellAdd-LocalGroupMember&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Remote Desktop Users"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Member&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CONTOSO\ajohnson"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or to allow all domain users to RDP into WS01:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;powershellAdd-LocalGroupMember&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Remote Desktop Users"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Member&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CONTOSO\Domain Users"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; RDP into WS01 from Linux as a domain user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xfreerdp /v:192.168.100.28 /u:ajohnson /d:CONTOSO /p:&lt;span class="s1"&gt;'Welcome1!'&lt;/span&gt; /cert:ignore /dynamic-resolution /clipboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Verify GPOs applied correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Quick summary in terminal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;gpresult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/r&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Full HTML report&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;gpresult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Users\ajohnson\Documents\gp-report.html"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Bonus — DNS &amp;amp; DHCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify and extend DNS
&lt;/h3&gt;

&lt;p&gt;Do the the following in DC01...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View all DNS zones&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Get-DnsServerZone&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# View A records in contoso.local&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Get-DnsServerResourceRecord&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ZoneName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contoso.local"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-RRType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Add a custom record for a simulated intranet server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Add-DnsServerResourceRecordA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ZoneName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contoso.local"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&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="s2"&gt;"intranet"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-IPv4Address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.29"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install and configure DHCP
&lt;/h3&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="nx"&gt;DHCP&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;span class="n"&gt;Add-DhcpServerv4Scope&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="s2"&gt;"CorpLAN"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-StartRange&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.100"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-EndRange&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.199"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SubnetMask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"255.255.255.0"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Set-DhcpServerv4OptionValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ScopeId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-DnsServer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.27"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Router&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.100.1"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Add-DhcpServerInDC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-DnsName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dc01.contoso.local"&lt;/span&gt;&lt;span class="w"&gt;
&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.amazonaws.com%2Fuploads%2Farticles%2F9wrzi63ubpzugqeahx41.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.amazonaws.com%2Fuploads%2Farticles%2F9wrzi63ubpzugqeahx41.png" alt="IT FIle Share" width="800" height="449"&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.amazonaws.com%2Fuploads%2Farticles%2Fsaeb0xqgfo4sb9l4lfy2.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.amazonaws.com%2Fuploads%2Farticles%2Fsaeb0xqgfo4sb9l4lfy2.png" alt="AD Users" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Skills This Project Demonstrates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Active Directory DS&lt;/strong&gt; — forest/domain deployment, OU structure design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group Policy&lt;/strong&gt; — security baseline, account lockout, drive map preferences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS &amp;amp; DHCP&lt;/strong&gt; — zone management, custom records, scope configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PowerShell automation&lt;/strong&gt; — bulk user/group creation, role installation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows networking&lt;/strong&gt; — static IPs, domain join, name resolution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security fundamentals&lt;/strong&gt; — password policy, account lockout, UAC enforcement&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;You now have a fully functional enterprise-grade Active Directory environment running in your lab — a Domain Controller, a domain-joined workstation, a structured OU hierarchy, enforced Group Policy, DNS, and DHCP, all provisioned largely through PowerShell.&lt;/p&gt;

&lt;p&gt;Want to extend this? Here are possible next steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add a second domain controller&lt;/strong&gt; and explore AD replication — understanding how DCs sync is essential knowledge for any enterprise environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up RSAT on WS01&lt;/strong&gt; and practice managing the domain entirely from the workstation, the way most administrators actually work day-to-day&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simulate a user offboarding workflow&lt;/strong&gt; — disable the account, move it to a dedicated Disabled OU, strip group memberships, and document the process as a runbook&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore fine-grained password policies&lt;/strong&gt; — apply stricter password requirements to the &lt;code&gt;_Admin&lt;/code&gt; OU without affecting standard users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Break something on purpose&lt;/strong&gt; — delete a GPO link, misconfigure DNS, corrupt a user account — and practice diagnosing and recovering from it. Troubleshooting under pressure is a skill, and the only way to build it is to practise it in a safe environment&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Drop a comment below or connect with me — I am always happy to talk through home lab setups and sysadmin career questions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;-&amp;gt; See the &lt;a href="https://github.com/nobleman97/Cloud_DevOps-projects/tree/master/active-directory" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;br&gt;
-&amp;gt; Connect with me &lt;a href="https://www.linkedin.com/in/davidomokhodion/" rel="noopener noreferrer"&gt;On LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sysadmin</category>
      <category>windows</category>
      <category>homelab</category>
      <category>azuread</category>
    </item>
    <item>
      <title>Stop Getting 'Access Denied': Fixing Cross-Account Access in AWS with IAM STS</title>
      <dc:creator>David Omokhodion</dc:creator>
      <pubDate>Tue, 03 Mar 2026 23:56:18 +0000</pubDate>
      <link>https://dev.to/nobleman97/stop-getting-access-denied-fixing-cross-account-access-in-aws-with-iam-sts-1d1m</link>
      <guid>https://dev.to/nobleman97/stop-getting-access-denied-fixing-cross-account-access-in-aws-with-iam-sts-1d1m</guid>
      <description>&lt;p&gt;If you've ever worked in a multi-account AWS environment, you've probably hit the dreaded &lt;code&gt;AccessDenied&lt;/code&gt; error when trying to access resources across accounts. Whether it's sharing data between dev and prod accounts, aggregating logs to a central security account, or enabling cross-team collaboration, cross-account access is essential—but it's also where many engineers struggle.&lt;/p&gt;

&lt;p&gt;In this post, I'll show you exactly how to implement secure cross-account resource access using &lt;strong&gt;AWS Identity &amp;amp; Access Management (IAM)&lt;/strong&gt; with &lt;strong&gt;AWS Security Token Service (STS)&lt;/strong&gt; with a real-world example: a Lambda function that tracks the International Space Station's location and stores the data in an S3 bucket in a different AWS account.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Have Terraform installed&lt;/li&gt;
&lt;li&gt;Have access to 2 AWS accounts&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗️ The Architecture
&lt;/h2&gt;

&lt;p&gt;Here's what we're building:&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.amazonaws.com%2Fuploads%2Farticles%2F8txamjff6phymwi0zdzs.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.amazonaws.com%2Fuploads%2Farticles%2F8txamjff6phymwi0zdzs.png" alt="Cross-Account Architecture" width="672" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Account A&lt;/strong&gt; (Source):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda function that fetches ISS position data&lt;/li&gt;
&lt;li&gt;IAM execution role with permission to assume a role in Account B&lt;/li&gt;
&lt;li&gt;EventBridge trigger (runs every 5 minutes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Account B&lt;/strong&gt; (Target):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 bucket for storing ISS position logs&lt;/li&gt;
&lt;li&gt;IAM role that trusts Account A's Lambda role&lt;/li&gt;
&lt;li&gt;Policies granting S3 access to the trusted role&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Flow&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;EventBridge triggers Lambda in Account A&lt;/li&gt;
&lt;li&gt;Lambda calls &lt;code&gt;sts:AssumeRole&lt;/code&gt; to get temporary credentials for Account B&lt;/li&gt;
&lt;li&gt;Lambda uses temporary credentials to read/write to S3 bucket in Account B&lt;/li&gt;
&lt;li&gt;Lambda fetches ISS position from public API and appends it to a file pulled from s3&lt;/li&gt;
&lt;li&gt;Lambda pushes the update file back to s3&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🛠️ Let's Implement the Trust Chain
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Configure the Terraform Providers
&lt;/h3&gt;

&lt;p&gt;Configure you cli with credentials from account A. Then run &lt;code&gt;aws sts get-caller-identity&lt;/code&gt; to get your identity.&lt;br&gt;
e.g&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;"UserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;redacted&amp;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;"Account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;redacted&amp;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;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;redacted&amp;gt;:user/nobleman"&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;Next, create a role (called terraform, for example) in account B. In its trust relationship, allow the previous identity to assume it.&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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;"AWS"&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="s2"&gt;"arn:aws:iam::&amp;lt;redacted&amp;gt;:user/nobleman"&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;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&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;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;Next, configure Terraform providers for both accounts like this...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0.0"&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 6.0"&lt;/span&gt;
    &lt;span class="p"&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;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="c1"&gt;# role_arn is set in ~/.aws/config&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"account_b"&lt;/span&gt;

  &lt;span class="nx"&gt;assume_role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::${var.account_b_id}:role/terraform"&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;h3&gt;
  
  
  Step 2: Lambda's Execution Role (Account A)
&lt;/h3&gt;

&lt;p&gt;Next, create a role that Lambda can assume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Account A: Role that Lambda assumes&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"cross_account_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"connect-to-bridge"&lt;/span&gt;
  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ec2_assume_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Trust policy: Allow Lambda service to assume this role&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"ec2_assume_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Service"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&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;&lt;strong&gt;What this means&lt;/strong&gt;: The Lambda service can wear this role like a badge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Grant Role Permission to Assume Cross-Account Role
&lt;/h3&gt;

&lt;p&gt;Now we give this role permission to assume another role in Account B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Account A: Policy that allows assuming role in Account B&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"assume_cross_account_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Role in Account B&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"ec2_assume_cross_account"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cross_account_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assume_cross_account_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this means&lt;/strong&gt;: "Hey Lambda role, you're allowed to assume the &lt;code&gt;access_s3_bucket&lt;/code&gt; role in Account B."&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: The Target Role in Account B
&lt;/h3&gt;

&lt;p&gt;Here's the role in Account B with the crucial trust policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Account B: Role that grants S3 access&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"access_s3_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_b&lt;/span&gt;  &lt;span class="c1"&gt;# Important: This is in Account B&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"access-s3-bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow_role_assumption_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Trust policy: Allow Account A's role to assume this role&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"allow_role_assumption_a"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;

    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cross_account_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Account A role ARN&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&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;&lt;strong&gt;This is the key&lt;/strong&gt;: Account B explicitly trusts Account A's role. Without this trust relationship, the assume role call will fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Grant S3 Permissions to the Account B Role
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"s3_bucket_access"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;

    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"s3:DeleteObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reporting-bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3_bucket_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"${module.reporting-bucket.s3_bucket_arn}/*"&lt;/span&gt;
    &lt;span class="p"&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"s3_bucket_access_attachment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_b&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3_bucket_access&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The complete trust chain&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;→ Lambda Service assumes Lambda Execution Role (Account A) 
→ Lambda Execution Role (Account A) assumes S3 Access Role (Account B) 
→ S3 Access Role (Account B) has permission to S3 Bucket (Account B)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💻 The Lambda Implementation
&lt;/h2&gt;

&lt;p&gt;Now let's look at how the Lambda function uses STS to assume the cross-account role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Environment variables from Terraform
&lt;/span&gt;    &lt;span class="n"&gt;account2_role_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ACCOUNT2_ROLE_ARN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;bucket_region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BUCKET_REGION&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;object_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;iss_position.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 1: Assume the role in Account B using STS
&lt;/span&gt;    &lt;span class="n"&gt;sts_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;assumed_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sts_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assume_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;RoleArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;account2_role_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;RoleSessionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LambdaCrossAccountS3Access&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 2: Extract temporary credentials
&lt;/span&gt;    &lt;span class="n"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assumed_role&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 3: Create S3 client with temporary credentials
&lt;/span&gt;    &lt;span class="n"&gt;s3_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AccessKeyId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SecretAccessKey&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;aws_session_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SessionToken&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_region&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 4: Read existing file from S3
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 5: Fetch ISS position from public API
&lt;/span&gt;    &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PoolManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://api.open-notify.org/iss-now.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromtimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 6: Append new data
&lt;/span&gt;    &lt;span class="n"&gt;new_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Time: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Latitude: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;iss_position&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latitude&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Longitude: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;iss_position&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;longitude&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 7: Write back to S3 using temporary credentials
&lt;/span&gt;    &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;new_content&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Cross-account S3 access successful&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Points&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;sts_client.assume_role()&lt;/code&gt;&lt;/strong&gt;: This is where the magic happens. Lambda uses its execution role to request temporary credentials for the Account B role.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Temporary Credentials&lt;/strong&gt;: STS returns short-lived credentials (valid for 1 hour by default). These credentials have all the permissions of the assumed role.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Session Name&lt;/strong&gt;: The &lt;code&gt;RoleSessionName&lt;/code&gt; appears in CloudTrail logs, making it easier to audit who assumed the role and when.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explicit Credential Usage&lt;/strong&gt;: We explicitly pass the temporary credentials to the S3 client. This is different from the default boto3 behavior which uses the Lambda's execution role.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📦 Deployment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two AWS accounts (A &amp;amp; B)&lt;/li&gt;
&lt;li&gt;AWS CLI configured with access to Account A&lt;/li&gt;
&lt;li&gt;Terraform &amp;gt;= 1.0&lt;/li&gt;
&lt;li&gt;A pre-existing &lt;code&gt;terraform&lt;/code&gt; role in Account B that Account A can assume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Steps&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/nobleman97/cross-account-iam.git
&lt;span class="nb"&gt;cd &lt;/span&gt;cross-account-iam
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;dev.tfvars&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;account_b_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"123456789012"&lt;/span&gt;  &lt;span class="c1"&gt;# Replace with your Account B ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Deploy the infrastructure:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;infra
terraform init
terraform plan &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev.tfvars
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Watch it work:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check Lambda logs&lt;/span&gt;
aws logs &lt;span class="nb"&gt;tail&lt;/span&gt; /aws/lambda/write-report-to-crossaccount-s3 &lt;span class="nt"&gt;--follow&lt;/span&gt;

&lt;span class="c"&gt;# After 5-10 minutes, check the S3 file in Account B&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://iss-reporting-24534576df/iss_position.txt - &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; account-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If setup properly, the file should look something 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;ISS Location Logs
===================


Time: 2025-12-06 17:19:45
Latitude: -50.5587
Longitude: 122.1887

Time: 2025-12-06 17:29:07
Latitude: -32.9030
Longitude: 163.3656

...

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Pitfalls and Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem 1: "User is not authorized to perform: sts:AssumeRole"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: The role in Account A doesn't have permission to assume the role in Account B.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Verify the policy attachment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam list-attached-role-policies &lt;span class="nt"&gt;--role-name&lt;/span&gt; connect-to-bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem 2: "AccessDenied" when accessing S3
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Causes&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The trust policy in Account B doesn't trust Account A's role&lt;/li&gt;
&lt;li&gt;The role in Account B doesn't have S3 permissions&lt;/li&gt;
&lt;li&gt;The S3 bucket has a restrictive bucket policy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Debug&lt;/strong&gt;:&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="c"&gt;# Check trust policy&lt;/span&gt;
aws iam get-role &lt;span class="nt"&gt;--role-name&lt;/span&gt; access-s3-bucket &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; account-b &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Role.AssumeRolePolicyDocument'&lt;/span&gt;

&lt;span class="c"&gt;# Check attached policies&lt;/span&gt;
aws iam list-attached-role-policies &lt;span class="nt"&gt;--role-name&lt;/span&gt; access-s3-bucket &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; account-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Problem 3: Terraform Can't Create Resources in Account B
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: Terraform doesn't have permission to assume the role in Account B.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Ensure you have a &lt;code&gt;terraform&lt;/code&gt; role in Account B with this trust policy:&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&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;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;arn_of_cli_identity&amp;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;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&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;h2&gt;
  
  
  🎓 Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Two-Way Trust&lt;/strong&gt;: Cross-account access requires both accounts to cooperate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account A must be allowed to assume roles in Account B&lt;/li&gt;
&lt;li&gt;Account B must trust Account A's role&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;STS is Your Friend&lt;/strong&gt;: Temporary credentials are more secure than permanent access keys. They expire automatically and can be audited.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Cross-account IAM access doesn't have to be scary. By understanding the trust relationships, using STS for temporary credentials, and following least privilege principles, you can securely share resources across AWS accounts.&lt;/p&gt;

&lt;p&gt;The pattern I've shown here—Lambda in Account A assuming a role in Account B to access S3—applies to many other scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instances accessing DynamoDB in another account&lt;/li&gt;
&lt;li&gt;Step Functions orchestrating resources across accounts&lt;/li&gt;
&lt;li&gt;EventBridge forwarding events between accounts&lt;/li&gt;
&lt;li&gt;Centralized logging and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you implemented cross-account access in your AWS environment? What challenges did you face? Drop a comment below!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nobleman97/cross-account-iam" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html" rel="noopener noreferrer"&gt;AWS STS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html" rel="noopener noreferrer"&gt;AWS IAM Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider starring the &lt;a href="https://github.com/nobleman97/cross-account-iam" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; and sharing it with your team!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sts</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Docker Networking 101: A Blueprint for Seamless Container Connectivity</title>
      <dc:creator>David Omokhodion</dc:creator>
      <pubDate>Wed, 13 Dec 2023 14:05:42 +0000</pubDate>
      <link>https://dev.to/nobleman97/docker-networking-101-a-blueprint-for-seamless-container-connectivity-3i5b</link>
      <guid>https://dev.to/nobleman97/docker-networking-101-a-blueprint-for-seamless-container-connectivity-3i5b</guid>
      <description>&lt;p&gt;Docker is a platform for developing, shipping, and running applications in containers. Containers are lightweight, standalone, and executable software packages that include everything needed to run a piece of software, including the code, runtime, libraries, and system tools. Docker provides a consistent and reproducible environment across different environments, making it easier to build, deploy, and scale applications.&lt;/p&gt;

&lt;p&gt;To enable communication between containers, your host and the outside world, docker implements an interesting networking system. It is this system that bridges the gap between isolated containers, allowing them to function as well-coordinated services.&lt;/p&gt;

&lt;p&gt;In this article, you will learn about the basics of the different network types and how to use them in development or deployment.&lt;/p&gt;

&lt;p&gt;We will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Network Types&lt;/li&gt;
&lt;li&gt;Using Docker Networks&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04" rel="noopener noreferrer"&gt;Have Docker Engine Installed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Have a basic understanding of Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Docker Network Types
&lt;/h2&gt;

&lt;p&gt;Docker comes with 7 (rumours say there are more) network types. However, we will discuss the most important four. They are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;None&lt;/li&gt;
&lt;li&gt;Bridge (default)&lt;/li&gt;
&lt;li&gt;Bridge (user-defined)&lt;/li&gt;
&lt;li&gt;Host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already have Docker setup on your host machine, and you run the command &lt;strong&gt;"docker network list"&lt;/strong&gt;, you should get an output similar to this:&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="nv"&gt;$ &lt;/span&gt;docker network list
NETWORK ID     NAME      DRIVER    SCOPE
6f23ed77734d   bridge    bridge    &lt;span class="nb"&gt;local
&lt;/span&gt;3bbca0d09738   host      host      &lt;span class="nb"&gt;local
&lt;/span&gt;a67ca10d6dfd   none      null      &lt;span class="nb"&gt;local&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;"Driver" in this list, tells us the &lt;em&gt;network type&lt;/em&gt;, and by default, docker creates one &lt;strong&gt;bridge&lt;/strong&gt;, &lt;strong&gt;host&lt;/strong&gt; and &lt;strong&gt;null&lt;/strong&gt;(none) network each.&lt;/p&gt;

&lt;p&gt;Let's discuss each network type in more detail...&lt;/p&gt;

&lt;h3&gt;
  
  
  1. None
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2F7xszaoi4zir8x7ygv7ow.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.amazonaws.com%2Fuploads%2Farticles%2F7xszaoi4zir8x7ygv7ow.png" alt="None network type" width="800" height="442"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;None&lt;/em&gt; is a docker network-type where the container is not attached to any network. As a result, the container is unable to communicate with any external network or other containers. It is isolated from every other network. &lt;/p&gt;

&lt;p&gt;You can run an nginx container in a "none" network type using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d --network none --name my_nginx nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Bridge (default)
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2F4prii12z3v8r1x8et54r.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.amazonaws.com%2Fuploads%2Farticles%2F4prii12z3v8r1x8et54r.png" alt="Default bridge network" width="800" height="442"&gt;&lt;/a&gt;&lt;br&gt;
The bridge network mode sets up an internal private network within the host. This allows communication between containers within such network, but isolates them from the host's network. &lt;/p&gt;

&lt;p&gt;When docker containers are created without specifying a network, they are automatically placed in the &lt;em&gt;default bridge&lt;/em&gt; network.&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;&lt;span class="c"&gt;# running a container in the default bridge network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-dit&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; Rock nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Bridge (user-defined)
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2Fskgslqwgc5kt84mxxrwx.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.amazonaws.com%2Fuploads%2Farticles%2Fskgslqwgc5kt84mxxrwx.png" alt="User-defined bridge network" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This network type is similar to the &lt;em&gt;user-defined bridge&lt;/em&gt; network. It also creates an internal private network within the host, but it does not come already created by Docker. You have to create it yourself. &lt;/p&gt;

&lt;p&gt;Here's how you create a user-defined bridge network called "demo_net":&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="c"&gt;# create a user-defined bridge network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create &lt;span class="nt"&gt;-d&lt;/span&gt; bridge demo_net

&lt;span class="c"&gt;# list all your docker networks&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network list
NETWORK ID     NAME       DRIVER    SCOPE
6f23ed77734d   bridge     bridge    &lt;span class="nb"&gt;local
&lt;/span&gt;7a824036d47a   demo_net   bridge    &lt;span class="nb"&gt;local
&lt;/span&gt;3bbca0d09738   host       host      &lt;span class="nb"&gt;local
&lt;/span&gt;a67ca10d6dfd   none       null      &lt;span class="nb"&gt;local&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Here's how to attach a container to the user-defined bridge network you just created...&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="c"&gt;# Attaching containers to bridged network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-dit&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; demo_net &lt;span class="nt"&gt;--name&lt;/span&gt; service_A nginx

&lt;span class="c"&gt;# create another container and publish a port&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-dit&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:80 &lt;span class="nt"&gt;--network&lt;/span&gt; demo_net &lt;span class="nt"&gt;--name&lt;/span&gt; service_B nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;service_A&lt;/em&gt; and &lt;em&gt;service_B&lt;/em&gt; containers are now attached to the 'demo_net' network. By default, all containers within the same network can communicate with one another freely but are isolated from the host network and other user-defined networks.  &lt;/p&gt;

&lt;p&gt;In order to access applications running on these containers from the host network, we have to publish (or expose) ports from the containers. In the snippet above, port 80 on &lt;em&gt;service_B&lt;/em&gt; container is mapped to port 8000 on the host, allowing access to an application running on service_B's container port 80 to be accessed from port 8000 on the host.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Host
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2Fwdsc9kpo0kpg73dn838y.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.amazonaws.com%2Fuploads%2Farticles%2Fwdsc9kpo0kpg73dn838y.png" alt="Host network" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Containers in host network mode directly utilize your host's network stack, lacking isolation. They do not receive separate IP addresses, and any port bindings are directly exposed on your host's network interface. In practical terms, if a container process is configured to listen on port 80, it will bind to your host machine's IP address on port 80.&lt;/p&gt;

&lt;p&gt;Here's an example of a container that binds nginx  to a host network:&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="c"&gt;# bind container to host network&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; host &lt;span class="nt"&gt;--name&lt;/span&gt; my_nginx nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using the host's network for your web container (like Nginx), it means you can't run multiple web containers on the same host and port. This is because they would all share the same network settings, causing conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Docker Networks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing Communications Within and Between Networks
&lt;/h3&gt;

&lt;p&gt;In an earlier step, we attached two containers (service_A and service_B) to the &lt;em&gt;demo_net&lt;/em&gt; network. Let's confirm that they can communicate with one another within the same network and whether they can communicate with a container in a different network(e.g the "Rock" container in the default bridge network).&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="c"&gt;# open a shell into service_A container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; service_A /bin/bash

&lt;span class="c"&gt;# when inside service_A container update its apt repos and install ping&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;iputils-ping &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Then ping service_B using the container name because name resolution is automatically enabled within the network &lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ping service_B

root@06dbaea8c2a2:/# ping service_B
PING service_B &lt;span class="o"&gt;(&lt;/span&gt;172.18.0.3&lt;span class="o"&gt;)&lt;/span&gt; 56&lt;span class="o"&gt;(&lt;/span&gt;84&lt;span class="o"&gt;)&lt;/span&gt; bytes of data.
64 bytes from service_B.demo_net &lt;span class="o"&gt;(&lt;/span&gt;172.18.0.3&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.086 ms
64 bytes from service_B.demo_net &lt;span class="o"&gt;(&lt;/span&gt;172.18.0.3&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.154 ms
64 bytes from service_B.demo_net &lt;span class="o"&gt;(&lt;/span&gt;172.18.0.3&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.119 ms
64 bytes from service_B.demo_net &lt;span class="o"&gt;(&lt;/span&gt;172.18.0.3&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.121 ms
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are similar when you exec into service_B and ping service_A container. However, none of the containers can reach the "Rock" container which is in the default bridge network because they exist in different virtual networks.&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="c"&gt;# Inside service_A&lt;/span&gt;
root@06dbaea8c2a2:/# ping Rock
ping: Rock: Temporary failure &lt;span class="k"&gt;in &lt;/span&gt;name resolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reaching Apps Running in Containers via Published Ports
&lt;/h3&gt;

&lt;p&gt;When creating &lt;em&gt;service_B&lt;/em&gt; in an earlier step, we ran the following command:&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="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-dit&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:80 &lt;span class="nt"&gt;--network&lt;/span&gt; demo_net &lt;span class="nt"&gt;--name&lt;/span&gt; service_B nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command mapped port 80 within the container to port 8000 on the host machine. Hence, when we visit localhost:8000 in our browser, we see the web server running in &lt;em&gt;service_B&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.amazonaws.com%2Fuploads%2Farticles%2Fmtk8emmt81xsrmfqok9c.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.amazonaws.com%2Fuploads%2Farticles%2Fmtk8emmt81xsrmfqok9c.png" alt="localhost:8000" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Manipulating Network Connections
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Disconnect service_A&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network disconnect demo_net service_A

&lt;span class="c"&gt;# Reconnect service_A to default "bridge" network&lt;/span&gt;
docker network connect bridge service_A

&lt;span class="c"&gt;#Next, use `docker inspect` to get the ip of the Rock container and try pinging it.&lt;/span&gt;

root@06dbaea8c2a2:/# ping 172.17.0.2
PING 172.17.0.2 &lt;span class="o"&gt;(&lt;/span&gt;172.17.0.2&lt;span class="o"&gt;)&lt;/span&gt; 56&lt;span class="o"&gt;(&lt;/span&gt;84&lt;span class="o"&gt;)&lt;/span&gt; bytes of data.
64 bytes from 172.17.0.2: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.093 ms
64 bytes from 172.17.0.2: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.186 ms
64 bytes from 172.17.0.2: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.149 ms

&lt;span class="c"&gt;# It works!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List Docker Networks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker network &lt;span class="nb"&gt;ls
&lt;/span&gt;NETWORK ID     NAME       DRIVER    SCOPE
a87871978d59   bridge     bridge    &lt;span class="nb"&gt;local
&lt;/span&gt;747d0aff98fc   demo_net   bridge    &lt;span class="nb"&gt;local
&lt;/span&gt;3bbca0d09738   host       host      &lt;span class="nb"&gt;local
&lt;/span&gt;a67ca10d6dfd   none       null      &lt;span class="nb"&gt;local&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deleting Docker Networks
&lt;/h3&gt;

&lt;p&gt;To delete a custom network, you must first stop or disconnect all running containers attached to the network. When that's done, proceed to delete the network by running the &lt;strong&gt;docker network rm&lt;/strong&gt; command like this:&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="nv"&gt;$ &lt;/span&gt;docker network &lt;span class="nb"&gt;rm &lt;/span&gt;demo_net

&lt;span class="c"&gt;# Confirm that the demo_net network has been removed&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network &lt;span class="nb"&gt;ls
&lt;/span&gt;NETWORK ID     NAME      DRIVER    SCOPE
a87871978d59   bridge    bridge    &lt;span class="nb"&gt;local
&lt;/span&gt;3bbca0d09738   host      host      &lt;span class="nb"&gt;local
&lt;/span&gt;a67ca10d6dfd   none      null      &lt;span class="nb"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Docker's networking system gives you different choices for handling communication between containers, their nearby containers, and your Docker host. In a network, containers can connect with each other using their names or IP addresses.&lt;/p&gt;

&lt;p&gt;User-defined bridge networks work well when you want various containers to talk to each other on the same Docker host. On the flip side, host networks are more suitable when you don't want the network stack to be separate from the Docker host, but you still want other parts of the container to be isolated.&lt;/p&gt;

&lt;p&gt;Docker's networking system could initially feel overwhelming, but I hope this gives you some clarity. &lt;/p&gt;

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

&lt;p&gt;If you have any questions or comments, you can leave them below, or reach out to me on &lt;a href="https://www.linkedin.com/in/davidomokhodion/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. Till we meet again...&lt;/p&gt;

&lt;p&gt;Stay awesome!&lt;br&gt;
~ David Omokhodion&lt;/p&gt;



&lt;h2&gt;
  
  
  Attributions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spacelift.io/blog/docker-networking#how-docker-networking-works" rel="noopener noreferrer"&gt;Docker Networking – Basics, Network Types &amp;amp; Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tonylixu.medium.com/docker-container-network-basics-2d7d0e45ccf8" rel="noopener noreferrer"&gt;Docker — Container Network Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kodekloud.com/blog/networking-docker-containers/" rel="noopener noreferrer"&gt;A Quick Guide to Docker Network Types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>kubernetes</category>
      <category>linux</category>
    </item>
    <item>
      <title>How to Provision a Linux EC2 Instance (server) on AWS Step-by-step</title>
      <dc:creator>David Omokhodion</dc:creator>
      <pubDate>Sun, 30 Oct 2022 17:58:52 +0000</pubDate>
      <link>https://dev.to/nobleman97/how-to-provision-a-linux-ec2-instance-server-on-aws-step-by-step-44o2</link>
      <guid>https://dev.to/nobleman97/how-to-provision-a-linux-ec2-instance-server-on-aws-step-by-step-44o2</guid>
      <description>&lt;p&gt;Have you ever needed a server to deploy a web app, complete a lab or something else? I have had need to do both, and AWS always came through for me.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will learn how to set up and connect to your own AWS Linux EC2 instance to meet your needs for a server.&lt;/p&gt;

&lt;p&gt;Let's go...&lt;/p&gt;

&lt;h3&gt;
  
  
  What is an EC2 Instance?
&lt;/h3&gt;

&lt;p&gt;An Amazon EC2 &lt;em&gt;instance&lt;/em&gt; is a virtual server in the AWS cloud. With the Amazon EC2 service, you can configure your instance to run operating system or applications of your choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;When launching an instance, you have to secure it using security groups(which acts like a virtual firewall to control traffic to and from your instance) and key pairs(a proof of identity).&lt;/p&gt;

&lt;p&gt;Here is an architectural diagram of what we will be setting up:&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.amazonaws.com%2Fuploads%2Farticles%2Fjcqogj2i5yre7uh0o0eg.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.amazonaws.com%2Fuploads%2Farticles%2Fjcqogj2i5yre7uh0o0eg.png" alt="ec2 setup architecture" width="409" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An AWS account&lt;/li&gt;
&lt;li&gt;Create key pair and security group by following instructions found &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/get-set-up-for-amazon-ec2.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;To avoid being billed unnecessarily, please ensure to terminate you instance when you complete this tutorial. Instructions for that are included in the last step here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 1: Launch Your Instance
&lt;/h3&gt;

&lt;p&gt;There are several ways to launch an EC2 instance, but in this tutorial, we will use the AWS console.&lt;/p&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open the EC2 console at &lt;a href="https://console.aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;AWS console link&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From the dashboard, click on &lt;strong&gt;Launch instance&lt;/strong&gt;&lt;/p&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F5e1w3e8fdr3vcflcpyfn.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.amazonaws.com%2Fuploads%2Farticles%2F5e1w3e8fdr3vcflcpyfn.png" alt="Launch Instance" width="617" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Next, enter a name for your server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under the &lt;strong&gt;Application and OS Images (Amazon Machine Image)&lt;/strong&gt;, select the "Quick Start" tab and select the Ubuntu image. This will be the OS for your instance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under the Amazon Machine Images(AMI) section, pick an option with the "free tier eligible" label for now.&lt;/p&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F65cnne6gq7q9xbrf16uf.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.amazonaws.com%2Fuploads%2Farticles%2F65cnne6gq7q9xbrf16uf.png" alt="Select image and AMIs" width="775" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Next, under &lt;strong&gt;Instance type&lt;/strong&gt;, choose an option that has "free tier eligible" too. These instance types are hardware configurations for your server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under &lt;strong&gt;Key pair(login)&lt;/strong&gt; choose a key pair you create before, or create one at this step.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: If you launch your instance without a key pair, then you can't connect to it.&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.amazonaws.com%2Fuploads%2Farticles%2F5u4i8th6ovqc70i9eyso.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.amazonaws.com%2Fuploads%2Farticles%2F5u4i8th6ovqc70i9eyso.png" alt="Instance type and key pair" width="599" height="581"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Next, we have to configure &lt;strong&gt;Network settings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose "select existing security group"&lt;/li&gt;
&lt;li&gt;From the "Common security groups" dropdown, pick the security group you configured earlier from the &lt;strong&gt;Prerequisites&lt;/strong&gt; section of this blog. If you didn't do that, go &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/get-set-up-for-amazon-ec2.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;In order to easily access your access your instance, check to see whether the "Auto-assign public ip" is enabled. If not, enable it to allow a public Ip to be associated with your instance when it is launched.&lt;/p&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fogpr8q9uaslb7ygml95f.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.amazonaws.com%2Fuploads%2Farticles%2Fogpr8q9uaslb7ygml95f.png" alt="security group and public IP" width="786" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Keep the other default selections, review the configurations for your instance and confirm instance launch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the &lt;strong&gt;Instances&lt;/strong&gt; screen, you could monitor the status of your instance. It may take some time for the instance to launch. Just wait until the &lt;strong&gt;state&lt;/strong&gt; changes to "running."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also wait for the &lt;strong&gt;Status check&lt;/strong&gt; to pass before attempting to connect to it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Connect to Your Instance (via SSH)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You can't connect to your instance unless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have the

&lt;code&gt;.pem&lt;/code&gt;

file for the key pair you used to create the instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Your security group is configured to allow you to connect via SSH from your computer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...so ensure that you have these setup correctly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;Instances&lt;/strong&gt; page, select your instance and copy the Public IP address.&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.amazonaws.com%2Fuploads%2Farticles%2Fh9tqmu8dyt85qgxq5n5a.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.amazonaws.com%2Fuploads%2Farticles%2Fh9tqmu8dyt85qgxq5n5a.png" alt="Copy public IP Address" width="799" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Head over to your terminal and let's connect to our instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The syntax for connecting to the instance is:&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="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &amp;lt;path_to_pem_file&amp;gt; user@&amp;lt;Instance_public_ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, it was:&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="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/Downloads/Davi-test.pem ubuntu@54.175.160.84
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Successful connection should look something like this:&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.amazonaws.com%2Fuploads%2Farticles%2Fcp36g2ou87ipvmhr0h1z.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.amazonaws.com%2Fuploads%2Farticles%2Fcp36g2ou87ipvmhr0h1z.png" alt="SSH connection successful" width="597" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If ssh complains about the key permission, ensure to change the permission of the .pem file to 400, using chmod. For example, by running:&lt;br&gt;
&lt;br&gt;
&lt;code&gt;sudo chmod 400 &amp;lt;path_to_pem_file&amp;gt;)&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you still can't connect to your instance, see &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/TroubleshootingInstancesConnecting.html" rel="noopener noreferrer"&gt;Troubleshoot Connecting to Your Instance&lt;/a&gt; for assistance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Clean Up Your Instance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Head over to your EC2 instances page and select the instance you just created.&lt;/li&gt;
&lt;li&gt;Click on the "Instance state" dropdown and select &lt;strong&gt;Terminate instance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Amazon will then shutdown and terminate your instance. You will no longer be able to connect to it after termination.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations! You just provisioned a server in the AWS cloud.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed the tutorial.&lt;/p&gt;

&lt;p&gt;If you have any questions or had any difficulty following along, kindly drop a comment below or reached out to me on &lt;a href="https://www.linkedin.com/in/davidomokhodion" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. Till we meet again...&lt;/p&gt;

&lt;p&gt;Stay awesome!&lt;br&gt;
~ David Omokhodion.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Configure Remote Linux Servers Using Ansible</title>
      <dc:creator>David Omokhodion</dc:creator>
      <pubDate>Tue, 18 Oct 2022 09:04:55 +0000</pubDate>
      <link>https://dev.to/nobleman97/how-to-configure-remote-linux-servers-using-ansible-1912</link>
      <guid>https://dev.to/nobleman97/how-to-configure-remote-linux-servers-using-ansible-1912</guid>
      <description>&lt;p&gt;Imagine that you were asked to install a piece of software (e.g apache2) on 250 different Linux servers. &lt;/p&gt;

&lt;p&gt;How would you go about it?&lt;/p&gt;

&lt;p&gt;Well, you could decide to ssh (open a secure connection) into the servers one after the other, and install the software, but that would take an awefully long time to complete.&lt;/p&gt;

&lt;p&gt;A more efficient approach will be to use a configuration management tool like Ansible to automate the process.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will learn how to use Ansible to target your servers and:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Apache web server and&lt;/li&gt;
&lt;li&gt;Change the default timezone of your servers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here, we will use two servers, but the procedure is similar when configuring 250 (or more) servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An AWS account&lt;/li&gt;
&lt;li&gt;A linux machine (could be a VM)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's go...&lt;/p&gt;

&lt;p&gt;From an Architectural standpoint, this is how Ansible works:&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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--N7bVEnyF--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F33ympesmiwld52do90ji.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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--N7bVEnyF--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F33ympesmiwld52do90ji.png" alt="Ansible Architecture" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Install Dependencies
&lt;/h3&gt;

&lt;p&gt;For Ansible to function properly you need to ensure you have Python and softwares-properties-common installed. Run the following commands to install them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install software-properties-common
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install python3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then install ansible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$sudo apt install ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm Ansible installation:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ ansible-playbook -v&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Provision servers on AWS
&lt;/h3&gt;

&lt;p&gt;For this tutorial, I will provision two Ubuntu servers(EC2 instances on AWS). If you don't already know how to do that, follow the instruction &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; &lt;br&gt;
When you provision your servers, ensure that you download the privatekey file(the file with the ".pem" extension), and that you note down the &lt;em&gt;public ip addresses&lt;/em&gt; of the provisioned EC2 instances. &lt;br&gt;
Also, for ease, I like to move my privatekeys to my ~/.ssh/ folder. So mine is located at &lt;strong&gt;~/.ssh/Davi-test.pem&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also ensure that your security group allows ssh and web traffic into the server.&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.amazonaws.com%2Fuploads%2Farticles%2Ft1d381ovmhty8sv5l4fn.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.amazonaws.com%2Fuploads%2Farticles%2Ft1d381ovmhty8sv5l4fn.png" alt="security group settings" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Setup host-inventory
&lt;/h3&gt;

&lt;p&gt;Create a directory for my this project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir ansible_proj &amp;amp;&amp;amp; cd ansible_proj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create your host-inventory file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch host-inventory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, using your favourite text editor (vi, nano or even VScode) add the ip addresses (or hostnames) of your servers. Like this:&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.amazonaws.com%2Fuploads%2Farticles%2F5ybl1pak7bz4jybrp2sg.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.amazonaws.com%2Fuploads%2Farticles%2F5ybl1pak7bz4jybrp2sg.png" alt="host-inventory image" width="698" height="250"&gt;&lt;/a&gt;&lt;br&gt;
and save.&lt;/p&gt;

&lt;p&gt;You would notice that I grouped all my servers under "webservers."&lt;/p&gt;

&lt;p&gt;Grouping targets like this makes it easy to separate different groups of machines and this often comes in handy.&lt;/p&gt;

&lt;p&gt;Next, you need to tell ansible how to locate your host-inventory file. If you don't, ansible will try to get it from /etc/ansible/hosts.&lt;/p&gt;

&lt;p&gt;But since we have the file at ~/ansible_proj, go ahead and do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ANSIBLE_INVENTORY=~/ansible_proj/host-inventory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we're ready to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Create the Ansible Playbook
&lt;/h3&gt;



&lt;p&gt;&lt;code&gt;touch test.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;And then, using your favourite text editor, paste in the following code in the test.yaml file:&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;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;Setup Web Server&lt;/span&gt;
    &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webservers&lt;/span&gt;
    &lt;span class="na"&gt;become&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;become_method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo&lt;/span&gt;
    &lt;span class="na"&gt;tasks&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;Install Apache Server&lt;/span&gt;
        &lt;span class="na"&gt;apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;name=apache2 state=present&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;Set timezone to Africa/Lagos&lt;/span&gt;
        &lt;span class="na"&gt;timezone&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;Africa/Lagos&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and save.&lt;/p&gt;

&lt;p&gt;In the yaml, we target the webserver group using&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;hosts: webservers&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
, and we describe 2 tasks, using the apt and timezone modules respectively.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5: Test the connection
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Before proceeding with this step, ensure that your $ANSIBLE_INVENTORY variable is set in your current bash as describe previously.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Based on the location of the key you got earlier, you will need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ansible --private-key PRIVATEKEY_FILE -u USER HOST_GROUP -m ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want to connect as the "ubuntu" user and target the servers under "webservers" host group. So that will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ansible --private-key ~/.ssh/Davi-test.pem -u ubuntu webservers -m ping
&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.amazonaws.com%2Fuploads%2Farticles%2F4h53w76woieyookdfqq5.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.amazonaws.com%2Fuploads%2Farticles%2F4h53w76woieyookdfqq5.png" alt="ping success" width="697" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Test the Playbook
&lt;/h3&gt;

&lt;p&gt;Before we run our ansible playbook, it is important to test using the "--check" flag along with the ansible-playbook command. 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;$ ansible-playbook --private-key ~/.ssh/Davi-test.pem -u ubuntu test.yaml --check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should look something like this:&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.amazonaws.com%2Fuploads%2Farticles%2Fetuh1y6wdbm283ppll75.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.amazonaws.com%2Fuploads%2Farticles%2Fetuh1y6wdbm283ppll75.png" alt="ansible playbook check successful" width="697" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you go ahead to finally run the playbook, ssh into any of the servers and check whether apache2 is running and confirm the timezone. If you don't know how to ssh into your server, go &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I checked inside one of my servers, and here is what I got:&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.amazonaws.com%2Fuploads%2Farticles%2Ftasmhxow7ljra8l0pidh.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.amazonaws.com%2Fuploads%2Farticles%2Ftasmhxow7ljra8l0pidh.png" alt="image showing apache2 is not installed" width="697" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apache2 was not not installed and timezone was Etc/UTC.&lt;/p&gt;

&lt;p&gt;Now let's run the playbook:&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 7: Run the Ansible playbook
&lt;/h3&gt;

&lt;p&gt;Apply the intended changes to the servers by running the command we used to check, but without the "--check" flag. i.e&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ansible-playbook --private-key ~/.ssh/Davi-test.pem -u ubuntu test.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are no errors and everything goes well, then ssh into any of the servers and try check apache2 and timezone again. Here's what I got:&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.amazonaws.com%2Fuploads%2Farticles%2F385an15sftkie3fxxxuu.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.amazonaws.com%2Fuploads%2Farticles%2F385an15sftkie3fxxxuu.png" alt="Ansible configured timezone and apache2 successfully" width="697" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome!, right? Ansible did it's thing again!.&lt;/p&gt;

&lt;p&gt;Before you leave, here's a little exercise:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Try adjusting the timezone to a different one within the test.yaml and run it again to see what happens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 8: Clean up
&lt;/h3&gt;

&lt;p&gt;Finally, if you don't need the servers for other reasons, ensure that you terminate them from the AWS console to avoid racking up unnecessary bills.&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.amazonaws.com%2Fuploads%2Farticles%2Fq1z6jyee07x3zuoec2hr.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.amazonaws.com%2Fuploads%2Farticles%2Fq1z6jyee07x3zuoec2hr.png" alt="ec2 instances terminated" width="796" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Hey,&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this tutorial, and I hope that you learned a thing or two about Ansible. &lt;/p&gt;

&lt;p&gt;As of the time of writing this tutorial, I just got started with Ansible myself, but I'll be using it a lot from now on because I think it's an awesome tool. &lt;/p&gt;

&lt;p&gt;If you have any questions, or comments, you can leave them below, or reach out to me on &lt;a href="https://www.linkedin.com/in/davidomokhodion" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. Till we meet again...&lt;/p&gt;

&lt;p&gt;Stay awesome!&lt;br&gt;
~ David Omokhodion&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
