Skip to content

Commit ae72492

Browse files
committed
Various fixes and update to Python 3.10
1 parent 6d6febc commit ae72492

File tree

19 files changed

+158
-106
lines changed

19 files changed

+158
-106
lines changed

dependencies/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
aws-lambda-powertools[tracer,validation,aws-sdk]==2.11.0
1+
aws-lambda-powertools[tracer,validation,aws-sdk]==2.14.1

mypy.ini

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[mypy]
2+
warn_return_any=False
3+
warn_unused_configs=True
4+
no_implicit_optional=True
5+
warn_redundant_casts=True
6+
warn_unused_ignores=True
7+
show_column_numbers = True
8+
show_error_codes = True
9+
show_error_context = True
10+
disable_error_code = annotation-unchecked
11+
12+
[mypy-boto3]
13+
ignore_missing_imports = True
14+
15+
[mypy-botocore]
16+
ignore_missing_imports = True
17+
18+
[mypy-botocore.response]
19+
ignore_missing_imports = True
20+
21+
[mypy-botocore.config]
22+
ignore_missing_imports = True
23+
24+
[mypy-botocore.compat]
25+
ignore_missing_imports = True
26+
27+
[mypy-botocore.exceptions]
28+
ignore_missing_imports = True

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.black]
2-
line-length = 88
3-
target-version = ['py39']
2+
line-length = 120
3+
target-version = ['py310']
44
include = '\.pyi?$'
55
extend-exclude = '''
66
(

requirements-dev.txt

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
black==23.3.0
22
wheel==0.40.0
3-
pre-commit==3.2.1
3+
pre-commit==3.2.2
4+
mypy-boto3-ec2==1.26.106
5+
mypy-boto3-ecs==1.26.116
6+
mypy-boto3-iam==1.26.97
7+
mypy-boto3-identitystore==1.26.107
8+
mypy-boto3-organizations==1.26.83
9+
mypy-boto3-sts==1.26.57
10+
mypy-boto3-servicecatalog==1.26.109
11+
mypy-boto3-sso-admin==1.26.63

src/regional/account_setup/lambda_handler.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,24 @@
3737
@tracer.capture_lambda_handler
3838
@logger.inject_lambda_context(log_event=True)
3939
def handler(event: Dict[str, Any], context: LambdaContext) -> None:
40-
4140
account_id = event["account_id"]
4241
region_name = event["region"]
4342

4443
logger.append_keys(account_id=account_id, region=region_name)
44+
tracer.put_annotation("AccountId", account_id)
45+
tracer.put_annotation("Region", region_name)
4546

4647
session = boto3.Session()
4748

4849
assumed_session = STS(session).assume_role(account_id)
4950

50-
logger.info(f"Deleting default VPC from {region_name} in {account_id}")
5151
ec2 = EC2(assumed_session, region_name)
5252
default_vpc_id = ec2.get_default_vpc_id()
5353
if default_vpc_id:
54+
logger.info(f"Deleting default VPC {default_vpc_id} from {region_name} in {account_id}")
5455
ec2.delete_vpc(default_vpc_id)
56+
else:
57+
logger.debug(f"No default VPC found in {region_name} in {account_id}")
5558

5659
logger.info(f"Setting default ECS settings in {region_name} in {account_id}")
5760
ecs = ECS(assumed_session, region_name)

src/regional/account_setup/resources/ec2.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@
1919
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020
"""
2121

22-
from typing import Optional
22+
from typing import Optional, TYPE_CHECKING
2323

2424
from aws_lambda_powertools import Logger
2525
import boto3
2626

27+
if TYPE_CHECKING:
28+
from mypy_boto3_ec2 import EC2Client, EC2ServiceResource
29+
2730
logger = Logger(child=True)
2831

2932
__all__ = ["EC2"]
3033

3134

3235
class EC2:
3336
def __init__(self, session: boto3.Session, region: str) -> None:
34-
self.client = session.client("ec2", region_name=region)
37+
self.client: EC2Client = session.client("ec2", region_name=region)
3538
self.session = session
3639
self.region_name = region
3740

@@ -43,11 +46,11 @@ def get_default_vpc_id(self) -> Optional[str]:
4346
if vpc["IsDefault"]:
4447
return vpc["VpcId"]
4548

46-
logger.debug("No default VPC found")
49+
logger.debug(f"No default VPC found in {self.region_name}", region=self.region_name)
4750
return None
4851

4952
def delete_vpc(self, vpc_id: str) -> None:
50-
ec2 = self.session.resource("ec2", region_name=self.region_name)
53+
ec2: EC2ServiceResource = self.session.resource("ec2", region_name=self.region_name)
5154
vpc = ec2.Vpc(vpc_id)
5255

5356
# detach and delete all gateways associated with the vpc
@@ -78,12 +81,16 @@ def delete_vpc(self, vpc_id: str) -> None:
7881
nacl.delete()
7982

8083
# DHCP Options
81-
if vpc.dhcp_options:
82-
vpc.associate_dhcp_options(
83-
DhcpOptionsId="default"
84-
) # associate no DHCP options
85-
vpc.dhcp_options.delete()
84+
if vpc.dhcp_options and vpc.dhcp_options_id != "default":
85+
dhcp_options_id = vpc.dhcp_options_id
86+
87+
vpc.associate_dhcp_options(DhcpOptionsId="default") # associate no DHCP options
88+
89+
dhcp_options = ec2.DhcpOptions(dhcp_options_id)
90+
dhcp_options.delete()
8691

8792
# Delete VPC
8893
self.client.delete_vpc(VpcId=vpc_id)
89-
logger.info(f"VPC {vpc_id} and associated resources has been deleted.")
94+
logger.info(
95+
f"VPC {vpc_id} and associated resources has been deleted in {self.region_name}.", region=self.region_name
96+
)

src/regional/account_setup/resources/ecs.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,24 @@
1919
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020
"""
2121

22+
from typing import TYPE_CHECKING
23+
24+
from aws_lambda_powertools import Logger
2225
import boto3
26+
import botocore
27+
28+
if TYPE_CHECKING:
29+
from mypy_boto3_ecs import ECSClient
30+
31+
logger = Logger(child=True)
2332

2433
__all__ = ["ECS"]
2534

2635

2736
class ECS:
2837
def __init__(self, session: boto3.Session, region: str) -> None:
29-
self.client = session.client("ecs", region_name=region)
38+
self.client: ECSClient = session.client("ecs", region_name=region)
39+
self.region = region
3040

3141
def put_account_setting_default(self) -> None:
3242
names = [
@@ -35,7 +45,16 @@ def put_account_setting_default(self) -> None:
3545
"containerInstanceLongArnFormat",
3646
"awsvpcTrunking",
3747
"containerInsights",
38-
# "dualStackIPv6", # not yet supported
48+
"dualStackIPv6",
3949
]
4050
for name in names:
41-
self.client.put_account_setting_default(name=name, value="enabled")
51+
try:
52+
self.client.put_account_setting_default(name=name, value="enabled")
53+
except botocore.exceptions.ClientError:
54+
logger.exception(f"Unable to enable ECS setting {name} in {self.region}")
55+
56+
# documentation on https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#tag-resources-setting is incorrect
57+
try:
58+
self.client.put_account_setting_default(name="tagResourceAuthorization", value="on")
59+
except botocore.exceptions.ClientError:
60+
logger.exception(f"Unable to enable ECS setting tagResourceAuthorization in {self.region}")

src/regional/account_setup/resources/sts.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020
"""
2121

2222
import os
23+
from typing import TYPE_CHECKING
2324

2425
from aws_lambda_powertools import Logger
2526
import boto3
2627

28+
if TYPE_CHECKING:
29+
from mypy_boto3_sts import STSClient
30+
2731
logger = Logger(child=True)
2832
EXECUTION_ROLE_NAME = os.environ["EXECUTION_ROLE_NAME"]
2933
AWS_PARTITION = os.environ["AWS_PARTITION"]
@@ -33,11 +37,9 @@
3337

3438
class STS:
3539
def __init__(self, session: boto3.Session) -> None:
36-
self.client = session.client("sts")
40+
self.client: STSClient = session.client("sts")
3741

38-
def assume_role(
39-
self, account_id: str, role_session_name: str = "AccountSetup"
40-
) -> boto3.Session:
42+
def assume_role(self, account_id: str, role_session_name: str = "AccountSetup") -> boto3.Session:
4143
"""
4244
Assume the AWSControlTowerExecution role in an account
4345
"""

src/service_catalog_portfolio/account_setup/lambda_handler.py

+4-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"""
2121

2222
import os
23-
from typing import Dict, Any, List
23+
from typing import Dict, Any, List, Optional
2424

2525
from aws_lambda_powertools import Logger, Tracer
2626
from aws_lambda_powertools.utilities.typing import LambdaContext
@@ -46,8 +46,7 @@ def get_env_list(key: str) -> List[str]:
4646
@tracer.capture_lambda_handler
4747
@logger.inject_lambda_context(log_event=True)
4848
def handler(event: Dict[str, Any], context: LambdaContext) -> None:
49-
50-
account_id = event.get("account", {}).get("accountId")
49+
account_id: Optional[str] = event.get("account", {}).get("accountId")
5150
if not account_id:
5251
raise Exception("Account ID not found in event")
5352

@@ -73,11 +72,7 @@ def handler(event: Dict[str, Any], context: LambdaContext) -> None:
7372
to_remove = existing_principals - role_arns
7473

7574
for role_arn in to_add:
76-
servicecatalog.associate_principal_with_portfolio(
77-
portfolio_id=portfolio_id, principal_arn=role_arn
78-
)
75+
servicecatalog.associate_principal_with_portfolio(portfolio_id=portfolio_id, principal_arn=role_arn)
7976

8077
for role_arn in to_remove:
81-
servicecatalog.disassociate_principal_from_portfolio(
82-
portfolio_id=portfolio_id, principal_arn=role_arn
83-
)
78+
servicecatalog.disassociate_principal_from_portfolio(portfolio_id=portfolio_id, principal_arn=role_arn)

src/service_catalog_portfolio/account_setup/resources/iam.py

+10-9
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,24 @@
2020
"""
2121

2222
from functools import lru_cache
23-
from typing import Optional, Dict
23+
from typing import Optional, Dict, TYPE_CHECKING
2424

2525
import boto3
2626

27+
if TYPE_CHECKING:
28+
from mypy_boto3_iam import IAMClient
29+
2730
__all__ = ["IAM"]
2831

2932
AWS_SSO_ROLE_PREFIX = "AWSReservedSSO_"
3033

3134

3235
class IAM:
33-
def __init__(self, session: boto3.Session) -> None:
34-
self.client = session.client("iam")
35-
self._roles = {}
36+
def __init__(self, session: Optional[boto3.Session] = None) -> None:
37+
if not session:
38+
session = boto3._get_default_session()
39+
self.client: IAMClient = session.client("iam")
40+
self._roles: Dict[str, str] = {}
3641

3742
def get_sso_roles(self) -> Dict[str, str]:
3843
"""
@@ -48,11 +53,7 @@ def get_sso_roles(self) -> Dict[str, str]:
4853
for role in page.get("Roles", []):
4954
if role["RoleName"].startswith(AWS_SSO_ROLE_PREFIX):
5055
# AWSReservedSSO_AWSAdministratorAccess_a1ff75f56dfb0e2f -> AWSAdministratorAccess
51-
permission_set_name = (
52-
role["RoleName"]
53-
.rsplit("_", 1)[0]
54-
.replace(AWS_SSO_ROLE_PREFIX, "")
55-
)
56+
permission_set_name = role["RoleName"].rsplit("_", 1)[0].replace(AWS_SSO_ROLE_PREFIX, "")
5657
roles[permission_set_name] = role["Arn"]
5758

5859
self._roles = roles

src/service_catalog_portfolio/account_setup/resources/servicecatalog.py

+11-16
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,34 @@
1919
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020
"""
2121

22-
from typing import Set
22+
from typing import Set, TYPE_CHECKING, Optional
2323

2424
from aws_lambda_powertools import Logger
2525
import boto3
2626
import botocore
2727

28+
if TYPE_CHECKING:
29+
from mypy_boto3_servicecatalog import ServiceCatalogClient, ListPrincipalsForPortfolioPaginator
30+
2831
logger = Logger(child=True)
2932

3033
__all__ = ["ServiceCatalog"]
3134

3235

3336
class ServiceCatalog:
34-
def __init__(self, session: boto3.Session) -> None:
37+
def __init__(self, session: Optional[boto3.Session] = None) -> None:
3538
if not session:
3639
session = boto3._get_default_session()
37-
self.client = session.client("servicecatalog")
40+
self.client: ServiceCatalogClient = session.client("servicecatalog")
3841

3942
def accept_portfolio_share(self, portfolio_id: str) -> None:
4043
try:
41-
self.client.accept_portfolio_share(
42-
PortfolioId=portfolio_id, PortfolioShareType="AWS_ORGANIZATIONS"
43-
)
44+
self.client.accept_portfolio_share(PortfolioId=portfolio_id, PortfolioShareType="AWS_ORGANIZATIONS")
4445
except botocore.exceptions.ClientError:
4546
logger.exception("Unable to accept portfolio share")
4647
raise
4748

48-
def associate_principal_with_portfolio(
49-
self, portfolio_id: str, principal_arn: str
50-
) -> None:
49+
def associate_principal_with_portfolio(self, portfolio_id: str, principal_arn: str) -> None:
5150
try:
5251
self.client.associate_principal_with_portfolio(
5352
PortfolioId=portfolio_id,
@@ -58,13 +57,9 @@ def associate_principal_with_portfolio(
5857
logger.exception("Unable to associate princpal with portfolio")
5958
raise
6059

61-
def disassociate_principal_from_portfolio(
62-
self, portfolio_id: str, principal_arn: str
63-
) -> None:
60+
def disassociate_principal_from_portfolio(self, portfolio_id: str, principal_arn: str) -> None:
6461
try:
65-
self.client.disassociate_principal_from_portfolio(
66-
PortfolioId=portfolio_id, PrincipalARN=principal_arn
67-
)
62+
self.client.disassociate_principal_from_portfolio(PortfolioId=portfolio_id, PrincipalARN=principal_arn)
6863
except botocore.exceptions.ClientError as error:
6964
if error.response["Error"]["Code"] != "ResourceNotFoundException":
7065
logger.exception("Unable to disassociate princpal from portfolio")
@@ -73,7 +68,7 @@ def disassociate_principal_from_portfolio(
7368
def list_principals_for_portfolio(self, portfolio_id: str) -> Set[str]:
7469
principals = set()
7570

76-
paginator = self.client.get_paginator("list_principals_for_portfolio")
71+
paginator: ListPrincipalsForPortfolioPaginator = self.client.get_paginator("list_principals_for_portfolio")
7772
page_iterator = paginator.paginate(PortfolioId=portfolio_id)
7873
for page in page_iterator:
7974
for principal in page.get("Principals", []):

src/service_catalog_portfolio/account_setup/resources/sts.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,24 @@
2020
"""
2121

2222
import os
23+
from typing import TYPE_CHECKING, Optional
2324

2425
import boto3
2526

27+
if TYPE_CHECKING:
28+
from mypy_boto3_sts import STSClient
29+
2630
EXECUTION_ROLE_NAME = os.environ["EXECUTION_ROLE_NAME"]
2731
AWS_PARTITION = os.environ["AWS_PARTITION"]
2832

2933
__all__ = ["STS"]
3034

3135

3236
class STS:
33-
def __init__(self, session: boto3.Session = None) -> None:
37+
def __init__(self, session: Optional[boto3.Session] = None) -> None:
3438
if not session:
3539
session = boto3._get_default_session()
36-
self.client = session.client("sts")
40+
self.client: STSClient = session.client("sts")
3741

3842
def assume_role(self, account_id: str, role_session_name: str) -> boto3.Session:
3943
"""

src/sso_assignment/account_setup/constants.py

-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020
"""
2121

22-
__all__ = ["GROUP_ACCOUNT_PREFIX", "GROUP_ORG_PREFIX"]
23-
2422
GROUP_ACCOUNT_PREFIX = "AWS-A-"
2523

2624
GROUP_ORG_PREFIX = "AWS-O-"

0 commit comments

Comments
 (0)