AWS code-library documentation change
Summary
Refactored S3 Batch Operations code examples with improved error handling, added job management methods (create/check/update/cancel jobs), enhanced resource cleanup, and implemented tag management functionality.
Security assessment
The changes focus on code structure improvements and expanded functionality for S3 Batch Operations. While error handling and IAM role usage are present, there's no evidence of addressing specific vulnerabilities or security incidents. Security-related elements like role ARN usage are part of normal AWS operations rather than new security documentation.
Diff
diff --git a/code-library/latest/ug/python_3_s3-control_code_examples.md b/code-library/latest/ug/python_3_s3-control_code_examples.md index 8d46e3262..b852658ca 100644 --- a//code-library/latest/ug/python_3_s3-control_code_examples.md +++ b//code-library/latest/ug/python_3_s3-control_code_examples.md @@ -81,2 +81,2 @@ Learn S3 Batch Basics Scenario. - class S3BatchScenario: - """Manages the S3 Batch Operations scenario.""" + class S3BatchWrapper: + """Wrapper class for managing S3 Batch Operations.""" @@ -84,2 +84,32 @@ Learn S3 Batch Basics Scenario. - DASHES = "-" * 80 - STACK_NAME = "MyS3Stack" + def __init__(self, s3_client: Any, s3control_client: Any, sts_client: Any) -> None: + """ + Initializes the S3BatchWrapper with AWS service clients. + + :param s3_client: A Boto3 Amazon S3 client. This client provides low-level + access to AWS S3 services. + :param s3control_client: A Boto3 Amazon S3 Control client. This client provides + low-level access to AWS S3 Control services. + :param sts_client: A Boto3 AWS STS client. This client provides low-level + access to AWS STS services. + """ + self.s3_client = s3_client + self.s3control_client = s3control_client + self.sts_client = sts_client + # Get region from the client for bucket creation logic + self.region_name = self.s3_client.meta.region_name + + def get_account_id(self) -> str: + """ + Get AWS account ID. + + Returns: + str: AWS account ID + """ + return self.sts_client.get_caller_identity()["Account"] + + def create_bucket(self, bucket_name: str) -> None: + """ + Create an S3 bucket. + + Args: + bucket_name (str): Name of the bucket to create @@ -87 +117,2 @@ Learn S3 Batch Basics Scenario. - def __init__(self, s3_batch_wrapper: S3BatchWrapper, cfn_helper: CloudFormationHelper) -> None: + Raises: + ClientError: If bucket creation fails @@ -89 +120,18 @@ Learn S3 Batch Basics Scenario. - Initialize the S3 Batch scenario. + try: + if self.region_name and self.region_name != 'us-east-1': + self.s3_client.create_bucket( + Bucket=bucket_name, + CreateBucketConfiguration={ + 'LocationConstraint': self.region_name + } + ) + else: + self.s3_client.create_bucket(Bucket=bucket_name) + print(f"Created bucket: {bucket_name}") + except ClientError as e: + print(f"Error creating bucket: {e}") + raise + + def upload_files_to_bucket(self, bucket_name: str, file_names: List[str]) -> str: + """ + Upload files to S3 bucket including manifest file. @@ -92,2 +140,8 @@ Learn S3 Batch Basics Scenario. - s3_batch_wrapper: S3BatchWrapper instance - cfn_helper: CloudFormationHelper instance + bucket_name (str): Target bucket name + file_names (list): List of file names to upload + + Returns: + str: ETag of the manifest file + + Raises: + ClientError: If file upload fails @@ -95,2 +149,15 @@ Learn S3 Batch Basics Scenario. - self.s3_batch_wrapper = s3_batch_wrapper - self.cfn_helper = cfn_helper + try: + for file_name in file_names: + if file_name != "job-manifest.csv": + content = f"Content for {file_name}" + self.s3_client.put_object( + Bucket=bucket_name, + Key=file_name, + Body=content.encode('utf-8') + ) + print(f"Uploaded {file_name} to {bucket_name}") + + manifest_content = "" + for file_name in file_names: + if file_name != "job-manifest.csv": + manifest_content += f"{bucket_name},{file_name}\n" @@ -98,4 +165,8 @@ Learn S3 Batch Basics Scenario. - def wait_for_input(self) -> None: - """Wait for user input to continue.""" - q.ask("\nPress Enter to continue...") - print() + manifest_response = self.s3_client.put_object( + Bucket=bucket_name, + Key="job-manifest.csv", + Body=manifest_content.encode('utf-8') + ) + print(f"Uploaded manifest file to {bucket_name}") + print(f"Manifest content:\n{manifest_content}") + return manifest_response['ETag'].strip('"') @@ -103 +174,6 @@ Learn S3 Batch Basics Scenario. - def setup_resources(self, bucket_name: str, file_names: list) -> Tuple[str, str]: + except ClientError as e: + print(f"Error uploading files: {e}") + raise + + def create_s3_batch_job(self, account_id: str, role_arn: str, manifest_location: str, + report_bucket_name: str) -> str: @@ -105 +181 @@ Learn S3 Batch Basics Scenario. - Set up initial resources for the scenario. + Create an S3 batch operation job. @@ -108,2 +184,4 @@ Learn S3 Batch Basics Scenario. - bucket_name (str): Name of the bucket to create - file_names (list): List of files to upload + account_id (str): AWS account ID + role_arn (str): IAM role ARN for batch operations + manifest_location (str): Location of the manifest file + report_bucket_name (str): Bucket for job reports @@ -112,19 +190,23 @@ Learn S3 Batch Basics Scenario. - tuple: Manifest location and report bucket ARN - """ - print("\nSetting up required resources...") - self.s3_batch_wrapper.create_bucket(bucket_name) - report_bucket_arn = f"arn:aws:s3:::{bucket_name}" - manifest_location = f"arn:aws:s3:::{bucket_name}/job-manifest.csv" - self.s3_batch_wrapper.upload_files_to_bucket(bucket_name, file_names) - return manifest_location, report_bucket_arn - - def run_scenario(self) -> None: - """Run the S3 Batch Operations scenario.""" - account_id = self.s3_batch_wrapper.get_account_id() - bucket_name = f"demo-s3-batch-{str(uuid.uuid4())}" - file_names = [ - "job-manifest.csv", - "object-key-1.txt", - "object-key-2.txt", - "object-key-3.txt", - "object-key-4.txt" + str: Job ID + + Raises: + ClientError: If job creation fails + """ + try: + bucket_name = manifest_location.split(':::')[1].split('/')[0] + manifest_key = 'job-manifest.csv' + manifest_obj = self.s3_client.head_object( + Bucket=bucket_name, + Key=manifest_key + ) + etag = manifest_obj['ETag'].strip('"') + + response = self.s3control_client.create_job( + AccountId=account_id, + Operation={ + 'S3PutObjectTagging': { + 'TagSet': [ + { + 'Key': 'BatchTag', + 'Value': 'BatchValue' + }, @@ -131,0 +214,191 @@ Learn S3 Batch Basics Scenario. + } + }, + Report={ + 'Bucket': report_bucket_name, + 'Format': 'Report_CSV_20180820', + 'Enabled': True, + 'Prefix': 'batch-op-reports', + 'ReportScope': 'AllTasks' + }, + Manifest={ + 'Spec': { + 'Format': 'S3BatchOperations_CSV_20180820', + 'Fields': ['Bucket', 'Key'] + }, + 'Location': { + 'ObjectArn': manifest_location, + 'ETag': etag + } + }, + Priority=10, + RoleArn=role_arn, + Description='Batch job for tagging objects', + ConfirmationRequired=True + ) + job_id = response['JobId'] + print(f"The Job id is {job_id}") + return job_id + except ClientError as e: