Spaces:
Runtime error
Runtime error
from typing import Optional, Any, List, Dict | |
from phi.aws.api_client import AwsApiClient | |
from phi.aws.resource.base import AwsResource | |
from phi.cli.console import print_info | |
from phi.utils.log import logger | |
class IamPolicy(AwsResource): | |
""" | |
Reference: | |
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html#policy | |
""" | |
resource_type: Optional[str] = "IamPolicy" | |
service_name: str = "iam" | |
# PolicyName | |
# The friendly name of the policy. | |
name: str | |
# The JSON policy document that you want to use as the content for the new policy. | |
# You must provide policies in JSON format in IAM. | |
# However, for CloudFormation templates formatted in YAML, you can provide the policy in JSON or YAML format. | |
# CloudFormation always converts a YAML policy to JSON format before submitting it to IAM. | |
policy_document: str | |
# The path for the policy. This parameter is optional. If it is not included, it defaults to a slash (/). | |
path: Optional[str] = None | |
# A friendly description of the policy. | |
description: Optional[str] = None | |
# A list of tags that you want to attach to the new policy. Each tag consists of a key name and an associated value. | |
tags: Optional[List[Dict[str, str]]] = None | |
arn: Optional[str] = None | |
def _create(self, aws_client: AwsApiClient) -> bool: | |
"""Creates the IamPolicy | |
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.tags: | |
not_null_args["Tags"] = self.tags | |
# Create Policy | |
service_resource = self.get_service_resource(aws_client) | |
policy = service_resource.create_policy( | |
PolicyName=self.name, | |
PolicyDocument=self.policy_document, | |
**not_null_args, | |
) | |
# logger.debug(f"Policy: {policy}") | |
# Validate Policy creation | |
create_date = policy.create_date | |
self.arn = policy.arn | |
logger.debug(f"create_date: {create_date}") | |
logger.debug(f"arn: {self.arn}") | |
if create_date is not None: | |
print_info(f"Policy created: {self.name}") | |
self.active_resource = policy | |
return True | |
logger.error("Policy 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 Policy to be created | |
if self.wait_for_create: | |
try: | |
print_info(f"Waiting for {self.get_resource_type()} to be created.") | |
if self.arn is not None: | |
waiter = self.get_service_client(aws_client).get_waiter("policy_exists") | |
waiter.wait( | |
PolicyArn=self.arn, | |
WaiterConfig={ | |
"Delay": self.waiter_delay, | |
"MaxAttempts": self.waiter_max_attempts, | |
}, | |
) | |
else: | |
logger.warning("Skipping waiter, No Policy ARN found") | |
except Exception as e: | |
logger.error("Waiter failed.") | |
logger.error(e) | |
return True | |
def _read(self, aws_client: AwsApiClient) -> Optional[Any]: | |
"""Returns the IamPolicy | |
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) | |
policy = None | |
for _policy in service_resource.policies.all(): | |
if _policy.policy_name == self.name: | |
policy = _policy | |
break | |
if policy is None: | |
logger.debug("No Policy found") | |
return None | |
policy.load() | |
create_date = policy.create_date | |
self.arn = policy.arn | |
logger.debug(f"create_date: {create_date}") | |
logger.debug(f"arn: {self.arn}") | |
if create_date is not None: | |
logger.debug(f"Policy found: {policy.policy_name}") | |
self.active_resource = policy | |
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 IamPolicy | |
Args: | |
aws_client: The AwsApiClient for the current cluster | |
""" | |
print_info(f"Deleting {self.get_resource_type()}: {self.get_resource_name()}") | |
try: | |
policy = self._read(aws_client) | |
# logger.debug(f"Policy: {policy}") | |
# logger.debug(f"Policy type: {type(policy)}") | |
self.active_resource = None | |
if policy is None: | |
logger.warning(f"No {self.get_resource_type()} to delete") | |
return True | |
# Before you can delete a managed policy, | |
# you must first detach the policy from all users, groups, and roles | |
# that it is attached to. In addition, you must delete all | |
# the policy's versions. | |
# detach all roles | |
roles = policy.attached_roles.all() | |
for role in roles: | |
print_info(f"Detaching policy from role: {role}") | |
policy.detach_role(RoleName=role.name) | |
# detach all users | |
users = policy.attached_users.all() | |
for user in users: | |
print_info(f"Detaching policy from user: {user}") | |
policy.detach_user(UserName=user.name) | |
# detach all groups | |
groups = policy.attached_groups.all() | |
for group in groups: | |
print_info(f"Detaching policy from group: {group}") | |
policy.detach_group(GroupName=group.name) | |
# delete all versions | |
default_version = policy.default_version | |
versions = policy.versions.all() | |
for version in versions: | |
if version.version_id == default_version.version_id: | |
print_info(f"Skipping deleting default PolicyVersion: {version}") | |
continue | |
print_info(f"Deleting PolicyVersion: {version}") | |
version.delete() | |
# delete policy | |
policy.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 | |