Adam Divall

Walkthrough Guides and Other Useful Information on AWS

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

2025-01-30 12 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 begin, ensure you have the following:

  • Access to the AWS Management account with the necessary permissions to modify the LZA setup.
  • Permissions to update the LZA configuration, including editing files in the aws-accelerator-config repository.
  • A brand-new email address for the new AWS account you’ll be creating.
  • Git access to download and upload LZA configuration files.
  • AWS Command Line Interface (CLI) installed and configured on your computer.
  • The required permissions in Microsoft Entra ID to create and manage groups, and connect them to AWS IAM Identity Center for access control.

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.

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 allows you to configure backup, tagging, and service control policies across your organization.

Backup Policies

AWS Backup Policies are a set of rules that define how and when your AWS resources are backed up. They act as the central control point for your backup strategy, allowing you to automate and standardize backups across your organisation. Think of them as the “what, when, and how” of your backups.

  • What: The resources to be backed up (e.g., EC2 instances, EBS volumes, RDS databases, DynamoDB tables, etc.). You can target specific resources or use tags to group resources for backup.  
  • When: The backup schedule (e.g., daily, weekly, monthly, or custom schedules). You can also define backup windows to minimise the impact on your applications.  
  • How: Backup lifecycle management, including retention policies (how long backups are kept) and transition to different storage tiers (e.g., moving older backups to cheaper archive storage like S3 Glacier). You can also configure copy operations to replicate backups to other AWS regions for disaster recovery.

Update the LZA Configuration:

  • 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.

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": {
        }
    }
}

Save your changes:

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

Tagging Policies

AWS Tagging Policies are a powerful tool within AWS Organizations that allow you to enforce tagging standards across your AWS accounts. They provide a centralised way to define which tags are allowed or required for specific resource types, ensuring consistency and compliance across your organisation.

  • Define Allowed Tags: Tagging policies let you specify which tag keys and values are permitted for particular resource types. For example, you might require all EC2 instances to have an “Environment” tag with values like “production,” “development,” or “staging.”  
  • Enforce Compliance: You can configure tagging policies to either report on non-compliant resources or actively prevent the creation of resources that don’t adhere to the defined tagging rules. This ensures that your tagging standards are consistently applied.  
  • Granular Control: Tagging policies can be applied at different levels within your AWS Organizations hierarchy (root, organizational unit (OU), or account), allowing you to tailor your tagging requirements to specific parts of your organization.

Update the LZA Configuration:

  • 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 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)

Service Control Policies (SCPs) are a type of policy within AWS Organizations that give you centralized control over the maximum permissions available to all accounts in your organization. Think of them as guardrails that define what actions can and cannot be taken within your AWS environment.

  • Define Permissions Boundaries: SCPs don’t grant permissions directly. Instead, they define the maximum permissions that can be granted by IAM policies within individual accounts. This means even if an IAM user or role has a policy granting them certain access, an SCP can restrict or deny that access at the organizational level.  
  • Hierarchical Application: SCPs can be applied at different levels within your AWS Organizations structure (root, organizational unit (OU), or account). Policies applied at higher levels inherit down the hierarchy, ensuring consistent enforcement.  
  • “Deny” by Default: SCPs operate on a “deny” by default principle. If a specific action or service isn’t explicitly allowed by an SCP, it’s implicitly denied. This provides a strong security posture.  

Update the LZA Configuration:

  • 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 explored setting up OUs, configuring backup, tagging, and service control policies using the LZA. These are crucial steps in organizing and managing your AWS environment effectively.

But there’s more to discover! In future posts, we’ll dive deeper into network configuration, identity and access management, security best practices, LZA customization, global configurations, and address any limitations or challenges encountered along the way.

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