AmmarFahmy
adding all files
105b369
from typing import Optional, Any, List, Dict
from phi.aws.api_client import AwsApiClient
from phi.aws.resource.base import AwsResource
from phi.aws.resource.iam.policy import IamPolicy
from phi.cli.console import print_info
from phi.utils.log import logger
class IamRole(AwsResource):
"""
Reference:
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html#service-resource
"""
resource_type: Optional[str] = "IamRole"
service_name: str = "iam"
# RoleName: The name of the role to create.
name: str
# The trust relationship policy document that grants an entity permission to assume the role.
assume_role_policy_document: str
# The path to the role. This parameter is optional. If it is not included, it defaults to a slash (/).
path: Optional[str] = None
# A description of the role.
description: Optional[str] = None
# The maximum session duration (in seconds) that you want to set for the specified role.
# If you do not specify a value for this setting, the default maximum of one hour is applied.
# This setting can have a value from 1 hour to 12 hours.
max_session_duration: Optional[int] = None
# The ARN of the policy that is used to set the permissions boundary for the role.
permissions_boundary: Optional[str] = None
# A list of tags that you want to attach to the new role. Each tag consists of a key name and an associated value.
tags: Optional[List[Dict[str, str]]] = None
# List of IAM policies to
# attach to the role after it is created
policies: Optional[List[IamPolicy]] = None
# List of IAM policy ARNs (Amazon Resource Name) to
# attach to the role after it is created
policy_arns: Optional[List[str]] = None
# The Amazon Resource Name (ARN) specifying the role.
# To get the arn, use get_arn() function
arn: Optional[str] = None
def _create(self, aws_client: AwsApiClient) -> bool:
"""Creates the IamRole
Args:
aws_client: The AwsApiClient for the current cluster
"""
print_info(f"Creating {self.get_resource_type()}: {self.get_resource_name()}")
try:
# create a dict of args which are not null, otherwise aws type validation fails
not_null_args: Dict[str, Any] = {}
if self.path:
not_null_args["Path"] = self.path
if self.description:
not_null_args["Description"] = self.description
if self.max_session_duration:
not_null_args["MaxSessionDuration"] = self.max_session_duration
if self.permissions_boundary:
not_null_args["PermissionsBoundary"] = self.permissions_boundary
if self.tags:
not_null_args["Tags"] = self.tags
# Create Role
service_resource = self.get_service_resource(aws_client)
role = service_resource.create_role(
RoleName=self.name,
AssumeRolePolicyDocument=self.assume_role_policy_document,
**not_null_args,
)
# logger.debug(f"Role: {role}")
# Validate Role creation
create_date = role.create_date
self.arn = role.arn
logger.debug(f"create_date: {create_date}")
logger.debug(f"arn: {self.arn}")
if create_date is not None:
print_info(f"Role created: {self.name}")
self.active_resource = role
return True
logger.error("Role could not be created")
except Exception as e:
logger.error(f"{self.get_resource_type()} could not be created.")
logger.error(e)
return False
def post_create(self, aws_client: AwsApiClient) -> bool:
# Wait for Role to be created
if self.wait_for_create:
try:
print_info(f"Waiting for {self.get_resource_type()} to be created.")
waiter = self.get_service_client(aws_client).get_waiter("role_exists")
waiter.wait(
RoleName=self.name,
WaiterConfig={
"Delay": self.waiter_delay,
"MaxAttempts": self.waiter_max_attempts,
},
)
except Exception as e:
logger.error("Waiter failed.")
logger.error(e)
# Attach policy arns to role
attach_policy_success = True
if self.active_resource is not None and self.policy_arns is not None:
_success = self.attach_policy_arns(aws_client)
if not _success:
attach_policy_success = False
# Attach policies to role
if self.active_resource is not None and self.policies is not None:
_success = self.attach_policies(aws_client)
if not _success:
attach_policy_success = False
# logger.info(f"attach_policy_success: {attach_policy_success}")
return attach_policy_success
def _read(self, aws_client: AwsApiClient) -> Optional[Any]:
"""Returns the IamRole
Args:
aws_client: The AwsApiClient for the current cluster
"""
from botocore.exceptions import ClientError
logger.debug(f"Reading {self.get_resource_type()}: {self.get_resource_name()}")
try:
service_resource = self.get_service_resource(aws_client)
role = service_resource.Role(name=self.name)
role.load()
create_date = role.create_date
self.arn = role.arn
logger.debug(f"create_date: {create_date}")
logger.debug(f"arn: {self.arn}")
if create_date is not None:
logger.debug(f"Role found: {role.role_name}")
self.active_resource = role
except ClientError as ce:
logger.debug(f"ClientError: {ce}")
except Exception as e:
logger.error(f"Error reading {self.get_resource_type()}.")
logger.error(e)
return self.active_resource
def _delete(self, aws_client: AwsApiClient) -> bool:
"""Deletes the IamRole
Args:
aws_client: The AwsApiClient for the current cluster
"""
print_info(f"Deleting {self.get_resource_type()}: {self.get_resource_name()}")
try:
role = self._read(aws_client)
# logger.debug(f"Role: {role}")
# logger.debug(f"Role type: {type(role)}")
self.active_resource = None
if role is None:
logger.warning(f"No {self.get_resource_type()} to delete")
return True
# detach all policies
policies = role.attached_policies.all()
for policy in policies:
print_info(f"Detaching policy: {policy}")
role.detach_policy(PolicyArn=policy.arn)
# detach all instance profiles
profiles = role.instance_profiles.all()
for profile in profiles:
print_info(f"Removing role from profile: {profile}")
profile.remove_role(RoleName=role.name)
# delete role
role.delete()
return True
except Exception as e:
logger.error(f"{self.get_resource_type()} could not be deleted.")
logger.error("Please try again or delete resources manually.")
logger.error(e)
return False
def attach_policy_arns(self, aws_client: AwsApiClient) -> bool:
"""
Attaches the specified managed policy to the specified IAM role.
When you attach a managed policy to a role, the managed policy becomes part of the
role's permission (access) policy.
Returns:
True if operation was successful
"""
if self.policy_arns is None:
return True
role = self._read(aws_client)
if role is None:
logger.warning(f"No {self.get_resource_type()} to attach")
return True
try:
# logger.debug("Attaching managed policies to role")
for arn in self.policy_arns:
if isinstance(arn, str):
role.attach_policy(PolicyArn=arn)
print_info(f"Attaching policy to {role.role_name}: {arn}")
return True
except Exception as e:
logger.error(e)
return False
def attach_policies(self, aws_client: AwsApiClient) -> bool:
"""
Returns:
True if operation was successful
"""
if self.policies is None:
return True
role = self._read(aws_client)
if role is None:
logger.warning(f"No {self.get_resource_type()} to attach")
return True
try:
logger.debug("Attaching managed policies to role")
for policy in self.policies:
if policy.arn is None:
create_success = policy.create(aws_client)
if not create_success:
return False
if policy.arn is not None:
role.attach_policy(PolicyArn=policy.arn)
print_info(f"Attaching policy to {role.role_name}: {policy.arn}")
return True
except Exception as e:
logger.error(e)
return False
def get_arn(self, aws_client: AwsApiClient) -> Optional[str]:
role = self._read(aws_client)
if role is None:
return None
self.arn = role.arn
return self.arn