From 5b0ff0a37dd89be4f487250e4ba29fd07f6d3f20 Mon Sep 17 00:00:00 2001 From: Brenton Buxell Date: Mon, 21 Jul 2025 15:57:37 -0700 Subject: [PATCH] Add permissions for AWS Elemental MediaLive --- aws/policy/application-services.yaml | 31 ++++ aws/policy/security-services.yaml | 24 ++- aws/terminator/application_services.py | 210 +++++++++++++++++++++++++ 3 files changed, 252 insertions(+), 13 deletions(-) diff --git a/aws/policy/application-services.yaml b/aws/policy/application-services.yaml index 25197e9..937488f 100644 --- a/aws/policy/application-services.yaml +++ b/aws/policy/application-services.yaml @@ -34,6 +34,8 @@ Statement: - events:PutTargets - events:RemoveTargets - glue:Get* + - medialive:Describe* + - medialive:List* - kinesis:Describe* - kinesis:List* - mq:Describe* @@ -62,6 +64,7 @@ Statement: - sqs:TagQueue - sqs:UntagQueue - ssm:AddTagsToResource + - ssm:CreateActivation - ssm:Describe* - ssm:Get* - ssm:List* @@ -117,6 +120,32 @@ Statement: - kinesis:RemoveTagsFromStream - kinesis:StartStreamEncryption - kinesis:StopStreamEncryption + - medialive:CreateChannel + - medialive:CreateChannelPlacementGroup + - medialive:CreateCluster + - medialive:CreateInput + - medialive:CreateInputSecurityGroup + - medialive:CreateNetwork + - medialive:CreateNode + - medialive:CreateNodeRegistrationScript + - medialive:CreateSdiSource + - medialive:CreateTags + - medialive:DeleteChannel + - medialive:DeleteChannelPlacementGroup + - medialive:DeleteCluster + - medialive:DeleteInput + - medialive:DeleteInputSecurityGroup + - medialive:DeleteNetwork + - medialive:DeleteNode + - medialive:DeleteSdiSource + - medialive:StartChannel + - medialive:StopChannel + - medialive:TagResource + - medialive:UntagResource + - medialive:UpdateChannel + - medialive:UpdateInput + - medialive:UpdateInputSecurityGroup + - medialive:UpdateSdiSource - mq:CreateTags - SNS:CreateTopic - SNS:DeleteTopic @@ -141,6 +170,8 @@ Statement: - 'arn:aws:glue:{{ aws_region }}:{{ aws_account_id }}:crawler/*' - 'arn:aws:glue:{{ aws_region }}:{{ aws_account_id }}:job/*' - 'arn:aws:kinesis:{{ aws_region }}:{{ aws_account_id }}:stream/*' + - 'arn:aws:medialive:{{ aws_region }}:{{ aws_account_id }}:*' + - 'arn:aws:medialive:{{ aws_region }}:{{ aws_account_id }}:input:*' - 'arn:aws:mq:{{ aws_region }}:{{ aws_account_id }}:*' - 'arn:aws:sns:{{ aws_region }}:{{ aws_account_id }}:*' - 'arn:aws:ssm:{{ aws_region }}:{{ aws_account_id }}:parameter/*' diff --git a/aws/policy/security-services.yaml b/aws/policy/security-services.yaml index 074bc93..20d50f5 100644 --- a/aws/policy/security-services.yaml +++ b/aws/policy/security-services.yaml @@ -14,11 +14,7 @@ Statement: ArnLike: iam:PolicyArn: - 'arn:aws:iam::aws:policy/AWSDenyAll' - - 'arn:aws:iam::aws:policy/AmazonEKSServicePolicy' - - 'arn:aws:iam::aws:policy/AmazonEKSClusterPolicy' - - 'arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy' - - 'arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy' - - 'arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy' + - 'arn:aws:iam::aws:policy/AmazonEKS*' - 'arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly' - 'arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess' - 'arn:aws:iam::aws:policy/IAMReadOnlyAccess' @@ -32,10 +28,11 @@ Statement: - 'arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole' - 'arn:aws:iam::aws:policy/service-role/AWSServiceRoleForVPCTransitGateway' - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' - - 'arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup' - - 'arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores' - - 'arn:aws:iam::aws:policy/AWSBackupServiceRolePolicyForS3Backup' - - 'arn:aws:iam::aws:policy/AWSBackupServiceRolePolicyForS3Restore' + - 'arn:aws:iam::aws:policy/*BackupService*' + - 'arn:aws:iam::aws:policy/CloudWatchFullAccessV2' + - 'arn:aws:iam::aws:policy/AWSElementalMediaLiveFullAccess' + - 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role' + - 'arn:aws:iam::{{ aws_account_id }}:policy/ansible-test-*' - Sid: AllowRegionalUnrestrictedResourceActionsWhichIncurNoFees Effect: Allow @@ -65,10 +62,11 @@ Statement: Effect: Allow Action: - access-analyzer:ValidatePolicy - - iam:GetRole + - iam:Get* - iam:List* - iam:Tag* - iam:Untag* + - sts:GetCallerIdentity - kms:CreateAlias - kms:CreateGrant - kms:DeleteAlias @@ -124,14 +122,13 @@ Statement: - acm:RequestCertificate - iam:AddRoleToInstanceProfile - iam:CreateInstanceProfile + - iam:CreatePolicy - iam:CreateRole - iam:CreateSAMLProvider - iam:DeleteInstanceProfile - iam:DeleteRole - iam:DeleteSAMLProvider - - iam:GetInstanceProfile - - iam:GetSAMLProvider - - iam:GetServerCertificate + - iam:Get* - iam:PassRole - iam:RemoveRoleFromInstanceProfile - iam:UpdateSAMLProvider @@ -154,6 +151,7 @@ Statement: - 'arn:aws:iam::{{ aws_account_id }}:saml-provider/ansible-test-*' - 'arn:aws:iam::{{ aws_account_id }}:server-certificate/ansible-test-*' - 'arn:aws:iam::{{ aws_account_id }}:role/ansible-test-*' + - 'arn:aws:iam::{{ aws_account_id }}:policy/ansible-test-*' # dms-vpc-role is hard coded into DMS... - 'arn:aws:iam::{{ aws_account_id }}:role/dms-vpc-role' - 'arn:aws:iam::{{ aws_account_id }}:role/rds_export_task' diff --git a/aws/terminator/application_services.py b/aws/terminator/application_services.py index 1af69ef..c804bd4 100644 --- a/aws/terminator/application_services.py +++ b/aws/terminator/application_services.py @@ -419,3 +419,213 @@ def id(self): def terminate(self): self.client.delete_broker(BrokerId=self.id) + + +class MediaLiveChannel(Terminator): + @staticmethod + def create(credentials): + def get_medialive_channels(client): + channels = client.list_channels()['Channels'] + return channels + + return Terminator._create(credentials, MediaLiveChannel, 'medialive', get_medialive_channels) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance['Name'] + + @property + def id(self): + return self.instance['Id'] + + @property + def ignore(self): + return self.instance['State'] == 'DELETING' + + def terminate(self): + # Stop the channel if it's running before deleting + if self.instance['State'] == 'RUNNING': + self.client.stop_channel(ChannelId=self.id) + # Wait for the channel to stop + waiter = self.client.get_waiter('channel_stopped') + waiter.wait(ChannelId=self.id) + self.client.delete_channel(ChannelId=self.id) + + +class MediaLiveInput(Terminator): + @staticmethod + def create(credentials): + def get_medialive_inputs(client): + inputs = client.list_inputs()['Inputs'] + return inputs + + return Terminator._create(credentials, MediaLiveInput, 'medialive', get_medialive_inputs) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance['Name'] + + @property + def id(self): + return self.instance['Id'] + + def terminate(self): + self.client.delete_input(InputId=self.id) + + +class MediaLiveInputSecurityGroup(Terminator): + @staticmethod + def create(credentials): + def get_medialive_input_security_groups(client): + security_groups = client.list_input_security_groups()['InputSecurityGroups'] + return security_groups + + return Terminator._create(credentials, MediaLiveInputSecurityGroup, 'medialive', get_medialive_input_security_groups) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance.get('Name', self.id) + + @property + def id(self): + return self.instance['Id'] + + def terminate(self): + self.client.delete_input_security_group(InputSecurityGroupId=self.id) + + +class MediaLiveChannelPlacementGroup(Terminator): + @staticmethod + def create(credentials): + def get_medialive_channel_placement_groups(client): + groups = client.list_channel_placement_groups()['ChannelPlacementGroups'] + return groups + + return Terminator._create(credentials, MediaLiveChannelPlacementGroup, 'medialive', get_medialive_channel_placement_groups) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance.get('Name', self.id) + + @property + def id(self): + return self.instance['Id'] + + def terminate(self): + self.client.delete_channel_placement_group(ChannelPlacementGroupId=self.id) + + +class MediaLiveCluster(Terminator): + @staticmethod + def create(credentials): + def get_medialive_clusters(client): + clusters = client.list_clusters()['Clusters'] + return clusters + + return Terminator._create(credentials, MediaLiveCluster, 'medialive', get_medialive_clusters) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance.get('Name', self.id) + + @property + def id(self): + return self.instance['Id'] + + def terminate(self): + self.client.delete_cluster(ClusterId=self.id) + + +class MediaLiveNetwork(Terminator): + @staticmethod + def create(credentials): + def get_medialive_networks(client): + networks = client.list_networks()['Networks'] + return networks + + return Terminator._create(credentials, MediaLiveNetwork, 'medialive', get_medialive_networks) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance.get('Name', self.id) + + @property + def id(self): + return self.instance['Id'] + + def terminate(self): + self.client.delete_network(NetworkId=self.id) + + +class MediaLiveNode(Terminator): + @staticmethod + def create(credentials): + def get_medialive_nodes(client): + nodes = client.list_nodes()['Nodes'] + return nodes + + return Terminator._create(credentials, MediaLiveNode, 'medialive', get_medialive_nodes) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance.get('Name', self.id) + + @property + def id(self): + return self.instance['Id'] + + def terminate(self): + self.client.delete_node(NodeId=self.id) + + +class MediaLiveSdiSource(Terminator): + @staticmethod + def create(credentials): + def get_medialive_sdi_sources(client): + sources = client.list_sdi_sources()['SdiSources'] + return sources + + return Terminator._create(credentials, MediaLiveSdiSource, 'medialive', get_medialive_sdi_sources) + + @property + def created_time(self): + return self.instance['CreatedAt'] + + @property + def name(self): + return self.instance.get('Name', self.id) + + @property + def id(self): + return self.instance['Id'] + + def terminate(self): + self.client.delete_sdi_source(SdiSourceId=self.id)