Skip to content

Repository files navigation

aws-organization

Manage your AWS Organization, Organizational Units, and Accounts as a single resource. Define your account hierarchy declaratively and let Crossplane handle the rest.

Why AWS Organizations?

Without Organizations:

  • Separate bills per account
  • Manual IAM user management in each account
  • No guardrails - anyone can do anything
  • No central view of resources

With Organizations:

  • Consolidated billing with cost allocation tags
  • Centralized identity via Identity Center
  • Service Control Policies (SCPs) for guardrails
  • Account factory - spin up new accounts in minutes
  • Delegated administration for security tools

The Journey

Stage 1: Adopt Your Existing Organization

Most teams already have an AWS Organization. Start by importing it.

Why import instead of create?

  • AWS allows only one Organization per account
  • Your existing OUs and accounts are preserved
  • No disruption to running workloads
apiVersion: aws.hops.ops.com.ai/v1alpha1kind: Organizationmetadata:
name: my-orgnamespace: defaultspec:
# Import existing org - get ID from: aws organizations describe-organizationexternalName: o-abc123xyz# Don't delete the org if this resource is deletedmanagementPolicies: ["Create", "Observe", "Update", "LateInitialize"]# Enable trusted access for services you useorganization:
awsServiceAccessPrincipals:
- sso.amazonaws.com

Stage 2: Define Your OU Structure

Organizational Units group accounts for policy application and billing.

Recommended OU structure:

  • Security - Security tooling, audit logs, GuardDuty
  • Infrastructure - Shared services, networking, CI/CD
  • Workloads - Application accounts
    • Workloads/Prod - Production workloads
    • Workloads/NonProd - Dev, staging, sandbox

Why this structure?

  • Security accounts are isolated from workloads
  • Infrastructure is shared but separate from applications
  • Prod/NonProd separation enables different SCPs
apiVersion: aws.hops.ops.com.ai/v1alpha1kind: Organizationmetadata:
name: acmenamespace: defaultspec:
externalName: o-abc123xyzmanagementPolicies: ["Create", "Observe", "Update", "LateInitialize"]organization:
awsServiceAccessPrincipals:
- sso.amazonaws.com
- cloudtrail.amazonaws.com
- config.amazonaws.com# Path-based OU definition - parent OUs created automaticallyorganizationalUnits:
- path: Security
- path: Infrastructure
- path: Workloads
- path: Workloads/Prod
- path: Workloads/NonProd
- path: Workloads/Sandboxtags:
organization: acmemanaged-by: crossplane

Stage 3: Add Accounts to OUs

Accounts are defined inline within OUs. This keeps the hierarchy visible and ensures accounts are created in the right place.

Why inline accounts?

  • Single source of truth for account placement
  • Account creation waits for parent OU to be ready
  • Easier to visualize the hierarchy
organizationalUnits:
- path: Securityaccounts:
- name: acme-securityemail: aws-security@acme.example.com
- path: Infrastructureaccounts:
- name: acme-shared-servicesemail: aws-shared@acme.example.com
- path: Workloads/Prodaccounts:
- name: acme-prodemail: aws-prod@acme.example.com
- path: Workloads/NonProdaccounts:
- name: acme-stagingemail: aws-staging@acme.example.com
- name: acme-devemail: aws-dev@acme.example.com

Stage 4: Import Existing Accounts

Already have accounts? Import them with externalName.

Why import?

  • Preserves existing resources and configurations
  • No downtime or migration needed
  • Gradually bring accounts under Crossplane management
organizationalUnits:
- path: SecurityexternalName: ou-abc1-security # Import existing OUaccounts:
- name: acme-securityemail: aws-security@acme.example.comexternalName: "111111111111"# Import existing accountmanagementPolicies: ["Create", "Observe", "Update", "LateInitialize"]
- path: Workloads/ProdexternalName: ou-abc1-prodaccounts:
- name: acme-prodemail: aws-prod@acme.example.comexternalName: "222222222222"managementPolicies: ["Create", "Observe", "Update", "LateInitialize"]

Stage 5: Delegate Administration

Move service administration out of the management account.

Why delegate?

  • Management account should only manage the Organization
  • Reduces blast radius if credentials are compromised
  • Teams can self-service within delegated scope
  • Required for some services (IPAM, Security Hub)
apiVersion: aws.hops.ops.com.ai/v1alpha1kind: Organizationmetadata:
name: acmenamespace: defaultspec:
externalName: o-abc123xyzmanagementPolicies: ["Create", "Observe", "Update", "LateInitialize"]organization:
awsServiceAccessPrincipals:
- sso.amazonaws.com
- ipam.amazonaws.com
- guardduty.amazonaws.com
- securityhub.amazonaws.com
- ram.amazonaws.comorganizationalUnits:
- path: Securityaccounts:
- name: acme-securityemail: aws-security@acme.example.com
- path: Infrastructureaccounts:
- name: acme-shared-servicesemail: aws-shared@acme.example.com
- path: Workloads/Prod
- path: Workloads/NonProd# Delegate services to appropriate accountsdelegatedAdministrators:
- servicePrincipal: sso.amazonaws.comaccountRef:
name: acme-shared-services
- servicePrincipal: ipam.amazonaws.comaccountRef:
name: acme-shared-services
- servicePrincipal: guardduty.amazonaws.comaccountRef:
name: acme-security
- servicePrincipal: securityhub.amazonaws.comaccountRef:
name: acme-securitytags:
organization: acme

Status

The Organization exposes IDs needed by other resources:

status:
ready: trueorganizationId: o-abc123xyzmanagementAccountId: "000000000000"rootId: r-abc1organizationalUnits:
Security: ou-abc1-securityInfrastructure: ou-abc1-infraWorkloads/Prod: ou-abc1-prodaccounts:
- name: acme-securityid: "111111111111"ready: trueadminRoleArn: arn:aws:iam::111111111111:role/OrganizationAccountAccessRole

Accessing Member Accounts

When accounts are created via Organizations, AWS creates OrganizationAccountAccessRole in each account. Create ProviderConfigs that assume this role:

apiVersion: aws.m.upbound.io/v1beta1kind: ProviderConfigmetadata:
name: acme-prodspec:
assumeRoleChain:
- roleARN: arn:aws:iam::222222222222:role/OrganizationAccountAccessRolecredentials:
source: PodIdentity

Development

make render # Render default example
make test# Run tests
make validate # Validate compositions
make e2e # E2E tests

License

Apache-2.0