1""" 2Connection module for Amazon S3 Buckets 3 4.. versionadded:: 2016.3.0 5 6:depends: 7 - boto 8 - boto3 9 10The dependencies listed above can be installed via package or pip. 11 12:configuration: This module accepts explicit Lambda credentials but can also 13 utilize IAM roles assigned to the instance through Instance Profiles. 14 Dynamic credentials are then automatically obtained from AWS API and no 15 further configuration is necessary. More Information available at: 16 17 .. code-block:: text 18 19 http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html 20 21 If IAM roles are not used you need to specify them either in a pillar or 22 in the minion's config file: 23 24 .. code-block:: yaml 25 26 s3.keyid: GKTADJGHEIQSXMKKRBJ08H 27 s3.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs 28 29 A region may also be specified in the configuration: 30 31 .. code-block:: yaml 32 33 s3.region: us-east-1 34 35 If a region is not specified, the default is us-east-1. 36 37 It's also possible to specify key, keyid and region via a profile, either 38 as a passed in dict, or as a string to pull from pillars or minion config: 39 40 .. code-block:: yaml 41 42 myprofile: 43 keyid: GKTADJGHEIQSXMKKRBJ08H 44 key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs 45 region: us-east-1 46 47""" 48# keep lint from choking on _get_conn and _cache_id 49# pylint: disable=E0602 50# disable complaints about perfectly valid non-assignment code 51# pylint: disable=W0106 52 53 54import logging 55 56import salt.utils.compat 57import salt.utils.json 58import salt.utils.versions 59from salt.exceptions import SaltInvocationError 60 61log = logging.getLogger(__name__) 62 63 64# pylint: disable=import-error 65try: 66 # pylint: disable=unused-import 67 import boto 68 import boto3 69 70 # pylint: enable=unused-import 71 from botocore.exceptions import ClientError 72 73 logging.getLogger("boto3").setLevel(logging.CRITICAL) 74 HAS_BOTO = True 75except ImportError: 76 HAS_BOTO = False 77# pylint: enable=import-error 78 79 80def __virtual__(): 81 """ 82 Only load if boto libraries exist and if boto libraries are greater than 83 a given version. 84 """ 85 # the boto_lambda execution module relies on the connect_to_region() method 86 # which was added in boto 2.8.0 87 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 88 return salt.utils.versions.check_boto_reqs(boto3_ver="1.2.1") 89 90 91def __init__(opts): 92 if HAS_BOTO: 93 __utils__["boto3.assign_funcs"](__name__, "s3") 94 95 96def exists(Bucket, region=None, key=None, keyid=None, profile=None): 97 """ 98 Given a bucket name, check to see if the given bucket exists. 99 100 Returns True if the given bucket exists and returns False if the given 101 bucket does not exist. 102 103 CLI Example: 104 105 .. code-block:: bash 106 107 salt myminion boto_s3_bucket.exists mybucket 108 109 """ 110 111 try: 112 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 113 buckets = conn.head_bucket(Bucket=Bucket) 114 return {"exists": True} 115 except ClientError as e: 116 if e.response.get("Error", {}).get("Code") == "404": 117 return {"exists": False} 118 err = __utils__["boto3.get_error"](e) 119 return {"error": err} 120 121 122def create( 123 Bucket, 124 ACL=None, 125 LocationConstraint=None, 126 GrantFullControl=None, 127 GrantRead=None, 128 GrantReadACP=None, 129 GrantWrite=None, 130 GrantWriteACP=None, 131 region=None, 132 key=None, 133 keyid=None, 134 profile=None, 135): 136 """ 137 Given a valid config, create an S3 Bucket. 138 139 Returns {created: true} if the bucket was created and returns 140 {created: False} if the bucket was not created. 141 142 CLI Example: 143 144 .. code-block:: bash 145 146 salt myminion boto_s3_bucket.create my_bucket \\ 147 GrantFullControl='emailaddress=example@example.com' \\ 148 GrantRead='uri="http://acs.amazonaws.com/groups/global/AllUsers"' \\ 149 GrantReadACP='emailaddress="exampl@example.com",id="2345678909876432"' \\ 150 LocationConstraint=us-west-1 151 152 """ 153 154 try: 155 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 156 kwargs = {} 157 for arg in ( 158 "ACL", 159 "GrantFullControl", 160 "GrantRead", 161 "GrantReadACP", 162 "GrantWrite", 163 "GrantWriteACP", 164 ): 165 if locals()[arg] is not None: 166 kwargs[arg] = str(locals()[arg]) 167 if LocationConstraint: 168 kwargs["CreateBucketConfiguration"] = { 169 "LocationConstraint": LocationConstraint 170 } 171 location = conn.create_bucket(Bucket=Bucket, **kwargs) 172 conn.get_waiter("bucket_exists").wait(Bucket=Bucket) 173 if location: 174 log.info( 175 "The newly created bucket name is located at %s", location["Location"] 176 ) 177 178 return {"created": True, "name": Bucket, "Location": location["Location"]} 179 else: 180 log.warning("Bucket was not created") 181 return {"created": False} 182 except ClientError as e: 183 return {"created": False, "error": __utils__["boto3.get_error"](e)} 184 185 186def delete( 187 Bucket, 188 MFA=None, 189 RequestPayer=None, 190 Force=False, 191 region=None, 192 key=None, 193 keyid=None, 194 profile=None, 195): 196 """ 197 Given a bucket name, delete it, optionally emptying it first. 198 199 Returns {deleted: true} if the bucket was deleted and returns 200 {deleted: false} if the bucket was not deleted. 201 202 CLI Example: 203 204 .. code-block:: bash 205 206 salt myminion boto_s3_bucket.delete mybucket 207 208 """ 209 210 try: 211 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 212 if Force: 213 empty( 214 Bucket, 215 MFA=MFA, 216 RequestPayer=RequestPayer, 217 region=region, 218 key=key, 219 keyid=keyid, 220 profile=profile, 221 ) 222 conn.delete_bucket(Bucket=Bucket) 223 return {"deleted": True} 224 except ClientError as e: 225 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 226 227 228def delete_objects( 229 Bucket, 230 Delete, 231 MFA=None, 232 RequestPayer=None, 233 region=None, 234 key=None, 235 keyid=None, 236 profile=None, 237): 238 """ 239 Delete objects in a given S3 bucket. 240 241 Returns {deleted: true} if all objects were deleted 242 and {deleted: false, failed: [key, ...]} otherwise 243 244 CLI Example: 245 246 .. code-block:: bash 247 248 salt myminion boto_s3_bucket.delete_objects mybucket '{Objects: [Key: myobject]}' 249 250 """ 251 252 if isinstance(Delete, str): 253 Delete = salt.utils.json.loads(Delete) 254 if not isinstance(Delete, dict): 255 raise SaltInvocationError("Malformed Delete request.") 256 if "Objects" not in Delete: 257 raise SaltInvocationError("Malformed Delete request.") 258 259 failed = [] 260 objs = Delete["Objects"] 261 for i in range(0, len(objs), 1000): 262 chunk = objs[i : i + 1000] 263 subset = {"Objects": chunk, "Quiet": True} 264 try: 265 args = {"Bucket": Bucket} 266 args.update({"MFA": MFA}) if MFA else None 267 args.update({"RequestPayer": RequestPayer}) if RequestPayer else None 268 args.update({"Delete": subset}) 269 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 270 ret = conn.delete_objects(**args) 271 failed += ret.get("Errors", []) 272 except ClientError as e: 273 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 274 275 if failed: 276 return {"deleted": False, "failed": failed} 277 else: 278 return {"deleted": True} 279 280 281def describe(Bucket, region=None, key=None, keyid=None, profile=None): 282 """ 283 Given a bucket name describe its properties. 284 285 Returns a dictionary of interesting properties. 286 287 CLI Example: 288 289 .. code-block:: bash 290 291 salt myminion boto_s3_bucket.describe mybucket 292 293 """ 294 295 try: 296 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 297 result = {} 298 conn_dict = { 299 "ACL": conn.get_bucket_acl, 300 "CORS": conn.get_bucket_cors, 301 "LifecycleConfiguration": conn.get_bucket_lifecycle_configuration, 302 "Location": conn.get_bucket_location, 303 "Logging": conn.get_bucket_logging, 304 "NotificationConfiguration": conn.get_bucket_notification_configuration, 305 "Policy": conn.get_bucket_policy, 306 "Replication": conn.get_bucket_replication, 307 "RequestPayment": conn.get_bucket_request_payment, 308 "Versioning": conn.get_bucket_versioning, 309 "Website": conn.get_bucket_website, 310 } 311 312 for key, query in conn_dict.items(): 313 try: 314 data = query(Bucket=Bucket) 315 except ClientError as e: 316 if e.response.get("Error", {}).get("Code") in ( 317 "NoSuchLifecycleConfiguration", 318 "NoSuchCORSConfiguration", 319 "NoSuchBucketPolicy", 320 "NoSuchWebsiteConfiguration", 321 "ReplicationConfigurationNotFoundError", 322 "NoSuchTagSet", 323 ): 324 continue 325 raise 326 if "ResponseMetadata" in data: 327 del data["ResponseMetadata"] 328 result[key] = data 329 330 tags = {} 331 try: 332 data = conn.get_bucket_tagging(Bucket=Bucket) 333 for tagdef in data.get("TagSet"): 334 tags[tagdef.get("Key")] = tagdef.get("Value") 335 except ClientError as e: 336 if not e.response.get("Error", {}).get("Code") == "NoSuchTagSet": 337 raise 338 if tags: 339 result["Tagging"] = tags 340 return {"bucket": result} 341 except ClientError as e: 342 err = __utils__["boto3.get_error"](e) 343 if e.response.get("Error", {}).get("Code") == "NoSuchBucket": 344 return {"bucket": None} 345 return {"error": __utils__["boto3.get_error"](e)} 346 347 348def empty( 349 Bucket, MFA=None, RequestPayer=None, region=None, key=None, keyid=None, profile=None 350): 351 """ 352 Delete all objects in a given S3 bucket. 353 354 Returns {deleted: true} if all objects were deleted 355 and {deleted: false, failed: [key, ...]} otherwise 356 357 CLI Example: 358 359 .. code-block:: bash 360 361 salt myminion boto_s3_bucket.empty mybucket 362 363 """ 364 365 stuff = list_object_versions( 366 Bucket, region=region, key=key, keyid=keyid, profile=profile 367 ) 368 Delete = {} 369 Delete["Objects"] = [ 370 {"Key": v["Key"], "VersionId": v["VersionId"]} 371 for v in stuff.get("Versions", []) 372 ] 373 Delete["Objects"] += [ 374 {"Key": v["Key"], "VersionId": v["VersionId"]} 375 for v in stuff.get("DeleteMarkers", []) 376 ] 377 if Delete["Objects"]: 378 ret = delete_objects( 379 Bucket, 380 Delete, 381 MFA=MFA, 382 RequestPayer=RequestPayer, 383 region=region, 384 key=key, 385 keyid=keyid, 386 profile=profile, 387 ) 388 failed = ret.get("failed", []) 389 if failed: 390 return {"deleted": False, "failed": ret[failed]} 391 return {"deleted": True} 392 393 394def list(region=None, key=None, keyid=None, profile=None): 395 """ 396 List all buckets owned by the authenticated sender of the request. 397 398 Returns list of buckets 399 400 CLI Example: 401 402 .. code-block:: yaml 403 404 Owner: {...} 405 Buckets: 406 - {...} 407 - {...} 408 409 """ 410 try: 411 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 412 buckets = conn.list_buckets() 413 if not bool(buckets.get("Buckets")): 414 log.warning("No buckets found") 415 if "ResponseMetadata" in buckets: 416 del buckets["ResponseMetadata"] 417 return buckets 418 except ClientError as e: 419 return {"error": __utils__["boto3.get_error"](e)} 420 421 422def list_object_versions( 423 Bucket, 424 Delimiter=None, 425 EncodingType=None, 426 Prefix=None, 427 region=None, 428 key=None, 429 keyid=None, 430 profile=None, 431): 432 """ 433 List objects in a given S3 bucket. 434 435 Returns a list of objects. 436 437 CLI Example: 438 439 .. code-block:: bash 440 441 salt myminion boto_s3_bucket.list_object_versions mybucket 442 443 """ 444 445 try: 446 Versions = [] 447 DeleteMarkers = [] 448 args = {"Bucket": Bucket} 449 args.update({"Delimiter": Delimiter}) if Delimiter else None 450 args.update({"EncodingType": EncodingType}) if Delimiter else None 451 args.update({"Prefix": Prefix}) if Prefix else None 452 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 453 IsTruncated = True 454 while IsTruncated: 455 ret = conn.list_object_versions(**args) 456 IsTruncated = ret.get("IsTruncated", False) 457 if IsTruncated in ("True", "true", True): 458 args["KeyMarker"] = ret["NextKeyMarker"] 459 args["VersionIdMarker"] = ret["NextVersionIdMarker"] 460 Versions += ret.get("Versions", []) 461 DeleteMarkers += ret.get("DeleteMarkers", []) 462 return {"Versions": Versions, "DeleteMarkers": DeleteMarkers} 463 except ClientError as e: 464 return {"error": __utils__["boto3.get_error"](e)} 465 466 467def list_objects( 468 Bucket, 469 Delimiter=None, 470 EncodingType=None, 471 Prefix=None, 472 FetchOwner=False, 473 StartAfter=None, 474 region=None, 475 key=None, 476 keyid=None, 477 profile=None, 478): 479 """ 480 List objects in a given S3 bucket. 481 482 Returns a list of objects. 483 484 CLI Example: 485 486 .. code-block:: bash 487 488 salt myminion boto_s3_bucket.list_objects mybucket 489 490 """ 491 492 try: 493 Contents = [] 494 args = {"Bucket": Bucket, "FetchOwner": FetchOwner} 495 args.update({"Delimiter": Delimiter}) if Delimiter else None 496 args.update({"EncodingType": EncodingType}) if Delimiter else None 497 args.update({"Prefix": Prefix}) if Prefix else None 498 args.update({"StartAfter": StartAfter}) if StartAfter else None 499 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 500 IsTruncated = True 501 while IsTruncated: 502 ret = conn.list_objects_v2(**args) 503 IsTruncated = ret.get("IsTruncated", False) 504 if IsTruncated in ("True", "true", True): 505 args["ContinuationToken"] = ret["NextContinuationToken"] 506 Contents += ret.get("Contents", []) 507 return {"Contents": Contents} 508 except ClientError as e: 509 return {"error": __utils__["boto3.get_error"](e)} 510 511 512def put_acl( 513 Bucket, 514 ACL=None, 515 AccessControlPolicy=None, 516 GrantFullControl=None, 517 GrantRead=None, 518 GrantReadACP=None, 519 GrantWrite=None, 520 GrantWriteACP=None, 521 region=None, 522 key=None, 523 keyid=None, 524 profile=None, 525): 526 """ 527 Given a valid config, update the ACL for a bucket. 528 529 Returns {updated: true} if the ACL was updated and returns 530 {updated: False} if the ACL was not updated. 531 532 CLI Example: 533 534 .. code-block:: bash 535 536 salt myminion boto_s3_bucket.put_acl my_bucket 'public' \\ 537 GrantFullControl='emailaddress=example@example.com' \\ 538 GrantRead='uri="http://acs.amazonaws.com/groups/global/AllUsers"' \\ 539 GrantReadACP='emailaddress="exampl@example.com",id="2345678909876432"' 540 541 """ 542 543 try: 544 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 545 kwargs = {} 546 if AccessControlPolicy is not None: 547 if isinstance(AccessControlPolicy, str): 548 AccessControlPolicy = salt.utils.json.loads(AccessControlPolicy) 549 kwargs["AccessControlPolicy"] = AccessControlPolicy 550 for arg in ( 551 "ACL", 552 "GrantFullControl", 553 "GrantRead", 554 "GrantReadACP", 555 "GrantWrite", 556 "GrantWriteACP", 557 ): 558 if locals()[arg] is not None: 559 kwargs[arg] = str(locals()[arg]) 560 conn.put_bucket_acl(Bucket=Bucket, **kwargs) 561 return {"updated": True, "name": Bucket} 562 except ClientError as e: 563 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 564 565 566def put_cors(Bucket, CORSRules, region=None, key=None, keyid=None, profile=None): 567 """ 568 Given a valid config, update the CORS rules for a bucket. 569 570 Returns {updated: true} if CORS was updated and returns 571 {updated: False} if CORS was not updated. 572 573 CLI Example: 574 575 .. code-block:: bash 576 577 salt myminion boto_s3_bucket.put_cors my_bucket '[{\\ 578 "AllowedHeaders":[],\\ 579 "AllowedMethods":["GET"],\\ 580 "AllowedOrigins":["*"],\\ 581 "ExposeHeaders":[],\\ 582 "MaxAgeSeconds":123,\\ 583 }]' 584 585 """ 586 587 try: 588 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 589 if CORSRules is not None and isinstance(CORSRules, str): 590 CORSRules = salt.utils.json.loads(CORSRules) 591 conn.put_bucket_cors(Bucket=Bucket, CORSConfiguration={"CORSRules": CORSRules}) 592 return {"updated": True, "name": Bucket} 593 except ClientError as e: 594 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 595 596 597def put_lifecycle_configuration( 598 Bucket, Rules, region=None, key=None, keyid=None, profile=None 599): 600 """ 601 Given a valid config, update the Lifecycle rules for a bucket. 602 603 Returns {updated: true} if Lifecycle was updated and returns 604 {updated: False} if Lifecycle was not updated. 605 606 CLI Example: 607 608 .. code-block:: bash 609 610 salt myminion boto_s3_bucket.put_lifecycle_configuration my_bucket '[{\\ 611 "Expiration": {...},\\ 612 "ID": "idstring",\\ 613 "Prefix": "prefixstring",\\ 614 "Status": "enabled",\\ 615 "Transitions": [{...},],\\ 616 "NoncurrentVersionTransitions": [{...},],\\ 617 "NoncurrentVersionExpiration": {...},\\ 618 }]' 619 620 """ 621 622 try: 623 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 624 if Rules is not None and isinstance(Rules, str): 625 Rules = salt.utils.json.loads(Rules) 626 conn.put_bucket_lifecycle_configuration( 627 Bucket=Bucket, LifecycleConfiguration={"Rules": Rules} 628 ) 629 return {"updated": True, "name": Bucket} 630 except ClientError as e: 631 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 632 633 634def put_logging( 635 Bucket, 636 TargetBucket=None, 637 TargetPrefix=None, 638 TargetGrants=None, 639 region=None, 640 key=None, 641 keyid=None, 642 profile=None, 643): 644 """ 645 Given a valid config, update the logging parameters for a bucket. 646 647 Returns {updated: true} if parameters were updated and returns 648 {updated: False} if parameters were not updated. 649 650 CLI Example: 651 652 .. code-block:: bash 653 654 salt myminion boto_s3_bucket.put_logging my_bucket log_bucket '[{...}]' prefix 655 656 """ 657 658 try: 659 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 660 logstate = {} 661 targets = { 662 "TargetBucket": TargetBucket, 663 "TargetGrants": TargetGrants, 664 "TargetPrefix": TargetPrefix, 665 } 666 for key, val in targets.items(): 667 if val is not None: 668 logstate[key] = val 669 if logstate: 670 logstatus = {"LoggingEnabled": logstate} 671 else: 672 logstatus = {} 673 if TargetGrants is not None and isinstance(TargetGrants, str): 674 TargetGrants = salt.utils.json.loads(TargetGrants) 675 conn.put_bucket_logging(Bucket=Bucket, BucketLoggingStatus=logstatus) 676 return {"updated": True, "name": Bucket} 677 except ClientError as e: 678 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 679 680 681def put_notification_configuration( 682 Bucket, 683 TopicConfigurations=None, 684 QueueConfigurations=None, 685 LambdaFunctionConfigurations=None, 686 region=None, 687 key=None, 688 keyid=None, 689 profile=None, 690): 691 """ 692 Given a valid config, update the notification parameters for a bucket. 693 694 Returns {updated: true} if parameters were updated and returns 695 {updated: False} if parameters were not updated. 696 697 CLI Example: 698 699 .. code-block:: bash 700 701 salt myminion boto_s3_bucket.put_notification_configuration my_bucket 702 [{...}] \\ 703 [{...}] \\ 704 [{...}] 705 706 """ 707 708 try: 709 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 710 if TopicConfigurations is None: 711 TopicConfigurations = [] 712 elif isinstance(TopicConfigurations, str): 713 TopicConfigurations = salt.utils.json.loads(TopicConfigurations) 714 if QueueConfigurations is None: 715 QueueConfigurations = [] 716 elif isinstance(QueueConfigurations, str): 717 QueueConfigurations = salt.utils.json.loads(QueueConfigurations) 718 if LambdaFunctionConfigurations is None: 719 LambdaFunctionConfigurations = [] 720 elif isinstance(LambdaFunctionConfigurations, str): 721 LambdaFunctionConfigurations = salt.utils.json.loads( 722 LambdaFunctionConfigurations 723 ) 724 # TODO allow the user to use simple names & substitute ARNs for those names 725 conn.put_bucket_notification_configuration( 726 Bucket=Bucket, 727 NotificationConfiguration={ 728 "TopicConfigurations": TopicConfigurations, 729 "QueueConfigurations": QueueConfigurations, 730 "LambdaFunctionConfigurations": LambdaFunctionConfigurations, 731 }, 732 ) 733 return {"updated": True, "name": Bucket} 734 except ClientError as e: 735 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 736 737 738def put_policy(Bucket, Policy, region=None, key=None, keyid=None, profile=None): 739 """ 740 Given a valid config, update the policy for a bucket. 741 742 Returns {updated: true} if policy was updated and returns 743 {updated: False} if policy was not updated. 744 745 CLI Example: 746 747 .. code-block:: bash 748 749 salt myminion boto_s3_bucket.put_policy my_bucket {...} 750 751 """ 752 753 try: 754 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 755 if Policy is None: 756 Policy = "{}" 757 elif not isinstance(Policy, str): 758 Policy = salt.utils.json.dumps(Policy) 759 conn.put_bucket_policy(Bucket=Bucket, Policy=Policy) 760 return {"updated": True, "name": Bucket} 761 except ClientError as e: 762 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 763 764 765def _get_role_arn(name, region=None, key=None, keyid=None, profile=None): 766 if name.startswith("arn:aws:iam:"): 767 return name 768 769 account_id = __salt__["boto_iam.get_account_id"]( 770 region=region, key=key, keyid=keyid, profile=profile 771 ) 772 if profile and "region" in profile: 773 region = profile["region"] 774 if region is None: 775 region = "us-east-1" 776 return "arn:aws:iam::{}:role/{}".format(account_id, name) 777 778 779def put_replication( 780 Bucket, Role, Rules, region=None, key=None, keyid=None, profile=None 781): 782 """ 783 Given a valid config, update the replication configuration for a bucket. 784 785 Returns {updated: true} if replication configuration was updated and returns 786 {updated: False} if replication configuration was not updated. 787 788 CLI Example: 789 790 .. code-block:: bash 791 792 salt myminion boto_s3_bucket.put_replication my_bucket my_role [...] 793 794 """ 795 796 try: 797 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 798 Role = _get_role_arn( 799 name=Role, region=region, key=key, keyid=keyid, profile=profile 800 ) 801 if Rules is None: 802 Rules = [] 803 elif isinstance(Rules, str): 804 Rules = salt.utils.json.loads(Rules) 805 conn.put_bucket_replication( 806 Bucket=Bucket, ReplicationConfiguration={"Role": Role, "Rules": Rules} 807 ) 808 return {"updated": True, "name": Bucket} 809 except ClientError as e: 810 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 811 812 813def put_request_payment(Bucket, Payer, region=None, key=None, keyid=None, profile=None): 814 """ 815 Given a valid config, update the request payment configuration for a bucket. 816 817 Returns {updated: true} if request payment configuration was updated and returns 818 {updated: False} if request payment configuration was not updated. 819 820 CLI Example: 821 822 .. code-block:: bash 823 824 salt myminion boto_s3_bucket.put_request_payment my_bucket Requester 825 826 """ 827 828 try: 829 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 830 conn.put_bucket_request_payment( 831 Bucket=Bucket, RequestPaymentConfiguration={"Payer": Payer} 832 ) 833 return {"updated": True, "name": Bucket} 834 except ClientError as e: 835 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 836 837 838def put_tagging(Bucket, region=None, key=None, keyid=None, profile=None, **kwargs): 839 """ 840 Given a valid config, update the tags for a bucket. 841 842 Returns {updated: true} if tags were updated and returns 843 {updated: False} if tags were not updated. 844 845 CLI Example: 846 847 .. code-block:: bash 848 849 salt myminion boto_s3_bucket.put_tagging my_bucket my_role [...] 850 851 """ 852 853 try: 854 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 855 tagslist = [] 856 for k, v in kwargs.items(): 857 if str(k).startswith("__"): 858 continue 859 tagslist.append({"Key": str(k), "Value": str(v)}) 860 conn.put_bucket_tagging(Bucket=Bucket, Tagging={"TagSet": tagslist}) 861 return {"updated": True, "name": Bucket} 862 except ClientError as e: 863 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 864 865 866def put_versioning( 867 Bucket, 868 Status, 869 MFADelete=None, 870 MFA=None, 871 region=None, 872 key=None, 873 keyid=None, 874 profile=None, 875): 876 """ 877 Given a valid config, update the versioning configuration for a bucket. 878 879 Returns {updated: true} if versioning configuration was updated and returns 880 {updated: False} if versioning configuration was not updated. 881 882 CLI Example: 883 884 .. code-block:: bash 885 886 salt myminion boto_s3_bucket.put_versioning my_bucket Enabled 887 888 """ 889 890 try: 891 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 892 VersioningConfiguration = {"Status": Status} 893 if MFADelete is not None: 894 VersioningConfiguration["MFADelete"] = MFADelete 895 kwargs = {} 896 if MFA is not None: 897 kwargs["MFA"] = MFA 898 conn.put_bucket_versioning( 899 Bucket=Bucket, VersioningConfiguration=VersioningConfiguration, **kwargs 900 ) 901 return {"updated": True, "name": Bucket} 902 except ClientError as e: 903 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 904 905 906def put_website( 907 Bucket, 908 ErrorDocument=None, 909 IndexDocument=None, 910 RedirectAllRequestsTo=None, 911 RoutingRules=None, 912 region=None, 913 key=None, 914 keyid=None, 915 profile=None, 916): 917 """ 918 Given a valid config, update the website configuration for a bucket. 919 920 Returns {updated: true} if website configuration was updated and returns 921 {updated: False} if website configuration was not updated. 922 923 CLI Example: 924 925 .. code-block:: bash 926 927 salt myminion boto_s3_bucket.put_website my_bucket IndexDocument='{"Suffix":"index.html"}' 928 929 """ 930 931 try: 932 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 933 WebsiteConfiguration = {} 934 for key in ( 935 "ErrorDocument", 936 "IndexDocument", 937 "RedirectAllRequestsTo", 938 "RoutingRules", 939 ): 940 val = locals()[key] 941 if val is not None: 942 if isinstance(val, str): 943 WebsiteConfiguration[key] = salt.utils.json.loads(val) 944 else: 945 WebsiteConfiguration[key] = val 946 conn.put_bucket_website( 947 Bucket=Bucket, WebsiteConfiguration=WebsiteConfiguration 948 ) 949 return {"updated": True, "name": Bucket} 950 except ClientError as e: 951 return {"updated": False, "error": __utils__["boto3.get_error"](e)} 952 953 954def delete_cors(Bucket, region=None, key=None, keyid=None, profile=None): 955 """ 956 Delete the CORS configuration for the given bucket 957 958 Returns {deleted: true} if CORS was deleted and returns 959 {deleted: False} if CORS was not deleted. 960 961 CLI Example: 962 963 .. code-block:: bash 964 965 salt myminion boto_s3_bucket.delete_cors my_bucket 966 967 """ 968 969 try: 970 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 971 conn.delete_bucket_cors(Bucket=Bucket) 972 return {"deleted": True, "name": Bucket} 973 except ClientError as e: 974 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 975 976 977def delete_lifecycle_configuration( 978 Bucket, region=None, key=None, keyid=None, profile=None 979): 980 """ 981 Delete the lifecycle configuration for the given bucket 982 983 Returns {deleted: true} if Lifecycle was deleted and returns 984 {deleted: False} if Lifecycle was not deleted. 985 986 CLI Example: 987 988 .. code-block:: bash 989 990 salt myminion boto_s3_bucket.delete_lifecycle_configuration my_bucket 991 992 """ 993 994 try: 995 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 996 conn.delete_bucket_lifecycle(Bucket=Bucket) 997 return {"deleted": True, "name": Bucket} 998 except ClientError as e: 999 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 1000 1001 1002def delete_policy(Bucket, region=None, key=None, keyid=None, profile=None): 1003 """ 1004 Delete the policy from the given bucket 1005 1006 Returns {deleted: true} if policy was deleted and returns 1007 {deleted: False} if policy was not deleted. 1008 1009 CLI Example: 1010 1011 .. code-block:: bash 1012 1013 salt myminion boto_s3_bucket.delete_policy my_bucket 1014 1015 """ 1016 1017 try: 1018 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 1019 conn.delete_bucket_policy(Bucket=Bucket) 1020 return {"deleted": True, "name": Bucket} 1021 except ClientError as e: 1022 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 1023 1024 1025def delete_replication(Bucket, region=None, key=None, keyid=None, profile=None): 1026 """ 1027 Delete the replication config from the given bucket 1028 1029 Returns {deleted: true} if replication configuration was deleted and returns 1030 {deleted: False} if replication configuration was not deleted. 1031 1032 CLI Example: 1033 1034 .. code-block:: bash 1035 1036 salt myminion boto_s3_bucket.delete_replication my_bucket 1037 1038 """ 1039 1040 try: 1041 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 1042 conn.delete_bucket_replication(Bucket=Bucket) 1043 return {"deleted": True, "name": Bucket} 1044 except ClientError as e: 1045 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 1046 1047 1048def delete_tagging(Bucket, region=None, key=None, keyid=None, profile=None): 1049 """ 1050 Delete the tags from the given bucket 1051 1052 Returns {deleted: true} if tags were deleted and returns 1053 {deleted: False} if tags were not deleted. 1054 1055 CLI Example: 1056 1057 .. code-block:: bash 1058 1059 salt myminion boto_s3_bucket.delete_tagging my_bucket 1060 1061 """ 1062 1063 try: 1064 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 1065 conn.delete_bucket_tagging(Bucket=Bucket) 1066 return {"deleted": True, "name": Bucket} 1067 except ClientError as e: 1068 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 1069 1070 1071def delete_website(Bucket, region=None, key=None, keyid=None, profile=None): 1072 """ 1073 Remove the website configuration from the given bucket 1074 1075 Returns {deleted: true} if website configuration was deleted and returns 1076 {deleted: False} if website configuration was not deleted. 1077 1078 CLI Example: 1079 1080 .. code-block:: bash 1081 1082 salt myminion boto_s3_bucket.delete_website my_bucket 1083 1084 """ 1085 1086 try: 1087 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) 1088 conn.delete_bucket_website(Bucket=Bucket) 1089 return {"deleted": True, "name": Bucket} 1090 except ClientError as e: 1091 return {"deleted": False, "error": __utils__["boto3.get_error"](e)} 1092