Skip to content

Commit

Permalink
Merge pull request #380 from noqdev/bug/en-2097-fix-read-only
Browse files Browse the repository at this point in the history
BUG-2097 fix read only
  • Loading branch information
Will-NOQ authored May 3, 2023
2 parents eb9a418 + bb244c2 commit 1049788
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 130 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@

# Change Log

## 0.6.1 (Target Date May 3rd, 2023)

ENHANCEMENTS:

* Additional clarity in the wizard as it relates to AWS cloudformation changes.
* Added the ability to check the IAMbic version from the CLI.

BUG FIXES:

* AWS read only spoke role is now working as designed
* Fixes to text being truncated in the wizard on smaller terminal windows.

THANKS:

* [rjulian](https://github.com/rjulian) for reporting [#377](https://github.com/noqdev/iambic/pull/377).

## 0.5.1 (Target Date May 2nd, 2023)

PERMISSION CHANGES:
Expand Down
230 changes: 131 additions & 99 deletions iambic/config/wizard.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Parameters:
HubRoleArn:
Type: String
Resources:
IambicSpokeRoleReadOnly:
IambicSpokeRole:
Type: 'AWS::IAM::Role'
Properties:
RoleName: !Ref SpokeRoleName
Expand Down
29 changes: 12 additions & 17 deletions iambic/plugins/v0_1_0/aws/cloud_formation/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def get_iambic_hub_role_template_body() -> str:


def get_iambic_spoke_role_template_body(read_only=False) -> str:
postfix = "ReadOnly" if read_only else ""
template = f"{TEMPLATE_DIR}/IambicSpokeRole{postfix}.yml"
suffix = "ReadOnly" if read_only else ""
template = f"{TEMPLATE_DIR}/IambicSpokeRole{suffix}.yml"
with open(template, "r") as f:
return f.read()

Expand Down Expand Up @@ -306,9 +306,9 @@ async def create_iambic_eventbridge_stacks(
)
if successfully_created:
log.info(
f"Creating stack instances. "
f"You can check the progress here: https://{region}.console.aws.amazon.com/cloudformation/home?region={region}#/stacksets/IAMbicForwardEventRule/stacks\n"
f"WARNING: Don't Exit"
f"WARNING: Do not exit; creating stack instances.\n"
f"You can check the progress here:\n"
f"https://{region}.console.aws.amazon.com/cloudformation/home?region={region}#/stacksets/IAMbicForwardEventRule/stacks\n"
)
return await create_change_detection_stack_sets(
cf_client, org_client, account_id
Expand Down Expand Up @@ -360,10 +360,9 @@ async def create_spoke_role_stack(
read_only=False,
) -> bool:
additional_kwargs = {"RoleARN": role_arn} if role_arn else {}
spoke_role_postfix = "ReadOnly" if read_only else ""
return await create_stack(
cf_client,
stack_name=f"IambicSpokeRole{spoke_role_postfix}",
stack_name="IambicSpokeRole",
template_body=get_iambic_spoke_role_template_body(read_only=read_only),
parameters=[
{
Expand All @@ -372,7 +371,7 @@ async def create_spoke_role_stack(
},
{
"ParameterKey": "SpokeRoleName",
"ParameterValue": f"{IAMBIC_SPOKE_ROLE_NAME}{spoke_role_postfix}",
"ParameterValue": IAMBIC_SPOKE_ROLE_NAME,
},
],
Capabilities=["CAPABILITY_NAMED_IAM"],
Expand All @@ -385,10 +384,8 @@ async def create_hub_role_stack(
hub_account_id: str,
assume_as_arn: str,
role_arn: str = None,
spoke_role_read_only=False,
) -> bool:
additional_kwargs = {"RoleARN": role_arn} if role_arn else {}
spoke_role_postfix = "ReadOnly" if spoke_role_read_only else ""
stack_created = await create_stack(
cf_client,
stack_name="IambicHubRole",
Expand All @@ -397,15 +394,15 @@ async def create_hub_role_stack(
{"ParameterKey": "HubRoleName", "ParameterValue": IAMBIC_HUB_ROLE_NAME},
{
"ParameterKey": "SpokeRoleName",
"ParameterValue": f"{IAMBIC_SPOKE_ROLE_NAME}{spoke_role_postfix}",
"ParameterValue": IAMBIC_SPOKE_ROLE_NAME,
},
{"ParameterKey": "AssumeAsArn", "ParameterValue": assume_as_arn},
],
Capabilities=["CAPABILITY_NAMED_IAM"],
**additional_kwargs,
)
if stack_created:
return await create_spoke_role_stack(cf_client, hub_account_id, role_arn, spoke_role_read_only)
return await create_spoke_role_stack(cf_client, hub_account_id, role_arn)

return stack_created

Expand All @@ -423,15 +420,13 @@ async def create_iambic_role_stacks(
hub_account_id,
assume_as_arn,
role_arn,
spoke_role_read_only=read_only,
)
spoke_role_postfix = "ReadOnly" if read_only else ""
region = cf_client.meta.region_name
if hub_role_created and org_client:
log.info(
f"Creating stack instances. "
f"You can check the progress here: https://{region}.console.aws.amazon.com/cloudformation/home?region={region}#/stacksets/IambicSpokeRole{spoke_role_postfix}/stacks\n"
f"WARNING: Don't Exit"
f"WARNING: Do not exit; creating stack instances.\n"
f"You can check the progress here:\n"
f"https://{region}.console.aws.amazon.com/cloudformation/home?region={region}#/stacksets/IambicSpokeRole/stacks\n"
)
return await create_spoke_role_stack_set(
cf_client, org_client, hub_account_id, read_only=read_only
Expand Down
4 changes: 4 additions & 0 deletions iambic/plugins/v0_1_0/aws/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ async def apply_iam_templates(
:param templates: The list of templates to apply.
:param remote_worker: The remote worker to use for applying templates.
"""
if config.spoke_role_is_read_only:
log.critical("Unable to apply resources when spoke_role_is_read_only is True")
return []

await generate_permission_set_map(config.accounts, templates)

template_changes: list[TemplateChangeDetails] = []
Expand Down
13 changes: 4 additions & 9 deletions iambic/plugins/v0_1_0/aws/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,8 @@ def get_hub_role_arn(account_id: str) -> str:
return f"arn:aws:iam::{account_id}:role/{IAMBIC_HUB_ROLE_NAME}"


def get_spoke_role_arn(account_id: str, read_only=False) -> str:
spoke_role_postfix = "ReadOnly" if read_only else ""
return (
f"arn:aws:iam::{account_id}:role/{IAMBIC_SPOKE_ROLE_NAME}{spoke_role_postfix}"
)
def get_spoke_role_arn(account_id: str) -> str:
return f"arn:aws:iam::{account_id}:role/{IAMBIC_SPOKE_ROLE_NAME}"


@yaml_object(yaml)
Expand Down Expand Up @@ -589,7 +586,7 @@ class AWSOrganization(BaseAWSAccountAndOrgModel):
)
spoke_role_is_read_only: bool = Field(
False,
description="if true, the spoke role name is IambicSpokeRoleReadOnly",
description="if true, the spoke role will be limited to read-only permissions",
)

async def _create_org_account_instance(
Expand Down Expand Up @@ -625,9 +622,7 @@ async def _create_org_account_instance(
variables=account["variables"],
identity_center_details=identity_center_details,
iambic_managed=account_rule.iambic_managed,
spoke_role_arn=get_spoke_role_arn(
account_id, read_only=self.spoke_role_is_read_only
),
spoke_role_arn=get_spoke_role_arn(account_id),
hub_session_info=dict(boto3_session=session),
default_region=region_name,
boto3_session_map={},
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "iambic-core"
packages = [
{ include="iambic", from="." },
]
version = "0.5.4"
version = "0.6.0"
license = "Apache-2.0"
description = "The python package used to generate, parse, and execute noqform yaml templates."
authors = ["Noq Software <hello@noq.dev>"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,29 @@ def test_description_validation_with_empty_string():


def test_permission_boundary_with_customer_managed_policy_ref_with_default_path():
properties = PermissionSetProperties(name="foo", description="A", permissions_boundary={"customer_managed_policy_reference": {"name": "foo"}})
properties = PermissionSetProperties(
name="foo",
description="A",
permissions_boundary={"customer_managed_policy_reference": {"name": "foo"}},
)
assert properties.permissions_boundary.customer_managed_policy_reference.path == "/"


def test_permission_boundary_with_customer_managed_policy_ref_with_custom_path():
properties = PermissionSetProperties(name="foo", description="A", permissions_boundary={"customer_managed_policy_reference": {"name": "foo", "path": "/engineering/"}})
assert properties.permissions_boundary.customer_managed_policy_reference.path == "/engineering/"
properties = PermissionSetProperties(
name="foo",
description="A",
permissions_boundary={
"customer_managed_policy_reference": {
"name": "foo",
"path": "/engineering/",
}
},
)
assert (
properties.permissions_boundary.customer_managed_policy_reference.path
== "/engineering/"
)


def test_description_validation_with_valid_string():
Expand Down

0 comments on commit 1049788

Please sign in to comment.