Adam Divall

Walkthrough Guides and Other Useful Information on AWS

AWS Landing Zone Accelerator - Part 2: Organizational Units and Account Configuration

2025-01-30 11 min read Walkthroughs Adam Divall

In Part 1 of our AWS Landing Zone Accelerator (LZA) series, we introduced the LZA and its benefits. Now, we’ll explore configuring OUs and other essential organizational settings, along with the process of creating AWS accounts within your LZA environment.

Prerequisites

Before we get started, let’s make sure you have the following:

  • Access to the AWS Management account: You’ll need the right permissions to make changes to the LZA setup.
  • Permissions to update the LZA configuration: This means being able to edit the files that control how the LZA works (e.g., in the aws-accelerator-config repository).
  • A brand new email address: This will be used for the new AWS account you’re setting up.
  • Git access: You’ll need to be able to download and upload the LZA configuration files from and to the aws-accelerator-config repository.
  • AWS CLI ready to go: Make sure you have the AWS Command Line Interface installed and set up on your computer.
  • The right permissions in Microsoft Entra ID: You’ll need to be able to create and manage groups in Microsoft Entra ID, and connect them to AWS IAM Identity Center (this is what you use to control who can access your AWS accounts).

Important Note: I’m assuming you’ve already set up the LZA in your AWS environment by following the official guide: https://docs.aws.amazon.com/solutions/latest/landing-zone-accelerator-on-aws/step-1.-launch-the-stack.html. This means you’ve got the basic LZA structure in place.

Setting up Organizational Units (OUs)

Organizational Units (OUs) are like folders; they help you organize your AWS accounts. This is crucial for applying policies, managing access, and maintaining a clear structure for your AWS resources. Here’s how to set them up using the LZA:

Update the LZA configuration:

  • Grab a copy of the aws-accelerator-config repository (or update your existing copy):
git clone <repository-url>

(Replace with the actual link to your repository)

  • Open the organizational-config.yaml file. This is where you define the structure of your AWS Organization.
  • Add the OUs you need under the organizationalUnits section. Here’s an example:
enable: true
organizationalUnits:
  - name: Security
  - name: Infrastructure
  - name: Policy Staging
  - name: Production
  - name: Non-Production
  - name: Suspended
    ignore: true
  • enable: true: This just means you’re using AWS Organizations, which is needed for the LZA.
  • name: This is the name of your OU (e.g., “Security”). You can create OUs within OUs (like subfolders) if you need to.
  • ignore: true: This is useful for OUs that hold accounts you’ve closed; it tells the LZA to ignore them.

Tip: The example above is a common way to structure your OUs, but you can adjust it to fit your needs. It’s best to keep things simple to start with and add more OUs as you need them.

Save your changes:

  • Use these commands to save your changes and send them to the main LZA configuration:
git add.
git commit -m "Creating Org Unit Structure"
git push

Note: The LZA will automatically create the OUs you defined. You can check that they’ve been created in AWS Organizations or AWS Control Tower.

Creating a New AWS Account

Now, let’s create a new AWS account using the LZA.

Update the LZA Configuration:

  • Open the accounts-config.yaml file in your local copy of the aws-accelerator-config repository. This file is where you manage all the AWS accounts in your organization.
  • Add a new entry for your account under the workloadAccounts section, like this:
workloadAccounts:
  - name: "Network"           # Give the account a descriptive name
    description: "Centralised Networking Account"  # Briefly describe its purpose
    email: "network@example.com"    # Use a unique email address
    organizationalUnit: "Infrastructure"    # Specify the OU it belongs to
  • name: Choose a clear and descriptive name for the account.
  • description: Briefly explain what the account will be used for.
  • email: Provide a unique email address for the account (this is important!).
  • organizationalUnit: Specify the OU where the account should be placed (make sure this OU already exists!).

For instance, we just created a “Network” account. This type of account is often used to manage all things networking across your organization in AWS. You might use it for things like:

  • IPAM (IP Address Management): Keeping track of your IP addresses.
  • Transit Gateway: Connecting your different AWS environments.
  • Inspection VPCs: Boosting security by inspecting network traffic.
  • Centralised Endpoint VPCs: Providing a central point for managing connections.

Tip: For a complete setup, you’ll typically want a few other accounts as well. For example, a “Shared Services” account for common resources like directory services, and a “Centralized Backup” account to manage backups for all accounts.

Save your changes:

  • Use these commands to save your changes and update the LZA configuration:
git add.
git commit -m "Creating AWS Account"
git push

Configuring Other Important Organizational Settings

The LZA also lets you set up things like backup and tagging policies across your organization. These policies help ensure data protection, cost optimisation, and consistent tagging practices across your accounts.

Backup Policies

You can create backup policies to make sure your important data is backed up regularly. Here’s how:

  • Open the organizational-config.yaml file.
  • Add your backup policy under the backupPolicies section:
backupPolicies:
  - name: OrgBackupPolicy  # Give your policy a name
    description: Organization Backup Policy  # Add a description
    policy: backup-policies/org-backup-policies.json  # Specify the policy file
    deploymentTargets:  # Specify where the policy should apply
        organizationalUnits:
          - Root  # This applies the policy to the whole organization
  • Create a JSON file with your backup settings (e.g., backup-policies/org-backup-policies.json). This file will define things like how often backups should be taken, where they should be stored, and how long they should be kept.

Example: You can create different backup policies for different types of data. For instance, you might back up critical data more frequently than less important data. Below is an example AWS Backup Policy that I use:

{
    "plans": {
        "Organization_Backup_Plan_Gold": {
            "rules": {
                "Backup_Rule": {
                    "schedule_expression": {
                        "@@assign": "cron(0 */4 * * ? *)"
                    },
                    "start_backup_window_minutes": {
                        "@@assign": "60"
                    },
                    "complete_backup_window_minutes": {
                        "@@assign": "180"
                    },
                    "enable_continuous_backup": {
                        "@@assign": false
                    },
                    "target_backup_vault_name": {
                        "@@assign": "LocalBackupVaultGold"
                    },
                    "lifecycle": {
                        "move_to_cold_storage_after_days": {
                            "@@assign": "7"
                        },
                        "delete_after_days": {
                            "@@assign": "365"
                        }
                    },
                    "copy_actions": {
                        "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultGold": {
                            "target_backup_vault_arn": {
                                "@@assign": "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultGold"
                            },
                            "lifecycle": {
                                "delete_after_days": {
                                    "@@assign": "365"
                                }
                            }
                        }
                    }
                }
            },
            "selections": {
                "tags": {
                    "Gold_Backup_Assignment": {
                        "iam_role_arn": {
                            "@@assign": "arn:aws:iam::$account:role/aws-service-role/backup.amazonaws.com/AWSServiceRoleForBackup"
                        },
                        "tag_key": {
                            "@@assign": "ServiceClassification"
                        },
                        "tag_value": {
                            "@@assign": [
                                "Gold"
                            ]
                        }
                    }
                }
            },
            "regions": {
                "@@append": [
                    "eu-west-1"
                ]
            },
            "advanced_backup_settings": {
                "ec2": {
                    "windows_vss": {
                        "@@assign": "enabled"
                    }
                }
            },
            "backup_plan_tags": {
            }
        },
        "Organization_Backup_Plan_Silver": {
            "rules": {
                "Backup_Rule": {
                    "schedule_expression": {
                        "@@assign": "cron(0 */8 * * ? *)"
                    },
                    "start_backup_window_minutes": {
                        "@@assign": "60"
                    },
                    "complete_backup_window_minutes": {
                        "@@assign": "180"
                    },
                    "enable_continuous_backup": {
                        "@@assign": false
                    },
                    "target_backup_vault_name": {
                        "@@assign": "LocalBackupVaultSilver"
                    },
                    "lifecycle": {
                        "move_to_cold_storage_after_days": {
                            "@@assign": "7"
                        },
                        "delete_after_days": {
                            "@@assign": "365"
                        }
                    },
                    "copy_actions": {
                        "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultSilver": {
                            "target_backup_vault_arn": {
                                "@@assign": "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultSilver"
                            },
                            "lifecycle": {
                                "delete_after_days": {
                                    "@@assign": "365"
                                }
                            }
                        }
                    }
                }
            },
            "selections": {
                "tags": {
                    "Silver_Backup_Assignment": {
                        "iam_role_arn": {
                            "@@assign": "arn:aws:iam::$account:role/aws-service-role/backup.amazonaws.com/AWSServiceRoleForBackup"
                        },
                        "tag_key": {
                            "@@assign": "ServiceClassification"
                        },
                        "tag_value": {
                            "@@assign": [
                                "Silver"
                            ]
                        }
                    }
                }
            },
            "regions": {
                "@@append": [
                    "eu-west-1"
                ]
            },
            "advanced_backup_settings": {
                "ec2": {
                    "windows_vss": {
                        "@@assign": "enabled"
                    }
                }
            },
            "backup_plan_tags": {
            }
        },
        "Organization_Backup_Plan_Bronze": {
            "rules": {
                "Backup_Rule": {
                    "schedule_expression": {
                        "@@assign": "cron(0 0 * * ? *)"
                    },
                    "start_backup_window_minutes": {
                        "@@assign": "60"
                    },
                    "complete_backup_window_minutes": {
                        "@@assign": "180"
                    },
                    "enable_continuous_backup": {
                        "@@assign": false
                    },
                    "target_backup_vault_name": {
                        "@@assign": "LocalBackupVaultBronze"
                    },
                    "lifecycle": {
                        "move_to_cold_storage_after_days": {
                            "@@assign": "7"
                        },
                        "delete_after_days": {
                            "@@assign": "365"
                        }
                    },
                    "copy_actions": {
                        "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultBronze": {
                            "target_backup_vault_arn": {
                                "@@assign": "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultBronze"
                            },
                            "lifecycle": {
                                "delete_after_days": {
                                    "@@assign": "365"
                                }
                            }
                        }
                    }
                }
            },
            "selections": {
                "tags": {
                    "Bronze_Backup_Assignment": {
                        "iam_role_arn": {
                            "@@assign": "arn:aws:iam::$account:role/aws-service-role/backup.amazonaws.com/AWSServiceRoleForBackup"
                        },
                        "tag_key": {
                            "@@assign": "ServiceClassification"
                        },
                        "tag_value": {
                            "@@assign": [
                                "Bronze"
                            ]
                        }
                    }
                }
            },
            "regions": {
                "@@append": [
                    "eu-west-1"
                ]
            },
            "advanced_backup_settings": {
                "ec2": {
                    "windows_vss": {
                        "@@assign": "enabled"
                    }
                }
            },
            "backup_plan_tags": {
            }
        },
        "Organization_Backup_Plan_Platinum": {
            "rules": {
                "Backup_Rule": {
                    "schedule_expression": {
                        "@@assign": "cron(0 * * * ? *)"
                    },
                    "start_backup_window_minutes": {
                        "@@assign": "60"
                    },
                    "complete_backup_window_minutes": {
                        "@@assign": "180"
                    },
                    "enable_continuous_backup": {
                        "@@assign": false
                    },
                    "target_backup_vault_name": {
                        "@@assign": "LocalBackupVaultPlatinum"
                    },
                    "lifecycle": {
                        "move_to_cold_storage_after_days": {
                            "@@assign": "7"
                        },
                        "delete_after_days": {
                            "@@assign": "365"
                        }
                    },
                    "copy_actions": {
                        "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultPlatinum": {
                            "target_backup_vault_arn": {
                                "@@assign": "arn:aws:backup:eu-west-1:012345678910:backup-vault:CentralizedBackupVaultPlatinum"
                            },
                            "lifecycle": {
                                "delete_after_days": {
                                    "@@assign": "365"
                                }
                            }
                        }
                    }
                }
            },
            "selections": {
                "tags": {
                    "Platinum_Backup_Assignment": {
                        "iam_role_arn": {
                            "@@assign": "arn:aws:iam::$account:role/aws-service-role/backup.amazonaws.com/AWSServiceRoleForBackup"
                        },
                        "tag_key": {
                            "@@assign": "ServiceClassification"
                        },
                        "tag_value": {
                            "@@assign": [
                                "Platinum"
                            ]
                        }
                    }
                }
            },
            "regions": {
                "@@append": [
                    "eu-west-1"
                ]
            },
            "advanced_backup_settings": {
                "ec2": {
                    "windows_vss": {
                        "@@assign": "enabled"
                    }
                }
            },
            "backup_plan_tags": {
        }
    }
}

Tagging Policies

Tagging is a great way to organize and keep track of your AWS resources. Here’s how to set up tagging policies:

  • Open the organizational-config.yaml file.
  • Add your tagging policies under the taggingPolicies section:
taggingPolicies:
  - name: tagging-policy-technical  # Give your policy a name
    description: Organization Technical Tagging Policy  # Add a description
    policy: tagging-policies/tag-policy-technical.json  # Specify the policy file
    deploymentTargets:  # Specify where the policy should apply
      organizationalUnits:
        - Root  # This applies the policy to the whole organization
  #... add more tagging policies as needed

Create JSON files with your tagging rules (e.g., tagging-policies/tag-policy-technical.json). These files will define the tags that should be used for different types of resources.

Example: You might have separate tagging policies for different departments or types of resources.

Saving your changes

Once you’ve configured your backup and tagging policies, save your changes and update the LZA configuration:

git add.
git commit -m "Adding backup and tagging policies"
git push

Here’s an example of how you might structure a tagging policy. This example includes different types of tags (technical, operational, business, security, and automation). I like to keep these in separate files to make them easier to manage and to work around some of the limitations of tagging policies (I’ve talked about these limitations in a previous blog post).

{
  "tags": {
      "Name": {
          "tag_key": {
              "@@assign": "Name"
          }
      },
      "Environment": {
          "tag_key": {
              "@@assign": "Environment"
          },
          "tag_value": {
              "@@assign": [
                  "Production",
                  "Non-Production"
              ]
          }
      },
      "SDLC": {
          "tag_key": {
              "@@assign": "SDLC"
          },
          "tag_value": {
              "@@assign": [
                  "Development",
                  "Test",
                  "Pre-Production",
                  "Production"
              ]
          }
      },
      "ServiceName": {
          "tag_key": {
              "@@assign": "ServiceName"
          }
      },
      "ApplicationRole": {
          "tag_key": {
              "@@assign": "ApplicationRole"
          }
      },
      "Cluster": {
          "tag_key": {
              "@@assign": "Cluster"
          }
      }
  }
}

Service Control Policies (SCPs)

SCPs are like guardrails for your AWS accounts. They let you set rules to prevent actions that might compromise security or compliance. This is crucial for establishing a strong security baseline and preventing accidental or malicious actions that could disrupt your environment or violate compliance policies. Here’s how to set up SCPs with the LZA:

  • Open the organizational-config.yaml file.
  • Add your SCPs under the serviceControlPolicies section:
serviceControlPolicies:
  - name: aws-custom-guardrails
    description: >
      A combined SCP that:
      - Protects essential Security Tooling
      - Enforces Centralized Identity Management
      - Secures the Root user by preventing Access Key Creation
      - Prevents Accounts from leaving the AWS Organization
    policy: service-control-policies/custom_security_guardrails.json
    type: customerManaged
    deploymentTargets:
      organizationalUnits:
        - Root

Create the SCP

  • Create a JSON file with your SCP rules (e.g., service-control-policies/custom_security_guardrails.json). This file will define the actions that are allowed or denied within your AWS accounts.

Example: The SCP below demonstrates several security-focused restrictions:

  • Prevents disabling key security services like Security Hub, GuardDuty, Macie, Access Analyzer, Inspector, and Detective.
  • Enforces centralized identity management by restricting IAM user creation and access key creation.
  • Secures the root user by preventing access key creation.
  • Prevents accounts from leaving the organization.
{
   "Version": "2012-10-17",
   "Statement": [
       {
           "Sid":"PreventDisablingSecurityHub",
           "Effect":"Deny",
           "Action":[
              "securityhub:DeleteInvitations",
              "securityhub:DisableSecurityHub",
              "securityhub:DisassociateFromMasterAccount",
              "securityhub:DeleteMembers",
              "securityhub:DisassociateMembers"
           ],
           "Resource":"*",
           "Condition":{
              "ArnNotLike":{
                 "aws:PrincipalARN":[
                    "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution"
                 ]
              }
           }
        },
        {
           "Sid":"PreventDisablingGuardDuty",
           "Effect":"Deny",
           "Action":[
              "guardduty:DeleteDetector",
              "guardduty:DeleteInvitations",
              "guardduty:DeleteIPSet",
              "guardduty:DeleteMembers",
              "guardduty:DeleteThreatIntelSet",
              "guardduty:DisassociateFromMasterAccount",
              "guardduty:DisassociateMembers",
              "guardduty:StopMonitoringMembers"
           ],
           "Resource":"*",
           "Condition":{
              "ArnNotLike":{
                 "aws:PrincipalARN":[
                    "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution"
                 ]
              }
           }
        },
        {
           "Sid":"PreventDisablingMacie",
           "Effect":"Deny",
           "Action":[
              "macie2:DisassociateFromMasterAccount",
              "macie2:DisableOrganizationAdminAccount",
              "macie2:DisableMacie",
              "macie2:DeleteMember"
           ],
           "Resource":"*",
           "Condition":{
              "ArnNotLike":{
                 "aws:PrincipalARN":[
                    "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution"
                 ]
              }
           }
        },
        {
           "Sid":"PreventDisablingAccessAnalyzer",
           "Effect":"Deny",
           "Action":[
              "access-analyzer:DeleteAnalyzer"
           ],
           "Resource":"*",
           "Condition":{
              "ArnNotLike":{
                 "aws:PrincipalARN":[
                    "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution"
                 ]
              }
           }
       },
       {
           "Sid": "PreventDisablingInspectorAndDetective",
           "Effect": "Deny",
           "Action": [
              "inspector:DeleteAssessmentRun",
              "inspector:DeleteAssessmentTarget",
              "inspector:DeleteAssessmentTemplate",
              "inspector2:Disable",
              "inspector2:DisableDelegatedAdminAccount",
              "inspector2:DisassociateMember",
              "inspector2:DeleteCisScanConfiguration",
              "detective:DisableOrganizationAdminAccount",
              "detective:DeleteGraph",
              "detective:DeleteMembers",
              "detective:DisassociateMembership",
              "detective:DisableOrganizationAdminAccount"
           ],
           "Resource": "*",
           "Condition": {
              "ArnNotLike": {
                 "aws:PrincipalARN": [
                       "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution"
                 ]
              }
           }
       },
       {
           "Sid": "EnforceCentralizedIdentityManagement",
           "Effect": "Deny",
           "Action": [
               "iam:CreateUser",
               "iam:CreateAccessKey"
           ],
           "Resource": "*",
           "Condition": {
            "ArnNotLike": {
               "aws:PrincipalARN": [
                     "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution"
               ]
            }
         }
       },
       {
           "Sid": "SecureRootUser",
           "Effect": "Deny",
           "Action": "iam:CreateAccessKey",
           "Resource": "*",
           "Condition": {
               "ArnLike": {
                   "aws:PrincipalArn": [
                       "arn:${AWS::Partition}:iam::*:root"
                   ]
               }
             }
       },
       {
           "Sid": "PreventAccountsLeavingOrganization",
           "Effect": "Deny",
           "Action": [
               "organizations:LeaveOrganization"
           ],
           "Resource": "*",
           "Condition": {
            "ArnNotLike": {
               "aws:PrincipalARN": [
                     "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution"
               ]
            }
         }
       }
   ]
}

Saving your changes

Once you’ve configured your backup and tagging policies, save your changes and update the LZA configuration:

git add.
git commit -m "Adding Service Control Policies"
git push

Wrapping up

In this post, we’ve looked at how to set up Organizational Units and configure backup, tagging, and service control policies using the LZA. These are important steps in organizing and managing your AWS environment effectively. By setting up these policies, you can ensure consistent security controls, automated data protection, and well-organized resources across your accounts.

But there’s much more to explore with the LZA! In upcoming posts, we’ll not only dive into network configuration (VPCs, subnets, and more) but also cover other key areas like:

  • Identity and Access Management (IAM): Controlling who has access to what in your AWS environment.
  • Security: Implementing security best practices and guardrails.
  • Customization: Tailoring the LZA to fit your specific needs.
  • Global configurations: Setting up global settings for your organization.

And because no solution is perfect, I’ll also share some of the limitations and challenges we’ve encountered while using the LZA in real-world scenarios, along with tips and workarounds.

Stay tuned for more insights and practical guides on mastering the AWS Landing Zone Accelerator!