From 2c561040ffc0df389a333ddda3fdfc83b83e55b3 Mon Sep 17 00:00:00 2001 From: Santiago Garcia Arango Date: Sat, 23 Sep 2023 21:52:20 -0500 Subject: [PATCH] Add initial OUs and Accounts for shared-services, finance, and scp-tests (#7) --- cdk/stacks/cdk_organization.py | 246 ++++++++++++++++++++++++++++++--- 1 file changed, 228 insertions(+), 18 deletions(-) diff --git a/cdk/stacks/cdk_organization.py b/cdk/stacks/cdk_organization.py index c71ee3c..2496471 100644 --- a/cdk/stacks/cdk_organization.py +++ b/cdk/stacks/cdk_organization.py @@ -28,18 +28,24 @@ class OrganizationStack(Stack): """ - Class to create the infrastructure of the AWS Organizations. + Class to create the infrastructure for AWS Organizations. """ def __init__( self, scope: Construct, construct_id: str, deployment_environment: str, **kwargs ) -> None: + """ + :param scope (Construct): Parent of this stack, usually an ``App`` or a ``Stage``, but could be any construct. + :param construct_id (str): The construct ID of this stack. + :param main_resources_name (str): The main solution name being deployed. + :param deployment_environment (str): Value that represents the deployment environment. For example: "dev" or "prod". + """ super().__init__(scope, construct_id, **kwargs) self.construct_id = construct_id self.deployment_environment = deployment_environment - # Organization creation, services configuration and SCPs + # AWS Organization creation, services configuration and SCPs self.create_root_organization() self.configure_organization_services() self.configure_service_control_policies() @@ -48,6 +54,19 @@ def __init__( self.create_ou_sandbox() self.create_accounts_inside_ou_sandbox() + # Create "infrastructure" OU with inner OUs and accounts inside + self.create_ou_infrastructure() + self.create_accounts_inside_ou_infrastructure() + + # Create "workloads" OU with inner OUs and accounts inside + self.create_ou_workloads() + self.create_ou_finance() + self.create_accounts_inside_ou_finance() + + # Create "policy_staging_tests" OU with inner OUs and accounts inside + self.create_ou_policy_staging_tests() + self.create_accounts_inside_ou_policy_staging_tests() + # !IMPORTANT: this is mandatory for adding CDK dependencies for each account self.add_cdk_accounts_dependencies() # DO NOT REMOVE! @@ -60,7 +79,7 @@ def create_root_organization(self): """ self.organization = Organization( self, - id="RootOrganization", + "RootOrganization", feature_set=FeatureSet.ALL, ) @@ -107,7 +126,7 @@ def configure_service_control_policies(self): # SCP for preventing accounts of leaving organization self.policy_deny_leave_org = Policy( self, - id="PolicyDenyLeave", + "PolicyDenyLeave", content=json.dumps(scp_prevent_leaving_org), policy_name="PreventLeavingOrganization", policy_type=PolicyType.SERVICE_CONTROL_POLICY, @@ -118,7 +137,7 @@ def configure_service_control_policies(self): # SCP for only allow access to specific regions in AWS (deny others) self.policy_allow_specific_regions = Policy( self, - id="PolicyAllowSpecificRegions", + "PolicyAllowSpecificRegions", content=json.dumps(scp_allow_specific_regions), policy_name="AllowSpecificRegions", policy_type=PolicyType.SERVICE_CONTROL_POLICY, @@ -128,23 +147,24 @@ def configure_service_control_policies(self): def create_ou_sandbox(self): """ - Method that creates inner Organizational Units (OUs) inside organization. + Method that creates inner Organizational Units (OUs) inside the AWS + Organization for "Sandbox". """ self.top_level_ou_sandbox = OrganizationalUnit( self, - id="SandboxOU", + "SandboxOU", parent=self.organization.root, organizational_unit_name="sandbox", ) def create_accounts_inside_ou_sandbox(self): """ - Method that creates AWS Accounts inside the required Organizational - Units (OUs). + Method that creates AWS Accounts inside the Organizational Units (OUs) + for "Sandbox". """ self.account_sandbox_1 = Account( self, - id="SandboxAccount1", + "SandboxAccount1", account_name="san99tiago-sandbox-1", email="san99tiagodemo+san99tiago-sandbox-1@gmail.com", parent=self.top_level_ou_sandbox, @@ -152,23 +172,171 @@ def create_accounts_inside_ou_sandbox(self): ) self.account_sandbox_2 = Account( self, - id="SandboxAccount2", + "SandboxAccount2", account_name="san99tiago-sandbox-2", email="san99tiagodemo+san99tiago-sandbox-2@gmail.com", parent=self.top_level_ou_sandbox, role_name="OrganizationAccountAccessRole", ) + def create_ou_infrastructure(self): + """ + Method that creates inner Organizational Units (OUs) inside the AWS + Organization for "Infrastructure". + """ + self.top_level_ou_infrastructure = OrganizationalUnit( + self, + "OUInfrastructure", + parent=self.organization.root, + organizational_unit_name="infrastructure", + ) + self.ou_infrastructure_non_prod = OrganizationalUnit( + self, + "OUInfrastructureNonProd", + parent=self.top_level_ou_infrastructure, + organizational_unit_name="non-prod", + ) + self.ou_infrastructure_prod = OrganizationalUnit( + self, + "OUInfrastructureProd", + parent=self.top_level_ou_infrastructure, + organizational_unit_name="prod", + ) + + def create_accounts_inside_ou_infrastructure(self): + """ + Method that creates AWS Accounts inside the Organizational Units (OUs) + for "Infrastructure". + """ + self.account_shared_services_non_prod = Account( + self, + "AccountSharedServicesNonProd", + account_name="shared-services-non-prod", + email="san99tiagodemo+shared-services-non-prod@gmail.com", + parent=self.ou_infrastructure_non_prod, + role_name="OrganizationAccountAccessRole", + ) + self.account_shared_services_prod = Account( + self, + "AccountSharedServicesProd", + account_name="shared-services-prod", + email="san99tiagodemo+shared-services-prod@gmail.com", + parent=self.ou_infrastructure_prod, + role_name="OrganizationAccountAccessRole", + ) + + def create_ou_workloads(self): + """ + Method that creates inner Organizational Units (OUs) inside the AWS + Organization for "Workloads". + """ + self.top_level_ou_workloads = OrganizationalUnit( + self, + "OUWorkloads", + parent=self.organization.root, + organizational_unit_name="workloads", + ) + + def create_ou_finance(self): + """ + Method that creates inner Organizational Units (OUs) inside the AWS + Organization for "Finance". + """ + self.ou_finance = OrganizationalUnit( + self, + "OUFinance", + parent=self.top_level_ou_workloads, + organizational_unit_name="finance", + ) + self.ou_finance_non_prod = OrganizationalUnit( + self, + "OUFinanceNonProd", + parent=self.ou_finance, + organizational_unit_name="non-prod", + ) + self.ou_finance_prod = OrganizationalUnit( + self, + "OUFinanceProd", + parent=self.ou_finance, + organizational_unit_name="prod", + ) + + def create_accounts_inside_ou_finance(self): + """ + Method that creates AWS Accounts inside the Organizational Units (OUs) + for "Finance". + """ + self.account_finance_dev = Account( + self, + "AccountFinanceDev", + account_name="finance-dev", + email="san99tiagodemo+finance-dev@gmail.com", + parent=self.ou_finance_non_prod, + role_name="OrganizationAccountAccessRole", + ) + self.account_finance_qa = Account( + self, + "AccountFinanceQA", + account_name="finance-qa", + email="san99tiagodemo+finance-qa@gmail.com", + parent=self.ou_finance_non_prod, + role_name="OrganizationAccountAccessRole", + ) + self.account_finance_prod = Account( + self, + "AccountFinanceProd", + account_name="finance-prod", + email="san99tiagodemo+finance-prod@gmail.com", + parent=self.ou_finance_prod, + role_name="OrganizationAccountAccessRole", + ) + + def create_ou_policy_staging_tests(self): + """ + Method that creates inner Organizational Units (OUs) inside the AWS + Organization for "PolicyStagingTests". + """ + self.top_level_ou_policy_staging_tests = OrganizationalUnit( + self, + "OUPolicyStagingTests", + parent=self.organization.root, + organizational_unit_name="policy-staging-tests", + ) + + def create_accounts_inside_ou_policy_staging_tests(self): + """ + Method that creates AWS Accounts inside the Organizational Units (OUs) + for "PolicyStagingTests". + """ + self.account_policy_staging_tests = Account( + self, + "AccountPolicyStagingTests", + account_name="policy-staging-tests", + email="san99tiagodemo+policy-staging-tests@gmail.com", + parent=self.top_level_ou_policy_staging_tests, + role_name="OrganizationAccountAccessRole", + ) + def add_cdk_accounts_dependencies(self): """ - ULTRA IMPORTANT METHOD to add CDK dependencies for the AWS Accounts that - are being created (to avoid 2 accounts creation simultaneously, which is + IMPORTANT METHOD to add CDK dependencies for the AWS Accounts that are + being created (to avoid 2 accounts creation simultaneously, which is not supported by AWS). This is because of AWS Organizations limitation. """ # ! IMPORTANT: We MUST add these dependencies, as AWS Organizations only support # ... one account creation "IN_PROGRESS". We add CDK dependency to solve issue # ... and wait for the previous one to finish, to continue with the next... self.account_sandbox_2.node.add_dependency(self.account_sandbox_1) + self.account_shared_services_non_prod.node.add_dependency( + self.account_sandbox_2 + ) + self.account_shared_services_prod.node.add_dependency( + self.account_shared_services_non_prod + ) + self.account_finance_dev.node.add_dependency(self.account_shared_services_prod) + self.account_finance_qa.node.add_dependency(self.account_finance_dev) + self.account_finance_prod.node.add_dependency(self.account_finance_qa) + self.account_policy_staging_tests.node.add_dependency(self.account_finance_prod) def generate_cloudformation_outputs(self): """ @@ -186,21 +354,21 @@ def generate_cloudformation_outputs(self): self, "OrganizationId", value=self.organization.organization_id, - description="ID of the Organization", + description="ID of AWS Organization", ) CfnOutput( self, "RootId", value=self.organization.root.identifier(), - description="ID of the Root OU", + description="ID of AWS Organization Root OU", ) CfnOutput( self, "ManagementAccountId", value=self.organization.management_account_id, - description="ID of the Management Account", + description="ID of Management Account", ) CfnOutput( @@ -214,12 +382,54 @@ def generate_cloudformation_outputs(self): self, "AccountSandbox1Id", value=self.account_sandbox_1.account_id, - description="ID of the SandboxAccount1", + description="ID of SandboxAccount1 Account", ) CfnOutput( self, "AccountSandbox2Id", value=self.account_sandbox_2.account_id, - description="ID of the SandboxAccount2", + description="ID of SandboxAccount2 Account", + ) + + CfnOutput( + self, + "AccountSharedServicesNonProdId", + value=self.account_shared_services_non_prod.account_id, + description="ID of AccountSharedServicesNonProd Account", + ) + + CfnOutput( + self, + "AccountSharedServicesProdId", + value=self.account_shared_services_prod.account_id, + description="ID of AccountSharedServicesProd Account", + ) + + CfnOutput( + self, + "AccountFinanceDevId", + value=self.account_finance_dev.account_id, + description="ID of AccountFinanceDev Account", + ) + + CfnOutput( + self, + "AccountFinanceQAId", + value=self.account_finance_qa.account_id, + description="ID of AccountFinanceQA Account", + ) + + CfnOutput( + self, + "AccountFinanceProdId", + value=self.account_finance_prod.account_id, + description="ID of AccountFinanceProd Account", + ) + + CfnOutput( + self, + "AccountPolicyStagingTestsId", + value=self.account_policy_staging_tests.account_id, + description="ID of AccountPolicyStagingTests Account", )