diff --git a/moto/organizations/models.py b/moto/organizations/models.py index e25342f04..93408a353 100644 --- a/moto/organizations/models.py +++ b/moto/organizations/models.py @@ -95,6 +95,19 @@ class FakeAccount(BaseModel): } } + @property + def close_account_status(self): + return { + "CloseAccountStatus": { + "Id": self.create_account_status_id, + "AccountName": self.name, + "State": "SUCCEEDED", + "RequestedTimestamp": unix_time(datetime.datetime.utcnow()), + "CompletedTimestamp": unix_time(datetime.datetime.utcnow()), + "AccountId": self.id, + } + } + def describe(self): return { "Id": self.id, @@ -445,6 +458,17 @@ class OrganizationsBackend(BaseBackend): self.attach_policy(PolicyId=utils.DEFAULT_POLICY_ID, TargetId=new_account.id) return new_account.create_account_status + def close_account(self, **kwargs): + for account_index in range(len(self.accounts)): + if kwargs["AccountId"] == self.accounts[account_index].id: + closed_account_status = self.accounts[ + account_index + ].close_account_status + del self.accounts[account_index] + return closed_account_status + + raise AccountNotFoundException + def get_account_by_id(self, account_id): account = next( (account for account in self.accounts if account.id == account_id), None diff --git a/moto/organizations/responses.py b/moto/organizations/responses.py index 5d0bdc482..c731a321a 100644 --- a/moto/organizations/responses.py +++ b/moto/organizations/responses.py @@ -67,6 +67,11 @@ class OrganizationsResponse(BaseResponse): self.organizations_backend.create_account(**self.request_params) ) + def close_account(self): + return json.dumps( + self.organizations_backend.close_account(**self.request_params) + ) + def describe_account(self): return json.dumps( self.organizations_backend.describe_account(**self.request_params) diff --git a/tests/test_organizations/organizations_test_utils.py b/tests/test_organizations/organizations_test_utils.py index 15bee591b..1a3caae22 100644 --- a/tests/test_organizations/organizations_test_utils.py +++ b/tests/test_organizations/organizations_test_utils.py @@ -135,3 +135,17 @@ def validate_service_control_policy(org, response): response.should.have.key("PolicySummary").should.be.a(dict) response.should.have.key("Content").should.be.a(str) validate_policy_summary(org, response["PolicySummary"]) + + +def validate_account_created(accounts_list, account_id): + account_created = False + for account in accounts_list: + if account_id == account["Id"]: + account_created = True + assert account_created + + +def validate_account_closed(accounts_list, account_id): + for account in accounts_list: + if account_id == account["Id"]: + assert False diff --git a/tests/test_organizations/test_organizations_boto3.py b/tests/test_organizations/test_organizations_boto3.py index a5bd14b80..52f9c8063 100644 --- a/tests/test_organizations/test_organizations_boto3.py +++ b/tests/test_organizations/test_organizations_boto3.py @@ -27,6 +27,8 @@ from .organizations_test_utils import ( validate_create_account_status, validate_service_control_policy, validate_policy_summary, + validate_account_created, + validate_account_closed, ) @@ -173,6 +175,46 @@ def test_create_account(): create_status["AccountName"].should.equal(mockname) +@mock_organizations +def test_close_account(): + client = boto3.client("organizations", region_name="us-east-1") + client.create_organization(FeatureSet="ALL") + create_status = client.create_account(AccountName=mockname, Email=mockemail)[ + "CreateAccountStatus" + ] + created_account_id = create_status["AccountId"] + accounts_list_before = client.list_accounts()["Accounts"] + validate_account_created( + accounts_list=accounts_list_before, + account_id=created_account_id, + ) + + client.close_account(AccountId=created_account_id) + + accounts_list_after = client.list_accounts()["Accounts"] + validate_account_closed(accounts_list_after, created_account_id) + number_accounts_before = len(accounts_list_before) + number_accounts_after = len(accounts_list_after) + (number_accounts_before - number_accounts_after).should.equal(1) + + +@mock_organizations +def test_close_account_exception(): + client = boto3.client("organizations", region_name="us-east-1") + client.create_organization(FeatureSet="ALL") + uncreated_fake_account_id = "123456789101" + + with pytest.raises(ClientError) as e: + client.close_account(AccountId=uncreated_fake_account_id) + ex = e.value + ex.operation_name.should.equal("CloseAccount") + ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + ex.response["Error"]["Code"].should.contain("AccountNotFoundException") + ex.response["Error"]["Message"].should.equal( + "You specified an account that doesn't exist." + ) + + @mock_organizations def test_describe_create_account_status(): client = boto3.client("organizations", region_name="us-east-1")