Skip to content

Commit

Permalink
feat: introduce-variable-org-stage (#636)
Browse files Browse the repository at this point in the history
* feat: introduce-variable-org-stage

* Add tests for main configuration parameters

* Update docs/admin-guide.md

* Fix linting issues

---------

Co-authored-by: Andreas Falkenberg <falkena@amazon.com>
Co-authored-by: Simon Kok <mail@simonkok.com>
Co-authored-by: Simon Kok <sbkok@users.noreply.github.com>
  • Loading branch information
4 people committed Aug 8, 2023
1 parent 0e6d022 commit 5de05bb
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 14 deletions.
11 changes: 11 additions & 0 deletions docs/admin-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,17 @@ Config has five components in `main-notification-endpoint`, `scp`, `scm`,
`master` instead. We recommend configuring the main scm branch name to
`main`. As new repositories will most likely use this branch name as their
default branch.
- `org` configures settings in case of staged multi-organization ADF deployments.
- `stage` defines the AWS Organization stage in case of staged multi-
organization ADF deployments. This is an optional setting. In enterprise-
grade deployments, it is a common practice to define an explicit dev, int
and prod AWS Organization with its own ADF instance per AWS organization.
This approach allows for well-tested and stable prod AWS Organization
deployments. If set, a matching SSM parameter `/adf/org/stage` gets
created that you can reference in your buildspec files to allow for
org-specific deployments; without hardcoding the AWS Organization stage in
your buildspec. If this variable is not set, the SSM parameter
`/adf/org/stage` defaults to "none".

## Accounts

Expand Down
3 changes: 3 additions & 0 deletions src/lambda_codebase/initial_commit/adfconfig.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ config:
scm:
auto-create-repositories: enabled
default-scm-branch: main
#org:
# Optional: Use this variable to define the AWS Organization in case of staged multi-organization ADF deployments
#stage: dev
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"ACCOUNT_BOOTSTRAPPING_STATE_MACHINE_ARN"
)
ADF_DEFAULT_SCM_FALLBACK_BRANCH = 'master'
ADF_DEFAULT_ORG_STAGE = "none"
LOGGER = configure_logger(__name__)


Expand Down Expand Up @@ -120,8 +121,8 @@ def prepare_deployment_account(sts, deployment_account_id, config):
f'{config.cross_account_access_role}',
'master'
)
for region in list(
set([config.deployment_account_region] + config.target_regions)):
for region in sorted(list(
set([config.deployment_account_region] + config.target_regions))):
deployment_account_parameter_store = ParameterStore(
region,
deployment_account_role
Expand Down Expand Up @@ -151,6 +152,13 @@ def prepare_deployment_account(sts, deployment_account_id, config):
ADF_DEFAULT_SCM_FALLBACK_BRANCH,
)
)
deployment_account_parameter_store.put_parameter(
'/adf/org/stage',
config.config.get('org', {}).get(
'stage',
ADF_DEFAULT_ORG_STAGE,
)
)
auto_create_repositories = config.config.get(
'scm', {}).get('auto-create-repositories')
if auto_create_repositories is not None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

from pytest import fixture
from parameter_store import ParameterStore
from mock import Mock, patch, call
from mock import MagicMock, Mock, patch, call
from main import (
Config,
ensure_generic_account_can_be_setup,
prepare_deployment_account,
update_deployment_account_output_parameters,
)

Expand All @@ -30,18 +31,20 @@ def cls():
@fixture
def sts():
sts = Mock()
sts.assume_cross_account_role.return_value = {
'Credentials': {
'AccessKeyId': 'string',
'SecretAccessKey': 'string',
'SessionToken': 'string',
'Expiration': 12345
},
'AssumedRoleUser': {
'AssumedRoleId': 'string',
'Arn': 'string'
}

role_mock = Mock()
role_mock.client = Mock()
role_mock.Credentials = {
'AccessKeyId': 'string',
'SecretAccessKey': 'string',
'SessionToken': 'string',
'Expiration': 12345
}
role_mock.AssumedRoleUser = {
'AssumedRoleId': 'string',
'Arn': 'string'
}
sts.assume_cross_account_role.return_value = role_mock
return sts


Expand Down Expand Up @@ -79,3 +82,171 @@ def test_update_deployment_account_output_parameters(cls, sts):
)
assert 4 == mock.call_count
mock.assert_has_calls(expected_calls, any_order=True)


@patch('main.ParameterStore')
def test_prepare_deployment_account_defaults(param_store_cls, cls, sts):
deploy_param_store = MagicMock()
parameter_stores = {
'eu-central-1': deploy_param_store,
'eu-west-1': MagicMock(),
'us-west-2': MagicMock(),
}
parameter_store_list = [
deploy_param_store,
parameter_stores['eu-west-1'],
parameter_stores['us-west-2'],
]
param_store_cls.side_effect = [
parameter_stores['eu-west-1'],
parameter_stores['us-west-2'],
deploy_param_store,
deploy_param_store,
]
deployment_account_id = "111122223333"
prepare_deployment_account(
sts=sts,
deployment_account_id=deployment_account_id,
config=cls,
)
assert param_store_cls.call_count == 4
param_store_cls.assert_has_calls(
[
call(
'eu-central-1',
sts.assume_cross_account_role.return_value,
),
call(
'eu-west-1',
sts.assume_cross_account_role.return_value,
),
call(
'us-west-2',
sts.assume_cross_account_role.return_value,
),
call(
'eu-central-1',
sts.assume_cross_account_role.return_value,
),
],
any_order=False,
)
for param_store in parameter_store_list:
assert param_store.put_parameter.call_count == (
11 if param_store == deploy_param_store else 2
)
param_store.put_parameter.assert_has_calls(
[
call('organization_id', 'o-123456789'),
call('/adf/extensions/terraform/enabled', 'False'),
],
any_order=False,
)
deploy_param_store.put_parameter.assert_has_calls(
[
call('adf_version', '1.0.0'),
call('adf_log_level', 'INFO'),
call('deployment_account_bucket', 'some_deployment_account_bucket'),
call('default_scm_branch', 'master'),
call('/adf/org/stage', 'none'),
call('cross_account_access_role', 'some_role'),
call('notification_type', 'email'),
call('notification_endpoint', 'john@example.com'),
call('/adf/extensions/terraform/enabled', 'False'),
],
)


@patch('main.ParameterStore')
def test_prepare_deployment_account_specific_config(param_store_cls, cls, sts):
deploy_param_store = MagicMock()
parameter_stores = {
'eu-central-1': deploy_param_store,
'eu-west-1': MagicMock(),
'us-west-2': MagicMock(),
}
parameter_store_list = [
deploy_param_store,
parameter_stores['eu-west-1'],
parameter_stores['us-west-2'],
]
param_store_cls.side_effect = [
parameter_stores['eu-west-1'],
parameter_stores['us-west-2'],
deploy_param_store,
deploy_param_store,
]
deployment_account_id = "111122223333"
# Set optional config
cls.notification_type = 'slack'
cls.notification_endpoint = 'slack-channel'
cls.notification_channel = 'slack-channel'
cls.config['scm'] = {
'auto-create-repositories': 'disabled',
'default-scm-branch': 'main',
}
cls.config['extensions'] = {
'terraform': {
'enabled': 'True',
},
}
cls.config['org'] = {
'stage': 'test-stage',
}
prepare_deployment_account(
sts=sts,
deployment_account_id=deployment_account_id,
config=cls,
)
assert param_store_cls.call_count == 4
param_store_cls.assert_has_calls(
[
call(
'eu-central-1',
sts.assume_cross_account_role.return_value,
),
call(
'eu-west-1',
sts.assume_cross_account_role.return_value,
),
call(
'us-west-2',
sts.assume_cross_account_role.return_value,
),
call(
'eu-central-1',
sts.assume_cross_account_role.return_value,
),
],
any_order=False,
)
for param_store in parameter_store_list:
assert param_store.put_parameter.call_count == (
13 if param_store == deploy_param_store else 2
)
param_store.put_parameter.assert_has_calls(
[
call('organization_id', 'o-123456789'),
call('/adf/extensions/terraform/enabled', 'False'),
],
any_order=False,
)
deploy_param_store.put_parameter.assert_has_calls(
[
call('adf_version', '1.0.0'),
call('adf_log_level', 'INFO'),
call('deployment_account_bucket', 'some_deployment_account_bucket'),
call('default_scm_branch', 'main'),
call('/adf/org/stage', 'test-stage'),
call('auto_create_repositories', 'disabled'),
call('cross_account_access_role', 'some_role'),
call('notification_type', 'slack'),
call(
'notification_endpoint',
"arn:aws:lambda:eu-central-1:"
f"{deployment_account_id}:function:SendSlackNotification",
),
call('/notification_endpoint/main', 'slack-channel'),
call('/adf/extensions/terraform/enabled', 'False'),
],
)

0 comments on commit 5de05bb

Please sign in to comment.