<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>AWS &#8211; Blog of Kliment Andreev &#8211; A place so I won&#039;t forget things</title>
	<atom:link href="https://blog.andreev.it/tag/aws/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.andreev.it</link>
	<description></description>
	<lastBuildDate>Tue, 18 Jun 2024 21:19:14 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>AWS: Access an S3 bucket using gateway and interface endpoints (PrivateLink)</title>
		<link>https://blog.andreev.it/2024/06/aws-access-an-s3-bucket-using-gateway-and-interface-endpoints-privatelink/</link>
					<comments>https://blog.andreev.it/2024/06/aws-access-an-s3-bucket-using-gateway-and-interface-endpoints-privatelink/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Tue, 18 Jun 2024 21:19:14 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[gateway endpoint]]></category>
		<category><![CDATA[interface endpoint]]></category>
		<category><![CDATA[PrivateLink]]></category>
		<category><![CDATA[S3]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9930</guid>

					<description><![CDATA[If you have a use case where you need to transfer a lot of&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>If you have a use case where you need to transfer a lot of data back-and-forth between various resources and an S3 bucket, you will definitely benefit if you use gateway or interface endpoints for S3. The monthly bill for regional and zonal transfer will be much less and on top of that it&#8217;s much more secure.<br />
From the AWS documentation, &#8220;<em>Amazon S3 supports both gateway endpoints and interface endpoints. With a gateway endpoint, you can access Amazon S3 from your VPC, without requiring an internet gateway or NAT device for your VPC, and with no additional cost. However, gateway endpoints do not allow access from on-premises networks, from peered VPCs in other AWS Regions, or through a transit gateway. For those scenarios, you must use an interface endpoint, which is available for an additional cost.</em>&#8221;<br />
Keep this table handy as it explains the differences between gateway and interface S3 endpoint.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-01.png"><img fetchpriority="high" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-1024x325.png" alt="" width="1024" height="325" class="aligncenter size-large wp-image-9934" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-1024x325.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-300x95.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-768x243.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-1170x371.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01-585x185.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-01.png 1404w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now that we know the use case and the differences, we&#8217;ll see three different scenarios. For this, you&#8217;ll need access to two different accounts and two different regions.</p>
<ul>
<li>An S3 bucket in account A and an ec2 instance in the same account A and the same region A</li>
<li>An S3 bucket in account A and an ec2 instance in a different account B, but the same region A</li>
<li>An S3 bucket in account A and an ec2 instance in a different account B and a different region B</li>
</ul>
<p>The diagram looks like this.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-03.png"><img decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-03.png" alt="" width="572" height="451" class="aligncenter size-full wp-image-9937" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-03.png 572w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-03-300x237.png 300w" sizes="(max-width: 572px) 100vw, 572px" /></a></p>
<h1>An S3 bucket in account A, ec2 in account A, both in the same region</h1>
<p>Let&#8217;s create an S3 bucket first. We&#8217;ll use AWS CLI with different profiles.</p>
<pre class="brush: bash; title: ; notranslate">
BUCKET_NAME=&quot;myuniquenameforthebucket&quot;
REGION_A=&quot;us-east-2&quot;
aws s3api create-bucket \
    --bucket $BUCKET_NAME \
    --region $REGION_A \
    --create-bucket-configuration LocationConstraint=$REGION_A
</pre>
<p>Let&#8217;s copy a file there.</p>
<pre class="brush: bash; title: ; notranslate">
dd if=/dev/zero of=somefile bs=1024 count=1
aws s3 cp somefile &quot;s3://$BUCKET_NAME/somefile&quot;
</pre>
<p>Using the same profile, create a VPC with one public subnet with the same CIDR as the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
CIDR_A=&quot;192.168.10.0/24&quot;
VPCA_ID=$(aws ec2 create-vpc \
    --cidr-block $CIDR_A \
    --region $REGION_A \
    --tag-specification &#039;ResourceType=vpc,Tags=&#x5B;{Key=Name,Value=VPC-A}]&#039; \
    --output text --query &#039;Vpc.VpcId&#039;)
echo $VPCA_ID
</pre>
<p>Enable DNS and hostnames resolution, needed for SSM. Two separate lines needed.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 modify-vpc-attribute --enable-dns-hostnames &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCA_ID
aws ec2 modify-vpc-attribute --enable-dns-support &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCA_ID
</pre>
<p>Create a default network ACL.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-network-acl --vpc-id $VPCA_ID
</pre>
<p>Create an Internet Gateway and attach it to the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
IGWA_ID=$(aws ec2 create-internet-gateway \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=internet-gateway,Tags=&#x5B;{Key=Name,Value=igw-A}]&#039; \
    --output text --query &#039;InternetGateway.InternetGatewayId&#039;)
echo $IGWA_ID
aws ec2 attach-internet-gateway \
    --internet-gateway-id $IGWA_ID \
    --vpc-id $VPCA_ID
</pre>
<p>Let&#8217;s create a subnet.</p>
<pre class="brush: bash; title: ; notranslate">
SUBA_ID=$(aws ec2 create-subnet \
    --vpc-id $VPCA_ID \
    --cidr-block 192.168.10.0/24 \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=subnet,Tags=&#x5B;{Key=Name,Value=subPubA}]&#039; \
    --output text --query &#039;Subnet.SubnetId&#039;)
echo $SUBA_ID
</pre>
<p>Create a route table.</p>
<pre class="brush: bash; title: ; notranslate">
RTA_ID=$(aws ec2 create-route-table \
     --vpc-id $VPCA_ID \
     --output text --query &#039;RouteTable.RouteTableId&#039;)
echo $RTA_ID
aws ec2 create-route --route-table-id $RTA_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGWA_ID
</pre>
<p>Associate this route table to the public subnet.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 associate-route-table --route-table-id $RTA_ID --subnet-id $SUBA_ID
</pre>
<p>Now, we have to create an EC2 instance where we can test the S3 access. We won&#8217;t be using keys and SSH to access, we&#8217;ll use SSM. For that we need the instance to have access to the Internet which means we need a security group that allows access to Internet.</p>
<pre class="brush: bash; title: ; notranslate">
SGA_ID=$(aws ec2 create-security-group \
    --group-name sgAllowICMPandOutboundAccess --description &quot;Allows ICMP and outbound access&quot; \
    --vpc-id $VPCA_ID \
    --output text --query &#039;GroupId&#039;)
echo $SGA_ID
</pre>
<p>Allow unrestricted egress. By default this rule is there, so don&#8217;t execute the code below.</p>
<pre class="brush: bash; title: ; notranslate">
#aws ec2 authorize-security-group-egress \
    --group-id $SGA_ID \
    --protocol all \
    --cidr &quot;0.0.0.0/0&quot; 
</pre>
<p>Do this one, so we can ping instances from each other when we do VPC peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 authorize-security-group-ingress \
  --group-id $SGA_ID \
  --ip-permissions IpProtocol=icmp,FromPort=-1,ToPort=-1,IpRanges=&quot;&#x5B;{CidrIp=192.168.10.0/24},{CidrIp=192.168.20.0/24},{CidrIp=192.168.30.0/24}]&quot;
</pre>
<p>Let&#8217;s create a role so we can access the EC2 over SSM. First we need a policy that the role can assume.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; &#039;EOF&#039; &gt;&gt; trust-policy.json
{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: &#x5B;
    {
      &quot;Sid&quot;: &quot;&quot;,
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Principal&quot;: {
        &quot;Service&quot;: &quot;ec2.amazonaws.com&quot;
      },
      &quot;Action&quot;: &quot;sts:AssumeRole&quot;
    }
  ]
}
EOF
</pre>
<p>Then create the role and attach the AWS Managed policy for SSM (<strong>AmazonSSMManagerInstanceCore</strong>).</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-role --role-name rolSSM --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore --role-name rolSSM
</pre>
<p>Create instance profile.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-instance-profile --instance-profile-name ipEC2
</pre>
<p>Add the role.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam add-role-to-instance-profile --role-name rolSSM --instance-profile-name ipEC2
</pre>
<p>Create an instance.</p>
<pre class="brush: bash; title: ; notranslate">
AMI_ID=&quot;ami-033fabdd332044f06&quot;
INS_TYPE=&quot;t3.micro&quot;
aws ec2 run-instances \
    --image-id $AMI_ID \
    --instance-type $INS_TYPE \
    --security-group-ids $SGA_ID \
    --subnet-id $SUBA_ID \
    --iam-instance-profile Name=&quot;ipEC2&quot; \
    --associate-public-ip-address  \
    --tag-specifications &#039;ResourceType=instance,Tags=&#x5B;{Key=Name,Value=ec2-A}]&#039;
</pre>
<p>We&#8217;ll need an IAM user that we&#8217;ll use to access the S3 bucket.</p>
<pre class="brush: bash; title: ; notranslate">
IAM_USER=&quot;usrMyBucket&quot;
aws iam create-user --user-name $IAM_USER
</pre>
<p>We&#8217;ll also need a policy that gives access to that specific bucket only.</p>
<pre class="brush: bash; title: ; notranslate">
cat &gt;&gt; bucket-policy.json &lt;&lt; EOF
{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: &#x5B;
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &quot;s3:*&quot;,
            &quot;Resource&quot;: &#x5B;
                &quot;arn:aws:s3:::$BUCKET_NAME&quot;,
                &quot;arn:aws:s3:::$BUCKET_NAME/*&quot;
            ]
        },
        {
            &quot;Effect&quot;: &quot;Deny&quot;,
            &quot;NotAction&quot;: &quot;s3:*&quot;,
            &quot;NotResource&quot;: &#x5B;
                &quot;arn:aws:s3:::$BUCKET_NAME&quot;,
                &quot;arn:aws:s3:::$BUCKET_NAME/*&quot;
            ]
        }
    ]
}
EOF
</pre>
<p>Create the policy.</p>
<pre class="brush: bash; title: ; notranslate">
POL_ARN=$(aws iam create-policy \
    --policy-name polMyBucketAccess \
    --policy-document file://bucket-policy.json \
    --output text --query &#039;Policy.Arn&#039;)
echo $POL_ARN
</pre>
<p>Attach the policy to the user.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam attach-user-policy --policy-arn $POL_ARN --user-name $IAM_USER
</pre>
<p>Create access keys.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-access-key --user-name $IAM_USER --output table --query &#x5B;&#039;AccessKey.AccessKeyId&#039;,&#039;AccessKey.SecretAccessKey&#039;]
</pre>
<p>Copy these two values.<br />
Now, go to AWS console, select the EC2 instance and click <strong>Connect</strong>. Use the 2nd tab <strong>Session Manager</strong> and click <strong>Connect</strong>.<br />
Type <em>aws configure</em> and paste the copied values for the IAM user you just created.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-04.png"><img decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-04.png" alt="" width="940" height="183" class="aligncenter size-full wp-image-9967" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-04.png 940w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-04-300x58.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-04-768x150.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-04-585x114.png 585w" sizes="(max-width: 940px) 100vw, 940px" /></a><br />
If you try to do <em>aws s3 ls</em>, you&#8217;ll get access denied, but if you try <em>aws s3 ls s3://myuniquenameforthebucket</em>, you&#8217;ll see that it works.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-1024x159.png" alt="" width="1024" height="159" class="aligncenter size-large wp-image-9968" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-1024x159.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-300x47.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-768x119.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05-585x91.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-05.png 1158w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now, this type of access goes over the Internet and you&#8217;ll be charged for the data transfers. In order to avoid that, we&#8217;ll create an S3 gateway endpoint.<br />
Go to <strong>VPC </strong>menu and from the left side choose <strong>Endpoints</strong>.<br />
Create the endpoint, name the endpoint, choose <strong>AWS services</strong>, filter by <strong>Type = Gateway</strong> and select the S3 service name. Then select the VPC where you created your EC2 and the route table that&#8217;s associated with the subnet where EC2 resides. For policy choose Full Access. This means, the S3 gateway endpoint will have access to all S3 buckets. Finally, click to create the endpoint.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1024x573.png" alt="" width="1024" height="573" class="aligncenter size-large wp-image-9969" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1024x573.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-300x168.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-768x430.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1536x860.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-2048x1146.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1920x1075.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-1170x655.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-06-585x327.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
If you check your route table, you&#8217;ll see that there is another entry there. Wait 10 seconds or less if you don&#8217;t see it.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-1024x650.png" alt="" width="1024" height="650" class="aligncenter size-large wp-image-9970" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-1024x650.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-300x190.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-768x488.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-1170x743.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07-585x371.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-07.png 1315w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
If you go back to the EC2 instance and do the <em>aws s3 ls s3://myuniquenameforthebucket</em> command again, you&#8217;ll see that nothing changed. So, how do you know that we did the right thing? It&#8217;s simple. Click on the endpoint that you&#8217;ve created, click on the <strong>Policy </strong>tab and then click the <strong>Edit Policy</strong> button.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1024x561.png" alt="" width="1024" height="561" class="aligncenter size-large wp-image-9971" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1024x561.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-300x164.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-768x421.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1536x842.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-2048x1122.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1920x1052.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-1170x641.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-08-585x321.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Change line 5 from <em>Allow </em>to <em>Deny </em>and click <strong>Save</strong>.<br />
Go back to the EC2 instance and do the <em>aws s3 ls s3://myuniquenameforthebucket</em> again. You&#8217;ll get access denied. There you go&#8230;Traffic goes over the endpoint. Revert the changes back from <em>Deny </em>to <em>Allow</em>. Let&#8217;s move to the 2nd use case.</p>
<h1>An S3 bucket in account A, an ec2 instance in a different account B, but both in the same region A</h1>
<p><strong>NOTE:Make sure you switch your AWS CLI to a different profile, because we&#8217;ll be building the EC2 instance in another account.</strong> The S3 bucket stays the same.<br />
Let&#8217;s build the 2nd VPC and EC2.</p>
<pre class="brush: bash; title: ; notranslate">
REGION_A=&quot;us-east-2&quot;
CIDR_B=&quot;192.168.20.0/24&quot;
VPCB_ID=$(aws ec2 create-vpc \
    --cidr-block $CIDR_B \
    --region $REGION_A \
    --tag-specification &#039;ResourceType=vpc,Tags=&#x5B;{Key=Name,Value=VPC-B}]&#039; \
    --output text --query &#039;Vpc.VpcId&#039;)
echo $VPCB_ID
</pre>
<p>Enable DNS and hostnames resolution, needed for SSM. Two separate lines needed.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 modify-vpc-attribute --enable-dns-hostnames &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCB_ID
aws ec2 modify-vpc-attribute --enable-dns-support &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCB_ID
</pre>
<p>Create a default network ACL.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-network-acl --vpc-id $VPCB_ID
</pre>
<p>Create an Internet Gateway and attach it to the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
IGWB_ID=$(aws ec2 create-internet-gateway \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=internet-gateway,Tags=&#x5B;{Key=Name,Value=igw-B}]&#039; \
    --output text --query &#039;InternetGateway.InternetGatewayId&#039;)
echo $IGWB_ID
aws ec2 attach-internet-gateway \
    --internet-gateway-id $IGWB_ID \
    --vpc-id $VPCB_ID
</pre>
<p>Let&#8217;s create a subnet.</p>
<pre class="brush: bash; title: ; notranslate">
SUBB_ID=$(aws ec2 create-subnet \
    --vpc-id $VPCB_ID \
    --cidr-block 192.168.20.0/24 \
    --region $REGION_A \
    --tag-specifications &#039;ResourceType=subnet,Tags=&#x5B;{Key=Name,Value=subPubB}]&#039; \
    --output text --query &#039;Subnet.SubnetId&#039;)
echo $SUBB_ID
</pre>
<p>Create a route table.</p>
<pre class="brush: bash; title: ; notranslate">
RTB_ID=$(aws ec2 create-route-table \
     --vpc-id $VPCB_ID \
     --output text --query &#039;RouteTable.RouteTableId&#039;)
echo $RTB_ID
aws ec2 create-route --route-table-id $RTB_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGWB_ID
</pre>
<p>Associate this route table to the public subnet.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 associate-route-table --route-table-id $RTB_ID --subnet-id $SUBB_ID
</pre>
<p>Now, we have to create an EC2 instance where we can test the S3 access. We won&#8217;t be using keys and SSH to access, we&#8217;ll use SSM. For that we need the instance to have access to the Internet which means we need a security group that allows access to Internet.</p>
<pre class="brush: bash; title: ; notranslate">
SGB_ID=$(aws ec2 create-security-group \
    --group-name sgAllowICMPandOutboundAccess --description &quot;Allows ICMP and outbound access&quot; \
    --vpc-id $VPCB_ID \
    --output text --query &#039;GroupId&#039;)
echo $SGB_ID
</pre>
<p>Allow unrestricted egress. By default this rule is there, so don&#8217;t execute the code below.</p>
<pre class="brush: bash; title: ; notranslate">
#aws ec2 authorize-security-group-egress \
    --group-id $SGB_ID \
    --protocol all \
    --cidr &quot;0.0.0.0/0&quot; 
</pre>
<p>Do this one, so we can ping instances from each other when we do VPC peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 authorize-security-group-ingress \
  --group-id $SGB_ID \
  --ip-permissions IpProtocol=icmp,FromPort=-1,ToPort=-1,IpRanges=&quot;&#x5B;{CidrIp=192.168.10.0/24},{CidrIp=192.168.20.0/24},{CidrIp=192.168.30.0/24}]&quot;
</pre>
<p>We can reuse the same policy to create a role that will allow us to use SSM.</p>
<pre class="brush: bash; title: ; notranslate">
cat trust-policy.json
</pre>
<p>Then create the role and attach the AWS Managed policy for SSM (<strong>AmazonSSMManagerInstanceCore</strong>).</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-role --role-name rolSSM --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore --role-name rolSSM
</pre>
<p>Create instance profile. We&#8217;ll use the same names, because they are different accounts.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam create-instance-profile --instance-profile-name ipEC2
</pre>
<p>Add the role.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam add-role-to-instance-profile --role-name rolSSM --instance-profile-name ipEC2
</pre>
<p>Create an instance. The AMI_ID is the same because it&#8217;s the same region.</p>
<pre class="brush: bash; title: ; notranslate">
AMI_ID=&quot;ami-033fabdd332044f06&quot;
INS_TYPE=&quot;t3.micro&quot;
aws ec2 run-instances \
    --image-id $AMI_ID \
    --instance-type $INS_TYPE \
    --security-group-ids $SGB_ID \
    --subnet-id $SUBB_ID \
    --iam-instance-profile Name=&quot;ipEC2&quot; \
    --associate-public-ip-address  \
    --tag-specifications &#039;ResourceType=instance,Tags=&#x5B;{Key=Name,Value=ec2-B}]&#039;
</pre>
<p>Wait for a couple of minutes and then connect from SSM, same as you did with the first instance. Configure AWS again using the same set of credentials that you did before and then try to list the content of the bucket. You should be able to see the file.</p>
<pre class="brush: bash; title: ; notranslate">
BUCKET_NAME=&quot;myuniquenameforthebucket&quot;
aws s3 ls s3://$BUCKET_NAME
</pre>
<p>Again, the traffic goes over the Internet. So we need another gateway S3 endpoint. You create the gateway S3 endpoint where the client is located, not the destination.<br />
Let&#8217;s create another S3 gateway endpoint as described above.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1024x505.png" alt="" width="1024" height="505" class="aligncenter size-large wp-image-9977" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1024x505.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-300x148.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-768x379.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1536x758.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1920x948.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-1170x577.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09-585x289.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-09.png 2020w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Check the route table and you&#8217;ll see the route created. At this point, you are accessing the bucket over the endpoint. Do the same test. Change the policy to Deny for the endpoint and you&#8217;ll get access denied.</p>
<h1>An S3 bucket in account A, an ec2 instance in a different account B and a different region B</h1>
<p>For the 3rd use case, we&#8217;ll use an interface endpoint (PrivateLink), not a gateway endpoint. This is what AWS documentation has to say. &#8220;<em>With AWS PrivateLink for Amazon S3, you can provision interface VPC endpoints (interface endpoints) in your virtual private cloud (VPC). These endpoints are directly accessible from applications that are on premises over VPN and AWS Direct Connect, or in a different AWS Region over VPC peering. Interface endpoints are represented by one or more elastic network interfaces (ENIs) that are assigned private IP addresses from subnets in your VPC. Requests to Amazon S3 over interface endpoints stay on the Amazon network. You can also access interface endpoints in your VPC from on-premises applications through AWS Direct Connect or AWS Virtual Private Network (AWS VPN).</em>&#8221;<br />
Let&#8217;s create the resources. VPC and EC2. Change the AWS CLI profile so it&#8217;s using the same account B and a new region B.</p>
<pre class="brush: bash; title: ; notranslate">
REGION_B=&quot;us-east-1&quot;
CIDR_C=&quot;192.168.30.0/24&quot;
VPCC_ID=$(aws ec2 create-vpc \
    --cidr-block $CIDR_C \
    --region $REGION_B \
    --tag-specification &#039;ResourceType=vpc,Tags=&#x5B;{Key=Name,Value=VPC-C}]&#039; \
    --output text --query &#039;Vpc.VpcId&#039;)
echo $VPCC_ID
</pre>
<p>Enable DNS and hostnames resolution, needed for SSM. Two separate lines needed.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 modify-vpc-attribute --enable-dns-hostnames &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCC_ID
aws ec2 modify-vpc-attribute --enable-dns-support &quot;{\&quot;Value\&quot;:true}&quot;  --vpc-id $VPCC_ID
</pre>
<p>Create a default network ACL.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-network-acl --vpc-id $VPCC_ID
</pre>
<p>Create an Internet Gateway and attach it to the VPC.</p>
<pre class="brush: bash; title: ; notranslate">
IGWC_ID=$(aws ec2 create-internet-gateway \
    --region $REGION_B \
    --tag-specifications &#039;ResourceType=internet-gateway,Tags=&#x5B;{Key=Name,Value=igw-C}]&#039; \
    --output text --query &#039;InternetGateway.InternetGatewayId&#039;)
echo $IGWC_ID
aws ec2 attach-internet-gateway \
    --internet-gateway-id $IGWC_ID \
    --vpc-id $VPCC_ID
</pre>
<p>Let&#8217;s create a subnet.</p>
<pre class="brush: bash; title: ; notranslate">
SUBC_ID=$(aws ec2 create-subnet \
    --vpc-id $VPCC_ID \
    --cidr-block 192.168.30.0/24 \
    --region $REGION_B \
    --tag-specifications &#039;ResourceType=subnet,Tags=&#x5B;{Key=Name,Value=subPubC}]&#039; \
    --output text --query &#039;Subnet.SubnetId&#039;)
echo $SUBC_ID
</pre>
<p>Create a route table.</p>
<pre class="brush: bash; title: ; notranslate">
RTC_ID=$(aws ec2 create-route-table \
     --vpc-id $VPCC_ID \
     --output text --query &#039;RouteTable.RouteTableId&#039;)
echo $RTC_ID
aws ec2 create-route --route-table-id $RTC_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGWC_ID
</pre>
<p>Associate this route table to the public subnet.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 associate-route-table --route-table-id $RTC_ID --subnet-id $SUBC_ID
</pre>
<p>Now, we have to create an EC2 instance where we can test the S3 access. We won&#8217;t be using keys and SSH to access, we&#8217;ll use SSM. For that we need the instance to have access to the Internet which means we need a security group that allows access to Internet.</p>
<pre class="brush: bash; title: ; notranslate">
SGC_ID=$(aws ec2 create-security-group \
    --group-name sgAllowICMPandOutboundAccess --description &quot;Allows ICMP and outbound access&quot; \
    --vpc-id $VPCC_ID \
    --output text --query &#039;GroupId&#039;)
echo $SGC_ID
</pre>
<p>Allow unrestricted egress. By default this rule is there, so don&#8217;t execute the code below.</p>
<pre class="brush: bash; title: ; notranslate">
#aws ec2 authorize-security-group-egress \
    --group-id $SGB_ID \
    --protocol all \
    --cidr &quot;0.0.0.0/0&quot; 
</pre>
<p>Do this one, so we can ping instances from each other when we do VPC peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 authorize-security-group-ingress \
  --group-id $SGC_ID \
  --ip-permissions IpProtocol=icmp,FromPort=-1,ToPort=-1,IpRanges=&quot;&#x5B;{CidrIp=192.168.10.0/24},{CidrIp=192.168.20.0/24},{CidrIp=192.168.30.0/24}]&quot;
</pre>
<p>We don&#8217;t have to create any roles. We&#8217;ll use the existing ones that we built for the 2nd use case.<br />
Create the EC2. The AMI ID will change this time.</p>
<pre class="brush: bash; title: ; notranslate">
AMI_ID=&quot;ami-08a0d1e16fc3f61ea&quot;
INS_TYPE=&quot;t3.micro&quot;
aws ec2 run-instances \
    --image-id $AMI_ID \
    --instance-type $INS_TYPE \
    --security-group-ids $SGC_ID \
    --subnet-id $SUBC_ID \
    --iam-instance-profile Name=&quot;ipEC2&quot; \
    --associate-public-ip-address  \
    --tag-specifications &#039;ResourceType=instance,Tags=&#x5B;{Key=Name,Value=ec2-C}]&#039;
</pre>
<p>Same thing as we did before, connect from SSM, configure AWS CLI using the previous credentials and try to list the bucket.</p>
<pre class="brush: bash; title: ; notranslate">
BUCKET_NAME=&quot;myuniquenameforthebucket&quot;
aws s3 ls s3://$BUCKET_NAME
</pre>
<p>You should see the file there. But, this access goes over Internet. Now, we have to create an interface endpoint in the destination VPC which is VPC-A. When we create an interface endpoint a private IP will be generated for this interface endpoint. In order this private IP to access the bucket, we need some sort of connection between regions, e.g. VPC peering or Transit Gateway. Let&#8217;s do the peering between account A (VPC-A) and account B (VPC-C). Enter the 12 digit account number for account A where the S3 bucket resides.</p>
<pre class="brush: bash; title: ; notranslate">
echo $VPCA_ID
echo $VPCC_ID
echo $REGION_A
ACCOUNT_A=&quot;123456789012&quot;
echo $ACCOUNT_A
</pre>
<p>Make sure all of the above returns some value. Do the peering.</p>
<pre class="brush: bash; title: ; notranslate">
aws ec2 create-vpc-peering-connection \
  --vpc-id $VPCC_ID --peer-vpc-id $VPCA_ID \
  --peer-owner-id $ACCOUNT_A --peer-region $REGION_A
</pre>
<p>Log to the account A and under <strong>VPC | Peering</strong> menu on the left, select the peering and accept it. But this is not enough. You need to tell the VPC to use the routes over the peering connection. Go to account A, VPC A, find the route for the subnet where the EC2 instance is and add a route for destination 192.168.30.0/24 to go over peering. As you can see this is the same route that we modified initially in our first use case.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-10.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1024x575.png" alt="" width="1024" height="575" class="aligncenter size-large wp-image-9985" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1024x575.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-300x168.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-768x431.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1200x675.png 1200w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-1170x657.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10-585x328.png 585w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-10.png 1290w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now, log to account B and VPC C and modify the route but this time, the destination is VPC A (192.168.10.0/24). At this point you should be able to ping EC2-A from EC2-C. Don&#8217;t worry if you can&#8217;t ping EC2-C from EC2-A.<br />
At this point we can create the interface endpoint where the bucket is. This time choose interface as type, VPC-A, our security group, full access policy and the subnet where EC2 is. This subnet has nothing to do with the bucket access, we are just creating a network interface there. We can choose any subnet in that VPC, we just need the IP assigned to the endpoint interface.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-11.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1024x835.png" alt="" width="1024" height="835" class="aligncenter size-large wp-image-9994" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1024x835.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-300x245.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-768x626.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1536x1252.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-2048x1669.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1920x1565.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-1170x954.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-11-585x477.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
There will be 2 DNS names created for you. One is referring the zone and the other the region. Use the regional DNS name, the first one and add the word bucket instead of the * character.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/06/P174-12.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/06/P174-12.png" alt="" width="913" height="526" class="aligncenter size-full wp-image-9995" srcset="https://blog.andreev.it/wp-content/uploads/2024/06/P174-12.png 913w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-12-300x173.png 300w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-12-768x442.png 768w, https://blog.andreev.it/wp-content/uploads/2024/06/P174-12-585x337.png 585w" sizes="(max-width: 913px) 100vw, 913px" /></a><br />
From instance EC2-C, this is how you will access the bucket.</p>
<pre class="brush: bash; title: ; notranslate">
aws s3 ls s3://myuniquenameforthebucket --endpoint-url https://bucket.vpce-038d5f94d78d8b216-vyvk5uon.s3.us-east-2.vpce.amazonaws.com
</pre>
<p>If you try from the instance EC2-C, this won&#8217;t work. Why? Because of the security group that we used. We have to allow HTTPS traffic from 192.168.30.0/24. Go find the first security group, that&#8217;s the one we used for the interface endpoint and allow HTTPS from 192.168.30.0/24. After the change, you&#8217;ll be able to access the bucket.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2024/06/aws-access-an-s3-bucket-using-gateway-and-interface-endpoints-privatelink/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Okta and AWS SSO (AWS IAM Identity Center)</title>
		<link>https://blog.andreev.it/2024/03/okta-and-aws-sso-aws-iam-identity-center/</link>
					<comments>https://blog.andreev.it/2024/03/okta-and-aws-sso-aws-iam-identity-center/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Wed, 20 Mar 2024 17:25:23 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[AWS IAM Identity Center]]></category>
		<category><![CDATA[OKTA]]></category>
		<category><![CDATA[SSO]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9783</guid>

					<description><![CDATA[In this post I&#8217;ll describe how to integrate Okta and your AWS account. In&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>In this post I&#8217;ll describe how to integrate Okta and your AWS account. In order everything to work, you&#8217;ll have to enable AWS Organization on the master account and then you can add multiple sub accounts in AWS.<br />
Best practice says that the AWS master account should be used for SSO only and you shouldn&#8217;t run any workloads. First thing first, let&#8217;s start with Okta. </p>
<h1>Okta</h1>
<p>Log to your Okta account or sign up for a 30 day trial. Once logged in, if you haven&#8217;t already done so, change the sign-in domain from trail*-okta.com to your domain. Or you can skip this step.<br />
Click on <strong>Admin </strong>from the upper right corner and then under <strong>Customizations</strong>, click on <strong>Domain</strong>. I&#8217;ve used the Okta-managed customization because it&#8217;s easier to manage.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-1024x507.png" alt="" width="1024" height="507" class="aligncenter size-large wp-image-9784" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-1024x507.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-300x148.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-768x380.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-1536x760.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-2048x1014.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-1920x950.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-1170x579.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-01-585x290.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
After you click <strong>Next</strong>, you&#8217;ll be prompted to add two DNS entries for your domain.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-1024x575.png" alt="" width="1024" height="575" class="aligncenter size-large wp-image-9785" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-1024x575.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-300x168.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-768x431.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-1536x862.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-1200x675.png 1200w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-1170x657.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-02-585x328.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-02.png 1579w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Change the DNS. This will allow us to access Okta dashboard using our domain in my case it&#8217;s okta.andreev.it. From the same menu on the left, click on <strong>Applications</strong>, then <strong>Applications </strong>again and click on <strong>Browse App Catalog</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-03.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-03-1024x584.png" alt="" width="1024" height="584" class="aligncenter size-large wp-image-9824" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-03-1024x584.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-03-300x171.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-03-768x438.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-03-1170x668.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-03-585x334.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-03.png 1230w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Search for <strong>AWS IAM</strong> and choose <strong>AWS IAM Identity Center</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-04-1024x477.png" alt="" width="1024" height="477" class="aligncenter size-large wp-image-9825" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-04-1024x477.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-04-300x140.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-04-768x358.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-04-585x273.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-04.png 1120w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Click on <strong>Add Integration</strong> and then <strong>Done</strong>.<br />
Click on the<strong> Sign On</strong> tab and scroll all the way down to <strong>SAML Signing Certificates</strong>. Click on <strong>Actions </strong>drop-down and then <strong>View IdP metadata</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-05-1024x386.png" alt="" width="1024" height="386" class="aligncenter size-large wp-image-9830" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-05-1024x386.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-05-300x113.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-05-768x290.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-05-1170x441.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-05-585x221.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-05.png 1228w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
This will open a new tab with an XML file. Save the file as <em>metadata.xml</em>.</p>
<h1>AWS</h1>
<p>Go to console.aws.amazon.com and log with your root account. Go to <strong>IAM Identity Center</strong> and click on <strong>Enable</strong> and then select  <strong>Enable with AWS Organizations</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-06.png" alt="" width="594" height="357" class="aligncenter size-full wp-image-9831" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-06.png 594w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-06-300x180.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-06-585x352.png 585w" sizes="(max-width: 594px) 100vw, 594px" /></a><br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-07-1024x789.png" alt="" width="1024" height="789" class="aligncenter size-large wp-image-9832" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-07-1024x789.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-07-300x231.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-07-768x591.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-07-1170x901.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-07-585x450.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-07.png 1309w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Click on <strong>Settings </strong>on the left, then <strong>Actions </strong>drop-down and click on <strong>Customize AWS access portal URL</strong>. Change the URL so it&#8217;s something that you can easily remember, e.g. yourorg or aws-master.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-10.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-1024x354.png" alt="" width="1024" height="354" class="aligncenter size-large wp-image-9836" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-1024x354.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-300x104.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-768x266.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-1536x532.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-2048x709.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-1920x664.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-1170x405.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-10-585x202.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Click on <strong>Settings </strong>on the left, then <strong>Actions </strong>drop-down and click on <strong>Change identity source</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-1024x320.png" alt="" width="1024" height="320" class="aligncenter size-large wp-image-9833" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-1024x320.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-300x94.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-768x240.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-1536x481.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-2048x641.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-1920x601.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-1170x366.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-08-585x183.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Choose <strong>External identity provider</strong> and click <strong>Next</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-09-1024x579.png" alt="" width="1024" height="579" class="aligncenter size-large wp-image-9834" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-09-1024x579.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-09-300x170.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-09-768x434.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-09-1170x663.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-09-585x331.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-09.png 1291w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
You&#8217;ll land on a page with two parts, <em>Service provider metadata</em> (that&#8217;s AWS) and <em>Identity provider metadata</em> (that&#8217;s Okta).<br />
Click on <strong>Download metadata file</strong> from upper right. You&#8217;ll need this in Okta. The file will be named with some date prefix and some characters plus metadata.xml<br />
Copy the values for the 2nd and 3rd entry. You&#8217;ll also need these for Okta.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-11.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-11-1024x296.png" alt="" width="1024" height="296" class="aligncenter size-large wp-image-9838" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-11-1024x296.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-11-300x87.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-11-768x222.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-11-585x169.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-11.png 1155w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Click on <strong>Choose file</strong> under <em>Identity provider metadata</em> and upload the <em>metadata.xml</em> file that you&#8217;ve downloaded earlier from Okta.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-12.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-12.png" alt="" width="649" height="319" class="aligncenter size-full wp-image-9839" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-12.png 649w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-12-300x147.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-12-585x288.png 585w" sizes="(max-width: 649px) 100vw, 649px" /></a><br />
Click <strong>Next</strong>, type <strong>ACCEPT </strong>and finally click <strong>Change identity source</strong>.</p>
<h1>Okta</h1>
<p>Go back to Okta, <strong>Sign On</strong> tab and right bellow click <strong>Edit</strong>. Enter the two URL entries that you copied from AWS. Click <strong>Save </strong>after.</p>
<h1>AWS</h1>
<p>Go back to AWS, then <strong>IAM Identity Center</strong>, click on <strong>Settings </strong>and click to <strong>Enable </strong>the <em>Automatic Provisioning</em>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-14.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-1024x144.png" alt="" width="1024" height="144" class="aligncenter size-large wp-image-9841" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-1024x144.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-300x42.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-768x108.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-1536x216.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-2048x287.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-1920x269.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-1170x164.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-14-585x82.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Click on <strong>Show Token</strong> and copy both values for the <strong>SCIM endpoint </strong>and the <strong>Access Token</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-15.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-15-1024x632.png" alt="" width="1024" height="632" class="aligncenter size-large wp-image-9842" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-15-1024x632.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-15-300x185.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-15-768x474.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-15-1170x722.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-15-585x361.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-15.png 1216w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Okta</h1>
<p>Go back to Okta and for the AWS app, click on <strong>Provisioning </strong>and then click on <strong>Configure API Integration</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-16.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-16-1024x759.png" alt="" width="1024" height="759" class="aligncenter size-large wp-image-9843" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-16-1024x759.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-16-300x222.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-16-768x569.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-16-1170x867.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-16-585x434.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-16.png 1345w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Click <strong>Enable API Integration</strong> and enter the SCIM endpoint and the token value in the corresponding fields. Click <strong>Save</strong>. Remove the trailing slash from the end of the Base URL (SCIM endpoint) if you get an error (<em>Base URL: Does not match required pattern</em>)<br />
Under <strong>Provisioning </strong>tab, click on <strong>To App</strong>, click <strong>Edit </strong>and select all three checkmarks. Click <strong>Save</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-18.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-18-1024x755.png" alt="" width="1024" height="755" class="aligncenter size-large wp-image-9844" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-18-1024x755.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-18-300x221.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-18-768x567.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-18-1536x1133.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-18-1170x863.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-18-585x432.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-18.png 1575w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Then, go to <strong>Assignments </strong>tab and assign a group or a user to this AWS app.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-19.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-19-1024x441.png" alt="" width="1024" height="441" class="aligncenter size-large wp-image-9846" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-19-1024x441.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-19-300x129.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-19-768x331.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-19-1170x504.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-19-585x252.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-19.png 1222w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>AWS</h1>
<p>Go back to AWS and under <strong>IAM Identity Center</strong> and then <strong>Users</strong>, you can see the user that we just assigned.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-20.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-20-1024x337.png" alt="" width="1024" height="337" class="aligncenter size-large wp-image-9847" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-20-1024x337.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-20-300x99.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-20-768x253.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-20-1170x385.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-20-585x193.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-20.png 1279w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
But this user has no rights in AWS, so we have to create a Permission set and then assign this user to that permission set and the account that this user has access to. Click on <strong>Permission sets</strong> on the left and then <strong>Create permission set</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-21.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-21-1024x381.png" alt="" width="1024" height="381" class="aligncenter size-large wp-image-9848" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-21-1024x381.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-21-300x112.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-21-768x286.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-21-585x218.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-21.png 1050w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
I chose <strong>AdministratorAccess </strong>from the <strong>Predefined permission sets</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-22.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-22.png" alt="" width="657" height="616" class="aligncenter size-full wp-image-9849" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-22.png 657w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-22-300x281.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-22-585x548.png 585w" sizes="(max-width: 657px) 100vw, 657px" /></a><br />
Go back to <strong>AWS accounts</strong>, click on the account and choose <strong>Assign users or groups</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-23.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-23-1024x380.png" alt="" width="1024" height="380" class="aligncenter size-large wp-image-9850" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-23-1024x380.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-23-300x111.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-23-768x285.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-23-1536x570.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-23-1170x434.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-23-585x217.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-23.png 1642w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Select the <strong>User</strong> tab, click on the user, click <strong>Next</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-24.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-24-1024x641.png" alt="" width="1024" height="641" class="aligncenter size-large wp-image-9851" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-24-1024x641.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-24-300x188.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-24-768x481.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-24-1170x732.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-24-585x366.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-24.png 1270w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Select the permission set that we just created and click <strong>Next </strong>then <strong>Submit</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-25.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-25.png" alt="" width="766" height="582" class="aligncenter size-full wp-image-9852" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-25.png 766w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-25-300x228.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-25-585x444.png 585w" sizes="(max-width: 766px) 100vw, 766px" /></a><br />
We just told AWS that the user xyz will have access to the account abc with AdministratorAccess permissions.</p>
<h1>Test SSO</h1>
<p>Open a new tab and go to the URL that you configured earlier (<em>Customize AWS access portal</em>). It should be https://whatever.awsapps.com/start.<br />
AWS will redirect you to Okta where you&#8217;ll enter your Okta username.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-26.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-26.png" alt="" width="693" height="886" class="aligncenter size-full wp-image-9853" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-26.png 693w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-26-235x300.png 235w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-26-585x748.png 585w" sizes="(max-width: 693px) 100vw, 693px" /></a><br />
Once authenticated, you&#8217;ll see the AWS accounts in your org and the permissions that you have.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P170-27.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P170-27.png" alt="" width="904" height="577" class="aligncenter size-full wp-image-9854" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P170-27.png 904w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-27-300x191.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-27-768x490.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P170-27-585x373.png 585w" sizes="(max-width: 904px) 100vw, 904px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2024/03/okta-and-aws-sso-aws-iam-identity-center/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Entra ID (Azure AD) and AWS SSO (AWS IAM Identity Center)</title>
		<link>https://blog.andreev.it/2024/03/entra-id-azure-ad-and-aws-sso/</link>
					<comments>https://blog.andreev.it/2024/03/entra-id-azure-ad-and-aws-sso/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Mon, 11 Mar 2024 18:58:39 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[AWS IAM Identity Center]]></category>
		<category><![CDATA[Azure AD]]></category>
		<category><![CDATA[entra ID]]></category>
		<category><![CDATA[SSO]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9787</guid>

					<description><![CDATA[I wrote about this topic a couple of years ago, but some things changed&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>I wrote about this <a href="https://blog.andreev.it/2021/06/azure-aws-use-azure-ad-with-aws-sso/" rel="noopener" target="_blank">topic </a>a couple of years ago, but some things changed so I am writing this post again. In this post I&#8217;ll explain how to log to AWS Console and AWS cli with Entra ID (former Azure AD) credentials. For this you&#8217;ll need admin access to both the AWS account and Entra ID. </p>
<h1>IAM Identity Center</h1>
<p>The former AWS Single Sign-on is now IAM Identity Center. You need to enable this in order to configure SSO. Go to <strong>IAM Identity Center</strong> and click on the <strong>Enable</strong> button.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-01-1024x277.png" alt="" width="1024" height="277" class="aligncenter size-large wp-image-9788" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-01-1024x277.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-01-300x81.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-01-768x208.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-01-1536x415.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-01-1170x316.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-01-585x158.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-01.png 1887w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Enable it with AWS Organizations.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-02-1024x828.png" alt="" width="1024" height="828" class="aligncenter size-large wp-image-9789" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-02-1024x828.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-02-300x243.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-02-768x621.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-02-1170x947.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-02-585x473.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-02.png 1246w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
What you want to do first is to change the AWS access portal URL. Click on the <strong>Edit </strong>button and change the URL. It has to be unique, so don&#8217;t expect you can type aws and be done with it. </p>
<p><a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-03-1.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-03-1.png" alt="" width="796" height="823" class="aligncenter size-full wp-image-9821" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-03-1.png 796w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-03-1-290x300.png 290w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-03-1-768x794.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-03-1-585x605.png 585w" sizes="(max-width: 796px) 100vw, 796px" /></a></p>
<p>Another setting that you have to change is the <strong>Identity source</strong>. On the left side of the <strong>IAM Identity Center</strong>, you are on the <strong>Dashboard </strong>screen. Click on <strong>Settings </strong>and then click on <strong>Change identity source</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-1024x450.png" alt="" width="1024" height="450" class="aligncenter size-large wp-image-9791" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-1024x450.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-300x132.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-768x337.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-1536x675.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-2048x900.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-1920x844.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-1170x514.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-04-585x257.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Choose <strong>External identity provider</strong> and click <strong>Next</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-05-1024x539.png" alt="" width="1024" height="539" class="aligncenter size-large wp-image-9792" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-05-1024x539.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-05-300x158.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-05-768x404.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-05-1170x616.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-05-585x308.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-05.png 1305w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
You will see this screen which consists of two parts, service provider metatada (that&#8217;s AWS) and Identity provider metadata (that&#8217;s Entra ID). Click on <strong>Download metadata file</strong>. You&#8217;ll get an XML file.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-06-906x1024.png" alt="" width="906" height="1024" class="aligncenter size-large wp-image-9793" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-06-906x1024.png 906w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-06-265x300.png 265w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-06-768x868.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-06-1170x1323.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-06-585x661.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-06.png 1239w" sizes="(max-width: 906px) 100vw, 906px" /></a><br />
Keep the page open and in a new browser window go to portal.azure.com.</p>
<h1>Entra ID</h1>
<p>Once logged to Azure, go to <strong>Enterprise Applications</strong> and search for <strong>AWS IAM Identity Center (successor to AWS Single Sign-On)</strong>. Click to install. When you are presented with the <strong>Overview </strong>of the application, click on <strong>Assign users and groups</strong>. Depending on your Entra ID level (P1 or P2) you might not be able to assign groups, so you have to assign users.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-1024x600.png" alt="" width="1024" height="600" class="aligncenter size-large wp-image-9795" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-1024x600.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-300x176.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-768x450.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-1536x900.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-2048x1199.png 2048w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-1920x1124.png 1920w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-1170x685.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-07-585x343.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
The next step is to setup the SSO. Click on <strong>2. Set up single sign on</strong> and then <strong>SAML</strong>. You&#8217;ll be presented with this page and eventually you&#8217;ll see a prompt if you want to save some SSO settings. If you don&#8217;t see the prompt, no worries. If you see the prompt, click <strong>Yes</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-08-814x1024.png" alt="" width="814" height="1024" class="aligncenter size-large wp-image-9798" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-08-814x1024.png 814w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-08-238x300.png 238w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-08-768x967.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-08-1170x1473.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-08-585x736.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-08.png 1218w" sizes="(max-width: 814px) 100vw, 814px" /></a><br />
Click on <strong>Upload metadata file</strong> and point to the XML file that you saved from AWS. Click <strong>Save </strong>and don&#8217;t worry about any of the fields there. If you see a popup to test the config, just click <strong>No, I&#8217;ll test later</strong>. Under step 3, <strong>SAML Certificates</strong>, click to <strong>Download </strong>Federation Metadata XML.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-09.png" alt="" width="1009" height="546" class="aligncenter size-full wp-image-9799" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-09.png 1009w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-09-300x162.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-09-768x416.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-09-585x317.png 585w" sizes="(max-width: 1009px) 100vw, 1009px" /></a><br />
You&#8217;ll get another XML file called <strong>AWS IAM Identity Center (successor to AWS Single Sign-On).xml</strong>.</p>
<h1>Back to AWS</h1>
<p>Go back to the same page where you&#8217;ve downloaded the XML metadata file, but this time look at the bottom part where it says <strong>Identity provider metadata</strong> and <strong>IdP SAML metadata</strong>. Click on <strong>Choose file</strong> and upload the file from Azure. Click <strong>Next</strong>, type <strong>ACCEPT </strong>and click <strong>Change identity source</strong>.<br />
Once completed click to <strong>Enable </strong>Automatic provisioning.  Copy the <strong>SCIM endpoint</strong> and the <strong>access token</strong>. </p>
<h1>Back to Azure</h1>
<p>Under the AWS SSO application. on the left side click on <strong>Provisioning</strong>. Change the <strong>Provisioning Mode</strong> to <strong>Automatic</strong>. Expand <strong>Admin credentials</strong> and for <strong>Tenant ID</strong> enter the <strong>SCIM endpoint</strong> and for <strong>Secret Token</strong>, enter the token. Click <strong>Test Connection</strong> and upon successful test, click <strong>Save</strong>. If you see clientsecret instead of Tenant ID, it means you are not using the right AWS application and you use the AWS Single Account application instead. Start from scratch.<br />
Anytime you assign a new user or a group to AWS application, Azure will sync the user to AWS. But, instead of waiting (40 mins by default) for the initial sync, click on the <strong>Overview </strong>and click <strong>Start provisioning</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-10.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-10-1024x704.png" alt="" width="1024" height="704" class="aligncenter size-large wp-image-9803" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-10-1024x704.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-10-300x206.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-10-768x528.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-10-1170x804.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-10-585x402.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-10.png 1200w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Back to AWS</h1>
<p>If your provisioning works fine, you&#8217;ll see your assigned Azure user under <strong>Users </strong>in IAM Identity Center. On the left side of the IAM Identity Center, click on <strong>Permission sets</strong>. Click <strong>Create Permission set</strong> and choose <strong>Predefined permission set</strong>, then <strong>AdministratorAccess</strong>. Change the name, description, session duration if needed and then click <strong>Next</strong> and then <strong>Create</strong>.<br />
Finally, click on <strong>AWS accounts</strong>, select an account from the organization, click <strong>Assign users or groups</strong>, click <strong>Users </strong>or <strong>Groups</strong>, select a user and click <strong>Next</strong>. Assign a permission set, click <strong>Next </strong>and <strong>Submit</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-11.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-11-1024x675.png" alt="" width="1024" height="675" class="aligncenter size-large wp-image-9804" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-11-1024x675.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-11-300x198.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-11-768x506.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-11-585x386.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-11.png 1048w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now, test the SSO by going to whatever.awsapps.com/start# and you&#8217;ll be redirected to the Microsoft sign in prompt. Follow the authentication process for Entra ID (username, password, MFA etc) and once you pass the authentication process you&#8217;ll see the AWS account that you have access to.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-12.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-12-1024x309.png" alt="" width="1024" height="309" class="aligncenter size-large wp-image-9806" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-12-1024x309.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-12-300x91.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-12-768x232.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-12-1170x353.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-12-585x177.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-12.png 1513w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>AWS CLI and SSO</h1>
<p>Follow the instructions to <a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener" target="_blank">install </a>AWS CLI for your OS, then configure SSO. Name your session, enter the URL you configured earlier, enter the region where you configured SSO and hit enter for the registration scopes. </p>
<pre class="brush: bash; title: ; notranslate">
aws configure sso
SSO session name (Recommended): sso
SSO start URL &#x5B;None]: https://your_url.awsapps.com/start
SSO region &#x5B;None]: us-east-1
SSO registration scopes &#x5B;sso:account:access]:
Attempting to automatically open the SSO authorization page in your default browser.
If the browser does not open or you wish to use a different device to authorize this request, open the following URL:

https://device.sso.us-east-1.amazonaws.com/

Then enter the code:

PWKG-CAFZ
</pre>
<p>After successful authentication, you&#8217;ll be presented with the accounts available to you. Pick one.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/03/P171-13.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/03/P171-13-1024x305.png" alt="" width="1024" height="305" class="aligncenter size-large wp-image-9819" srcset="https://blog.andreev.it/wp-content/uploads/2024/03/P171-13-1024x305.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-13-300x89.png 300w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-13-768x229.png 768w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-13-1536x458.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-13-1170x349.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-13-585x174.png 585w, https://blog.andreev.it/wp-content/uploads/2024/03/P171-13.png 1734w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
You&#8217;ll be prompted to choose the roles available to you and then specify your default region, default output and the profile. \<br />
NOTE: If you&#8217;ll be working with multiple accounts, maybe you should specify a different profile than the default. It&#8217;s up to you. E.g. specify prod and dev profiles. </p>
<pre class="brush: plain; title: ; notranslate">
There are 2 AWS accounts available to you.
Using the account ID 123456789012
The only role available to you is: AdministratorAccess
Using the role name &quot;AdministratorAccess&quot;
CLI default client Region &#x5B;None]: us-east-2
CLI default output format &#x5B;None]: json
CLI profile name &#x5B;AdministratorAccess-123456789012]: default

To use this profile, specify the profile name using --profile, as shown:

aws s3 ls --profile default
</pre>
<p>After your SSO session expires, you have to log back in.</p>
<pre class="brush: bash; title: ; notranslate">
aws sso login
</pre>
<p>If you are like me and always uses the <strong>default </strong>profile so I don&#8217;t have to type <em>&#8211;profile</em> parameter, then anytime you want to use a different account, just <strong>aws configure sso</strong> again, but specify the other account now. </p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2024/03/entra-id-azure-ad-and-aws-sso/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWS: Get secrets from Secrets Manager using CSI Driver and ASCP</title>
		<link>https://blog.andreev.it/2024/01/aws-get-secrets-from-secrets-manager-using-csi-driver-and-ascp/</link>
					<comments>https://blog.andreev.it/2024/01/aws-get-secrets-from-secrets-manager-using-csi-driver-and-ascp/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Wed, 03 Jan 2024 21:02:53 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[ascp]]></category>
		<category><![CDATA[secrets manager]]></category>
		<category><![CDATA[secrets store csi driver]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9718</guid>

					<description><![CDATA[In one of my previous posts, I wrote about accessing DynamoDB and AWS Gateway/Lambda&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>In one of my previous <a href="https://blog.andreev.it/2023/09/aws-access-dynamodb-and-secrets-manager-from-node-js-on-ec2-and-eks/" rel="noopener" target="_blank">posts</a>, I wrote about accessing DynamoDB and AWS Gateway/Lambda using IAM roles and secrets from EC2/EKS Node.js application. The idea is to reference passwords in your code and never store and secrets/passwords in the code. By using roles, we can access the secrets from the AWS Secrets Manager and use them dynamically in the code. However, that solution has its own pros and cons. The pros are that you don&#8217;t have to do anything extra except creating an IAM role and secrets. The cons are that you need AWS SDK as part of your application. You will use AWS SDK for Node.js to get the credentials from the Secrets Manager. This means you have to maintain up-to-date AWS SDK and add extra code. Another way is to use the k8s CSI driver and ASCP (provider) that pretty much gets the secret and mounts it as a file. You still need IAM roles to access the Secrets Manager but you don&#8217;t need AWS SDK. You can read the secrets as you read a file. </p>
<h1>Create an EKS cluster</h1>
<p>We&#8217;ll create an EKS cluster using the <strong>eksctl</strong> tool.</p>
<pre class="brush: bash; title: ; notranslate">
CLUSTER_NAME=&quot;eksDemoCluster&quot;
REGION=&quot;us-east-2&quot;
NAMESPACE=&quot;default&quot;

eksctl create cluster --name $CLUSTER_NAME \
  --region $REGION --version 1.28 \
  --node-type t3.small --nodes 2
</pre>
<p>We&#8217;ll use helm to install the CSI driver and the specific ASCP provider for AWS.</p>
<pre class="brush: bash; title: ; notranslate">
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm repo add aws-secrets-manager https://aws.github.io/secrets-store-csi-driver-provider-aws
helm repo update
helm install -n kube-system csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver
helm install -n kube-system secrets-provider-aws aws-secrets-manager/secrets-store-csi-driver-provider-aws
</pre>
<p>Check if everything is OK.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl --namespace=kube-system get pods -l &quot;app=secrets-store-csi-driver&quot;
NAME                                               READY   STATUS    RESTARTS   AGE
csi-secrets-store-secrets-store-csi-driver-nhq7b   3/3     Running   0          19s
csi-secrets-store-secrets-store-csi-driver-tft6q   3/3     Running   0          19s
</pre>
<h1>Create a secret</h1>
<p>Let&#8217;s create a secret that we&#8217;ll be using in a mock-up app.</p>
<pre class="brush: bash; highlight: [1,2,3,4,5]; title: ; notranslate">
SECRET_NAME=&quot;demoapp/mysecret&quot;
SECRET_ARN=$(aws secretsmanager create-secret  --name $SECRET_NAME \
  --secret-string &#039;{&quot;username&quot;:&quot;Administrator&quot;, &quot;password&quot;:&quot;SuperSecretPassword$&quot;}&#039; \
  --output text --query &#039;ARN&#039; --region $REGION)
echo $SECRET_ARN
arn:aws:secretsmanager:us-east-2:123456789012:secret:demoapp/mysecret-lboyfd
</pre>
<h1>Create an IAM policy</h1>
<p>We also need to define an IAM policy that will allow our EKS pods to access only this particular secret and nothing else.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; EOF &gt; polDemoSecret.json
{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: &#x5B; {
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Action&quot;: &#x5B;&quot;secretsmanager:GetSecretValue&quot;, &quot;secretsmanager:DescribeSecret&quot;],
        &quot;Resource&quot;: &#x5B;&quot;$SECRET_ARN&quot;]
    } ]
}
EOF
</pre>
<p>Create the policy.</p>
<pre class="brush: bash; highlight: [1,2,3,4]; title: ; notranslate">
POLICY_ARN=$(aws iam create-policy --policy-name polDemoSecret \
  --policy-document file://polDemoSecret.json \
  --output text --query &#039;Policy.Arn&#039;)
echo $POLICY_ARN
arn:aws:iam::123456789012:policy/polDemoSecret
</pre>
<h1>Create an IAM OIDC provider</h1>
<p>Let’s determine whether we have an existing IAM OIDC provider for our cluster.</p>
<pre class="brush: bash; highlight: [1,2]; title: ; notranslate">
aws eks describe-cluster --name $CLUSTER_NAME --region $REGION \
  --query &quot;cluster.identity.oidc.issuer&quot; --output text
https://oidc.eks.us-east-2.amazonaws.com/id/D7FAD6F2BEF430FC1CB673777A9E4FED
</pre>
<p>When we provisioned the cluster with eksctl, the OIDC was created automatically. We need the ID of the OIDC which is the hex value at the end.</p>
<pre class="brush: bash; highlight: [1,2,3]; title: ; notranslate">
OIDCID=$(aws eks describe-cluster --name $CLUSTER_NAME --region $REGION \
  --query &quot;cluster.identity.oidc.issuer&quot; --output text | cut -d &#039;/&#039; -f 5)
echo $OIDCID
E9B5E80C37A71FA3932B94EFF5BA3437
</pre>
<p>Check if the IAM OIDC provider is already configured. It shouldn’t if you provisioned a brand new cluster.</p>
<pre class="brush: bash; title: ; notranslate">
aws iam list-open-id-connect-providers | grep $OIDCID 
</pre>
<p>If for whatever reason, you had an output from the command above, skip this <em>associate-iam-oidc-provider</em> step below.<br />
If you don&#8217;t have any output, associate the OIDC provider with the cluster.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --region $REGION --approve
2023-12-22 12:27:47 &#x5B;ℹ]  will create IAM Open ID Connect provider for cluster &quot;eksDemoCluster&quot; in &quot;us-east-2&quot;
2023-12-22 12:27:47 &#x5B;✔]  created IAM Open ID Connect provider for cluster &quot;eksDemoCluster&quot; in &quot;us-east-2&quot;
</pre>
<h1>Cluster service account</h1>
<p>We also need a service account for the cluster and we need to attache the policy for the secrets that we created before.</p>
<pre class="brush: bash; title: ; notranslate">
SA_NAME=&quot;sademosecret&quot;
eksctl create iamserviceaccount --name $SA_NAME --namespace $NAMESPACE \
--cluster $CLUSTER_NAME  --region $REGION \
--attach-policy-arn $POLICY_ARN --approve --override-existing-serviceaccounts
kubectl get sa
</pre>
<h1>Service Provider Class</h1>
<p>&#8230;and finally we need a service provider class that we&#8217;ll deploy on the EKS cluster.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; EOF &gt; spc.yaml
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: spc-demosecret
  namespace: $NAMESPACE
spec:
  provider: aws
  parameters:
    objects: |
        - objectName: &quot;$SECRET_NAME&quot;
          objectType: &quot;secretsmanager&quot;
EOF
kubectl apply -f spc.yaml
</pre>
<h1>Test with busybox pod</h1>
<p>Create this YAML file that will deploy a pod. Pay attention to highlighted lines. We have to provide the namespace, the service account and the service provider class that specifies the secret.Check this <a href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_csi_driver.html" rel="noopener" target="_blank">link</a> on how to mount and specify different secrets.</p>
<pre class="brush: bash; highlight: [6,8,15]; title: ; notranslate">
cat &lt;&lt; EOF &gt; app.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: $NAMESPACE
spec:
  serviceAccountName: $SA_NAME
  volumes:
  - name: secretsvolume
    csi:
       driver: secrets-store.csi.k8s.io
       readOnly: true
       volumeAttributes:
         secretProviderClass: &quot;spc-demosecret&quot;
  containers:
  - image: public.ecr.aws/docker/library/busybox:1.36
    command:
      - sleep
      - &quot;3600&quot;
    imagePullPolicy: IfNotPresent
    name: busybox
    volumeMounts:
    - name: secretsvolume
      mountPath: &quot;/mnt/secrets-store&quot;
      readOnly: true
  restartPolicy: Always
EOF
kubectl apply -f app.yaml
</pre>
<p>Test the secrets retrieval. As you can see, the secret that we created was <strong>demoapp/mysecret</strong>, but in this case the forward slash would mean a directory, so the actual secret is <strong>demoapp_mysecret</strong>&#8230; and it&#8217;s simple as that. The secret is just a file under the <em>/mnt/secrets-store</em> directory.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl exec -it $(kubectl get pods | awk &#039;/busybox/{print $1}&#039; | head -1) -- cat /mnt/secrets-store/demoapp_mysecret; echo
{&quot;username&quot;:&quot;Administrator&quot;, &quot;password&quot;:&quot;SuperSecretPassword$&quot;}
</pre>
<h1>Node.js app</h1>
<p>Create a folder and add this file. Save it as <strong>server.js</strong>.</p>
<pre class="brush: bash; highlight: [8]; title: ; notranslate">
&#039;use strict&#039;;
const express = require(&#039;express&#039;)
const app = express()
const port = 3000
 
var fs = require(&#039;fs&#039;);
try {  
    var data = fs.readFileSync(&#039;/mnt/secrets-store/demoapp_mysecret&#039;, &#039;utf8&#039;);
    console.log(data.toString());    
} catch(e) {
    console.log(&#039;Error:&#039;, e.stack);
}

app.get(&#039;/&#039;, (req, res) =&gt; {
    res.send(JSON.stringify(data));
})
 
app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`)
})
</pre>
<p>Let&#8217;s create the <strong>Dockerfile</strong> so we can build the image.</p>
<pre class="brush: bash; title: ; notranslate">
FROM node:20
  
# Create app directory
WORKDIR /usr/src/app
  
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
  
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
  
# Bundle app source
COPY . .
  
EXPOSE 3000
CMD &#x5B; &quot;node&quot;, &quot;server.js&quot; ]
</pre>
<p>Create a <strong>.dockerignore</strong> file.</p>
<pre class="brush: bash; title: ; notranslate">
node_modules
npm-debug.log
</pre>
<p>Now, we can create the image and push to Dockerhub or some other image repo. Use your own repo here at line 1.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
sudo docker build . -t klimenta/secretsdemo
sudo docker images
sudo docker push klimenta/secretsdemo
</pre>
<p>Create a file to deploy 2 replicas and a load balancer. Save it as something.yaml and deploy it. Analyze the file and see how we use the service account, secret provider class and the image.</p>
<pre class="brush: bash; title: ; notranslate">
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demosecret
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      run: demosecret
  template:
    metadata:
      labels:
        run: demosecret
    spec:
      serviceAccountName: sademosecret
      volumes:
      - name: secretsvolume
        csi:
          driver: secrets-store.csi.k8s.io
          readOnly: true
          volumeAttributes:
            secretProviderClass: &quot;spc-demosecret&quot;
      containers:
      - name: demosecret
        image: klimenta/secretsdemo
        volumeMounts:
        - name: secretsvolume
          mountPath: &quot;/mnt/secrets-store&quot;
          readOnly: true
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: loadbalancer
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  type: LoadBalancer
  selector:
    run: demosecret
</pre>
<p>Once you deploy with <em>kubectl apply -f something.yaml</em>, type <em>kubectl get svc</em> and you&#8217;ll see a public URL.<br />
If you go to that URL, you&#8217;ll see our secret.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2024/01/P169-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1024x123.png" alt="" width="1024" height="123" class="aligncenter size-large wp-image-9746" srcset="https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1024x123.png 1024w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-300x36.png 300w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-768x92.png 768w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1536x185.png 1536w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-1170x141.png 1170w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01-585x70.png 585w, https://blog.andreev.it/wp-content/uploads/2024/01/P169-01.png 1812w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2024/01/aws-get-secrets-from-secrets-manager-using-csi-driver-and-ascp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWS: Install EKS cluster and Ingress Controller using Terraform and GitLab using OIDC and web identity</title>
		<link>https://blog.andreev.it/2023/11/aws-install-eks-cluster-and-ingress-controller-using-terraform-and-gitlab-using-oidc-and-web-identity/</link>
					<comments>https://blog.andreev.it/2023/11/aws-install-eks-cluster-and-ingress-controller-using-terraform-and-gitlab-using-oidc-and-web-identity/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Tue, 21 Nov 2023 18:39:16 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Containers]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[EKS]]></category>
		<category><![CDATA[GitLab]]></category>
		<category><![CDATA[ingress]]></category>
		<category><![CDATA[OIDC]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9581</guid>

					<description><![CDATA[In this post, I&#8217;ll explain how to create an EKS cluster in AWS and&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>In this post, I&#8217;ll explain how to create an EKS cluster in AWS and add an Ingress Controller. We&#8217;ll use GitLab as a repo and execute the CI/CD pipeline from there.<br />
For this you&#8217;ll need:</p>
<ul>
<li>GitLab and AWS account</li>
<li>Terraform, AWS CLI, helm installed</li>
</ul>
<h1>AWS</h1>
<p>From the IAM console, create a new Identity Provider in AWS and use <strong>https://gitlab.com</strong> for both <em>Provider URL</em> and <em>Audience</em>. For self hosted GitLab use <em>gitlab.domain.com</em> or whatever is your GitLab URL.<br />
Click on <strong>Get thumbprint</strong> before clicking on <strong>Add provider</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/11/P167-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/11/P167-02-1020x1024.png" alt="" width="1020" height="1024" class="aligncenter size-large wp-image-9585" srcset="https://blog.andreev.it/wp-content/uploads/2023/11/P167-02-1020x1024.png 1020w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-02-300x300.png 300w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-02-150x150.png 150w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-02-768x771.png 768w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-02-1170x1175.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-02-585x587.png 585w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-02.png 1288w" sizes="(max-width: 1020px) 100vw, 1020px" /></a><br />
In the IAM console, create a role. I&#8217;ll name the role <strong>rolGitLab</strong>, name it whatever you like. When you create the policy, choose <strong>Custom trust policy</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/11/P167-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/11/P167-04-1024x647.png" alt="" width="1024" height="647" class="aligncenter size-large wp-image-9587" srcset="https://blog.andreev.it/wp-content/uploads/2023/11/P167-04-1024x647.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-04-300x190.png 300w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-04-768x485.png 768w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-04-1536x971.png 1536w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-04-1170x739.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-04-585x370.png 585w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-04.png 1725w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now, here is what you have to decide. If you want to manage the cluster from GitLab only, you can ommit lines 16-23 where I allow the IAM user called gitlab to assume this role. But if you want some IAM user to manage the cluster using aws cli or kubectl, add the ARN of that IAM user in line 20. Add <em>AdministratorAccess </em>to that gitlab user but you can fine-tune those policies later. If you want to manage the cluster from GitLab, you have to register an agent and provide a config file. You can read how to do that <a href="https://docs.gitlab.com/ee/user/clusters/agent/" rel="noopener" target="_blank">here</a>.<br />
Replace the default custom trust policy with this JSON.</p>
<pre class="brush: xml; highlight: [7,12,20]; title: ; notranslate">
{
	&quot;Version&quot;: &quot;2012-10-17&quot;,
	&quot;Statement&quot;: &#x5B;
		{
			&quot;Effect&quot;: &quot;Allow&quot;,
			&quot;Principal&quot;: {
				&quot;Federated&quot;: &quot;arn:aws:iam::123456789012:oidc-provider/gitlab.com&quot;
			},
			&quot;Action&quot;: &quot;sts:AssumeRoleWithWebIdentity&quot;,
			&quot;Condition&quot;: {
				&quot;StringLike&quot;: {
					&quot;gitlab.com:sub&quot;: &quot;project_path:*/eks-gitlab:ref_type:branch:ref:main&quot;
				}
			}
		},
		{
			&quot;Sid&quot;: &quot;&quot;,
			&quot;Effect&quot;: &quot;Allow&quot;,
			&quot;Principal&quot;: {
				&quot;AWS&quot;: &quot;arn:aws:iam::123456789012:user/gitlab&quot;
			},
			&quot;Action&quot;: &quot;sts:AssumeRole&quot;
		}
	]
}
</pre>
<p>In line 7 and 20, change the <em>123456789012 </em>with your account number and in line 7 change <em>gitlab.com</em> with however you named the Identity Provider (IdP). In line 12 replace <em>eks-gitlab </em>with how you plan to name your GitLab project. In line 20 change the <em>gitlab </em>user to however you named your IAM user to access the EKS cluster. Click <strong>Next </strong>and when asked for a permission policy, choose <strong>AdministratorAccess</strong>. Best practice is to limit access to only what&#8217;s needed, but EKS has so many policies, it will take a lot to list them all.<br />
In case you created the gitlab IAM user, attach the AdministratorPolicy (fine tune later) and this inline policy. Change the ARN in line 7. Name this inline policy something like <em>polAssumeGitLabRole</em>. </p>
<pre class="brush: bash; highlight: [7]; title: ; notranslate">
{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: &#x5B;
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &quot;sts:AssumeRole&quot;,
            &quot;Resource&quot;: &quot;arn:aws:iam::123456789012:role/rolGitLab&quot;
        }
    ]
}
</pre>
<h1>GitLab</h1>
<p>Create a new blank project in Gitlab with the default settings. In my case the project name is called <strong>eks-gitlab</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/11/P167-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/11/P167-01-1024x737.png" alt="" width="1024" height="737" class="aligncenter size-large wp-image-9615" srcset="https://blog.andreev.it/wp-content/uploads/2023/11/P167-01-1024x737.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-01-300x216.png 300w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-01-768x553.png 768w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-01-1170x843.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-01-585x421.png 585w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-01.png 1497w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Go to <strong>Settings </strong>on the left, then <strong>CI/CD</strong> and then <strong>Variables</strong>. Add these two variables. </p>
<ul>
<li>AWS_PROFILE and a value of <strong>oidc</strong>. Yes, the value is <strong>oidc</strong>. Just type these 4 characters for the value.</li>
<li>ROLE_ARN and a value of the role ARN from the role you just created for <strong>rolGitLab</strong></li>
</ul>
<p>Create a <strong>.gitlab-ci.yml</strong> file with the following content. Change the domain for <em>gitlab.com</em> at line 7 if you use self-hosted GitLab.</p>
<pre class="brush: yaml; highlight: [7]; title: ; notranslate">
assume role:
  image: 
    name: amazon/aws-cli:latest
    entrypoint: &#x5B;&quot;&quot;]
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  before_script:
    - mkdir -p ~/.aws
    - echo &quot;${GITLAB_OIDC_TOKEN}&quot; &gt; /tmp/web_identity_token
    - echo -e &quot;&#x5B;profile oidc]\nrole_arn=${ROLE_ARN}\nweb_identity_token_file=/tmp/web_identity_token&quot; &gt; ~/.aws/config
  script:
      - aws sts get-caller-identity
      - aws s3 ls
</pre>
<p>By the time you commit changes, the pipeline will start and if everything is OK, you&#8217;ll see your S3 buckets in the logs of the job if you go to <strong>Build | Jobs</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/11/P167-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/11/P167-05-878x1024.png" alt="" width="878" height="1024" class="aligncenter size-large wp-image-9617" srcset="https://blog.andreev.it/wp-content/uploads/2023/11/P167-05-878x1024.png 878w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-05-257x300.png 257w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-05-768x895.png 768w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-05-585x682.png 585w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-05.png 1012w" sizes="(max-width: 878px) 100vw, 878px" /></a><br />
One more thing related to Terraform state as we&#8217;ll keep the state in GitLab is that we have to create an <strong>Access Token</strong>. If you have a self-hosted or paid GitLab account, create an <em>Access Token</em> from the <em>Project | Settings | Access Tokens</em>.<br />
If you have a free GitLab account, you have to create a <strong>Personal Access Token</strong> and you do that if you click on your avatar in the upper left corner, then select <strong>Edit Profile</strong> and then click <strong>Access Tokens</strong> on the left. Click <strong>Add new token</strong>, name it <strong>GITLAB_PAT</strong> and select API only. Click <strong>Create personal access token</strong>. Once the token has been created click on the eye icon where it says <em>Your new personal access token</em> and copy &#038; paste somewhere safe.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/11/P167-03.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/11/P167-03-852x1024.png" alt="" width="852" height="1024" class="aligncenter size-large wp-image-9620" srcset="https://blog.andreev.it/wp-content/uploads/2023/11/P167-03-852x1024.png 852w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-03-250x300.png 250w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-03-768x923.png 768w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-03-585x703.png 585w, https://blog.andreev.it/wp-content/uploads/2023/11/P167-03.png 964w" sizes="(max-width: 852px) 100vw, 852px" /></a><br />
Go to the project settings and add these 2 variables.</p>
<ul>
<li>TF_USERNAME which is your username, click on the avatar, look at the @ under your name, your username is without the @ sign</li>
<li>TF_PASSWORD which is the value of your personal access token you just created</li>
</ul>
<h1>Local machine</h1>
<p>Now that we know the pipeline is working properly, let&#8217;s clone the project on our local machine.<br />
If you plan to manage the cluster from your local machine, now it&#8217;s time to configure the <strong>gitlab </strong>IAM user on your local machine using access keys.<br />
You need to have a valid <a href="https://docs.gitlab.com/ee/user/ssh.html" rel="noopener" target="_blank">key </a>in order to clone your private repo.<br />
Upload your .pub key to GitLab by clicking your avatar, then <strong>Edit Profile</strong>, click <strong>SSH Keys</strong> and then <strong>Add new key</strong>.<br />
Finally, check the connection with the following command. It doesn&#8217;t matter what your local Linux user is, just use git@gitlab.com as the username@hostname. Type as is. </p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
ssh -T git@gitlab.com
Welcome to GitLab, @&#x5B;your_gitlab_username_here]
</pre>
<p>Once you clone the repo add the following files locally.<br />
<strong>FILE: .gitignore</strong></p>
<pre class="brush: bash; collapse: true; light: false; title: ; toolbar: true; notranslate">
# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version 
# control as they are data points which are potentially sensitive and subject 
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc
</pre>
<p><strong>FILE: LICENSE</strong></p>
<pre class="brush: plain; collapse: true; light: false; title: ; toolbar: true; notranslate">
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to &lt;https://unlicense.org&gt;
</pre>
<p><strong>FILE: alb_ingress_iam_policy.json</strong></p>
<pre class="brush: xml; collapse: true; light: false; title: ; toolbar: true; notranslate">
{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: &#x5B;
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;iam:CreateServiceLinkedRole&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;,
            &quot;Condition&quot;: {
                &quot;StringEquals&quot;: {
                    &quot;iam:AWSServiceName&quot;: &quot;elasticloadbalancing.amazonaws.com&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;ec2:DescribeAccountAttributes&quot;,
                &quot;ec2:DescribeAddresses&quot;,
                &quot;ec2:DescribeAvailabilityZones&quot;,
                &quot;ec2:DescribeInternetGateways&quot;,
                &quot;ec2:DescribeVpcs&quot;,
                &quot;ec2:DescribeVpcPeeringConnections&quot;,
                &quot;ec2:DescribeSubnets&quot;,
                &quot;ec2:DescribeSecurityGroups&quot;,
                &quot;ec2:DescribeInstances&quot;,
                &quot;ec2:DescribeNetworkInterfaces&quot;,
                &quot;ec2:DescribeTags&quot;,
                &quot;ec2:GetCoipPoolUsage&quot;,
                &quot;ec2:DescribeCoipPools&quot;,
                &quot;elasticloadbalancing:DescribeLoadBalancers&quot;,
                &quot;elasticloadbalancing:DescribeLoadBalancerAttributes&quot;,
                &quot;elasticloadbalancing:DescribeListeners&quot;,
                &quot;elasticloadbalancing:DescribeListenerCertificates&quot;,
                &quot;elasticloadbalancing:DescribeSSLPolicies&quot;,
                &quot;elasticloadbalancing:DescribeRules&quot;,
                &quot;elasticloadbalancing:DescribeTargetGroups&quot;,
                &quot;elasticloadbalancing:DescribeTargetGroupAttributes&quot;,
                &quot;elasticloadbalancing:DescribeTargetHealth&quot;,
                &quot;elasticloadbalancing:DescribeTags&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;cognito-idp:DescribeUserPoolClient&quot;,
                &quot;acm:ListCertificates&quot;,
                &quot;acm:DescribeCertificate&quot;,
                &quot;iam:ListServerCertificates&quot;,
                &quot;iam:GetServerCertificate&quot;,
                &quot;waf-regional:GetWebACL&quot;,
                &quot;waf-regional:GetWebACLForResource&quot;,
                &quot;waf-regional:AssociateWebACL&quot;,
                &quot;waf-regional:DisassociateWebACL&quot;,
                &quot;wafv2:GetWebACL&quot;,
                &quot;wafv2:GetWebACLForResource&quot;,
                &quot;wafv2:AssociateWebACL&quot;,
                &quot;wafv2:DisassociateWebACL&quot;,
                &quot;shield:GetSubscriptionState&quot;,
                &quot;shield:DescribeProtection&quot;,
                &quot;shield:CreateProtection&quot;,
                &quot;shield:DeleteProtection&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;ec2:AuthorizeSecurityGroupIngress&quot;,
                &quot;ec2:RevokeSecurityGroupIngress&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;ec2:CreateSecurityGroup&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;ec2:CreateTags&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:ec2:*:*:security-group/*&quot;,
            &quot;Condition&quot;: {
                &quot;StringEquals&quot;: {
                    &quot;ec2:CreateAction&quot;: &quot;CreateSecurityGroup&quot;
                },
                &quot;Null&quot;: {
                    &quot;aws:RequestTag/elbv2.k8s.aws/cluster&quot;: &quot;false&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;ec2:CreateTags&quot;,
                &quot;ec2:DeleteTags&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:ec2:*:*:security-group/*&quot;,
            &quot;Condition&quot;: {
                &quot;Null&quot;: {
                    &quot;aws:RequestTag/elbv2.k8s.aws/cluster&quot;: &quot;true&quot;,
                    &quot;aws:ResourceTag/elbv2.k8s.aws/cluster&quot;: &quot;false&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;ec2:AuthorizeSecurityGroupIngress&quot;,
                &quot;ec2:RevokeSecurityGroupIngress&quot;,
                &quot;ec2:DeleteSecurityGroup&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;,
            &quot;Condition&quot;: {
                &quot;Null&quot;: {
                    &quot;aws:ResourceTag/elbv2.k8s.aws/cluster&quot;: &quot;false&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:CreateLoadBalancer&quot;,
                &quot;elasticloadbalancing:CreateTargetGroup&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;,
            &quot;Condition&quot;: {
                &quot;Null&quot;: {
                    &quot;aws:RequestTag/elbv2.k8s.aws/cluster&quot;: &quot;false&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:CreateListener&quot;,
                &quot;elasticloadbalancing:DeleteListener&quot;,
                &quot;elasticloadbalancing:CreateRule&quot;,
                &quot;elasticloadbalancing:DeleteRule&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:AddTags&quot;,
                &quot;elasticloadbalancing:RemoveTags&quot;
            ],
            &quot;Resource&quot;: &#x5B;
                &quot;arn:aws:elasticloadbalancing:*:*:targetgroup/*/*&quot;,
                &quot;arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*&quot;,
                &quot;arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*&quot;
            ],
            &quot;Condition&quot;: {
                &quot;Null&quot;: {
                    &quot;aws:RequestTag/elbv2.k8s.aws/cluster&quot;: &quot;true&quot;,
                    &quot;aws:ResourceTag/elbv2.k8s.aws/cluster&quot;: &quot;false&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:AddTags&quot;,
                &quot;elasticloadbalancing:RemoveTags&quot;
            ],
            &quot;Resource&quot;: &#x5B;
                &quot;arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*&quot;,
                &quot;arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*&quot;,
                &quot;arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*&quot;,
                &quot;arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*&quot;
            ]
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:ModifyLoadBalancerAttributes&quot;,
                &quot;elasticloadbalancing:SetIpAddressType&quot;,
                &quot;elasticloadbalancing:SetSecurityGroups&quot;,
                &quot;elasticloadbalancing:SetSubnets&quot;,
                &quot;elasticloadbalancing:DeleteLoadBalancer&quot;,
                &quot;elasticloadbalancing:ModifyTargetGroup&quot;,
                &quot;elasticloadbalancing:ModifyTargetGroupAttributes&quot;,
                &quot;elasticloadbalancing:DeleteTargetGroup&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;,
            &quot;Condition&quot;: {
                &quot;Null&quot;: {
                    &quot;aws:ResourceTag/elbv2.k8s.aws/cluster&quot;: &quot;false&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:AddTags&quot;
            ],
            &quot;Resource&quot;: &#x5B;
                &quot;arn:aws:elasticloadbalancing:*:*:targetgroup/*/*&quot;,
                &quot;arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*&quot;,
                &quot;arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*&quot;
            ],
            &quot;Condition&quot;: {
                &quot;StringEquals&quot;: {
                    &quot;elasticloadbalancing:CreateAction&quot;: &#x5B;
                        &quot;CreateTargetGroup&quot;,
                        &quot;CreateLoadBalancer&quot;
                    ]
                },
                &quot;Null&quot;: {
                    &quot;aws:RequestTag/elbv2.k8s.aws/cluster&quot;: &quot;false&quot;
                }
            }
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:RegisterTargets&quot;,
                &quot;elasticloadbalancing:DeregisterTargets&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:elasticloadbalancing:*:*:targetgroup/*/*&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &#x5B;
                &quot;elasticloadbalancing:SetWebAcl&quot;,
                &quot;elasticloadbalancing:ModifyListener&quot;,
                &quot;elasticloadbalancing:AddListenerCertificates&quot;,
                &quot;elasticloadbalancing:RemoveListenerCertificates&quot;,
                &quot;elasticloadbalancing:ModifyRule&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        }
    ]
}
</pre>
<p><strong>FILE: backend.tf</strong></p>
<pre class="brush: bash; title: ; notranslate">
terraform {
  backend &quot;http&quot; {
  }
}
</pre>
<p><strong>FILE: data.tf</strong></p>
<pre class="brush: bash; title: ; notranslate">
data &quot;aws_availability_zones&quot; &quot;available&quot; {
}

data &quot;aws_eks_cluster&quot; &quot;cluster&quot; {
  name = module.eks.cluster_name
  depends_on = &#x5B;module.eks.cluster_name]
}

data &quot;aws_eks_cluster_auth&quot; &quot;cluster&quot; {
  name = module.eks.cluster_name
  depends_on = &#x5B;module.eks.cluster_name]
}
</pre>
<p><strong>FILE: main.tf NOTE:</strong> Make sure you add lines 40-41 and specify the <strong>rolGitlab</strong> role created earlier. If you don&#8217;t do this when you destroy the cluster, the KMS key for EKS will stay there and there is no way you can delete it unless you call AWS support.</p>
<pre class="brush: bash; collapse: true; light: false; title: ; toolbar: true; notranslate">
# Create a VPC
module &quot;vpc&quot; {
  source  = &quot;terraform-aws-modules/vpc/aws&quot;
  version = &quot;5.0.0&quot;

  name = var.vpcName

  cidr = var.vpcCIDR
  azs  = slice(data.aws_availability_zones.available.names, 0, 3)

  private_subnets = var.privateSubnets
  public_subnets  = var.publicSubnets

  enable_nat_gateway   = var.enable_nat_gateway
  single_nat_gateway   = var.single_nat_gateway
  enable_dns_hostnames = var.enable_dns_hostnames

  public_subnet_tags = {
    &quot;kubernetes.io/cluster/${var.cluster_name}&quot; = &quot;shared&quot;
    &quot;kubernetes.io/role/elb&quot;                    = 1
  }

  private_subnet_tags = {
    &quot;kubernetes.io/cluster/${var.cluster_name}&quot; = &quot;shared&quot;
    &quot;kubernetes.io/role/internal-elb&quot;           = 1
  }
}

# Create an EKS cluster
module &quot;eks&quot; {
  source  = &quot;terraform-aws-modules/eks/aws&quot;
  version = &quot;19.15.3&quot;

  cluster_name    = var.cluster_name
  cluster_version = var.cluster_version

  vpc_id                         = module.vpc.vpc_id
  subnet_ids                     = module.vpc.private_subnets
  cluster_endpoint_public_access = var.cluster_endpoint_public_access
  kms_key_administrators         = &#x5B;&quot;arn:aws:iam::123456789012:role/rolGitLab&quot;]
  kms_key_enable_default_policy  = true

  eks_managed_node_group_defaults = {
    ami_type = var.ami_type
  }

  eks_managed_node_groups = {
    one = {
      name = var.nodeGroup

      instance_types = var.instanceTypes

      min_size     = var.minSize
      max_size     = var.maxSize
      desired_size = var.desiredSize
    }
  }
}

# Create an IAM policy for the ALB ingress
resource &quot;aws_iam_policy&quot; &quot;worker_policy&quot; {
  name        = &quot;worker-policy&quot;
  description = &quot;Worker policy for the ALB Ingress&quot;

  policy = file(var.iamALBIngressPolicyFileName)
}

resource &quot;aws_iam_role_policy_attachment&quot; &quot;additional&quot; {
  for_each = module.eks.eks_managed_node_groups

  policy_arn = aws_iam_policy.worker_policy.arn
  role       = each.value.iam_role_name
}

# Crete the Ingress using Helm chart from AWS
resource &quot;helm_release&quot; &quot;ingress&quot; {
  name       = &quot;ingress&quot;
  chart      = &quot;aws-load-balancer-controller&quot;
  repository = &quot;https://aws.github.io/eks-charts&quot;
  version    = &quot;1.6.1&quot;

  set {
    name  = &quot;autoDiscoverAwsRegion&quot;
    value = &quot;true&quot;
  }

  set {
    name  = &quot;autoDiscoverAwsVpcID&quot;
    value = &quot;true&quot;
  }

  set {
    name  = &quot;clusterName&quot;
    value = module.eks.cluster_name
  }
}
</pre>
<p><strong>FILE: outputs.tf</strong></p>
<pre class="brush: bash; title: ; notranslate">
output &quot;cluster_endpoint&quot; {
  description = &quot;Endpoint for EKS control plane&quot;
  value       = module.eks.cluster_endpoint
}

output &quot;cluster_security_group_id&quot; {
  description = &quot;Security group ids attached to the cluster control plane&quot;
  value       = module.eks.cluster_security_group_id
}

output &quot;region&quot; {
  description = &quot;AWS region&quot;
  value       = var.region
}

output &quot;cluster_name&quot; {
  description = &quot;Kubernetes Cluster Name&quot;
  value       = module.eks.cluster_name
}
</pre>
<p><strong>FILE: providers.tf</strong></p>
<pre class="brush: bash; title: ; notranslate">
provider &quot;aws&quot; {
  region = var.region

  shared_config_files      = &#x5B;&quot;~/.aws/config&quot;]  
  profile                  = &quot;oidc&quot;
}

provider &quot;helm&quot; {
  kubernetes {
    host                   = data.aws_eks_cluster.cluster.endpoint
    token                  = data.aws_eks_cluster_auth.cluster.token
    cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data)
  }
}
</pre>
<p><strong>FILE: variables.tf</strong></p>
<pre class="brush: bash; collapse: true; light: false; title: ; toolbar: true; notranslate">
variable &quot;region&quot; {
  type    = string
  default = &quot;us-east-2&quot;
}

variable &quot;vpcName&quot; {
  type    = string
  default = &quot;vpcFromGitLab&quot;
}

variable &quot;vpcCIDR&quot; {
  type    = string
  default = &quot;10.0.0.0/16&quot;
}

variable &quot;privateSubnets&quot; {
  type    = list(string)
  default = &#x5B;&quot;10.0.1.0/24&quot;, &quot;10.0.2.0/24&quot;, &quot;10.0.3.0/24&quot;]
}

variable &quot;publicSubnets&quot; {
  type    = list(string)
  default = &#x5B;&quot;10.0.4.0/24&quot;, &quot;10.0.5.0/24&quot;, &quot;10.0.6.0/24&quot;]
}

variable &quot;enable_nat_gateway&quot; {
  type    = bool
  default = true
}

variable &quot;single_nat_gateway&quot; {
  type    = bool
  default = false
}

variable &quot;enable_dns_hostnames&quot; {
  type    = bool
  default = true
}

variable &quot;cluster_name&quot; {
  type    = string
  default = &quot;eksFromGitLab&quot;
}

variable &quot;cluster_version&quot; {
  type    = string
  default = &quot;1.27&quot;
}

variable &quot;cluster_endpoint_public_access&quot; {
  type    = bool
  default = true
}

variable &quot;ami_type&quot; {
  type    = string
  default = &quot;AL2_x86_64&quot;
}

variable &quot;nodeGroup&quot; {
  type    = string
  default = &quot;ngFromGitLab&quot;
}

variable &quot;instanceTypes&quot; {
  type    = list(string)
  default = &#x5B;&quot;t3.small&quot;]
}

variable &quot;minSize&quot; {
  type    = number
  default = 1
}

variable &quot;maxSize&quot; {
  type    = number
  default = 3
}

variable &quot;desiredSize&quot; {
  type    = number
  default = 3
}


variable &quot;iamALBIngressPolicyFileName&quot; {
  type    = string
  default = &quot;alb_ingress_iam_policy.json&quot;
}
</pre>
<p>&#8230;and finally your .gitlab-ci.yml file.<br />
<strong>FILE: .gitlab-ci.yml. NOTE: </strong>Remove line 38 if you don&#8217;t use it, don&#8217;t keep it with a comment or you&#8217;ll get an error in the pipeline.</p>
<pre class="brush: bash; highlight: [38]; title: ; notranslate">
stages:          
  - validate
  - plan
  - apply
  - destroy

image:
  name: hashicorp/terraform:1.6
  entrypoint:
    - &quot;/usr/bin/env&quot;
    - &quot;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&quot;

variables:
  TF_DIR: ${CI_PROJECT_DIR}
  TF_STATE_NAME: &quot;eks-tf-state&quot;
  TF_ADDRESS: &quot;https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}&quot;

default:
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com

  before_script:
    - terraform --version
    - cd ${TF_DIR}
    - terraform init
      -reconfigure 
      -backend-config=&quot;address=${TF_ADDRESS}&quot; 
      -backend-config=&quot;lock_address=${TF_ADDRESS}/lock&quot; 
      -backend-config=&quot;unlock_address=${TF_ADDRESS}/lock&quot; 
      -backend-config=&quot;username=${TF_USERNAME}&quot; 
      -backend-config=&quot;password=${TF_PASSWORD}&quot; 
      -backend-config=&quot;lock_method=POST&quot; 
      -backend-config=&quot;unlock_method=DELETE&quot; 
      -backend-config=&quot;retry_wait_min=5&quot;
    - mkdir -p ~/.aws
    - echo &quot;${GITLAB_OIDC_TOKEN}&quot; &gt; /tmp/web_identity_token
    - # echo &quot;${GITLAB_OIDC_TOKEN}&quot; | base64 - If you want to see the token, reveal it this way and then use base64 --decode to get the real one
    - echo -e &quot;&#x5B;profile oidc]\nrole_arn=${ROLE_ARN}\nweb_identity_token_file=/tmp/web_identity_token&quot; &gt; ~/.aws/config

job-validate:
  stage: validate
  script:
    - terraform validate

job-plan:
  stage: plan
  script:
    - terraform plan -out &quot;tf_plan&quot;
  dependencies:
    - job-validate
  artifacts:
    paths:
      - tf_plan

job-apply:
  stage: apply
  script:
    - terraform apply &quot;tf_plan&quot;
  dependencies:
    - job-plan
  allow_failure: true

job-destroy:
  stage: destroy
  script:
    - terraform destroy  -auto-approve
  dependencies:
    #- job-plan
    #- job-apply
  when: manual
</pre>
<p>Push the code back to Gitlab (<em>git add, git commit, git push</em>) and once everything completes successfully, you&#8217;ll have a working EKS cluster after 10-15 mins.<br />
If you choose to manage the EKS from GitLab, you can skip to the next section. If you decide to manage the cluster outside GitLab and you already have another IAM user added to the role <strong>rolGitLab </strong>as described above, then do the following. Replace account # in line 1.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
aws eks update-kubeconfig --region us-east-2 --name eksFromGitLab --role-arn arn:aws:iam::123456789012:role/rolGitLab
aws eks list-clusters
eksctl get cluster --name=eksFromGitLab --region us-east-2
kubectl get svc
</pre>
<h1>Manage EKS from GitLab</h1>
<p>Generally, you would want this to be in another project. Our first project was to build the cluster, but our second project builds on top of that and manages the cluster. In addition, the second project also uses the CI/CD pipeline file <strong>.gitlab-ci.yml</strong> so in order to avoid a single complex project &#8211; just create a new project. I&#8217;ll name my new project &#8211; <strong>project1</strong>. Clone the project on your local machine using <em>git clone</em>.<br />
Create an empty config file for the agent on your local machine. It has to be under this folder structure.</p>
<pre class="brush: bash; title: ; notranslate">
mkdir -p .gitlab/agents/eks-agent &amp;&amp; touch .gitlab/agents/eks-agent/config.yaml
</pre>
<p>In my case the name of the agent is <strong>eks-agent</strong>. Use whatever name you want but follow DNS naming <a href="https://docs.gitlab.com/ee/user/clusters/agent/install/index.html" rel="noopener" target="_blank">convention</a>.<br />
Push the local changes to GitLab.<br />
On the left side in GitLab, go to <strong>Operate | Kubernetes clusters</strong> and then click <strong>Connect a cluster</strong> from the upper right corner.<br />
Select the agent from the drop down and click <strong>Register</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/11/P169-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/11/P169-02-1024x304.png" alt="" width="1024" height="304" class="aligncenter size-large wp-image-9767" srcset="https://blog.andreev.it/wp-content/uploads/2023/11/P169-02-1024x304.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-02-300x89.png 300w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-02-768x228.png 768w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-02-1170x347.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-02-585x174.png 585w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-02.png 1522w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
You&#8217;ll get an access token and a bunch of helm commands to install the agent on the cluster. Copy and paste and run those commands.</p>
<pre class="brush: bash; title: ; notranslate">
helm repo add gitlab https://charts.gitlab.io
helm repo update
helm upgrade --install eks-agent gitlab/gitlab-agent \
    --namespace gitlab-agent-eks-agent \
    --create-namespace \
    --set image.tag=v16.9.0-rc2 \
    --set config.token=glagent-2r_uW63e1w_mkNzR_keU2hmaNhhcKauSpujg8OdLQG74wrF73g \
    --set config.kasAddress=wss://kas.gitlab.com
</pre>
<p>Refresh the GitLab screen after the helm chart has been installed and you&#8217;ll see your cluster connected.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/11/P169-03.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-1024x174.png" alt="" width="1024" height="174" class="aligncenter size-large wp-image-9769" srcset="https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-1024x174.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-300x51.png 300w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-768x131.png 768w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-1536x261.png 1536w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-2048x348.png 2048w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-1920x326.png 1920w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-1170x199.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/11/P169-03-585x99.png 585w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Now you need to <a href="https://docs.gitlab.com/ee/user/clusters/agent/ci_cd_workflow.html#authorize-the-agent" rel="noopener" target="_blank">authorize</a> the agent to access our project.<br />
In the blank <strong>config.yaml</strong> file on the local machine under <em>.gitlab/agents/eks-agent/</em> directory, add these lines. Change the path the project in line 3 (look at the URL, that&#8217;s your project name after gitlab.com) and change the manifest file if you want in line 6. You can also change the namespace in line 5 as well.</p>
<pre class="brush: yaml; highlight: [3,6]; title: ; notranslate">
gitops:
  manifest_projects:
  - id: andreevkliment/project1
    default_namespace: default
    paths:
      - glob: &#039;manifest.yaml&#039;
</pre>
<p>Then create the manifest file under the root of the project. Save it as <em>manifest.yaml</em> or however you referenced it in line 6 in the previous file.</p>
<pre class="brush: yaml; title: ; notranslate">
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
</pre>
<p>If you push this code back to GitLab, the agent will execute it and you&#8217;ll (with <em>kubect get pods</em>) see the nginx pods running on your cluster.<br />
Another way of managing the EKS cluster is using <strong>.gitlab-ci.yml</strong> file.<br />
Create <strong>.gitlab-ci.yml</strong> with these lines. Replace your username, project and the agent name in line 7.</p>
<pre class="brush: yaml; highlight: [7]; title: ; notranslate">
deploy:
  image:
    name: bitnami/kubectl:latest
    entrypoint: &#x5B;&#039;&#039;]
  script:
    - kubectl config get-contexts
    - kubectl config use-context andreevkliment/project1:eks-agent
    - kubectl get pods
</pre>
<p>If you push this back to GitLab, the pipeline will start and you&#8217;ll see the pods from the cluster in the logs. This example doesn&#8217;t execute <strong>manifest.yaml</strong>. You can have both ways of interacting with the cluster or you can use them separately.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2023/11/aws-install-eks-cluster-and-ingress-controller-using-terraform-and-gitlab-using-oidc-and-web-identity/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWS: EKS running out of IPs, increase pod density</title>
		<link>https://blog.andreev.it/2023/06/aws-eks-and-running-out-of-ips/</link>
					<comments>https://blog.andreev.it/2023/06/aws-eks-and-running-out-of-ips/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Tue, 06 Jun 2023 21:04:16 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Calico]]></category>
		<category><![CDATA[CNI]]></category>
		<category><![CDATA[EKS]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9358</guid>

					<description><![CDATA[When I was doing some proof of concept using EKS, I&#8217;ve noticed that there&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>When I was doing some proof of concept using EKS, I&#8217;ve noticed that there is a limitation on how many pods can a node run and how EKS CNI assigns the subnet IPs. Apparently, a node can run a <a href="https://github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt" rel="noopener" target="_blank">maximum of XX pods </a>(based on type) which is very low, e.g. a t3.large instance with 2 CPUs and 8GB RAM can run only 35 pods. On top of that, the IPs that are assigned for the pods are used from the existing pool of IPs and if you have a /24 subnet for the EKS cluster, you&#8217;ll run out of IPs in no time. Here is an example of what I did and options how to solve this issue.</p>
<h1>Option 1 &#8211; Calico 3rd party CNI</h1>
<p>I&#8217;ll provision an EKS cluster in a /24 subnet with 3 public and 3 private subnets.</p>
<pre class="brush: bash; title: ; notranslate">
eksctl create cluster --name eksECIC --region us-east-2 --instance-types t3.large \
    --managed --vpc-cidr 192.168.100.0/24 --node-private-networking --version 1.24 --without-nodegroup
</pre>
<p>I am using the Ohio region which has 3 availability zones. The public and private subnets (total of 6) have /27 subnet which is 32 IPs. Not all of them are usable of course. Let&#8217;s create a node group with 3 nodes, and because <em>t3.large</em> is a Nitro instance, we can set the <a href="https://docs.aws.amazon.com/eks/latest/userguide/cni-increase-ip-addresses.html" rel="noopener" target="_blank">max number of pods</a> to be 110.</p>
<pre class="brush: bash; title: ; notranslate">
eksctl create nodegroup --cluster eksECIC --name old-nodegroup --nodes 3 \
    --node-type t3.large --node-private-networking --managed --max-pods-per-node 200
</pre>
<p>These are the available 31 IPs that I have in the private subnets.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/05/P163-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/05/P163-01-1024x201.png" alt="" width="1024" height="201" class="aligncenter size-large wp-image-9371" srcset="https://blog.andreev.it/wp-content/uploads/2023/05/P163-01-1024x201.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-01-300x59.png 300w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-01-768x151.png 768w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-01-1170x230.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-01-585x115.png 585w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-01.png 1211w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Let&#8217;s provision a small deployment with a single pod and 10 replicas and a network load balancer.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl apply -f demo.yaml
</pre>
<p>&#8230;and this is the deployment file <strong>demo.yaml</strong>.</p>
<pre class="brush: yaml; title: ; notranslate">
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 10
  selector:
    matchLabels:
      run: demo
  template:
    metadata:
      labels:
        run: demo
    spec:
      containers:
      - name: demo
        image: klimenta/serverip
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: loadbalancer
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  type: LoadBalancer
  selector:
    run: demo
</pre>
<p>If you look at the available address now, there are 7. So from 31 to 7 with just 10 pods. Check this guy&#8217;s <a href="https://medium.com/codex/kubernetes-cluster-running-out-of-ip-addresses-on-aws-eks-c7b8e5dd8606" rel="noopener" target="_blank">blog </a>to see what&#8217;s going on.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/05/P163-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/05/P163-02-1024x116.png" alt="" width="1024" height="116" class="aligncenter size-large wp-image-9372" srcset="https://blog.andreev.it/wp-content/uploads/2023/05/P163-02-1024x116.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-02-300x34.png 300w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-02-768x87.png 768w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-02-1170x133.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-02-585x66.png 585w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-02.png 1173w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Let&#8217;s scale up the cluster to 100 pods.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl scale --replicas=100 deployment/demo
</pre>
<p>After a minute or two, check the running pods.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get pods --field-selector=status.phase=Running | wc -l
69
</pre>
<p>The console will show 0 available subnets.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/05/P163-03.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/05/P163-03-1024x109.png" alt="" width="1024" height="109" class="aligncenter size-large wp-image-9373" srcset="https://blog.andreev.it/wp-content/uploads/2023/05/P163-03-1024x109.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-03-300x32.png 300w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-03-768x82.png 768w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-03-1170x125.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-03-585x62.png 585w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-03.png 1212w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
If you check the pods with <strong>kubectl get pods</strong>, you&#8217;ll see the remaining containers have a <em>ContainerCreating </em>status.<br />
And if you check one of them, you&#8217;ll see this message.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl describe pod demo-698f6fc958-z8smb
....
d9b65ccfeccb7e9e7433ec61fef78faf83d1bae&quot;: plugin type=&quot;aws-cni&quot; name=&quot;aws-cni&quot; failed (add): add cmd: failed to assign an IP address to container
  Warning  FailedCreatePodSandBox  2m29s (x17 over 5m59s)  kubelet            (combined from similar events): Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox &quot;edfa987c3eddf415068691f914d740fa9ca9ca6ffdfb8db44d1340d913ece0b0&quot;: plugin type=&quot;aws-cni&quot; name=&quot;aws-cni&quot; failed (add): add cmd: failed to assign an IP address to container
</pre>
<p>Get the load balancer service. </p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get svc
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP                                                                     PORT(S)        AGE
kubernetes     ClusterIP      10.100.0.1      &lt;none&gt;                                                                          443/TCP        37m
loadbalancer   LoadBalancer   10.100.25.241   a105789cd3a264ac596c367fce463640-80d2b88acdc17771.elb.us-east-2.amazonaws.com   80:32396/TCP   17m
</pre>
<p>If you go to <em>a105789cd3a264&#8230;amazonaws.com</em> URL, you&#8217;ll see the load balancer hitting different pods. Wait for 2-3 minutes if you see that the page can&#8217;t be opened. It takes time for DNS to propagate.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/05/P163-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/05/P163-04-1024x355.png" alt="" width="1024" height="355" class="aligncenter size-large wp-image-9374" srcset="https://blog.andreev.it/wp-content/uploads/2023/05/P163-04-1024x355.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-04-300x104.png 300w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-04-768x266.png 768w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-04-1170x406.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-04-585x203.png 585w, https://blog.andreev.it/wp-content/uploads/2023/05/P163-04.png 1470w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Let&#8217;s delete the deployment and the old node group.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl delete -f demo.yaml
eksctl delete nodegroup --name old-nodegroup --cluster eksECIC
</pre>
<p>At this point, we still have the EKS cluster, the etcd database, networking etc, we just don&#8217;t have the nodes. Now it&#8217;s time to remove the EKS CNI, install Calico and add nodes that will use Calico.<br />
Installation is very simple. Remove the AWS CNI.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl delete daemonset -n kube-system aws-node
</pre>
<p>Check the pods in all namespaces.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get pods --all-namespaces
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   coredns-5c5677bc78-2k88b   0/1     Pending   0          4m
kube-system   coredns-5c5677bc78-2zz2n   0/1     Pending   0          4m
</pre>
<p>You will see <strong>coredns </strong>in pending state, it can&#8217;t be deployed anywhere.<br />
Deploy Calico.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/tigera-operator.yaml
</pre>
<p>&#8230;keep going&#8230;</p>
<pre class="brush: bash; title: ; notranslate">
kubectl create -f - &lt;&lt;EOF
kind: Installation
apiVersion: operator.tigera.io/v1
metadata:
  name: default
spec:
  kubernetesProvider: EKS
  cni:
    type: Calico
  calicoNetwork:
    bgp: Disabled
EOF
</pre>
<p>Deploy the new node group.</p>
<pre class="brush: bash; title: ; notranslate">
eksctl create nodegroup --cluster eksECIC --name new-nodegroup --nodes 3 \
    --node-type t3.large --node-private-networking --managed --max-pods-per-node 200
</pre>
<p>Let&#8217;s deploy the same demo deployment now.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl apply -f demo.yaml
</pre>
<p>&#8230;scale it up to 601 pods.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl scale --replicas=601 deployment/demo
</pre>
<p>Wait 2-3 mins and check the running ones.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl get pods --field-selector=status.phase=Running | wc -l
</pre>
<p>There will be 585 pods running, 195 per node. Much, much better. This time the remaining containers will be in <em>Pending </em>state.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl describe pod demo-698f6fc958-smlbq
....
Events:
  Type     Reason            Age                  From               Message
  ----     ------            ----                 ----               -------
  Warning  FailedScheduling  3m27s (x3 over 13m)  default-scheduler  0/3 nodes are available: 3 Too many pods. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod.
</pre>
<h1>Option 2 &#8211; CNI Custom networking and prefix assignment mode</h1>
<p>In this case, we&#8217;ll keep the AWS CNI network plugin, but we&#8217;ll use what is called CNI Custom networking, meaning we&#8217;ll use another CIDR attached to the VPC where pods will run. NOTE: The nodes will still run in the 192.168/24 subnets, it&#8217;s just the pods that will run in a much bigger IP space. In addition, this CNI Custom networking won&#8217;t solve the density of the pods that we can run on the nodes. We&#8217;ll need something called prefix assignment mode that will allow us to run much more pods on a node than usual. See <a href="https://aws.amazon.com/blogs/containers/leveraging-cni-custom-networking-alongside-security-groups-for-pods-in-amazon-eks/" rel="noopener" target="_blank">this </a>and <a href="https://docs.aws.amazon.com/eks/latest/userguide/cni-custom-network.html" rel="noopener" target="_blank">this </a>links for more information. For more info regarding prefix assignment mode check out <a href="https://aws.amazon.com/blogs/containers/amazon-vpc-cni-increases-pods-per-node-limits/" rel="noopener" target="_blank">this </a>link. The solution described below is much more complicated than if you just use Calico.<br />
Let&#8217;s provision a cluster with the same VPC CIDR and no nodegroups.</p>
<pre class="brush: bash; title: ; notranslate">
eksctl create cluster --name eksECIC --region us-east-2 --instance-types t3.large \
    --managed --vpc-cidr 192.168.100.0/24 --node-private-networking --version 1.24 --without-nodegroup
</pre>
<p>Add a new CIDR and associate it with the VPC. I am using the console but you can use <em>aws ec2 associate-vpc-cidr-block</em> command.Go to the VPC where the EKS cluster resides. From the <strong>Actions </strong>button, select <strong>Edit CIDRs</strong> and then click the <strong>Add new IPv4 CIDR</strong> button.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/06/P163-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/06/P163-05.png" alt="" width="982" height="511" class="aligncenter size-full wp-image-9386" srcset="https://blog.andreev.it/wp-content/uploads/2023/06/P163-05.png 982w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-05-300x156.png 300w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-05-768x400.png 768w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-05-585x304.png 585w" sizes="(max-width: 982px) 100vw, 982px" /></a><br />
I&#8217;ll add <strong>100.64.0.0/16</strong> CIDR and then I&#8217;ll create 3 /19 subnets.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/06/P163-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/06/P163-06-1024x124.png" alt="" width="1024" height="124" class="aligncenter size-large wp-image-9387" srcset="https://blog.andreev.it/wp-content/uploads/2023/06/P163-06-1024x124.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-06-300x36.png 300w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-06-768x93.png 768w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-06-1536x185.png 1536w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-06-1170x141.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-06-585x71.png 585w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-06.png 1789w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
These 3 subnets will be where the pods will run. I also want them to initiate connections to Internet, so I&#8217;ll modify the route table and add the Internet Gateway. The three new subnets 100.64/19 that we created have the same route table.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/06/P163-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/06/P163-07-1024x287.png" alt="" width="1024" height="287" class="aligncenter size-large wp-image-9388" srcset="https://blog.andreev.it/wp-content/uploads/2023/06/P163-07-1024x287.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-07-300x84.png 300w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-07-768x216.png 768w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-07-1170x328.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-07-585x164.png 585w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-07.png 1350w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Just add the route to 0.0.0.0/0 to go over Internet Gateway.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/06/P163-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/06/P163-08-1024x261.png" alt="" width="1024" height="261" class="aligncenter size-large wp-image-9389" srcset="https://blog.andreev.it/wp-content/uploads/2023/06/P163-08-1024x261.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-08-300x77.png 300w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-08-768x196.png 768w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-08-1170x299.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-08-585x149.png 585w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-08.png 1528w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Let&#8217;s configure the custom networking on the cluster.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true
</pre>
<p>Get the EKS cluster security group.</p>
<pre class="brush: bash; title: ; notranslate">
sec_grp=$(aws eks describe-cluster --name eksECIC --query cluster.resourcesVpcConfig.clusterSecurityGroupId --output text)
echo $sec_grp
</pre>
<p>For each of the subnets create a custom resource. Define the three 100.64 subnet-ids as variables.</p>
<pre class="brush: bash; title: ; notranslate">
export sub1=subnet-0f3ec5d75caf1b981
export sub2=subnet-0fc39edc5a1ce91c0
export sub3=subnet-0bd6bdfd5543aa086
</pre>
<pre class="brush: xml; title: ; notranslate">
cat &gt;us-east-2a.yaml &lt;&lt;EOF
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata: 
  name: us-east-2a
spec: 
  securityGroups: 
    - $sec_grp
  subnet: $sub1
EOF
</pre>
<p>2nd subnet.</p>
<pre class="brush: xml; title: ; notranslate">
cat &gt;us-east-2b.yaml &lt;&lt;EOF
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata: 
  name: us-east-2b
spec: 
  securityGroups: 
    - $sec_grp
  subnet: $sub2
EOF
</pre>
<p>3rd subnet.</p>
<pre class="brush: xml; title: ; notranslate">
cat &gt;us-east-2c.yaml &lt;&lt;EOF
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata: 
  name: us-east-2c
spec: 
  securityGroups: 
    - $sec_grp
  subnet: $sub3
EOF
</pre>
<p><strong>NOTE:</strong>Make sure you name the metadata in line 5 as I did based on your region and AZ.<br />
Deploy the custom resources.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl apply -f us-east-2a.yaml
kubectl apply -f us-east-2b.yaml
kubectl apply -f us-east-2c.yaml
</pre>
<p>Confirm it looks good.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get ENIConfigs
NAME         AGE
us-east-2a   16s
us-east-2b   12s
us-east-2c   8s
</pre>
<p>Update your aws-node DaemonSet to automatically apply the ENIConfig for an Availability Zone to any new Amazon EC2 nodes created in your cluster.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl set env daemonset aws-node -n kube-system ENI_CONFIG_LABEL_DEF=topology.kubernetes.io/zone
</pre>
<p>Enable prefix assignment mode. This will allow much more pods per node.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true
</pre>
<p>Another change.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl set env ds aws-node -n kube-system WARM_PREFIX_TARGET=1
</pre>
<p>With the default setting, WARM_PREFIX_TARGET will allocate one additional complete (/28) prefix even if the existing prefix is used by only one pod. If the ENI does not have enough space to assign a prefix, a new ENI is generated.<br />
Provision the node group.</p>
<pre class="brush: bash; title: ; notranslate">
eksctl create nodegroup --cluster eksECIC --name my-nodegroup --nodes 3 \
    --node-type t3.large --node-private-networking --managed --max-pods-per-node 200
</pre>
<p>Check the nodes, they are running in the 192.168/24 original VPC CIDR. See the IP in the names of the nodes.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get nodes
NAME                                            STATUS   ROLES    AGE     VERSION
ip-192-168-100-122.us-east-2.compute.internal   Ready    &lt;none&gt;   7m39s   v1.24.13-eks-0a21954
ip-192-168-100-155.us-east-2.compute.internal   Ready    &lt;none&gt;   7m39s   v1.24.13-eks-0a21954
ip-192-168-100-167.us-east-2.compute.internal   Ready    &lt;none&gt;   7m37s   v1.24.13-eks-0a21954
</pre>
<p>Check the pods.</p>
<pre class="brush: bash; highlight: [1,6,7]; title: ; notranslate">
kubectl get pods -A -o wide
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE    IP                NODE                                            NOMINATED NODE   READINESS GATES
kube-system   aws-node-txwrd             1/1     Running   0          9m4s   192.168.100.167   ip-192-168-100-167.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
kube-system   aws-node-vdmv7             1/1     Running   0          9m6s   192.168.100.155   ip-192-168-100-155.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
kube-system   aws-node-xss7c             1/1     Running   0          9m6s   192.168.100.122   ip-192-168-100-122.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
kube-system   coredns-5c5677bc78-2lkcx   1/1     Running   0          80m    100.64.76.225     ip-192-168-100-122.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
kube-system   coredns-5c5677bc78-mj6wr   1/1     Running   0          80m    100.64.76.224     ip-192-168-100-122.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
kube-system   kube-proxy-lt7v4           1/1     Running   0          9m6s   192.168.100.122   ip-192-168-100-122.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
kube-system   kube-proxy-m8pf6           1/1     Running   0          9m4s   192.168.100.167   ip-192-168-100-167.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
kube-system   kube-proxy-nlvw7           1/1     Running   0          9m6s   192.168.100.155   ip-192-168-100-155.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
</pre>
<p>Look at <strong>coredns </strong>pods. They are running in the new CIDR.<br />
Deploy the demo.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl apply -f demo.yaml
</pre>
<p>Get the load balancer service. </p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get svc
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP                                                                     PORT(S)        AGE
kubernetes     ClusterIP      10.100.0.1       &lt;none&gt;                                                                          443/TCP        83m
loadbalancer   LoadBalancer   10.100.206.187   a7ad3ecf1d52e4fc58dbbdc550237009-ac4778e11af2c0ad.elb.us-east-2.amazonaws.com   80:30230/TCP   45s
</pre>
<p>If you go to <em>a7ad3ecf1d&#8230;amazonaws.com</em> URL, you&#8217;ll see the load balancer hitting different pods. Wait for 2-3 minutes if you see that the page can&#8217;t be opened. It takes time for DNS to propagate.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/06/P163-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/06/P163-09-1024x460.png" alt="" width="1024" height="460" class="aligncenter size-large wp-image-9401" srcset="https://blog.andreev.it/wp-content/uploads/2023/06/P163-09-1024x460.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-09-300x135.png 300w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-09-768x345.png 768w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-09-1170x526.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-09-585x263.png 585w, https://blog.andreev.it/wp-content/uploads/2023/06/P163-09.png 1531w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
The pods are running in the 100.64 subnet.<br />
Scale up to 601 pods.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl scale --replicas=601 deployment/demo
</pre>
<p>Wait 2-3 mins and check the running ones.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl get pods --field-selector=status.phase=Running | wc -l
</pre>
<p>I got 593 total pods. Slightly better than 585 with Calico. The ones that are in pending state show up an error that there are no more resources.</p>
<pre class="brush: bash; title: ; notranslate">
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  3m49s  default-scheduler  0/3 nodes are available: 3 Too many pods. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod.
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2023/06/aws-eks-and-running-out-of-ips/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Kubernetes: Monitoring Node.js application with Prometheus and Grafana, Helm charts in AWS EKS</title>
		<link>https://blog.andreev.it/2023/01/kubernetes-monitoring-node-js-application-with-prometheus-and-grafana-helm-charts/</link>
					<comments>https://blog.andreev.it/2023/01/kubernetes-monitoring-node-js-application-with-prometheus-and-grafana-helm-charts/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Tue, 24 Jan 2023 16:01:05 +0000</pubDate>
				<category><![CDATA[Containers]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[EKS]]></category>
		<category><![CDATA[grafana]]></category>
		<category><![CDATA[helm]]></category>
		<category><![CDATA[nodejs]]></category>
		<category><![CDATA[prom-client]]></category>
		<category><![CDATA[prometheus]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=9312</guid>

					<description><![CDATA[Recently, we were migrating some Node.js applications to a different AWS account and we&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>Recently, we were migrating some Node.js applications to a different AWS account and we had a need to monitor the application with Prometheus and Grafana. In this post, I&#8217;ll explain how to install both Prometheus and Grafana using Helm charts, then we&#8217;ll create a simple Express.js web app, create a Helm chart for it and deploy it on an EKS cluster in AWS. I am using an EKS cluster, but you can use any Kubernetes cluster that you can manage it with kubectl. You might have to make some changes though in how you access the Load Balancer for Grafana.<br />
<strong>Requirements</strong>:</p>
<ul>
&#8211; eksctl<br />
&#8211; kubectl<br />
&#8211; helm</ul>
<h1>Create EKS cluster</h1>
<p>This is optional, if you already have a running Kubernetes cluster, you can skip this step.</p>
<pre class="brush: bash; title: ; notranslate">
eksctl create cluster --name eksTest \
--region us-east-2 \
--instance-types t3.medium \
--nodes 2 \
--managed \
--version 1.22
</pre>
<p>This command will create a 2 node cluster, with t3.medium instances in Ohio region using Amazon Linux 2 image. It will take about 15 minutes.<br />
Verify that everything looks OK.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get nodes
NAME                                           STATUS   ROLES    AGE     VERSION
ip-192-168-10-4.us-east-2.compute.internal     Ready    &lt;none&gt;   8m54s   v1.22.15-eks-fb459a0
ip-192-168-74-160.us-east-2.compute.internal   Ready    &lt;none&gt;   8m51s   v1.22.15-eks-fb459a0
</pre>
<h1>Install Prometheus and Grafana</h1>
<p>Add the Helm repos for Prometheus, Grafana and update any changes.</p>
<pre class="brush: bash; title: ; notranslate">
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
</pre>
<p>We&#8217;ll create a separate namespace for Prometheus and install the chart there.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl create namespace prometheus
helm install prometheus prometheus-community/prometheus \
    --namespace prometheus \
    --set alertmanager.persistentVolume.storageClass=&quot;gp2&quot; \
    --set server.persistentVolume.storageClass=&quot;gp2&quot;
</pre>
<p>Create this file. It tells Grafana what to use as a source (url). It&#8217;s the Prometheus endpoint service that we just installed.</p>
<pre class="brush: plain; title: ; notranslate">
cat &lt;&lt; EOF &gt; grafana.yaml
datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
    - name: Prometheus
      type: prometheus
      url: http://prometheus-server.prometheus.svc.cluster.local
      access: proxy
      isDefault: true
EOF
</pre>
<p>Now, let&#8217;s create a namespace for Grafana and then install Grafana in that namespace. Change the admin password. In my case it&#8217;s Password1$.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl create namespace grafana
helm install grafana grafana/grafana \
    --namespace grafana \
    --set persistence.storageClassName=&quot;gp2&quot; \
    --set persistence.enabled=true \
    --set adminPassword=&#039;Password1$&#039; \
    --values grafana.yaml \
    --set service.type=LoadBalancer
</pre>
<p>You&#8217;ll get something like this on the screen when the install ends.</p>
<pre class="brush: bash; title: ; notranslate">
export SERVICE_IP=$(kubectl get svc --namespace grafana grafana -o jsonpath=&#039;{.status.loadBalancer.ingress&#x5B;0].ip}&#039;)
</pre>
<p>That&#8217;s your load balancer IP so you can access Grafana. If you use EKS, your IP is different.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl get services -n grafana
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP                                                               PORT(S)        AGE
grafana   LoadBalancer   10.100.20.53   a074996f790b74793a74bec584c04460-2034633908.us-east-2.elb.amazonaws.com   80:31916/TCP   3m21s
</pre>
<p>The IP is the gibberish URL that ends with amazonaws.com. If you go to that URL, you can log as <strong>admin </strong>and your password.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/01/P161-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/01/P161-01-1024x929.png" alt="" width="1024" height="929" class="aligncenter size-large wp-image-9316" srcset="https://blog.andreev.it/wp-content/uploads/2023/01/P161-01-1024x929.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-01-300x272.png 300w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-01-768x696.png 768w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-01-585x531.png 585w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-01.png 1149w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><br />
Go to <strong>Dashboards</strong>, click on the <strong>New </strong>button on the right and click <strong>Import</strong>.<br />
Add the following dashboard IDs (one by one then click <strong>Load</strong>) and choose Prometheus as a source. The IDs are 3119 (Kubernetes cluster), 6417 (pods) and 11159 (Node.js). For the first dashboard, you&#8217;ll have some metrics. The Node.js dashboard is empty and our goal is to get the metrics from the application.</p>
<h1>Node.js application</h1>
<p>Create an empty folder and add this file.</p>
<pre class="brush: bash; title: ; notranslate">
mkdir mynodeapp
cd mynodeapp
cat &lt;&lt; EOF &gt; package.json
{
  &quot;name&quot;: &quot;mynodeapp&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;Node.js on Docker&quot;,
  &quot;author&quot;: &quot;Kliment Andreev &lt;kliment@andreev.it&gt;&quot;,
  &quot;main&quot;: &quot;server.js&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;node server.js&quot;
  },
  &quot;dependencies&quot;: {
    &quot;express&quot;: &quot;^4.16.1&quot;,
    &quot;prom-client&quot;: &quot;^14.1.1&quot;
  }
}
EOF
</pre>
<p>I use Node v18.12. Install the dependencies.</p>
<pre class="brush: bash; title: ; notranslate">
npm install
</pre>
<p>This is the simple web application. It listens on port 3000.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; &quot;EOF&quot; &gt; server.js
&#039;use strict&#039;
const express = require(&#039;express&#039;)
const prom = require(&#039;prom-client&#039;)

const collectDefaultMetrics = prom.collectDefaultMetrics();

const app = express()
const port = process.env.PORT || 3000

app.get(&#039;/&#039;, (req, res, next) =&gt; {
  res.send(&#039;Hello World!&#039;);
});

app.get(&#039;/metrics&#039;, async (req, res) =&gt; {
  try {
	  res.set(&#039;Content-Type&#039;, prom.register.contentType);
	  res.end(await prom.register.metrics());
  } 
  catch (ex) {
	  res.status(500).end(ex);
  }
});

const server = app.listen(port, () =&gt; {
  console.log(`mynodeapp listening on port ${port}!`)
})
EOF
</pre>
<p>The app is a super simple web app that prints Hello World! The important part is the <strong>/metrics</strong> router. That&#8217;s how you get the metrics from the app. When we created the package.json file, we specify the <strong>prom-client</strong> as dependency. That&#8217;s the part that collects the metrics. Go to this <a href="https://github.com/siimon/prom-client" rel="noopener" target="_blank">link </a>for more info.<br />
Start the app with <strong>node server.js</strong>.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
node server.js
mynodeapp listening on port 3000!
</pre>
<p>Go to the localhost or the server IP where this app is running and you&#8217;ll see <em>Hello World!</em> printed out.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/01/P161-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/01/P161-02.png" alt="" width="634" height="177" class="aligncenter size-full wp-image-9321" srcset="https://blog.andreev.it/wp-content/uploads/2023/01/P161-02.png 634w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-02-300x84.png 300w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-02-585x163.png 585w" sizes="(max-width: 634px) 100vw, 634px" /></a><br />
And if you go to the /metrics URL, you&#8217;ll see this text. That&#8217;s what Prometheus expects.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/01/P161-03.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/01/P161-03.png" alt="" width="874" height="325" class="aligncenter size-full wp-image-9322" srcset="https://blog.andreev.it/wp-content/uploads/2023/01/P161-03.png 874w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-03-300x112.png 300w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-03-768x286.png 768w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-03-585x218.png 585w" sizes="(max-width: 874px) 100vw, 874px" /></a><br />
Now that we know the app is working, let&#8217;s create a Docker image.</p>
<h1>Docker image and container</h1>
<p>Create this Dockerfile.</p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt; EOF &gt; Dockerfile
FROM node:18-alpine

# Create app directory
WORKDIR /app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install
# If you are building your code for production
# RUN npm install --only=production

# Bundle app source
COPY server.js /app

EXPOSE 3000
CMD &#x5B; &quot;npm&quot;, &quot;start&quot;, &quot;server.js&quot; ]
EOF
</pre>
<p>Create a Docker image based on the Dockerfile above. Replace the username with your Docker Hub username.</p>
<pre class="brush: bash; title: ; notranslate">
docker build -t &lt;username&gt;/mynodeapp:latest .
</pre>
<p>Run the container from the image.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
docker run -p 3000:3000 &lt;username&gt;/mynodeapp:latest

&gt; mynodeapp@1.0.0 start
&gt; node server.js server.js

mynodeapp listening on port 3000!
</pre>
<p>Use the same test as before by going to the localhost or IP URL on port 3000.<br />
If everything is OK, publish the image on Dockerhub.</p>
<pre class="brush: bash; title: ; notranslate">
docker login
docker push &lt;username&gt;/mynodeapp
</pre>
<h1>Helm chart</h1>
<p>Now that we have the Docker image, we&#8217;ll create a Helm chart so we can deploy the web app to the Kubernetes cluster.<br />
Create the necessary structure.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
helm create mynodeapp
Creating mynodeapp
</pre>
<p>Go to the sub-directory <strong>mynodeapp </strong>and edit the <strong>values.yaml</strong> file first. Look for the image key first and replace it so it looks like this. Make sure you replace the <strong><username></strong> with your Docker Hub username.</p>
<pre class="brush: bash; title: ; notranslate">
image:
  repository: &lt;username&gt;/mynodeapp
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: &quot;latest&quot;
</pre>
<p>Then look for <strong>podAnnotations: {}</strong> line and replace it so it looks like this.</p>
<pre class="brush: bash; title: ; notranslate">
podAnnotations:
  prometheus.io/scrape: &quot;true&quot;
  prometheus.io/path: &quot;/metrics&quot;
  prometheus.io/port: &quot;3000&quot;
</pre>
<p>Look for the <strong>service </strong>parameter and change it so it looks like this.</p>
<pre class="brush: bash; title: ; notranslate">
service:
  type: LoadBalancer
  port: 80
  targetPort: 3000
  name: mynodeapp-service
</pre>
<p>Look for the resources key and uncomment the defaults.</p>
<pre class="brush: bash; title: ; notranslate">
resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after &#039;resources:&#039;.
limits:
  cpu: 100m
  memory: 128Mi
requests:
  cpu: 100m
  memory: 128Mi
</pre>
<p>Edit the <strong>templates/deployment.yaml</strong> file and find these lines.</p>
<pre class="brush: bash; title: ; notranslate">
ports:
  - name: http
    containerPort: {{ .Values.service.port }}
</pre>
<p>Replace the containerPort to look like this.</p>
<pre class="brush: bash; title: ; notranslate">
containerPort: 3000
</pre>
<p>Go back to the root of the <strong>mynodeapp </strong>folder and check the chart.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
helm install mynodeapp --generate-name
NAME: mynodeapp-1674575241
LAST DEPLOYED: Tue Jan 24 10:47:22 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running &#039;kubectl get --namespace default svc -w mynodeapp-1674575241&#039;
  export SERVICE_IP=$(kubectl get svc --namespace default mynodeapp-1674575241 --template &quot;{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}&quot;)
  echo http://$SERVICE_IP:80
</pre>
<p>Execute the <strong>export SERVICE_IP</strong> command above and echo the IP. That&#8217;s your URL. If you go to that URL you&#8217;ll see the <em>Hello World!</em> greetings.<br />
What we care is the pod. Get the pod running. It&#8217;s in the default namespace.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
mynodeapp-1674575241-5ff9d7469d-pddcz   1/1     Running   0          3m
</pre>
<p>Get the annotations. Replace with your pod name.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get pod mynodeapp-1674575241-5ff9d7469d-pddcz -o jsonpath=&#039;{.metadata.annotations}&#039;
{&quot;kubernetes.io/psp&quot;:&quot;eks.privileged&quot;,&quot;prometheus.io/path&quot;:&quot;/metrics&quot;,&quot;prometheus.io/port&quot;:&quot;3000&quot;,&quot;prometheus.io/scrape&quot;:&quot;true&quot;}
</pre>
<p>You can see the prometheus annotations.<br />
And if you go to Grafana and open up the NodeJS dash you&#8217;ll see the metrics.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2023/01/P161-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2023/01/P161-04-1024x740.png" alt="" width="1024" height="740" class="aligncenter size-large wp-image-9327" srcset="https://blog.andreev.it/wp-content/uploads/2023/01/P161-04-1024x740.png 1024w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-04-300x217.png 300w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-04-768x555.png 768w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-04-1170x845.png 1170w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-04-585x423.png 585w, https://blog.andreev.it/wp-content/uploads/2023/01/P161-04.png 1251w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2023/01/kubernetes-monitoring-node-js-application-with-prometheus-and-grafana-helm-charts/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWS: Web Servers in HA config behind Application Load Balancer and Web Application Firewall (WAF)</title>
		<link>https://blog.andreev.it/2021/12/aws-web-servers-in-ha-config-behind-application-load-balancer-and-web-application-firewall-waf/</link>
					<comments>https://blog.andreev.it/2021/12/aws-web-servers-in-ha-config-behind-application-load-balancer-and-web-application-firewall-waf/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Mon, 06 Dec 2021 14:19:04 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[WAF]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=8991</guid>

					<description><![CDATA[As the title says, I&#8217;ll create two web servers in a private subnet, put&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>As the title says, I&#8217;ll create two web servers in a private subnet, put an Application Load Balancer in front and protect the content with Web Application Firewall (WAF). In addition, I&#8217;ll create a bastion host so I can access the web servers using SSH. Web Servers are not reachable from the Internet. </p>
<h1>Elastic IP</h1>
<p>First thing to do is to create an Elastic IP for the NAT gateway. NAT gateway is needed if you want to give the Web Servers Internet access so they can be patched. Go to <strong>Elastic IPs</strong> under <strong>Network &#038; Security</strong> in EC2 menu and click on <strong>Allocate Elastic IP address</strong> in the top right corner. Accept the defaults and click <strong>Allocate</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-01.png" alt="" width="826" height="763" class="aligncenter size-full wp-image-8994" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-01.png 826w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-01-300x277.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-01-768x709.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-01-585x540.png 585w" sizes="(max-width: 826px) 100vw, 826px" /></a></p>
<h1>VPC</h1>
<p>We&#8217;ll create a VPC with two public and two private subnets. Minimum two public subnets are needed for the Application Load Balancer. You can&#8217;t create an ALB with one public subnet.<br />
Click on Launch VPC Wizard in the VPC menu and select <strong>VPC with Public and Private Subnets</strong>. I&#8217;ll use this CIDR (192.168.100.0/22) which gives me 1024 hosts enough for 4 Class C /24 subnets, you can use whatever CIDR you want. As you can see I&#8217;ll use 192.168.100.0/24 for the first public subnet and 192.168.102.0/24 for the first private subnet. I&#8217;ll create the other public and private subnet later. You&#8217;ll have to associate the Elastic IP that you created in the previous step to the NAT gateway. Click <strong>Create VPC</strong> in the bottom right corner.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-02.png" alt="" width="711" height="657" class="aligncenter size-full wp-image-8995" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-02.png 711w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-02-300x277.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-02-585x541.png 585w" sizes="(max-width: 711px) 100vw, 711px" /></a><br />
Go to <strong>Subnets </strong>menu click on the public subnet and look at the Availability Zone.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-04.png" alt="" width="1091" height="552" class="aligncenter size-full wp-image-8997" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-04.png 1091w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-04-300x152.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-04-1024x518.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-04-768x389.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-04-585x296.png 585w" sizes="(max-width: 1091px) 100vw, 1091px" /></a><br />
When you create the next subnet, you have to put it on another zone.<br />
Click on the <strong>Route table</strong> tab. Remember/write-down the route table id (rtb-0e3cbsomething). It will be different for you.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-03.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-03.png" alt="" width="613" height="454" class="aligncenter size-full wp-image-8996" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-03.png 613w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-03-300x222.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-03-585x433.png 585w" sizes="(max-width: 613px) 100vw, 613px" /></a><br />
Then click on <strong>Create subnet</strong> in the upper right corner. Choose your VPC, name the subnet (in my case subPublic2), assign an <strong>Availability Zone</strong> (e.g. us-east-1a) and add the CIDR 192.168.101.0/24. Make sure that your 2nd public Availability Zone is different than the 1st Availability Zone. Click <strong>Create subnet</strong>.<br />
If you click on the new subnet now and then click on the Route table tab, you&#8217;ll see that AWS created a route table for you. But as you can notice below, the destination 0.0.0.0/0 goes through the NAT gateway.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-05.png" alt="" width="1088" height="618" class="aligncenter size-full wp-image-8998" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-05.png 1088w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-05-300x170.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-05-1024x582.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-05-768x436.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-05-585x332.png 585w" sizes="(max-width: 1088px) 100vw, 1088px" /></a><br />
We don&#8217;t want that. We want the public subnet to use the Internet gateway, not the NAT gateway. So click on the E<strong>dit route table association</strong> on the right. From the drop down choose the other route table, that&#8217;s the Internet gateway, that&#8217;s the one that you have wrote down earlier. When you change to the other route table, you&#8217;ll see that the destination for 0.0.0.0/0 changes to igw which is the Internet gateway. Click <strong>Save</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-06.png" alt="" width="863" height="572" class="aligncenter size-full wp-image-8999" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-06.png 863w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-06-300x199.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-06-768x509.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-06-780x516.png 780w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-06-585x388.png 585w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-06-263x175.png 263w" sizes="(max-width: 863px) 100vw, 863px" /></a><br />
Now, do the same for the other private subnet. Click on the private subnet and look at the Availability Zone. In my case it&#8217;s us-east-1b. This means, I have to create my 2nd private subnet in another zone.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-07.png" alt="" width="1005" height="462" class="aligncenter size-full wp-image-9000" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-07.png 1005w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-07-300x138.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-07-768x353.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-07-585x269.png 585w" sizes="(max-width: 1005px) 100vw, 1005px" /></a><br />
So, I am creating the 2nd private subnet within my VPC and I named my subnet subPvt2 in us-east-1a and 192.168.103.0/24 CIDR.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-08.png" alt="" width="657" height="634" class="aligncenter size-full wp-image-9001" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-08.png 657w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-08-300x289.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-08-585x565.png 585w" sizes="(max-width: 657px) 100vw, 657px" /></a><br />
Check the route table for this new private subnet. The route table for 0.0.0.0/0 should be the NAT gateway which is correct, but just in case&#8230;<br />
So these are my subnets now.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-09.png" alt="" width="1005" height="133" class="aligncenter size-full wp-image-9002" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-09.png 1005w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-09-300x40.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-09-768x102.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-09-585x77.png 585w" sizes="(max-width: 1005px) 100vw, 1005px" /></a></p>
<h1>Bastion host</h1>
<p>You use a bastion host as a jump server to get to the web servers. If you have any other means to get to them such as VPN or Systems Manager, then go for it. This is just an example of how to get to the web servers.<br />
I&#8217;ll use the AWS Linux for this. It really doesn&#8217;t matter as long you have a SSH client installed on the bastion host. Launch a simple t2.micro instance in any public subnet. Make sure the bastion host has a public IP. Also make sure that this bastion host is accessible from a single IP or some dedicated subnet. You don&#8217;t want this bastion host to be exposed to the world. In my case, the security group for the bastion host is restricted to my home IP only on port 22 for SSH. </p>
<h1>Web Servers</h1>
<p>I&#8217;ll use two AWS Linux instances deployed in the private subnets. Do not assign a public IP for these. That&#8217;s what the bastion host is for. Create a security group that allows traffic for SSH from the security group that you created for the bastion host. In my case it looks like this. Start typing <strong>sg </strong>in the circled field and choose the security group that you create earlier. If you named it properly, you&#8217;ll see it. If not, go get the security group ID for the bastion host.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-10.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-10.png" alt="" width="813" height="406" class="aligncenter size-full wp-image-9003" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-10.png 813w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-10-300x150.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-10-768x384.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-10-585x292.png 585w" sizes="(max-width: 813px) 100vw, 813px" /></a><br />
Launch two instances in two different private subnets. </p>
<h2>Test connection</h2>
<p>Log to the bastion host using the key you created. Now, we&#8217;ll connect to the web servers from the bastion host. If you used the same key for bastion host and web servers, copy and paste the PEM certificate in a file called key.pem or whatever. If you used different keys, it doesn&#8217;t matter, just make sure you have the keys as a file on the bastion host. I used the same key, which is not the best practice, but for the sake of simplicity, I have a file called key.pem on the bastion host. Log to each web server using  ssh ec2-user@192.168.102.176 -i key.pem where ec2-user is the user for the AWS Linux (it differs for different Linux distros), 192.168.102.176 is the IP of the first web server and the parameter -i tells which key file certificate to use. Make sure you do <strong>chmod 600 key.pem</strong> so you can log in. You should be able to log in to both web servers. </p>
<h1>Install Apache web server</h1>
<p>Log to each web server and execute these commands to install Apache web server, start it and enable to start on boot. </p>
<pre class="brush: bash; title: ; notranslate">
sudo yum -y install httpd
sudo systemctl start httpd
sudo systemctl enable httpd
</pre>
<p>Switch to root.</p>
<pre class="brush: bash; title: ; notranslate">
sudo su
</pre>
<p>Then on each server do this. Do Server 2 on the second server. This is just an example. You need the same code on both web servers. The difference is because we&#8217;ll test the ALB later and see how the page changes. </p>
<pre class="brush: bash; title: ; notranslate">
echo &quot;Server 1&quot; &gt; /var/www/html/index.html
</pre>
<p>For the 2nd server. </p>
<pre class="brush: bash; title: ; notranslate">
echo &quot;Server 2&quot; &gt; /var/www/html/index.html
</pre>
<h1>Application Load Balancer</h1>
<p>Now, we are moving to the ALB. Before we create the ALB, let&#8217;s create a security group that will allow port 80 from everywhere. Looks like this. Create the security group from the EC2 menu. This is a security group that we&#8217;ll assign to the load balancer.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-11.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-11.png" alt="" width="1198" height="668" class="aligncenter size-full wp-image-9005" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-11.png 1198w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-11-300x167.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-11-1024x571.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-11-768x428.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-11-1170x652.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-11-585x326.png 585w" sizes="(max-width: 1198px) 100vw, 1198px" /></a><br />
From the EC2 menu on the left side, go to <strong>Load Balancing</strong> and click on <strong>Load Balancers</strong>. Click on <strong>Create Load Balancer</strong>. Choose <strong>Application Load Balancer</strong>. Make sure you choose the public subnets and for the security group use the one that we created earlier for the load balancer.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-12.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-12.png" alt="" width="871" height="764" class="aligncenter size-full wp-image-9006" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-12.png 871w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-12-300x263.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-12-768x674.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-12-585x513.png 585w" sizes="(max-width: 871px) 100vw, 871px" /></a><br />
If you scroll down, you&#8217;ll see that you need to create a target group for the ALB. The target is our web servers. Click on <strong>Create target group</strong>. It will open up a new browser window. Choose <strong>Instances</strong>, name the target group, leave HTTP1 for protocol and for the <strong>Health checks</strong> type /index.html. Remember, we created these files earlier.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-13.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-13.png" alt="" width="862" height="712" class="aligncenter size-full wp-image-9007" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-13.png 862w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-13-300x248.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-13-768x634.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-13-585x483.png 585w" sizes="(max-width: 862px) 100vw, 862px" /></a><br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-14.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-14.png" alt="" width="872" height="767" class="aligncenter size-full wp-image-9008" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-14.png 872w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-14-300x264.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-14-768x676.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-14-585x515.png 585w" sizes="(max-width: 872px) 100vw, 872px" /></a><br />
Click <strong>Next</strong>, select the web servers and click on <strong>Include as pending below</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-15.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-15.png" alt="" width="1134" height="570" class="aligncenter size-full wp-image-9009" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-15.png 1134w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-15-300x151.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-15-1024x515.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-15-768x386.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-15-585x294.png 585w" sizes="(max-width: 1134px) 100vw, 1134px" /></a><br />
Finally, click on <strong>Create target group</strong>.<br />
Go back to the original tab when you created the load balancer, click on the refresh button and choose the target group you just created.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-16.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-16.png" alt="" width="1061" height="288" class="aligncenter size-full wp-image-9010" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-16.png 1061w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-16-300x81.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-16-1024x278.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-16-768x208.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-16-585x159.png 585w" sizes="(max-width: 1061px) 100vw, 1061px" /></a><br />
Scroll down and click on <strong>Create load balancer</strong>.<br />
Wait for a couple minutes until you see that the state changes to <strong>Active</strong>. Click Refresh on the right side.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-17.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-17.png" alt="" width="670" height="199" class="aligncenter size-full wp-image-9011" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-17.png 670w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-17-300x89.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-17-585x174.png 585w" sizes="(max-width: 670px) 100vw, 670px" /></a><br />
While you are waiting, if you click on the <strong>Description </strong>tab for the load balancer and  scroll a little bit down, you&#8217;ll see the security group that we create for the load balancer.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-18.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-18.png" alt="" width="727" height="537" class="aligncenter size-full wp-image-9012" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-18.png 727w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-18-300x222.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-18-585x432.png 585w" sizes="(max-width: 727px) 100vw, 727px" /></a><br />
We need to add this security group to the web servers. Right now, the web servers are configured to accept traffic from the bastion host only, but we need for the web servers to accept HTTP traffic as well. Now, the security group for the load balancer allows HTTP from everywhere, but that doesn&#8217;t matter. When we add this security group to the web servers they&#8217;ll also allow HTTP request from everywhere. But the web servers have no public access so it really doesn&#8217;t matter. Does this mean that any other server in these public/private subnets will have access to HTTP to the web servers? Yes. If I create another server in the private/public subnet, it will have access to HTTP to the web servers.<br />
Let&#8217;s add the ALB security group to the instances. In my case it looks like this where the bottom security group is the security group for allowing HTTP from everywhere, the one that we assigned to the ALB.<br />
 <a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-19.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-19.png" alt="" width="1293" height="386" class="aligncenter size-full wp-image-9014" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-19.png 1293w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-19-300x90.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-19-1024x306.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-19-768x229.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-19-1170x349.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-19-585x175.png 585w" sizes="(max-width: 1293px) 100vw, 1293px" /></a><br />
Before you do anything, go to the <strong>Target Groups</strong> menu under <strong>Load Balancing</strong>. You should see both instances as <strong>healthy</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-20.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-20.png" alt="" width="1253" height="757" class="aligncenter size-full wp-image-9016" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-20.png 1253w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-20-300x181.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-20-1024x619.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-20-768x464.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-20-1170x707.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-20-585x353.png 585w" sizes="(max-width: 1253px) 100vw, 1253px" /></a><br />
If this looks good, go back to the load balancer and get the URL.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-21.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-21.png" alt="" width="992" height="332" class="aligncenter size-full wp-image-9017" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-21.png 992w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-21-300x100.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-21-768x257.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-21-585x196.png 585w" sizes="(max-width: 992px) 100vw, 992px" /></a><br />
Open up a new browser window and go to that URL. Click refresh in your browser several times. You&#8217;ll see how it changes from Server 1 to Server 2.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-22.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-22.png" alt="" width="735" height="192" class="aligncenter size-full wp-image-9018" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-22.png 735w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-22-300x78.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-22-585x153.png 585w" sizes="(max-width: 735px) 100vw, 735px" /></a><br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-23.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-23.png" alt="" width="702" height="199" class="aligncenter size-full wp-image-9019" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-23.png 702w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-23-300x85.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-23-585x166.png 585w" sizes="(max-width: 702px) 100vw, 702px" /></a><br />
If everything is OK, proceed further. </p>
<h1>Web Application Firewall</h1>
<p>The good news is that you can directly create your rules from the ALB. Click on the load balancer you created and click on the <strong>Integrated services</strong> tab. Click on C<strong>reate Web ACL</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-24.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-24.png" alt="" width="631" height="655" class="aligncenter size-full wp-image-9021" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-24.png 631w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-24-289x300.png 289w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-24-585x607.png 585w" sizes="(max-width: 631px) 100vw, 631px" /></a><br />
Name the ACL and click on Add AWS resources where we gonna add the ALB.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-25.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-25.png" alt="" width="867" height="660" class="aligncenter size-full wp-image-9022" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-25.png 867w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-25-300x228.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-25-768x585.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-25-585x445.png 585w" sizes="(max-width: 867px) 100vw, 867px" /></a><br />
Here we are. Click <strong>Next </strong>after.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-26.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-26.png" alt="" width="867" height="448" class="aligncenter size-full wp-image-9023" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-26.png 867w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-26-300x155.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-26-768x397.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-26-585x302.png 585w" sizes="(max-width: 867px) 100vw, 867px" /></a><br />
And now you are on your own. You have to add rules. There are some that are templated and there are some that you can buy from the AWS Marketplace.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/12/P157-27.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/12/P157-27.png" alt="" width="869" height="694" class="aligncenter size-full wp-image-9024" srcset="https://blog.andreev.it/wp-content/uploads/2021/12/P157-27.png 869w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-27-300x240.png 300w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-27-768x613.png 768w, https://blog.andreev.it/wp-content/uploads/2021/12/P157-27-585x467.png 585w" sizes="(max-width: 869px) 100vw, 869px" /></a><br />
Once you add these rules, you can see what&#8217;s blocked and what&#8217;s not under <strong>Cloud Watch | All metrics | WAFV2</strong>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2021/12/aws-web-servers-in-ha-config-behind-application-load-balancer-and-web-application-firewall-waf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure, AWS: Use Azure AD with AWS SSO</title>
		<link>https://blog.andreev.it/2021/06/azure-aws-use-azure-ad-with-aws-sso/</link>
					<comments>https://blog.andreev.it/2021/06/azure-aws-use-azure-ad-with-aws-sso/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Sat, 12 Jun 2021 16:14:21 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Azure AD]]></category>
		<category><![CDATA[SSO]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=8872</guid>

					<description><![CDATA[There is a newer version of this post but feel free to refer to&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>There is a <a href="https://blog.andreev.it/2024/03/entra-id-azure-ad-and-aws-sso/" rel="noopener" target="_blank">newer</a> version of this post but feel free to refer to this one in case you have any issues.<br />
In this post, I&#8217;ll explain how to configure both Azure and AWS so when you log to AWS you&#8217;ll use your Azure AD credentials. In my lab, I have an Azure subscription and Azure AD (free tier). I also have a multi account AWS in AWS Organization structure. I want to be able to log to both Azure and AWS using the same Azure AD credentials. This way, I can use only Azure AD to control access to my cloud resources.<br />
First, we&#8217;ll configure AWS for SSO. Log to your management/root account in AWS. Type <strong>sso </strong>in the search bar and choose <strong>Azure Single Sign-On</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-01.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-01.png" alt="" width="676" height="240" class="aligncenter size-full wp-image-8873" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-01.png 676w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-01-300x107.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-01-585x208.png 585w" sizes="(max-width: 676px) 100vw, 676px" /></a><br />
Click on the button to <strong>Enable AWS SSO</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-02.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-02.png" alt="" width="867" height="358" class="aligncenter size-full wp-image-8874" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-02.png 867w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-02-300x124.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-02-768x317.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-02-585x242.png 585w" sizes="(max-width: 867px) 100vw, 867px" /></a><br />
From the <strong>Dashboard</strong>, <strong>Choose your identity source</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-03.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-03.png" alt="" width="1219" height="540" class="aligncenter size-full wp-image-8877" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-03.png 1219w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-03-300x133.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-03-1024x454.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-03-768x340.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-03-1170x518.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-03-585x259.png 585w" sizes="(max-width: 1219px) 100vw, 1219px" /></a><br />
You&#8217;ll be taken to the <strong>Settings </strong>pane. Click on <strong>Change </strong>under <strong>Identity source</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-04.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-04.png" alt="" width="940" height="408" class="aligncenter size-full wp-image-8878" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-04.png 940w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-04-300x130.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-04-768x333.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-04-585x254.png 585w" sizes="(max-width: 940px) 100vw, 940px" /></a><br />
Select <strong>External identity provider</strong> and click on <strong>Download metadata file</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-05.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-05.png" alt="" width="1278" height="490" class="aligncenter size-full wp-image-8879" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-05.png 1278w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-05-300x115.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-05-1024x393.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-05-768x294.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-05-1170x449.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-05-585x224.png 585w" sizes="(max-width: 1278px) 100vw, 1278px" /></a><br />
In Azure, go to <strong>Azure Active Directory</strong> and from the left, click on <strong>Enterprise applications</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-06.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-06.png" alt="" width="392" height="484" class="aligncenter size-full wp-image-8880" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-06.png 392w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-06-243x300.png 243w" sizes="(max-width: 392px) 100vw, 392px" /></a><br />
Click on <strong>New application</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-07.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-07.png" alt="" width="484" height="313" class="aligncenter size-full wp-image-8881" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-07.png 484w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-07-300x194.png 300w" sizes="(max-width: 484px) 100vw, 484px" /></a><br />
Search for <strong>AWS Single</strong> and choose <strong>AWS Single Sign-on</strong>. Make sure you choose this one if you have multi accounts in AWS. The setup is slightly different for the other single account access.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-08.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-08.png" alt="" width="742" height="501" class="aligncenter size-full wp-image-8882" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-08.png 742w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-08-300x203.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-08-585x395.png 585w" sizes="(max-width: 742px) 100vw, 742px" /></a><br />
Click on the <strong>Create </strong>button on the right and wait for about 30 seconds. When this screen shows up, click on <strong>Single sign-on</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-09.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-09.png" alt="" width="571" height="465" class="aligncenter size-full wp-image-8883" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-09.png 571w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-09-300x244.png 300w" sizes="(max-width: 571px) 100vw, 571px" /></a><br />
Choose <strong>SAML </strong>from the choices.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-10.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-10.png" alt="" width="1143" height="310" class="aligncenter size-full wp-image-8884" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-10.png 1143w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-10-300x81.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-10-1024x278.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-10-768x208.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-10-585x159.png 585w" sizes="(max-width: 1143px) 100vw, 1143px" /></a><br />
When this screen shows up, choose <strong>Upload metadata file</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-11.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-11.png" alt="" width="798" height="510" class="aligncenter size-full wp-image-8885" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-11.png 798w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-11-300x192.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-11-768x491.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-11-585x374.png 585w" sizes="(max-width: 798px) 100vw, 798px" /></a><br />
Select the file that you just downloaded from the last step you did previously in AWS. You don&#8217;t have to do anything, just click <strong>Save</strong> and then close the blade on the right.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-12.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-12.png" alt="" width="780" height="564" class="aligncenter size-full wp-image-8887" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-12.png 780w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-12-300x217.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-12-768x555.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-12-585x423.png 585w" sizes="(max-width: 780px) 100vw, 780px" /></a><br />
You&#8217;ll get something like this, just click there as instructed to fix it.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-13.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-13.png" alt="" width="522" height="136" class="aligncenter size-full wp-image-8888" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-13.png 522w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-13-300x78.png 300w" sizes="(max-width: 522px) 100vw, 522px" /></a><br />
If you are asked if you want to test the SSO, just ignore the message and click on, <strong>No, I&#8217;ll test later</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-14.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-14.png" alt="" width="595" height="169" class="aligncenter size-full wp-image-8889" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-14.png 595w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-14-300x85.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-14-585x166.png 585w" sizes="(max-width: 595px) 100vw, 595px" /></a><br />
Scroll down a bit to step 4 and click on Install the extension. This is the easiest way to do this. You can go the manual way, but it&#8217;s not worth it. Don&#8217;t worry, you can always remove the extension after. It&#8217;s for a one time setup. I use Firefox and it works fine, which means it will definitely work on Chrome and Edge.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-15.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-15.png" alt="" width="804" height="197" class="aligncenter size-full wp-image-8890" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-15.png 804w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-15-300x74.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-15-768x188.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-15-585x143.png 585w" sizes="(max-width: 804px) 100vw, 804px" /></a><br />
Once you install the extension, click on it in the upper right corner.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-16.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-16.png" alt="" width="324" height="77" class="aligncenter size-full wp-image-8891" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-16.png 324w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-16-300x71.png 300w" sizes="(max-width: 324px) 100vw, 324px" /></a><br />
Click on <strong>Sign in to get started</strong> button.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-17.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-17.png" alt="" width="376" height="279" class="aligncenter size-full wp-image-8892" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-17.png 376w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-17-300x223.png 300w" sizes="(max-width: 376px) 100vw, 376px" /></a><br />
You should be automatically signed if you have the Azure session open in another tab. You&#8217;ll see something like this if everything is OK.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-18.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-18.png" alt="" width="804" height="378" class="aligncenter size-full wp-image-8893" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-18.png 804w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-18-300x141.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-18-768x361.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-18-585x275.png 585w" sizes="(max-width: 804px) 100vw, 804px" /></a><br />
Click on the button <strong>Set up AWS Single Sign-on</strong> under step 5. Azure will prompt you to save the federation XML file and just click OK for the Azure prompt.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-25.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-25.png" alt="" width="1108" height="736" class="aligncenter size-full wp-image-8908" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-25.png 1108w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-25-300x199.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-25-1024x680.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-25-768x510.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-25-585x389.png 585w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-25-263x175.png 263w" sizes="(max-width: 1108px) 100vw, 1108px" /></a><br />
Azure will open a new AWS tab and ask you to confirm if it&#8217;s OK to make changes in AWS. Click OK.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-26.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-26.png" alt="" width="748" height="451" class="aligncenter size-full wp-image-8909" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-26.png 748w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-26-300x181.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-26-585x353.png 585w" sizes="(max-width: 748px) 100vw, 748px" /></a><br />
You&#8217;ll see some action on the screen like someone clicks in your browser. Don&#8217;t do anything. Just click Yes or OK when asked.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-27.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-27.png" alt="" width="714" height="414" class="aligncenter size-full wp-image-8910" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-27.png 714w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-27-300x174.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-27-585x339.png 585w" sizes="(max-width: 714px) 100vw, 714px" /></a><br />
The extension will ask you to upload the federation XML file from Azure that you saved earlier and then it will reconfigure AWS and you should see this at the end.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-28.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-28.png" alt="" width="1290" height="697" class="aligncenter size-full wp-image-8911" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-28.png 1290w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-28-300x162.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-28-1024x553.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-28-768x415.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-28-1170x632.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-28-585x316.png 585w" sizes="(max-width: 1290px) 100vw, 1290px" /></a><br />
You&#8217;ll end up at the initial screen for step 1 in Azure. Just click <strong>Save </strong>and you are OK.<br />
Now, if you don&#8217;t have any users or groups in Azure AD, go create some. In my case, I use free Azure AD so I am not gonna be able to use groups. It requires Azure AD P1 which is not free. But, I&#8217;ll show you how to assign a user in Azure AD. If you use Azure AD Connect to sync your on-prem AD with your Azure AD, you can choose that user as well. With your <strong>AWS Single Sign-on</strong> app selected, click on <strong>Users and groups</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-19.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-19.png" alt="" width="411" height="346" class="aligncenter size-full wp-image-8894" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-19.png 411w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-19-300x253.png 300w" sizes="(max-width: 411px) 100vw, 411px" /></a><br />
Click on Add user/group.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-20.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-20.png" alt="" width="405" height="232" class="aligncenter size-full wp-image-8895" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-20.png 405w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-20-300x172.png 300w" sizes="(max-width: 405px) 100vw, 405px" /></a><br />
As I mentioned earlier, I can&#8217;y choose groups because of my Azure AD free tier, so click on <strong>None Selected</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-21.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-21.png" alt="" width="722" height="320" class="aligncenter size-full wp-image-8896" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-21.png 722w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-21-300x133.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-21-585x259.png 585w" sizes="(max-width: 722px) 100vw, 722px" /></a><br />
Select the user and just click <strong>Assign </strong>at the bottom.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-22.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-22.png" alt="" width="364" height="234" class="aligncenter size-full wp-image-8897" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-22.png 364w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-22-300x193.png 300w" sizes="(max-width: 364px) 100vw, 364px" /></a><br />
I&#8217;ve assigned this guy from Azure AD (awsadmin). Just click on <strong>Provisioning </strong>on the left. We have to sync these users to AWS now.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-23.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-23.png" alt="" width="688" height="450" class="aligncenter size-full wp-image-8898" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-23.png 688w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-23-300x196.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-23-585x383.png 585w" sizes="(max-width: 688px) 100vw, 688px" /></a><br />
Click on <strong>Get started</strong> and choose <strong>Automatic</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-24.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-24.png" alt="" width="811" height="642" class="aligncenter size-full wp-image-8899" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-24.png 811w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-24-300x237.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-24-768x608.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-24-585x463.png 585w" sizes="(max-width: 811px) 100vw, 811px" /></a><br />
You&#8217;ll have to provide a <strong>Tenant URL</strong> and a <strong>Secret Token</strong>.<br />
Go back to AWS, then <strong>SSO</strong>, click on <strong>Settings</strong> and click on <strong>Enable automatic provisioning</strong> under <strong>Provisioning</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-31.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-31.png" alt="" width="922" height="609" class="aligncenter size-full wp-image-8916" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-31.png 922w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-31-300x198.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-31-768x507.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-31-780x516.png 780w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-31-585x386.png 585w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-31-263x175.png 263w" sizes="(max-width: 922px) 100vw, 922px" /></a><br />
Click on <strong>Show token</strong>. Copy both values on the clipboard, those are the <strong>Tenant URL</strong> and the <strong>token </strong>needed for Azure. Copy and paste them in Azure.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-29.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-29.png" alt="" width="1192" height="727" class="aligncenter size-full wp-image-8913" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-29.png 1192w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-29-300x183.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-29-1024x625.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-29-768x468.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-29-1170x714.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-29-585x357.png 585w" sizes="(max-width: 1192px) 100vw, 1192px" /></a><br />
Click <strong>Test Connection</strong> and make sure you get OK as a result. Click <strong>Save</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-30.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-30.png" alt="" width="1204" height="840" class="aligncenter size-full wp-image-8914" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-30.png 1204w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-30-300x209.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-30-1024x714.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-30-768x536.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-30-1170x816.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-30-585x408.png 585w" sizes="(max-width: 1204px) 100vw, 1204px" /></a><br />
You&#8217;ll be back here. Click on <strong>Edit attribute mappings</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-32.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-32.png" alt="" width="1144" height="715" class="aligncenter size-full wp-image-8917" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-32.png 1144w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-32-300x188.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-32-1024x640.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-32-768x480.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-32-585x366.png 585w" sizes="(max-width: 1144px) 100vw, 1144px" /></a><br />
Change the <strong>Provisioning status</strong> to <strong>On </strong>and click <strong>Save</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-33.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-33.png" alt="" width="641" height="886" class="aligncenter size-full wp-image-8918" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-33.png 641w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-33-217x300.png 217w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-33-585x809.png 585w" sizes="(max-width: 641px) 100vw, 641px" /></a><br />
If you close that blade, you&#8217;ll see that one user synced successfully.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-34.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-34.png" alt="" width="733" height="540" class="aligncenter size-full wp-image-8919" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-34.png 733w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-34-300x221.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-34-585x431.png 585w" sizes="(max-width: 733px) 100vw, 733px" /></a><br />
Go to AWS and under <strong>Users </strong>you&#8217;ll see the same user. Yay!<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-35.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-35.png" alt="" width="1303" height="664" class="aligncenter size-full wp-image-8920" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-35.png 1303w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-35-300x153.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-35-1024x522.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-35-768x391.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-35-1170x596.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-35-585x298.png 585w" sizes="(max-width: 1303px) 100vw, 1303px" /></a><br />
OK, if you look at the User portal, you&#8217;ll see the URL for your Single Sign-on. You definitely want to customize the URL to suit your needs.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-36.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-36.png" alt="" width="1663" height="514" class="aligncenter size-full wp-image-8922" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-36.png 1663w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-36-300x93.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-36-1024x316.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-36-768x237.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-36-1536x475.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-36-1170x362.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-36-585x181.png 585w" sizes="(max-width: 1663px) 100vw, 1663px" /></a><br />
Go to that URL, but use a separate browser or some private mode, so you don&#8217;t affect any open sessions. You&#8217;ll be redirected and prompted to log with your Azure AD. You might get a prompt to configure MFA, skip it for now, you can always configure it later.<br />
This is what you&#8217;ll see.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-37.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-37.png" alt="" width="1006" height="646" class="aligncenter size-full wp-image-8923" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-37.png 1006w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-37-300x193.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-37-768x493.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-37-585x376.png 585w" sizes="(max-width: 1006px) 100vw, 1006px" /></a><br />
But we assigned AWS app in Azure to this user. What&#8217;s going on?<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-38.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-38.png" alt="" width="744" height="610" class="aligncenter size-full wp-image-8924" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-38.png 744w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-38-300x246.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-38-585x480.png 585w" sizes="(max-width: 744px) 100vw, 744px" /></a><br />
Just go to <strong>AWS | SSO | AWS Accounts</strong>, select the accounts that you assign to a user and click <strong>Assign users</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-39.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-39.png" alt="" width="1327" height="886" class="aligncenter size-full wp-image-8925" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-39.png 1327w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-39-300x200.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-39-1024x684.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-39-768x513.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-39-1170x781.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-39-585x391.png 585w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-39-263x175.png 263w" sizes="(max-width: 1327px) 100vw, 1327px" /></a><br />
Select the user (in my case awsadmin) and click<strong> Permission sets</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-40.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-40.png" alt="" width="1608" height="979" class="aligncenter size-full wp-image-8926" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-40.png 1608w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-40-300x183.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-40-1024x623.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-40-768x468.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-40-1536x935.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-40-1170x712.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-40-585x356.png 585w" sizes="(max-width: 1608px) 100vw, 1608px" /></a><br />
Click on <strong>Create new permission set</strong>. This will open a new tab.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-41.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-41.png" alt="" width="1594" height="634" class="aligncenter size-full wp-image-8927" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-41.png 1594w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-41-300x119.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-41-1024x407.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-41-768x305.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-41-1536x611.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-41-1170x465.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-41-585x233.png 585w" sizes="(max-width: 1594px) 100vw, 1594px" /></a><br />
This is where you assign permission to that user or group (if you have license). Use the second option for custom policies, e.g. access to only a set of S3 buckets or assign tagging policies only. I&#8217;ll use the first option for the pre-made policies. Click on <strong>Details</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-42.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-42.png" alt="" width="1566" height="505" class="aligncenter size-full wp-image-8928" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-42.png 1566w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-42-300x97.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-42-1024x330.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-42-768x248.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-42-1536x495.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-42-1170x377.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-42-585x189.png 585w" sizes="(max-width: 1566px) 100vw, 1566px" /></a><br />
I&#8217;ll use the <strong>Administrator access</strong>. Click <strong>Tags </strong>to proceed.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-43.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-43.png" alt="" width="1585" height="1090" class="aligncenter size-full wp-image-8929" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-43.png 1585w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-43-300x206.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-43-1024x704.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-43-768x528.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-43-1536x1056.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-43-1170x805.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-43-585x402.png 585w" sizes="(max-width: 1585px) 100vw, 1585px" /></a><br />
Finish the rest and go back to the first AWS tab. On the right click the <strong>Refresh </strong>button.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-44.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-44.png" alt="" width="1633" height="295" class="aligncenter size-full wp-image-8930" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-44.png 1633w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-44-300x54.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-44-1024x185.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-44-768x139.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-44-1536x277.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-44-1170x211.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-44-585x106.png 585w" sizes="(max-width: 1633px) 100vw, 1633px" /></a><br />
The permission set should show up. Select and click <strong>Finish</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-45.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-45.png" alt="" width="1561" height="399" class="aligncenter size-full wp-image-8931" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-45.png 1561w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-45-300x77.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-45-1024x262.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-45-768x196.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-45-1536x393.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-45-1170x299.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-45-585x150.png 585w" sizes="(max-width: 1561px) 100vw, 1561px" /></a><br />
If the permission set doesn&#8217;t show up, don&#8217;t worry. Repeat the same same procedure from the AWS accounts menu to select accounts and users and you&#8217;ll see that this time the permission set will show up.<br />
Finally, go to the same URL where nothing showed up when we logged with that awsadmin user. Click Refresh in the browser and you&#8217;ll see the accounts now.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-46.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-46.png" alt="" width="1677" height="1132" class="aligncenter size-full wp-image-8934" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-46.png 1677w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-46-300x203.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-46-1024x691.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-46-768x518.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-46-1536x1037.png 1536w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-46-1170x790.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-46-585x395.png 585w" sizes="(max-width: 1677px) 100vw, 1677px" /></a><br />
Click to log to the Management Console and you are all set!<br />
<a href="https://blog.andreev.it/wp-content/uploads/2021/06/P155-47.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2021/06/P155-47.png" alt="" width="1189" height="226" class="aligncenter size-full wp-image-8935" srcset="https://blog.andreev.it/wp-content/uploads/2021/06/P155-47.png 1189w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-47-300x57.png 300w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-47-1024x195.png 1024w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-47-768x146.png 768w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-47-1170x222.png 1170w, https://blog.andreev.it/wp-content/uploads/2021/06/P155-47-585x111.png 585w" sizes="(max-width: 1189px) 100vw, 1189px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2021/06/azure-aws-use-azure-ad-with-aws-sso/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWS: DataSync using VPN and Internet</title>
		<link>https://blog.andreev.it/2020/10/aws-datasync-using-vpn-and-internet/</link>
					<comments>https://blog.andreev.it/2020/10/aws-datasync-using-vpn-and-internet/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Sun, 04 Oct 2020 14:54:06 +0000</pubDate>
				<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[DataSync]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[VPN]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=7308</guid>

					<description><![CDATA[In this post I&#8217;ll explain how to use AWS DataSync service. Per AWS, AWS&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>In this post I&#8217;ll explain how to use AWS DataSync service. Per AWS, <em>AWS DataSync makes it simple and fast to move large amounts of data online between on-premises storage and Amazon S3, Amazon Elastic File System (Amazon EFS), or Amazon FSx for Windows File Server.</em><br />
You can used this service when you migrate your on-prem shares to Win FSx or EFS. In my case, I&#8217;ll migrate a Windows share that I have at home to an S3 bucket. In the first scenario, I&#8217;ll use the straight public Internet to migrate data and the second scenario, I&#8217;ll use a VPN connection. The config for VPN transfer is pretty much what you&#8217;ll have if you use Direct Connect instead. In both cases you&#8217;ll need a VM instance on-prem. I have a vCenter running, so I&#8217;ll download an OVA file from AWS, but they also offer VMs for KVM, Hyper-V even EC2. </p>
<h1>Install a VM</h1>
<p>No matter if you use public Internet or VPN/Direct Connect, you&#8217;ll need an on-prem VM that will act as a proxy between your on-prem data and AWS. There is no need to install any agents on any servers. For example, I&#8217;ll have a Windows share and the only thing this VM requires is access to that share with a domain or a workgroup user. Go to AWS DataSync service and click to download the image for your hypervisor.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-01.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-01.jpg" alt="" width="717" height="253" class="aligncenter size-full wp-image-7312" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-01.jpg 717w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-01-300x106.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-01-585x206.jpg 585w" sizes="(max-width: 717px) 100vw, 717px" /></a><br />
The OVA file is about 600+ MBs compressed. Once downloaded, deploy it as with any OVA file. Specify the details for your vCenter, network, host, cluster&#8230; By default, the VM needs 80GB disk and eats up 4CPUs and 16GB RAM. Once deployed, open the console, log with <em>admin/password</em> which is <strong>admin </strong>and <strong>password </strong>and change the IP to static if you need and/or change the default password or whatever you want. The console looks like this, some modified Linux distro.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-02.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-02.jpg" alt="" width="713" height="355" class="aligncenter size-full wp-image-7313" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-02.jpg 713w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-02-300x149.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-02-585x291.jpg 585w" sizes="(max-width: 713px) 100vw, 713px" /></a></p>
<h1>AWS DataSync over public Internet</h1>
<p>Now, go back to AWS DataSync and enter the IP of the appliance. But before you click <strong>Get key</strong>, make sure you have selected the <strong>public service endpoint</strong> for the <strong>Service endpoint</strong> as in the screenshot below. This means we&#8217;ll use public Internet. No, worries, the traffic is encrypted. Click <strong>Get key</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-03.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-03.jpg" alt="" width="1155" height="532" class="aligncenter size-full wp-image-7314" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-03.jpg 1155w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-03-300x138.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-03-1024x472.jpg 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-03-768x354.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-03-585x269.jpg 585w" sizes="(max-width: 1155px) 100vw, 1155px" /></a><br />
Probably you wonder, wait &#8211; how can AWS access my internal IP when I don&#8217;t have any VPN or Direct Connect. Well, AWS doesn&#8217;t care about your internal IP. Your browser have access to the internal IP on port 80 to the VM appliance. AWS doesn&#8217;t initiate the traffic, the appliance does.<br />
So, you have the appliance activated now. Name the agent and tag it if you want. Click <strong>Create agent</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-04.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-04.jpg" alt="" width="1109" height="604" class="aligncenter size-full wp-image-7315" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-04.jpg 1109w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-04-300x163.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-04-1024x558.jpg 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-04-768x418.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-04-585x319.jpg 585w" sizes="(max-width: 1109px) 100vw, 1109px" /></a></p>
<h2>Creating a task</h2>
<p>You&#8217;ll see you agent online. Click on the <strong>Agent ID</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-05.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-05.jpg" alt="" width="1589" height="285" class="aligncenter size-full wp-image-7317" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-05.jpg 1589w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-05-300x54.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-05-1024x184.jpg 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-05-768x138.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-05-1536x275.jpg 1536w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-05-1170x210.jpg 1170w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-05-585x105.jpg 585w" sizes="(max-width: 1589px) 100vw, 1589px" /></a><br />
Now, you have to create a task. For each share, you need a task. Click on <strong>Create task</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-06.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-06.jpg" alt="" width="1576" height="684" class="aligncenter size-full wp-image-7318" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-06.jpg 1576w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-06-300x130.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-06-1024x444.jpg 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-06-768x333.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-06-1536x667.jpg 1536w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-06-1170x508.jpg 1170w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-06-585x254.jpg 585w" sizes="(max-width: 1576px) 100vw, 1576px" /></a><br />
Configure the source location on-prem.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-07.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-07.jpg" alt="" width="860" height="592" class="aligncenter size-full wp-image-7319" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-07.jpg 860w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-07-300x207.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-07-768x529.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-07-585x403.jpg 585w" sizes="(max-width: 860px) 100vw, 860px" /></a><br />
I chose SMB which is a Windows share. Specify the agent, the server IP, the share name with &#8220;/&#8221; prefix and the username, password, domain (optional) in case the share is restricted, which probably is.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-08.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-08.jpg" alt="" width="827" height="527" class="aligncenter size-full wp-image-7320" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-08.jpg 827w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-08-300x191.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-08-768x489.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-08-585x373.jpg 585w" sizes="(max-width: 827px) 100vw, 827px" /></a><br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-09.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-09.jpg" alt="" width="855" height="515" class="aligncenter size-full wp-image-7321" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-09.jpg 855w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-09-300x181.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-09-768x463.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-09-585x352.jpg 585w" sizes="(max-width: 855px) 100vw, 855px" /></a><br />
Click <strong>Next</strong>. Now, choose a destination. I am using some S3 bucket and choose to Autogenerate a role that will allow access to that bucket. Click <strong>Next </strong>again.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-10.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-10.jpg" alt="" width="819" height="832" class="aligncenter size-full wp-image-7322" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-10.jpg 819w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-10-295x300.jpg 295w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-10-768x780.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-10-585x594.jpg 585w" sizes="(max-width: 819px) 100vw, 819px" /></a><br />
On this screen, you can choose options that suit your needs. If you scroll down, you can enable detailed logging in CloudWatch. Click <strong>Next</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-11.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-11.jpg" alt="" width="798" height="815" class="aligncenter size-full wp-image-7323" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-11.jpg 798w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-11-294x300.jpg 294w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-11-768x784.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-11-585x597.jpg 585w" sizes="(max-width: 798px) 100vw, 798px" /></a><br />
Check if everything is OK and click to <strong>Create task</strong>.<br />
You&#8217;ll see the task creating. It takes about 10-15 secs.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-12.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-12.jpg" alt="" width="385" height="274" class="aligncenter size-full wp-image-7324" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-12.jpg 385w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-12-300x214.jpg 300w" sizes="(max-width: 385px) 100vw, 385px" /></a><br />
Click on <strong>Start </strong>in the upper right corner.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-13.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-13.jpg" alt="" width="1624" height="306" class="aligncenter size-full wp-image-7325" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-13.jpg 1624w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-13-300x57.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-13-1024x193.jpg 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-13-768x145.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-13-1536x289.jpg 1536w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-13-1170x220.jpg 1170w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-13-585x110.jpg 585w" sizes="(max-width: 1624px) 100vw, 1624px" /></a><br />
I put a single 10KB file in my share. After some init work, it took about&#8230; maybe 2-3 mins and the task status said &#8220;available&#8221;. I checked the bucket and it was there. Most likely, if you are migrating a share that&#8217;s changing on a daily basis, you want to schedule the transfer. If you edit the task, you can change the schedule.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-14.jpg"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-14.jpg" alt="" width="820" height="734" class="aligncenter size-full wp-image-7327" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-14.jpg 820w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-14-300x269.jpg 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-14-768x687.jpg 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-14-585x524.jpg 585w" sizes="(max-width: 820px) 100vw, 820px" /></a></p>
<h1>AWS DataSync over VPN and Direct Connect</h1>
<p>For DataSync to work over VPN and Direct Connect, you need a new endpoint and a new appliance. You can&#8217;t use the existing agent and appliance that was configured for public Internet. The endpoint allows you to privately connect to an AWS service. The traffic won&#8217;t go over direct Internet. Actually, with VPN it will, but the traffic will be tunneled. With Direct Connect, the traffic from on-prem to your endpoint is going over dedicated links.<br />
Follow the first section above on how to deploy a new OVA appliance. Once completed, we need to create an endpoint now. But before doing that, create a new security group that allows TCP ports 22, 443 and 1024-1064 range from your appliance.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-20.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-20.png" alt="" width="1141" height="325" class="aligncenter size-full wp-image-7337" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-20.png 1141w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-20-300x85.png 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-20-1024x292.png 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-20-768x219.png 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-20-585x167.png 585w" sizes="(max-width: 1141px) 100vw, 1141px" /></a><br />
In AWS DataSync console, click to create a new agent, but this time choose <strong>VPC endpoints using PrivateLink</strong> and then click on <strong>Create VPC endpoint</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-15.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-15.png" alt="" width="1051" height="326" class="aligncenter size-full wp-image-7330" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-15.png 1051w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-15-300x93.png 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-15-1024x318.png 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-15-768x238.png 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-15-585x181.png 585w" sizes="(max-width: 1051px) 100vw, 1051px" /></a><br />
I&#8217;ll create a VPC endpoint for DataSync in a VPC that&#8217;s connected to my VPN. I have only one public subnet there and I&#8217;ll choose a security group that allows TCP 443, 1024–1064, and port 22.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-16.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-16.png" alt="" width="1117" height="749" class="aligncenter size-full wp-image-7332" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-16.png 1117w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-16-300x201.png 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-16-1024x687.png 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-16-768x515.png 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-16-585x392.png 585w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-16-263x175.png 263w" sizes="(max-width: 1117px) 100vw, 1117px" /></a><br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-17.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-17.png" alt="" width="1183" height="794" class="aligncenter size-full wp-image-7333" /></a><br />
Click on <strong>Create endpoint</strong>. It takes about 3-5 minutes for the endpoint to be created.<br />
Go back to DataSync screen and you&#8217;ll see the parameters populated. Enter the IP of the VM appliance and click on <strong>Get Key</strong>.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-18.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-18.png" alt="" width="1117" height="781" class="aligncenter size-full wp-image-7340" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-18.png 1117w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-18-300x210.png 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-18-1024x716.png 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-18-768x537.png 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-18-585x409.png 585w" sizes="(max-width: 1117px) 100vw, 1117px" /></a><br />
Once you see the appliance activated, you can create the agent and follow the instructions in <a href="https://blog.andreev.it/?p=7308#Creating_a_task">Creating a task</a> section above.<br />
<strong>NOTE:</strong> I created a task to transfer a 2GB file from my share to S3. When I checked my pfSense IPSec tunnel, I saw these values, which means that the transfer goes over the private link.<br />
<a href="https://blog.andreev.it/wp-content/uploads/2020/10/P152-21.png"><img loading="lazy" decoding="async" src="https://blog.andreev.it/wp-content/uploads/2020/10/P152-21.png" alt="" width="1127" height="93" class="aligncenter size-full wp-image-7344" srcset="https://blog.andreev.it/wp-content/uploads/2020/10/P152-21.png 1127w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-21-300x25.png 300w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-21-1024x85.png 1024w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-21-768x63.png 768w, https://blog.andreev.it/wp-content/uploads/2020/10/P152-21-585x48.png 585w" sizes="(max-width: 1127px) 100vw, 1127px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2020/10/aws-datasync-using-vpn-and-internet/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
