<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Derek Sedlmyer</title>
    <description>The latest articles on Forem by Derek Sedlmyer (@dereksedlmyer).</description>
    <link>https://forem.com/dereksedlmyer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F359857%2F2d807497-7acb-4621-a03b-b2e8b0bf31ef.png</url>
      <title>Forem: Derek Sedlmyer</title>
      <link>https://forem.com/dereksedlmyer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dereksedlmyer"/>
    <language>en</language>
    <item>
      <title>Maintaining and Governing Developer Accounts with AWS Control Tower, Part 2</title>
      <dc:creator>Derek Sedlmyer</dc:creator>
      <pubDate>Fri, 12 Jun 2020 18:10:22 +0000</pubDate>
      <link>https://forem.com/dereksedlmyer/maintaining-and-governing-developer-accounts-with-aws-control-tower-part-2-1bg6</link>
      <guid>https://forem.com/dereksedlmyer/maintaining-and-governing-developer-accounts-with-aws-control-tower-part-2-1bg6</guid>
      <description>&lt;p&gt;I work at a consulting company where there are numerous developers and consultants that require a sandbox environment in AWS. Developers may be working in isolation or they may be collaborating with other developers on a shared project.&lt;/p&gt;

&lt;p&gt;At present, everyone shares a single AWS account and it becomes challenging to govern. We routinely have to prune resources no longer in use when our monthly bill becomes out of control. This requires an admin to look through the resources in numerous regions and reach out to developers to figure out who owns a resource.&lt;/p&gt;

&lt;p&gt;To help manage and govern our AWS usage, I'm going to use AWS Control Tower to allow admins and developers to provision isolated accounts at the developer or project scope and to provide a set of guardrails to enforce AWS best practices.&lt;/p&gt;

&lt;p&gt;In the second part of this series, I'm going to describe how the Landing Zone is configured for my team and the customizations added to support some of our policies.&lt;/p&gt;

&lt;h1&gt;
  
  
  User Management
&lt;/h1&gt;

&lt;p&gt;The first administrative task is to create User accounts in AWS Single Sign-On so that developers can access appropriate AWS accounts in the organization.&lt;/p&gt;

&lt;p&gt;Control Tower configures an AWS Single Sign-On directory in the Master account. This directory can be configured to use a user identity store in Active Directory, an external identity provider, or through the built-in identity store in AWS SSO. For my team, we're going to use the built-in identity store.&lt;/p&gt;

&lt;p&gt;The next step is to begin inviting the users into the Organization. The users will be added to the &lt;strong&gt;AWSAccountFactory&lt;/strong&gt; group initially. This will give them permissions to use Account Factory to create new AWS accounts within the Organization. Upon create a new user, an invitation will be sent via email where the user can set their password and login.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A recommended practice is to create groups that model your organization's roles and responsibilities. Since groups in AWS SSO can't be members of other groups, assign users to groups managed by your organization and the AWS-managed groups.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm going to create a &lt;strong&gt;Developers&lt;/strong&gt; group within AWS SSO and assign the developer users to that group.&lt;/p&gt;

&lt;p&gt;There are a few of us on the team that will be administrators on Control Tower and the Organization. These users will have administrator access to the Master account and full access to AWS Control Tower. For them, I'm going to create an &lt;strong&gt;Admins&lt;/strong&gt; group and assign their user accounts to that group.&lt;/p&gt;

&lt;p&gt;In AWS SSO, the current list of groups:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgmqunzzd7l1y4536o6ww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgmqunzzd7l1y4536o6ww.png" alt="AWS SSO Groups" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For my organization-managed &lt;strong&gt;Admins&lt;/strong&gt; group, I'm going to assign it the &lt;strong&gt;AWSAdministratorAccess&lt;/strong&gt; Permission Set to the Master, Log archive, and Audit AWS accounts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Permission Sets are backed by an IAM policy. Use Permission Sets to enforce permissions on a User or Group for an AWS Account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Landing Zone Configuration
&lt;/h1&gt;

&lt;p&gt;A common issue with using a single-shared AWS account is that the account becomes like the wild west. Resources are created but never terminated, team members come and go, and it is tedious to determine who created a resource when it comes time to prune resources when the AWS bill comes due.&lt;/p&gt;

&lt;p&gt;Another issue with the single-shared AWS account is the blast radius if the account were ever to become compromised. When an account is compromised AWS disables the account until all issues are resolved. This could have a wide impact on other developers and projects.&lt;/p&gt;

&lt;p&gt;To improve management and governance, each developer will have a dedicated AWS account. Additionally, there are shared projects which numerous developers work and collaborate. Each shared project will so have a dedicated AWS account.&lt;/p&gt;

&lt;p&gt;Each developer AWS account and project AWS account will be added to the Landing Zone configured by Control Tower. The account will inherit the guardrails assigned to the account's OU. All API activities and resource configurations will also be logged.&lt;/p&gt;

&lt;p&gt;Two Organizational Units (OUs) will be added as well. One for &lt;strong&gt;developers&lt;/strong&gt; and one for &lt;strong&gt;projects&lt;/strong&gt;. Using separate OUs will allow us to manage and govern the accounts with different guardrails and policies.&lt;/p&gt;

&lt;p&gt;This diagram illustrates the layout of the OUs within the Landing Zone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7urewd5gsb2uvk9k3vda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7urewd5gsb2uvk9k3vda.png" alt="OU Layout" width="715" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When creating OUs for Control Tower, it is best practice to create them through Control Tower and not through AWS Organizations. Using Control Tower to add OUs to the Landing Zone will ensure that they display within Control Tower and have guardrails applied to them. Control Tower will manage the lifecycle of an OU within AWS Organizations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OUs within Control Tower should be parented to the Root OU and not nested in another OU. Nested OUs are not accessible to Control Tower as it only displays the top-level OUs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After creating the &lt;strong&gt;developers&lt;/strong&gt; and &lt;strong&gt;projects&lt;/strong&gt; OUs within Control Tower, the Organizational Units page looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9ra7sn6tmfe2bht08bx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9ra7sn6tmfe2bht08bx6.png" alt="Organizational Units page" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Examining the developers OU, it will show details on the OU including accounts assigned to the OU and guardrails.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkm3nozxvgtdhu1nrpr60.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkm3nozxvgtdhu1nrpr60.png" alt="OU Details" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this time, the OU is ready to be used and new accounts can be assigned to the OU. There are further customizations required to enforce some custom policies which will be covered next.&lt;/p&gt;

&lt;h1&gt;
  
  
  Landing Zone Customizations
&lt;/h1&gt;

&lt;p&gt;While AWS Control Tower provides a good base set of guardrails on member accounts, my team would like to add further policies. For instance for developer accounts, initially we would like to enforce these policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limit EC2 Instance Types to nano, micro, small, or medium instance types&lt;/li&gt;
&lt;li&gt;Limit RDS Instance Types to micro, small, or medium instance types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To enforce these policies, Service Control Policies (SCPs) can be configured within AWS Organizations and attached to an OU or an individual account.&lt;/p&gt;

&lt;p&gt;Additionally, we would like to add a resource to each developer AWS account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up an initial budget of $25 per month and send notifications at configured thresholds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resources can be added to AWS accounts using CloudFormation and by using StackSets to deploy to the member AWS accounts from the Master account.&lt;/p&gt;

&lt;p&gt;Configuring Service Control Policies and CloudFormation stacks is straightforward, it can be difficult to do at scale. Managing SCPs at the OU level in AWS Organizations makes it easier to enforce policies, the same is not true for CloudFormation stack sets. After accounts are creating through the Account Factory, an admin will have to remember to set up the appropriate CloudFormation stacks on the new account. Also, if new resources and stacks need to be added to exist accounts, then an admin must manually configure the stacks on each AWS account. This can be very tedious and error prone.&lt;/p&gt;

&lt;p&gt;AWS provides a custom solution named Customizations for AWS Control Tower.&lt;/p&gt;

&lt;p&gt;From the Customizations for AWS Control Tower Deployment Guide:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Customizations for AWS Control Tower solution combines AWS Control Tower and other highly-available, trusted AWS services to help customers more quickly set up a secure, multi-account AWS environment using AWS best practices. Before deploying this solution, customers need to have an AWS Control Tower landing zone deployed in their account. This solution enables customers to easily add customizations to their AWS Control Tower landing zone using an AWS CloudFormation template and service control policies (SCPs). You can deploy the custom template and policies to individual accounts and organizational units (OUs) within your organization. This solution integrates with AWS Control Tower lifecycle events to ensure that resource deployments stay in sync with the customer's landing zone. For example, when a new account is created using the AWS Control Tower account factory, the solution ensures that all resources attached to the account's OUs will be automatically deployed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Information on Customizations for AWS Control Tower can be found here: &lt;a href="https://aws.amazon.com/solutions/implementations/customizations-for-aws-control-tower/"&gt;https://aws.amazon.com/solutions/implementations/customizations-for-aws-control-tower/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Customizations for AWS Control Tower configures the following deployment architecture in the Master account to deploy SCPs and CloudFormation Stack Sets on existing member accounts and new member accounts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdc6osmgzqpys1d5wmo27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdc6osmgzqpys1d5wmo27.png" alt="AWS Control Tower Customizations architecture" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the Customizations for AWS Control Tower Solution
&lt;/h2&gt;

&lt;p&gt;To configure Customizations for AWS Control Tower in the Master account use the CloudFormation template that's included. It can be found at: &lt;a href="https://s3.amazonaws.com/solutions-reference/customizations-for-aws-control-tower/latest/custom-control-tower-initiation.template"&gt;https://s3.amazonaws.com/solutions-reference/customizations-for-aws-control-tower/latest/custom-control-tower-initiation.template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are 2 parameters for the CloudFormation stack -- PipelineApprovalEmail and PipelineApprovalStage. &lt;/p&gt;

&lt;p&gt;Set &lt;em&gt;PipelineApprovalStage&lt;/em&gt; to &lt;em&gt;Yes&lt;/em&gt; if you would like to approve any changes to SCPs or CloudFormation StackSets before deployment. An email will be sent to email address set in the &lt;em&gt;PipelineApprovalEmail&lt;/em&gt; parameter to notify when there is a pending approval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Customization Configuration Package
&lt;/h2&gt;

&lt;p&gt;The Customizations for AWS Control Tower will deploy SCPs and CloudFormation StackSets using policy files and templates contained in a Configuration Package. AWS CodePipeline in the Customizations for AWS Control Tower will create SCPs using the policy files and create CloudFormation StackSets from the templates and apply them to the appropriate accounts at the OU or account levels.&lt;/p&gt;

&lt;p&gt;The developers guide for the Configuration Package and Customizations for AWS Control Tower can be found at: &lt;a href="https://s3.amazonaws.com/solutions-reference/customizations-for-aws-control-tower/latest/customizations-for-aws-control-tower-developer-guide.pdf"&gt;https://s3.amazonaws.com/solutions-reference/customizations-for-aws-control-tower/latest/customizations-for-aws-control-tower-developer-guide.pdf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Configuration Package is uploaded into an S3 bucket which will trigger the deployment process in AWS CodePipeline. &lt;/p&gt;

&lt;p&gt;I created a GitHub repo with a sample Configuration Package that contains Service Control Policies for limiting instance types for EC2 and RDS and a CloudFormation template for configuring a Budget in each member account.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/DerekSedlmyer"&gt;
        DerekSedlmyer
      &lt;/a&gt; / &lt;a href="https://github.com/DerekSedlmyer/aws-landing-zone-sample"&gt;
        aws-landing-zone-sample
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Sample Configuration Package for the Customizations for AWS Control Tower solution.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The manifest file in the Configuration File controls the deployment of SCPs and CloudFormation templates. In the manifest file, the list of Organization Policies (SCPs) and CloudFormation Resources is configured to include the OUs or individual member accounts to target. The configuration of the manifest file will be described in the following sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Service Control Policies
&lt;/h2&gt;

&lt;p&gt;In our team's member accounts, we want to limit the size of the EC2 and RDS instances in order to save costs. This can be controlled via Service Control Policies (SCPs). SCPs are defined as IAM policies. The SCPs will be configured within AWS Organizations via the Customizations for AWS Control Tower solution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Service Control Policies (SCPs) offer central control over the maximum available permissions for all accounts in your organization. SCPs help you to ensure your accounts stay within your organization’s access control guidelines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following is a sample SCP for limiting EC2 instance types to only nano, small, micro, or medium. In this policy there is an explicit deny to Run EC2 Instances that don't match the pattern of *.nano, *.small, *.micro, or *.medium.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DerekSedlmyer/aws-landing-zone-sample/blob/master/custom-control-tower-configuration/policies/ec2-instance-types.json"&gt;custom-control-tower-configuration/policies/ec2-instance-types.json&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LimitEC2InstanceType"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ec2:RunInstances"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:ec2:*:*:instance/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ForAnyValue:StringNotLike"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"ec2:InstanceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"*.nano"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"*.small"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"*.micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"*.medium"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following is a sample SCP for limiting RDS instance types to only small, micro, or medium. In this policy there is an explicit deny to Create or Start RDS databases that don't match the pattern of *.small, *.micro, or *.medium.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DerekSedlmyer/aws-landing-zone-sample/blob/master/custom-control-tower-configuration/policies/rds-instance-types.json"&gt;custom-control-tower-configuration/policies/rds-instance-types.json&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LimitRDSInstanceType"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"rds:CreateDBInstance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"rds:StartDBInstance"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;  
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:rds:*:*:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ForAnyValue:StringNotLike"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"rds:DatabaseClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"db.*.micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"db.*.small"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"db.*.medium"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the SCPs defined in the two separate files, the next step is to configure them within the manifest so they are applied appropriately. It will apply the SCP to all current and future accounts within the &lt;strong&gt;developers&lt;/strong&gt; OU.&lt;/p&gt;

&lt;p&gt;This section of the manifest file will apply the SCPs to the appropriate member accounts in the landing zone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DerekSedlmyer/aws-landing-zone-sample/blob/master/custom-control-tower-configuration/manifest.yaml"&gt;custom-control-tower-configuration/manifest.yaml&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;organization_policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;limit-ec2-instance-types&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Limit EC2 instance type in member accounts&lt;/span&gt;
    &lt;span class="na"&gt;policy_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policies/ec2-instance-types.json&lt;/span&gt;
    &lt;span class="na"&gt;apply_to_accounts_in_ou&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;developers&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;limit-rds-instance-types&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Limit RDS instance type in member accounts&lt;/span&gt;
    &lt;span class="na"&gt;policy_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policies/rds-instance-types.json&lt;/span&gt;
    &lt;span class="na"&gt;apply_to_accounts_in_ou&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;developers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After deployment, we can see the SCP configured in AWS Organizations for the developers OU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgmoqk4laekcemi96q121.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgmoqk4laekcemi96q121.png" alt="SCP in AWS Organizations" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In any AWS account within the &lt;strong&gt;developers&lt;/strong&gt; OU, the creation of EC2 or RDS instances outside of the policy will not be allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring CloudFormation Resources
&lt;/h2&gt;

&lt;p&gt;In our team's member accounts, we want to create a budget for $25 per month (for example) and then notify once budget thresholds of 75/90/100/110% thresholds. This will help us govern costs so that we can stop unused resources before the bill becomes out of control.&lt;/p&gt;

&lt;p&gt;To set up the Budget, a CloudFormation template is needed. A CloudFormation StackSet will be created within the Master account using the template and will create individual CloudFormation stacks in the appropriate member AWS accounts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AWS CloudFormation StackSets extends the functionality of stacks by enabling you to create, update, or delete stacks across multiple accounts and regions with a single operation. Using an administrator account, you define and manage an AWS CloudFormation template, and use the template as the basis for provisioning stacks into selected target accounts across specified regions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The CloudFormation template to define the Budget is shown here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DerekSedlmyer/aws-landing-zone-sample/blob/master/custom-control-tower-configuration/templates/budget.template"&gt;custom-control-tower-configuration/templates/budget.template&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2010-09-09&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configures Budget Notifications for member accounts at 75/90/100/110% thresholds&lt;/span&gt;
&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BudgetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name to assign budget&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;BudgetAmount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Budget Limit for one month&lt;/span&gt; 
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Number&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt;
  &lt;span class="na"&gt;NotificationEmailAddress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Email Address to notify when 75/90/100/110% thresholds are reached&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;AccountBudget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Budgets::Budget&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Budget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BudgetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;BudgetName&lt;/span&gt;
        &lt;span class="na"&gt;BudgetLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Amount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;BudgetAmount&lt;/span&gt;
          &lt;span class="na"&gt;Unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;USD&lt;/span&gt;
        &lt;span class="na"&gt;TimeUnit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MONTHLY&lt;/span&gt;
        &lt;span class="na"&gt;BudgetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;COST&lt;/span&gt;
      &lt;span class="na"&gt;NotificationsWithSubscribers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Notification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;NotificationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ACTUAL&lt;/span&gt;
            &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GREATER_THAN&lt;/span&gt;
            &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;75&lt;/span&gt;
            &lt;span class="na"&gt;ThresholdType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PERCENTAGE&lt;/span&gt;
          &lt;span class="na"&gt;Subscribers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;SubscriptionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EMAIL&lt;/span&gt;
              &lt;span class="na"&gt;Address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;NotificationEmailAddress&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Notification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;NotificationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ACTUAL&lt;/span&gt;
            &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GREATER_THAN&lt;/span&gt;
            &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
            &lt;span class="na"&gt;ThresholdType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PERCENTAGE&lt;/span&gt;
          &lt;span class="na"&gt;Subscribers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;SubscriptionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EMAIL&lt;/span&gt;
              &lt;span class="na"&gt;Address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;NotificationEmailAddress&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Notification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;NotificationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ACTUAL&lt;/span&gt;
            &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GREATER_THAN&lt;/span&gt;
            &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
            &lt;span class="na"&gt;ThresholdType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PERCENTAGE&lt;/span&gt;
          &lt;span class="na"&gt;Subscribers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;SubscriptionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EMAIL&lt;/span&gt;
              &lt;span class="na"&gt;Address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;NotificationEmailAddress&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Notification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;NotificationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ACTUAL&lt;/span&gt;
            &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GREATER_THAN&lt;/span&gt;
            &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;110&lt;/span&gt;
            &lt;span class="na"&gt;ThresholdType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PERCENTAGE&lt;/span&gt;
          &lt;span class="na"&gt;Subscribers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;SubscriptionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EMAIL&lt;/span&gt;
              &lt;span class="na"&gt;Address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;NotificationEmailAddress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to configure the CloudFormation template within the manifest so the CloudFormation resources are created appropriately and targets the right accounts. The CloudFormation template to define a Budget will be configured to be created in all current and future AWS accounts in the &lt;strong&gt;developers&lt;/strong&gt; OU.&lt;/p&gt;

&lt;p&gt;This section of the manifest file will apply the CloudFormation resources to the appropriate member accounts in the landing zone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DerekSedlmyer/aws-landing-zone-sample/blob/master/custom-control-tower-configuration/manifest.yaml"&gt;custom-control-tower-configuration/manifest.yaml&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cloudformation_resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;budget-small&lt;/span&gt;
    &lt;span class="na"&gt;template_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;templates/budget.template&lt;/span&gt;
    &lt;span class="na"&gt;parameter_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;parameters/budget.small.json&lt;/span&gt;
    &lt;span class="na"&gt;deploy_method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stack_set&lt;/span&gt;
    &lt;span class="na"&gt;deploy_to_ou&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;developers&lt;/span&gt;
    &lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters file will define the parameter values to be used when creating the CloudFormation StackSets. In this case, the values for a small budget will be used. You will notice that &lt;code&gt;BudgetAmount&lt;/code&gt; is $&lt;code&gt;25&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DerekSedlmyer/aws-landing-zone-sample/blob/master/custom-control-tower-configuration/parameters/budget.small.json"&gt;custom-control-tower-configuration/parameters/budget.small.json&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ParameterKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BudgetName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ParameterValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"budget-small"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ParameterKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BudgetAmount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ParameterValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"25"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ParameterKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NotificationEmailAddress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ParameterValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name@email.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After deployment, we can see the CloudFormation StackSet created in the Master account in the landing zone with a few instances in the member accounts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe4n3afnm9eaj0u6gcnc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe4n3afnm9eaj0u6gcnc5.png" alt="CloudFormation StackSet for Budgets" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look in a member account, we can see the instance of the CloudFormation stack that was created from the StackSet in the Customizations for AWS Control Tower solution in the Master account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpukjcsnttwx90b1pmh5r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpukjcsnttwx90b1pmh5r.png" alt="CloudFormation Stack for Budgets" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This post shows how to configure the Landing Zone created by AWS Control Tower and also how to deploy custom policies and resources to AWS accounts in the Landing Zone using the Customizations for AWS Control Tower.&lt;/p&gt;

&lt;p&gt;Stay tuned for additional posts in this series on AWS Control Tower and governance.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>controltower</category>
      <category>governance</category>
    </item>
    <item>
      <title>Maintaining and Governing Developer Accounts with AWS Control Tower, Part 1</title>
      <dc:creator>Derek Sedlmyer</dc:creator>
      <pubDate>Fri, 12 Jun 2020 18:10:03 +0000</pubDate>
      <link>https://forem.com/dereksedlmyer/maintaining-and-governing-developer-accounts-with-aws-control-tower-part-1-l4g</link>
      <guid>https://forem.com/dereksedlmyer/maintaining-and-governing-developer-accounts-with-aws-control-tower-part-1-l4g</guid>
      <description>&lt;p&gt;I work at a consulting company where there are numerous developers and consultants that require a sandbox environment in AWS. Developers may be working in isolation or they may be collaborating with other developers on a shared project.&lt;/p&gt;

&lt;p&gt;At present, everyone shares a single AWS account and it becomes challenging to govern. We routinely have to prune resources no longer in use when our monthly bill becomes out of control. This requires an admin to look through the resources in numerous regions and reach out to developers to figure out who owns a resource.&lt;/p&gt;

&lt;p&gt;To help manage and govern our AWS usage, I'm going to use AWS Control Tower to allow admins and developers to provision isolated accounts at the developer or project scope and to provide a set of guardrails to enforce AWS best practices.&lt;/p&gt;

&lt;p&gt;In the first part of this series, I'm going to walkthrough the process of configuring a Landing Zone in AWS Control Tower. Further articles will dive deeper into the specific configuration for my team.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS Control Tower Initial Setup
&lt;/h1&gt;

&lt;p&gt;At my company we already have an existing AWS account that's acting as a shared space for all developers. I'm going to use this account as the AWS Control Tower landing zone. A landing zone is a is a well-architected, multi-account AWS environment that's based on security and compliance best practices. This is the enterprise-wide container that holds all of your organizational units (OUs), accounts, users, and other resources that you want to be subject to compliance regulation.&lt;/p&gt;

&lt;p&gt;To start configuring AWS Control Tower:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in with a IAM user that has AdministrativeAccess in the account&lt;/li&gt;
&lt;li&gt;Navigate to the AWS Control Tower console &lt;a href="https://console.aws.amazon.com/controltower/home/dashboard?region=us-east-1" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/controltower/home/dashboard?region=us-east-1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the console, if AWS Control Tower has not been configured, the following screen will be shown, click &lt;strong&gt;Set up landing zone&lt;/strong&gt; to begin&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftem1ueymz2u4c5t7zp3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftem1ueymz2u4c5t7zp3x.png" alt="Control Tower Console home"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the &lt;strong&gt;Set up landing zone&lt;/strong&gt; screen, the following information is required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Master account&lt;/strong&gt;: This is the email address used to set up your master account. This account will be used as the master account for the landing zone and will be the consolidated billing account (each member account managed by AWS Control Tower will have bills consolidated under the master account).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log archive account&lt;/strong&gt;: This is the email address used to create the AWS account that contains a log archive of all API activities and resource configurations from the master account and all member accounts managed by Control Tower. This email address must be unique. I had to work with my company's system administrator to create an email alias for this account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit account&lt;/strong&gt;: This is the email address used to create the AWS account that strictly used by security and compliance teams. These teams will have read/write access to all accounts managed by Control Tower. This email address also has to be unique.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdypiwd737ittpjjnl23e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdypiwd737ittpjjnl23e.png" alt="Set up landing zone"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creating the landing zone takes about 60 minutes or less. The status can be monitored from the Control Tower console&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh7wnosm0csirokq8ua90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh7wnosm0csirokq8ua90.png" alt="Landing Zone set up progress"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When complete, the Control Tower dashboard will indicate success and provide a summary of the items that were configured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqu9zxzximymliy8lj1fu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqu9zxzximymliy8lj1fu.png" alt="Landing Zone set up complete"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point, there is a base-level landing zone configured and ready to be used. In the next section, I'll walk through the items that were configured by Control Tower.&lt;/p&gt;

&lt;h1&gt;
  
  
  What was created
&lt;/h1&gt;

&lt;p&gt;A Landing Zone in AWS Control Tower creates items in other AWS services such as AWS Organizations, AWS Single Sign On, CloudTrail, Config and others.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Organizations
&lt;/h2&gt;

&lt;p&gt;Control Tower creates an organization in AWS Organizations to create and centrally manage multiple AWS accounts. Accounts in AWS Organization can be organized into groups and policy-based controls can be attached to those accounts.&lt;/p&gt;

&lt;p&gt;Within Control Tower, Organizations help centrally manage billing. The bills for the member accounts will be billed under the Control Tower master account as a single bill with detailed views for each member account. &lt;/p&gt;

&lt;p&gt;Organizations also helps control access, compliance and security across the member accounts. Access, compliance and security policies can be mapped to groups of AWS accounts called Organizational Units to efficiently manage the policies at scale. &lt;/p&gt;

&lt;p&gt;Other AWS resources can also be shared across accounts in an Organization. One example, is sharing a VPC across all accounts in an Organization. This allows the networking team to centrally manage network policies, gateways, and VPN/Direct Connect connections. &lt;/p&gt;

&lt;h2&gt;
  
  
  Organizational Units
&lt;/h2&gt;

&lt;p&gt;While AWS Organizations provide the capabilities to organize AWS accounts into Organizational Units (OUs), Control Tower provides the blueprint on what the pre-configured OUs should be and the policies attached to those OUs.&lt;/p&gt;

&lt;p&gt;In addition to the Root OU, which contains the master account, Control Tower configures these additional OUs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core: Contains the Audit and Log Archive shared AWS accounts that were created when Control Tower was configured&lt;/li&gt;
&lt;li&gt;Custom: Contains the member accounts for users to perform their workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Guardrails are applied at the OU scope for all accounts that are a member of that OU.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Shared Accounts
&lt;/h2&gt;

&lt;p&gt;Control Tower creates three shared AWS accounts in the Landing Zone.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Master Account: This is the account where the AWS Control Tower Landing Zone was first configured. All billing will be consolidated to this account for all accounts within the Landing Zone. OUs and Guardrails are configured in the Master account and the Account Factory uses the Master account to provision member accounts &lt;/li&gt;
&lt;li&gt;Log Archive Account: This account maintains a log of all of API activities and resource configurations. This account has CloudTrail, CloudWatch Logs and S3 buckets for storing the logs files.&lt;/li&gt;
&lt;li&gt;Audit Account: Restricted account for audit and compliance teams. Those teams will have read/write access to all member accounts in the Landing Zone. Only programmatic access from the audit account to other member accounts is allowed.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Guardrails
&lt;/h2&gt;

&lt;p&gt;Guardrails are high-level rules for providing governance across all accounts in the Landing Zone. Guardrails can be expressed in simple language to convey their goal clearly. Guardrails can be mandatory, strongly recommended, and elective. Mandatory guardrails cannot be disabled.&lt;/p&gt;

&lt;p&gt;Guardrails are attached to OUs within the Organization and not specific accounts. &lt;/p&gt;

&lt;p&gt;Guardrails can either be preventive or detective. An example of a preventative guardrail is &lt;strong&gt;Disallow policy changes to log archive&lt;/strong&gt;. This prevents any IAM policy changes in the log archive shared account. Any attempt to perform a prevented action is denied and logged in CloudTrail. The resource is also logged in AWS Config.&lt;/p&gt;

&lt;p&gt;A detective guardrail detects an event and logs the event in CloudTrail. An admin can view resources that are out-of-specification with the guardrail. For example, the &lt;strong&gt;Enable encryption for EBS volumes attached to EC2 instances&lt;/strong&gt; detects if an unencrypted Amazon EBS volume is attached to an EC2 instance in your landing zone.&lt;/p&gt;

&lt;p&gt;Guardrails are implemented with Service Control Policies (SCPs)in AWS Organizations for Preventative guardrails and AWS Config rules and Lambda functions for Detective guardrails.&lt;/p&gt;

&lt;p&gt;At this time, Guardrails are provided by AWS and controlled via Control Tower versions. Admins of a Landing Zone cannot create their own Guardrails and apply them to OUs. Later in this article, I'll show how to create custom Service Control Policies and apply them to OUs within Control Tower.&lt;/p&gt;

&lt;h2&gt;
  
  
  Account Factory
&lt;/h2&gt;

&lt;p&gt;There are three methods to creating a member account within AWS Control Tower:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Account Factory console (AWS Service Catalog)&lt;/li&gt;
&lt;li&gt;Enroll Account (AWS Control Tower)&lt;/li&gt;
&lt;li&gt;Lamdba with IAM roles in Landing Zone Master Account&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The recommended approach is through the Account Factory console. Users will the appropriate permissions can launch a new member account within the Landing Zone maintained by AWS Control Tower. The new member account will assigned to an OU and will inherit the enabled guardrails of that OU.&lt;/p&gt;

&lt;p&gt;When launching a new account from the Account Factory console, end users won't know they are working within Control Tower. They will simply use the console to create and terminate an AWS account.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Single Sign-On (SSO)
&lt;/h2&gt;

&lt;p&gt;An AWS Single Sign-On directory is created in the Master account when creating a new Landing Zone with Control Tower. AWS Single Sign-On allows cloud administrators and end users to manage access to multiple AWS accounts.&lt;/p&gt;

&lt;p&gt;Within AWS SSO, Control Tower configures multiple user groups that have various permissions to perform operations within the Landing Zone. The following user groups are created:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWSAccountFactory&lt;/td&gt;
&lt;td&gt;Read-only access to account factory in AWS Service Catalog for end users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSAuditAccountAdmins&lt;/td&gt;
&lt;td&gt;Admin rights to cross-account audit account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSControlTowerAdmins&lt;/td&gt;
&lt;td&gt;Admin rights to AWS Control Tower core and provisioned accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSLogArchiveAdmins&lt;/td&gt;
&lt;td&gt;Admin rights to log archive account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSLogArchiveViewers&lt;/td&gt;
&lt;td&gt;Read-only access to log archive account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSSecurityAuditors&lt;/td&gt;
&lt;td&gt;Read-only access to all accounts for security audits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSSecurityAuditPowerUsers&lt;/td&gt;
&lt;td&gt;Power user access to all accounts for security audits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSServiceCatalogAdmins&lt;/td&gt;
&lt;td&gt;Admin rights to account factory in AWS Service Catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AWS SSO also defines a list of Permission Sets to define permissions that a User/Group have within an AWS Account. Permission Sets are created with an IAM Policy that defines the Allow and Deny permissions to be given to a user/group.&lt;/p&gt;

&lt;p&gt;The following Permission Sets are created by default:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Permission set&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWSReadOnlyAccess&lt;/td&gt;
&lt;td&gt;This policy grants permissions to view resources and basic metadata across all AWS services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSServiceCatalogAdminFullAccess&lt;/td&gt;
&lt;td&gt;Provides full access to AWS Service Catalog admin capabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSServiceCatalogEndUserAccess&lt;/td&gt;
&lt;td&gt;Provides access to the AWS Service Catalog end user console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSPowerUserAccess&lt;/td&gt;
&lt;td&gt;Provides full access to AWS services and resources, but does not allow management of Users and groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSAdministratorAccess&lt;/td&gt;
&lt;td&gt;Provides full access to AWS services and resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWSOrganizationsFullAccess&lt;/td&gt;
&lt;td&gt;Provides full access to AWS Organizations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Control Tower admins can create additional permission sets within the Landing Zone to meet their needs.&lt;/p&gt;

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

&lt;p&gt;This article gave a high-level overview of AWS Control Tower and a walkthrough on configuring a Landing Zone using it. In the next article of this series, I'll discuss how I configured the Landing Zone for our group of consultants and developers.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>governance</category>
      <category>controltower</category>
    </item>
    <item>
      <title>Preparing for SnowPro Core Certification</title>
      <dc:creator>Derek Sedlmyer</dc:creator>
      <pubDate>Tue, 28 Apr 2020 02:36:54 +0000</pubDate>
      <link>https://forem.com/dereksedlmyer/preparing-for-snowpro-core-certification-3ie1</link>
      <guid>https://forem.com/dereksedlmyer/preparing-for-snowpro-core-certification-3ie1</guid>
      <description>&lt;p&gt;Recently I sat for the SnowPro Core Certification exam via online proctoring by Kryterion. I wanted to share my exam preparation and exam experience in order to help fellow colleagues interested in obtaining this certification.&lt;/p&gt;

&lt;h1&gt;
  
  
  About Snowflake
&lt;/h1&gt;

&lt;p&gt;Snowflake is a cloud data platform built from the ground up for the cloud. Snowflake is built on a patented, multi-cluster, shared data architecture created for the cloud to revolutionize data warehousing, data lakes, data analytics and a host of other use cases.&lt;/p&gt;

&lt;p&gt;Snowflake is a single platform comprised of storage, compute, and services layers that are logically integrated but scale infinitely and independent from one another.&lt;/p&gt;

&lt;p&gt;Snowflake is a SaaS solution and is maintained by Snowflake and not the customer. Snowflake manages the services, compute, and storage layers. Data is only accessible via SQL in Snowflake. Snowflake can be deployed within AWS, Azure or GCP although not in customer controlled accounts.&lt;/p&gt;

&lt;h1&gt;
  
  
  About SnowPro Core Certification
&lt;/h1&gt;

&lt;p&gt;The SnowPro Core Certification demonstrates an individual's knowledge to apply specific core expertise implementing and migrating to Snowflake. &lt;/p&gt;

&lt;p&gt;The subject areas covered includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snowflake Architecture&lt;/li&gt;
&lt;li&gt;Snowflake Client Interfaces and Connectivity&lt;/li&gt;
&lt;li&gt;Data Loading/Unloading&lt;/li&gt;
&lt;li&gt;Semi-Structured Data&lt;/li&gt;
&lt;li&gt;Working with Snowflake&lt;/li&gt;
&lt;li&gt;Account and Resource Management&lt;/li&gt;
&lt;li&gt;Performance Optimization&lt;/li&gt;
&lt;li&gt;Data Sharing&lt;/li&gt;
&lt;li&gt;Security &amp;amp; Access Control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exam Format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100 multiple select and multiple choice&lt;/li&gt;
&lt;li&gt;Length: 2 hours&lt;/li&gt;
&lt;li&gt;Registration fee: $175 USD&lt;/li&gt;
&lt;li&gt;Currently only available in English&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Exam Preparation
&lt;/h1&gt;

&lt;p&gt;To prepare for the exam, I utilized the following resources: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hands-On Lab Guide&lt;/li&gt;
&lt;li&gt;Training videos and Practice Exams hosted on Udemy and developed by Hamid Qureshi&lt;/li&gt;
&lt;li&gt;Snowflake University&lt;/li&gt;
&lt;li&gt;Snowflake documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I estimate that I spent about 8-10 hours on exam preparation. &lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-On Lab Guide
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://s3.amazonaws.com/snowflake-workshop-lab/Snowflake_free_trial_LabGuide.pdf"&gt;Hands-On Lab Guide&lt;/a&gt; is a great place to become familiar with Snowflake. This will require a trial account to complete. Snowflake provides a 30-day trial of the &lt;a href="https://docs.snowflake.com/en/user-guide/intro-editions.html#standard-edition"&gt;Standard edition&lt;/a&gt; that can be used for this tutorial.&lt;/p&gt;

&lt;p&gt;The Hands-On Lab Guide tutorial takes a 1-2 hours to complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Udemy Courses
&lt;/h2&gt;

&lt;p&gt;There are 2 Udemy courses that I found valuable in preparing for the exam, both of them developed by Hamid Qureshi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.udemy.com/share/1022wiAEIYclhUQ38J/"&gt;Snowflake Decoded - Fundamentals and hands on Training&lt;/a&gt; contains 60 video lectures with hands-on labs and quizzes which took about 4 hours to complete. The short videos covered all the major topics of Snowflake. The questions in the quizzes are a lot easier than the questions in the exam. I purchased this training for $11.99 from Udemy using a promotion they were running at the time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.udemy.com/share/102mo0AEIYclhUQ38J/"&gt;Tackling Snowflake Certification - Practice Questions&lt;/a&gt; contains 5 practice exams that really do a good job of helping you prepare for the exam. Each exam contains 56-74 questions that you will find on the exam. This course was also $11.99 which was a great value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snowflake University
&lt;/h2&gt;

&lt;p&gt;Snowflake University contains a number of free courses to help you learn Snowflake and help with certification exam preparation. Snowflake University has the following courses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.snowflake.com/snowflake-essentials-training/"&gt;Hands On Essentials Web User Interface Course&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mindtickle.app.link/VO7ThA1oK1"&gt;Level Up Series&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mindtickle.app.link/l9e7g5mvP1"&gt;Snowflake Customer Onboarding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mindtickle.app.link/l9e7g5mvP1"&gt;Snowflake Advanced Capabilities and Transformation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used the Level Up Series to help fine tune some areas where I needed some improvement. The Level Up Series has 8 videos with quizzes dedicated to various topics included first concepts and performance topics. The performance topics were really beneficial in learning about caching and query and results history.&lt;/p&gt;

&lt;p&gt;Additionally, Snowflake University provides a &lt;a href="https://snowflakeuniversity.mindtickle.com/#/update/1240380887853059998?series=1172211196159106059"&gt;study guide&lt;/a&gt; for the exam. This has valuable links to the documentation and other exam resources like videos and blogs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Snowflake Documentation
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.snowflake.com/"&gt;Snowflake Documentation&lt;/a&gt; is the source for all content required to pass the exam. If there is a single piece of advice I could give is to study the documentation. Use the practice exams to determine areas of improvement and use the documentation to study in further depth.&lt;/p&gt;

&lt;h1&gt;
  
  
  Exam
&lt;/h1&gt;

&lt;p&gt;The exam itself is proctored by Kryterion. Due to COVID-19, I used online proctoring since onsite testing centers were closed. When using online proctoring, software will be required to deliver the exam to your computer and enable the webcam and audio for proctoring.&lt;/p&gt;

&lt;p&gt;To schedule, go to &lt;a href="https://webassessor.com/snowflake"&gt;WebAssessor&lt;/a&gt; and find a time slot that will fit your schedule. At the time I scheduled, the available time slots were very limited. &lt;/p&gt;

&lt;p&gt;Prior to your exam, be sure to install the Sentinel software that will deliver the exam to your computer. This software will block you from accessing other software on your computer during the duration of the exam. The software will also require access to your webcam and microphone. After installing the Sentinel software, a biometric profile will require setup. The biometric profile setup will prompt you to enter your full name multiple times and it will perform facial recognition.&lt;/p&gt;

&lt;p&gt;The exam itself will contain 100 multiple choice and multiple select questions. You will have 2 hours to complete the exam. At the end of the exam, your results will be provided.&lt;/p&gt;

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

&lt;p&gt;Overall the exam is straightforward. By doing the hand-on lab tutorial, reading the documentation in depth and utilizing some existing free resources provided by Snowflake you have a good start to passing the exam. Using the Udemy courses, especially the practice exams, will provide a great foundation. &lt;/p&gt;

</description>
      <category>snowpro</category>
      <category>snowflake</category>
      <category>certification</category>
    </item>
    <item>
      <title>Getting Started with Amazon WorkSpaces</title>
      <dc:creator>Derek Sedlmyer</dc:creator>
      <pubDate>Mon, 06 Apr 2020 18:50:35 +0000</pubDate>
      <link>https://forem.com/dereksedlmyer/getting-started-with-amazon-workspaces-1gd5</link>
      <guid>https://forem.com/dereksedlmyer/getting-started-with-amazon-workspaces-1gd5</guid>
      <description>&lt;p&gt;As I write this, the world is dealing with the COVID-19 pandemic. A majority of the world's citizens are under some sort of strict lockdown or stay-at-home orders by their government in an effort to slow the spread or "flatten the curve" in order to not overwhelm the hospital system. This has brought an immediate challenge to organizations to quickly enable a remote workforce in order to achieve business continuity.&lt;/p&gt;

&lt;p&gt;One of the challenges facing organizations in this crisis is connecting remote workers in order to maintain productivity and data security. Organizations are faced with a number of issues at this time of crisis including hardware and network constraints, desktop software patching, endpoint security and others. The crisis is forcing many organizations to adapt to a new reality of remote workers.&lt;/p&gt;

&lt;p&gt;With the influx of a large number of remote workers, on-premise networks including VPNs are unable to meet the new demands. Workers may have limited to no connectivity if corporate VPNs are out of capacity. Additionally, remote workers using legacy desktop applications on their laptops may face networking issues due to increased latency and limited bandwidth due to unplanned usage scenarios of legacy 2-tier desktop apps.&lt;/p&gt;

&lt;p&gt;Some organizations may have workers that depend on higher performance workstation to support more demanding workloads. When workers are suddenly forced to work remote their productivity will be impacted due to the lack of access to the higher-performance hardware. They may be relegated to use underpowered laptops which can severely affect their productivity.&lt;/p&gt;

&lt;p&gt;A solution to these issues is to use Desktop-as-a-Service (DaaS). DaaS is the next generation of Virtual Desktop Infrastructure (VDI). Previous VDI implementations required complicated capacity planning, large capital expenditures for hardware, complicated licensing agreements, long implementation schedules and scalability limitations that prohibit dynamic scaling for an increased remote workforce.&lt;/p&gt;

&lt;p&gt;AWS offers Amazon WorkSpaces as a DaaS solution. Amazon WorkSpaces simplifies desktop delivery, keeps your data secure, reduces costs and allows an organization to centrally manage and scale global desktop deployments.  Using Amazon WorkSpaces an organization can launch Windows or Linux desktops in a matter of minutes as well as scale to thousands of desktops to support workers across the globe.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In response to increased demand for virtual desktops due to COVID-19, Amazon Web Services recently announced a new offer for organizations to use Amazon WorkSpaces for up to 50 users at no charge beginning on April 1, 2020 and running through June 30, 2020. For more details refer to this blog post: &lt;a href="https://aws.amazon.com/blogs/desktop-and-application-streaming/new-offers-to-enable-work-from-home-from-amazon-workspaces-and-amazon-workdocs/" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/desktop-and-application-streaming/new-offers-to-enable-work-from-home-from-amazon-workspaces-and-amazon-workdocs/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Creating WorkSpaces
&lt;/h1&gt;

&lt;p&gt;I have deployed Amazon WorkSpaces for a few organizations and found them to be very beneficial and easy to use. In this blog post, I'm going to walkthrough a quick start to stand up an Amazon WorkSpaces environment. This should allow an organization to begin using the offer from Amazon for  50 free WorkSpaces.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an AWS account if you don't have one already. New accounts are eligible for the WorkSpaces free offer. Existing accounts are also eligible provided they haven't used WorkSpaces prior to the offer.&lt;/li&gt;
&lt;li&gt;Open the Amazon WorkSpaces console at &lt;a href="https://console.aws.amazon.com/workspaces/" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/workspaces/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the upper-right hand corner, be sure to choose a region that is closest to your users. WorkSpaces require minimal latency between the client and AWS. WorkSpaces is available in the following regions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;US East (N. Virginia): us-east-1&lt;/li&gt;
&lt;li&gt;US West (Oregon): us-west-2&lt;/li&gt;
&lt;li&gt;Asia Pacific (Seoul): ap-northeast-2&lt;/li&gt;
&lt;li&gt;Asia Pacific (Singapore): ap-southeast-1&lt;/li&gt;
&lt;li&gt;Asia Pacific (Sydney): ap-southeast-2&lt;/li&gt;
&lt;li&gt;Asia Pacific (Tokyo): ap-northeast-1&lt;/li&gt;
&lt;li&gt;Canada (Central): ca-central-1&lt;/li&gt;
&lt;li&gt;Europe (Frankfurt): eu-central-1&lt;/li&gt;
&lt;li&gt;Europe (Ireland): eu-west-1&lt;/li&gt;
&lt;li&gt;Europe (London): eu-west-2&lt;/li&gt;
&lt;li&gt;South America (São Paulo): sa-east-1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frhuffw152bs6seh61m69.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frhuffw152bs6seh61m69.jpg" alt="Select Region"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the console, click the &lt;strong&gt;Get Started Now&lt;/strong&gt; button. The Getting Started Now button will be displayed if you haven't used WorkSpaces in the account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the &lt;strong&gt;Get Started&lt;/strong&gt; screen, click &lt;strong&gt;Launch&lt;/strong&gt; next to &lt;strong&gt;Quick Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz6x5q4tf380bjl50apou.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz6x5q4tf380bjl50apou.jpg" alt="WorkSpaces Console"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the &lt;strong&gt;Get Started with Amazon WorkSpaces&lt;/strong&gt; screen, in the  &lt;strong&gt;Bundles&lt;/strong&gt; section choose an appropriate bundle. I chose &lt;strong&gt;Standard with Windows 10 and Office 2016&lt;/strong&gt; which will provide 2 vCPU and 4GiB of Memory. I'll write more about Bundles in another post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;Enter User Details&lt;/strong&gt; section, add the list of users to create WorkSpaces. Required fields are Username, First Name, Last Name, and Email. Only one user is required at this time. Additional users can be added later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once complete, click the &lt;strong&gt;Launch Workspaces&lt;/strong&gt; button. This will create a WorkSpace for each user. Once the WorkSpace is created, each user will receive an email message providing instructions on accessing their WorkSpace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgg25as13stn69mx2y0eu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgg25as13stn69mx2y0eu.jpg" alt="Get Started with Amazon WorkSpaces"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After WorkSpaces are launched, go back to the WorkSpaces console. It may take around 20 minutes to launch the WorkSpace. After the WorkSpace is successfully created, you will see the WorkSpace listed with a status set to &lt;strong&gt;AVAILABLE&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;At this point, the user will have received an email notifying them that the WorkSpace is ready for use. It will provide instructions on how to install the WorkSpace client app on their device, register the WorkSpace in the client app and use the WorkSpace.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Configuring WorkSpaces Client
&lt;/h1&gt;

&lt;p&gt;At this point, a system administrator has created a WorkSpace for a user. The user received an email from AWS notifying them that a WorkSpace has been created. The next steps are for the user to install the client app on their device and register the WorkSpace in the client app.&lt;/p&gt;

&lt;p&gt;The email received by the user looks something like this:&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From the email follow the link in Step 1 to complete user profile and download a client app. The following screen will be displayed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyth0x53ssxwl5rrh9hzt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyth0x53ssxwl5rrh9hzt.jpg" alt="WorkSpaces User Profile"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complete the form, by entering and confirming the password, then click &lt;strong&gt;Update User&lt;/strong&gt;. Note that passwords are case-sensitive and must be between 8 and 64 characters in length, inclusive. Passwords must contain at least one character from three of the following categories: lowercase letters (a-z), uppercase letters (A-Z), numbers (0-9), and the set ~!@#$%^&amp;amp;*_-+=`|(){}[]:;"'&amp;lt;&amp;gt;,.?/.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You will then be sent to the WorkSpaces Client Download page which will allow you to download the WorkSpaces client app for your device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd4xorcogzo48csendd21.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd4xorcogzo48csendd21.jpg" alt="WorkSpaces Client Download"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download and install the client app appropriate for your device. Supported devices are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows&lt;/li&gt;
&lt;li&gt;iPad&lt;/li&gt;
&lt;li&gt;MacOS X&lt;/li&gt;
&lt;li&gt;Android Tablet&lt;/li&gt;
&lt;li&gt;Chromebook&lt;/li&gt;
&lt;li&gt;Fire Tablet&lt;/li&gt;
&lt;li&gt;Web Access&lt;/li&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The next step is to register the WorkSpace with the client app. Go back to the email and find Step 2. Copy the registration code to the clipboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the WorkSpaces client app on your device. The following screen will be displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbrc6ii0qwox4ler593et.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbrc6ii0qwox4ler593et.jpg" alt="WorkSpace Registration"&gt;&lt;/a&gt;    &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Paste your registration code in the text box and click Register&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After successful registration, the login screen is displayed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn35by6tf16xqyv0659zu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn35by6tf16xqyv0659zu.jpg" alt="WorkSpace Login"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the login screen, enter your username and password (set in Step 2) and click &lt;strong&gt;Sign In&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  What's Created
&lt;/h1&gt;

&lt;p&gt;So far, I have shown how a WorkSpace is provisioned by a system administrator and how a user accesses the WorkSpace from their device. Next, I'll dive a little deeper to show the AWS resources that were created during this process.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Virtual Private Cloud (VPC)&lt;/strong&gt; was created during the Quick Start. The VPC has 2 public subnets, each residing in a different availability zone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple AD Directory&lt;/strong&gt; created in the VPC. The Simple AD Directory is a basic Active Directory-compatible directory used to store user and WorkSpace information. The Simple AD is deployed across 2 availability zones for high-availability and redundancy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Account&lt;/strong&gt; in the Simple AD Directory, &lt;em&gt;john.doe&lt;/em&gt; in this case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WorkSpace instance&lt;/strong&gt;. The instance is associated with an elastic network interface in the VPC and the network interface is assigned a public IP address to provide internet access. &lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Summary and Next Steps
&lt;/h1&gt;

&lt;p&gt;This blog post showed a quick start for standing up an Amazon WorkSpaces environment. While this is beneficial for training and sandbox purposes, deploying to WorkSpaces in a production environment for an organization requires more planning and architecture.&lt;/p&gt;

&lt;p&gt;In future blog posts, I'll write about various WorkSpace features, pricing and best practices.&lt;/p&gt;

&lt;p&gt;Stay tuned. In the meantime, please be safe.&lt;/p&gt;




</description>
      <category>aws</category>
      <category>cloud</category>
      <category>daas</category>
      <category>workspaces</category>
    </item>
    <item>
      <title>Using AWS Batch to Generate Image Thumbnails for Voyager</title>
      <dc:creator>Derek Sedlmyer</dc:creator>
      <pubDate>Tue, 01 Aug 2017 01:53:09 +0000</pubDate>
      <link>https://forem.com/dereksedlmyer/using-aws-batch-to-generate-image-thumbnails-for-voyager-fa4</link>
      <guid>https://forem.com/dereksedlmyer/using-aws-batch-to-generate-image-thumbnails-for-voyager-fa4</guid>
      <description>&lt;p&gt;In the 1.9.9 release of Voyager, some exciting new capabilities were added. Most notably are Voyagers new support for cloud deployments and AWS S3. Many organizations, including a lot of our customers, are increasingly deploying applications, services and data in public clouds, which is one of the reasons why they have asked us to deploy Voyager in the cloud and index data stored in cloud repositories.&lt;/p&gt;

&lt;p&gt;For general users, Voyagers easy-to-use interface and its ability to find any piece of data within their organizations are key benefits. System administrators like Voyager for its enterprise integration and data management features; while developers like Voyager because of its REST APIs and extensibility for creating custom locations, extractors and pipeline steps.&lt;/p&gt;

&lt;p&gt;This blog post will highlight a new way for developers to build on top of Voyager and use some elasticity features that public cloud providers offer and focus on how AWS Batch can help developers horizontally scale out processes that will push data into Voyager.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS and NAIP Content
&lt;/h1&gt;

&lt;p&gt;As part of our recent VoyagerODN release, we are including NAIP content from the NAIP on AWS hosted dataset. NAIP on AWS contains leaf-on imagery for the continental United States for the prior two collection years and is hosted in Amazon S3 in a Requester Pay bucket in us-east-1 (Northern Virginia) with over 1.3 million images accumulated between 2012 and 2015.&lt;/p&gt;

&lt;p&gt;With VoyagerODN, we wanted to index NAIP content while providing users with visually appealing thumbnails in the search results. Adding the new S3 capabilities enables us to easily index NAIP content, but in order to generate thumbnails, we needed a solution to scale out beyond a single server so it could be completed quickly. Given the NAIP imagery was over 90TB in volume, processing would take weeks to build thumbnails. We needed to build the thumbnails faster.&lt;/p&gt;

&lt;p&gt;Enter AWS Batch. AWS Batch was announced at AWS re:Invent 2016. AWS Batch can run hundreds of thousands of batch computing jobs by dynamically provisioning compute resources based on the volume and specific resource requirements of the batch jobs.&lt;/p&gt;

&lt;p&gt;The key components of AWS Batch are jobs, job definitions, job queues and compute environments. Jobs are executed as commands in a Docker container, and the job definitions define the Docker container to use, the command to execute, the resource requirements, and other configurations for a job. Jobs are then submitted to a job queue. Compute environments define the cluster of servers to execute the jobs, pulling them from one or more job queues and executing them. AWS Batch manages the execution, provides up-to-date status, and writes console output to CloudWatch for monitoring. The only real work is developing the scripts for actually processing the data.&lt;/p&gt;

&lt;p&gt;For VoyagerODN, the plan for building thumbnails is:&lt;/p&gt;

&lt;p&gt;Develop a script to build thumbnails for a set of NAIP images. In this case, well use State Name and Year combination prefix to define a set. Use GDAL in the script to build the thumbnails After building thumbnails for the set, push the thumbnails to S3 In Voyager, when indexing NAIP data, turn off thumbnail generation, and add a Pipeline step to set the external thumbnail URL.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 1: Build Docker image
&lt;/h1&gt;

&lt;p&gt;In this step, I need to build a Docker image based on Ubuntu with GDAL and Python installed and configured.&lt;/p&gt;

&lt;p&gt;The full Docker file shows how the Docker image is built. The aws-cli, python, and gdal software packages will be installed via apt. Additionally, Python libraries will be installed via pip. The script to build thumbnails will be copied into the image as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ubuntu:trusty

# Define Python required libraries in requirements.txt
# Copy requirements file into the Docker image
COPY requirements.txt /tmp/requirements.txt

# Install required software via apt and pip
RUN sudo apt-get -y update &amp;amp;&amp;amp; \
      apt-get install -y \
    awscli \
    python \
    python-pip \
    software-properties-common \
 &amp;amp;&amp;amp; add-apt-repository ppa:ubuntugis/ppa \
 &amp;amp;&amp;amp; apt-get -y update \
 &amp;amp;&amp;amp; apt-get install -y \
      gdal-bin \
 &amp;amp;&amp;amp; pip install --requirement /tmp/requirements.txt

# Copy Build Thumbnail script to Docker image and add execute permissions
COPY build-thumbnails.py build-thumbnails.py

RUN chmod +x build-thumbnails.py

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

&lt;/div&gt;



&lt;p&gt;The Docker image is built and tagged (vg-gdal) with the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$docker build -t vg-gdal .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The benefit of using Docker is the same image can be used in development, test, and production environments. No more works on my machine. Lets test this new Docker image on a developer machine by running the script in a Docker container with the Docker image:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$docker run -e AWS_ACCESS_KEY_ID=&amp;lt;ACCESSKEY&amp;gt; -e&lt;br&gt;
AWS_SECRET_KEY=&amp;lt;SECRETKEY&amp;gt; -it vg-gdal /usr/bin/python build-thumbnails.py&lt;br&gt;
aws-naip al/2013/ voyager-aws-naip&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Runs the build-thumbnails script in a container running the previously built Docker image named vg-gdal&lt;/li&gt;
&lt;li&gt;Specifies environment variables for AWS credentials (this wont be needed when running in AWS Batch, more on that later)&lt;/li&gt;
&lt;li&gt;Build thumbnails from images in the aws-naip bucket with key that begin with al/2013/ (All images collected over Alabama in 2013)&lt;/li&gt;
&lt;li&gt;Stores the thumbnails in the voyager-aws-naip bucket (key names will be in sync between aws-naip and voyager-aws-naip)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After successful execution, thumbnails will be built and stored in the voyager-aws-naip S3 bucket. From here, the Docker image needs to be pushed to an image repository, either Docker Hub or Amazon ECR. In this case, Ill use a Docker Hub repository. The following commands will login to Docker Hub, re-tag the Docker image appropriately and push the Docker image to the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$docker login
$docker tag vg-gdal dsedlmyervg/vg-gdal:latest
$docker push dsedlmyervg/vg-gdal:latest

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step 2: Configure AWS Batch
&lt;/h1&gt;

&lt;p&gt;Now that I have a working script to build thumbnails running inside a Docker container and with the Docker image pushed to an image repository, lets configure AWS Batch to run batch jobs.&lt;/p&gt;

&lt;p&gt;First step is to configure the Compute Environment. This will configure the instance type, security groups, networking, scaling policies and provisioning model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D1AlV-L2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047632619/o-21Km2Gg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D1AlV-L2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047632619/o-21Km2Gg.png" alt="Create Compute Environment" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cr-EI-M1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047664749/mV-L8e25T.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cr-EI-M1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047664749/mV-L8e25T.png" alt="Configure Compute Environment" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To build NAIP thumbnails, Im going to configure the compute environment with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managed Compute Environment. AWS will scale and configure the cluster instances for me.&lt;/li&gt;
&lt;li&gt;On-demand provisioning model. If I wanted to run this on SPOT that is another option, but I prefer to run this on-demand for now.&lt;/li&gt;
&lt;li&gt;M4 instance family. M4 instances are good general purpose instance types and will fit fine for building NAIP thumbnails.&lt;/li&gt;
&lt;li&gt;96 Maximum vCPUs. I chose 96 because there are 48 states with NAIP imagery and 2 years of data for each year, so I can process the entire dataset in parallel.&lt;/li&gt;
&lt;li&gt;I also assigned an Instance Role to the instances. This will give the instances access to only write thumbnails to a specific S3 bucket. The instance profile will not grant access for the instances to access any other AWS service. Now I dont have to specify an AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY in my scripts, credential files or environment variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step is to create the job queue and connect it to the previously created Compute Environment. Ill assign a priority of 1 to the job queue so its jobs are given higher priority for compute resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xcF-dMoF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047730037/PTIUFQwzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xcF-dMoF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047730037/PTIUFQwzt.png" alt="Create Job Queue" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, its time to create a job definition to build thumbnails using the Docker image built in Step 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0rN8Lm6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047768635/pWR043Iae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0rN8Lm6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047768635/pWR043Iae.png" alt="Create Job Definition" width="800" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created this job definition with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name is set to vg-aws-naip-thumbs&lt;/li&gt;
&lt;li&gt;Using my previously created Docker image, dsedlmyervg/vg-gdal, as the Container Image&lt;/li&gt;
&lt;li&gt;The command is defined as:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a similar command that I used when testing the Docker image in Step 1. The difference is Im specifying the input_prefix a Parameter in the Job Definition. Because its a Parameter, I can pass the actual input prefix when submitting Jobs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vCPUs is set to 1. The job doesnt need multiple vCPUs&lt;/li&gt;
&lt;li&gt;Memory (MiB) is set to 512. 512MiB of RAM will be enough for the OS and the Job to run.&lt;/li&gt;
&lt;li&gt;Job Attempts is set to 2. In case of failure, the entire job will automatically be restarted. The build-thumbnails script will look to see if a thumbnail already exists before building it. This saves processing time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Compute Environment, Job Queue and Job Definition are configured. Next, its time to submit jobs to be executed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 3: Submit Jobs
&lt;/h1&gt;

&lt;p&gt;With AWS Batch configured to build thumbnails, I can now submit jobs to the job queue. AWS Batch will handle the execution and status updates for the jobs.&lt;/p&gt;

&lt;p&gt;I decided to define a job using a State and Year. For example, Alabama has data for 2013 and 2015, so Ill submit two jobs for Alabama, one for 2013, and a second for 2015. I wrote a simple Python script to submit the jobs:&lt;br&gt;
&lt;/p&gt;

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

batch = boto3.client('batch')
s3 = boto3.client('s3')

states = s3.list_objects(Bucket='aws-naip',
      Delimiter="/",
      RequestPayer="requester")

for state_prefix in states['CommonPrefixes']:
  if state_prefix['Prefix'] == ".misc/":
    continue

  years = s3.list_objects(Bucket='aws-naip',
      Prefix="{0}".format(state_prefix['Prefix']),
      Delimiter="/",
      RequestPayer="requester")

  if 'CommonPrefixes' not in years:
    continue

  for year_prefix in years['CommonPrefixes']:
    input_prefix = year_prefix['Prefix']
    jobName = "NAIP_{0}".format(input_prefix.replace("/", "_"))
    print(jobName)

    try:
      response = batch.submit_job(jobQueue='vg-naip-thumbs',
        jobName=jobName,
        jobDefinition='vg-aws-naip-thumbs',
        parameters={"input_prefix":input_prefix })

      print ("Response: " + json.dumps(response, indent=2))
    except Exception as e:
      print ("Error submitting Batch Job")
      print (e)

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

&lt;/div&gt;



&lt;p&gt;I can run this script from my developer machine and will need to set the AWS credentials before running the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$export AWS_ACCESS_KEY_ID=&amp;lt;ACCESSKEY&amp;gt;
$export AWS_SECRET_ACCESS_KEY=&amp;lt;SECRETKEY&amp;gt;
$python submit-aws-naip-thumb-jobs.py

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

&lt;/div&gt;



&lt;p&gt;After all jobs are submitted, I my submitted jobs in the AWS Batch console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FBCiiN4H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047886155/nf1rA4Zh1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FBCiiN4H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047886155/nf1rA4Zh1.png" alt="Jobs" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can monitor these jobs move through the Pending, Runnable, Startable, Running, and ultimately Succeeded states.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 4: Index NAIP
&lt;/h1&gt;

&lt;p&gt;The final step is to configure Voyager to index NAIP and set the thumbnail appropriately. To index the NAIP data, create a new Cloud Storage location and set the Thumbnails Strategy to Do Not Build Images. Since I built thumbnails externally, I dont want Voyager to build the thumbnails.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XzyS3sz4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047923809/KjGCsOkGP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XzyS3sz4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047923809/KjGCsOkGP.png" alt="Configure Thumbnails" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I can use a Pipeline step to set the external URL for NAIP thumbs. Voyager's Indexing Pipeline provides functions to transform and manipulate the properties (metadata) of data records as it adds them to the Index. Pipeline steps can easily be developed using Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XcOoLE1Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047949662/SnwFxXg-d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XcOoLE1Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654047949662/SnwFxXg-d.png" alt="Pipeline Configuration" width="746" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Python code for the &lt;code&gt;external_naip_thumbs&lt;/code&gt; step is:&lt;br&gt;
&lt;/p&gt;

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

def run(entry):
    """
    Python step to compute the Thumb URL for a NAIP on AWS record. Thumbs are located in a
    publicly accessible AWS bucket. Thumbs have the same key as the full resolution image only
    ending in .thumb.jpg instead of .tif
    :param entry: a JSON file containing a voyager entry.
    """
    new_entry = json.load(open(entry, "rb"))
    key = new_entry['entry']['fields']['fs_key']
    thumb_key = key.replace(".tif", ".thumb.jpg")
    thumb_url = "https://voyager-aws-naip.s3.amazonaws.com/{0}".format(thumb_key)
    new_entry['entry']['fields']['image_url'] = thumb_url

    sys.stdout.write(json.dumps(new_entry))
    sys.stdout.flush()

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;external_naip_thumbs.py&lt;/code&gt; script should be copied to the /app/py/pipeline/steps directory.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;external_naip_thumbs&lt;/code&gt; script does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defines a run method that accepts an entry parameter. The run method is required for a pipeline step. The entry parameter is a file path that contains the documents JSON representation.&lt;/li&gt;
&lt;li&gt;The file path is opened and parsed as JSON into new_entry. new_entry is a Python dict that represents the indexed document.&lt;/li&gt;
&lt;li&gt;The key (fs_key) field is retrieved. The key field was populated as a result of indexing S3 content. It contains the key name of the NAIP image in S3.&lt;/li&gt;
&lt;li&gt;Using the key value, a URL to the Thumbnail is computed. The thumbnail is stored in S3 from running the jobs through AWS Batch.&lt;/li&gt;
&lt;li&gt;The Thumbnail URL is stored in a separate field in the indexed document named image_url. Voyager will interpret image_url as an absolute URL to a thumbnail for the document.&lt;/li&gt;
&lt;li&gt;As the final action in the pipeline step, the indexed document is serialized to JSON to stdout. The Voyager Pipeline process will read the indexed document from stdout and continue the indexing process.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;Using AWS Batch, I was able to build thumbnails for over 1.3 million NAIP images with data volume over 90TB over a weekend. That is a far better improvement compared to running on a single server and taking weeks to execute.&lt;/p&gt;

&lt;p&gt;Below is the indexed content in Voyager. You will notice the thumbnails generated in AWS and hosted in S3.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P6vFpgae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654048038992/-C8pNtnYu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P6vFpgae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1654048038992/-C8pNtnYu.png" alt="Search Results" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This is a simple example of how developers can integrate a custom solution to push content into Voyager and still leverage Voyager Searchs capabilities within their organization. Future blog posts will build on this sample and show more advanced custom solutions.&lt;/p&gt;

&lt;p&gt;For those interested in viewing source code, visit: &lt;a href="https://github.com/voyagersearch/voyager-samples/tree/master/aws-batch-naip-thumbs"&gt;https://github.com/voyagersearch/voyager-samples/tree/master/aws-batch-naip-thumbs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>docker</category>
      <category>python</category>
    </item>
  </channel>
</rss>
