Discover the AWS CloudControl API
CloudControl is an AWS service you don't think you need, until you do...
Capabilities
- schema-based create, describe, and update for every AWS resource type
- exposes the plumbing mechanism behind CloudFormation
- provides access to new resource types before they are added to the Terraform AWS provider (via the Terraform AWS CloudControl provider)
- lets you see the complete schema for a given resource type
- this captures syntax but not necessarily complete semantics; a syntactically valid create or update request might still fail
- allows configuring all attributes of a compound resource (like an S3 bucket) in one request
- hides inconsistencies in AWS service API method names (
GetversusDescribe, etc.) - cannot hide inconsistencies in attribute names or attribute value formats, which vary from one AWS service to another, and sometimes, within the same AWS service
- introduced on 2021-09-20
Permissions
- requires non-specific base permissions:
cloudformation:{Create|List|Get|Update|Delete}Resource - also requires specific permissions for the underlying resource types
- uses the caller's privileges, or you can pass a role (cf. a CloudFormation service role / deployment role)
- excellent for least-privilege discipline
- for compound resources like an S3 bucket, you need only grant permission to read, and to set or update, the resource attributes actually referenced in your update request
Semantics
- create and update are asynchronous: you must check status
- boto3, the AWS Python SDK, provides
client("cloudcontrol").get_waiter("resource_request_success")
- boto3, the AWS Python SDK, provides
- update relies on JSON patch documents
- resources are identified by their physical names or physical identifiers, not usually by their ARNs
- checks for idempotence, just like CloudFormation or Terraform
- this is my own conclusion; I do not see it mentioned specifically in AWS's documentation, although it could well be implicit at some other level
- gotcha for tests that expect failure: no-op updates succeed even if you didn't grant permission to update the resource attributes in question!
- preserves atomic, set-based update semantics for attributes like S3 bucket tags, reducing code paths (but note the element-based permissions and scope for newer AWS service API methods, like
s3:UntagResource)
Examples
Jump to: Schema • Resource • Update • Status • Python
Get the schema for a resource type
aws cloudformation describe-type \
--type 'RESOURCE' --type-name 'AWS::S3::Bucket' \
--query 'Schema' --output text | jq '.' | head --lines=25
S3 bucket schema (excerpt)
{
"typeName": "AWS::S3::Bucket",
"description": "The ``AWS::S3::Bucket`` resource creates an Amazon S3 bucket in the same AWS Region where you create the AWS CloudFormation stack.\n To control how AWS CloudFormation handles the bucket when the stack is deleted, you can set a deletion policy for your bucket. You can choose to *retain* the bucket or to *delete* the bucket. For more information, see [DeletionPolicy Attribute](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html).\n You can only delete empty buckets. Deletion fails for buckets that have contents.",
"additionalProperties": false,
"properties": {
"AccelerateConfiguration": {
"$ref": "#/definitions/AccelerateConfiguration",
"description": "Configures the transfer acceleration state for an Amazon S3 bucket. For more information, see [Amazon S3 Transfer Acceleration](https://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html) in the *Amazon S3 User Guide*."
},
"AccessControl": {
"description": "This is a legacy property, and it is not recommended for most use cases. A majority of modern use cases in Amazon S3 no longer require the use of ACLs, and we recommend that you keep ACLs disabled. For more information, see [Controlling object ownership](https://docs.aws.amazon.com//AmazonS3/latest/userguide/about-object-ownership.html) in the *Amazon S3 User Guide*.\n A canned access control list (ACL) that grants predefined permissions to the bucket. For more information about canned ACLs, see [Canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) in the *Amazon S3 User Guide*.\n S3 buckets are created with ACLs disabled by default. Therefore, unless you explicitly set the [AWS::S3::OwnershipControls](https://docs.aws.amazon.com//AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-ownershipcontrols.html) property to enable ACLs, your resource will fail to deploy with any value other than Private. Use cases requiring ACLs are uncommon.\n The majority of access control configurations can be successfully and more easily achieved with bucket policies. For more information, see [AWS::S3::BucketPolicy](https://docs.aws.amazon.com//AWSCloudFormation/latest/UserGuide/aws-properties-s3-policy.html). For examples of common policy configurations, including S3 Server Access Logs buckets and more, see [Bucket policy examples](https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html) in the *Amazon S3 User Guide*.",
"enum": [
"AuthenticatedRead",
"AwsExecRead",
"BucketOwnerFullControl",
"BucketOwnerRead",
"LogDeliveryWrite",
"Private",
"PublicRead",
"PublicReadWrite"
],
"type": "string"
},
"AnalyticsConfigurations": {
"description": "Specifies the configuration and any analyses for the analytics filter of an Amazon S3 bucket.",
Get the current configuration of a resource
aws cloudcontrol get-resource \
--type-name 'AWS::S3::Bucket' --identifier 'name-of-my-s3-bucket' \
--query 'ResourceDescription.Properties' --output text | jq '.'
{
"AbacStatus": "Enabled",
"PublicAccessBlockConfiguration": {
"RestrictPublicBuckets": true,
"BlockPublicPolicy": true,
"BlockPublicAcls": true,
"IgnorePublicAcls": true
},
"BucketName": "name-of-my-s3-bucket",
"RegionalDomainName": "name-of-my-s3-bucket.s3.us-west-2.amazonaws.com",
"OwnershipControls": {
"Rules": [
{
"ObjectOwnership": "BucketOwnerEnforced"
}
]
},
"DomainName": "name-of-my-s3-bucket.s3.amazonaws.com",
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"BucketKeyEnabled": true,
"BlockedEncryptionTypes": {
"EncryptionType": [
"NONE"
]
},
"ServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
},
"WebsiteURL": "http://name-of-my-s3-bucket.s3-website-us-west-2.amazonaws.com",
"DualStackDomainName": "name-of-my-s3-bucket.s3.dualstack.us-west-2.amazonaws.com",
"Arn": "arn:aws:s3:::name-of-my-s3-bucket",
"Tags": [
{
"Value": "test-tag-key",
"Key": "test-tag-value"
}
]
}
Update the bucket tag set for an S3 bucket
aws cloudcontrol update-resource \
--type-name 'AWS::S3::Bucket' --identifier 'name-of-my-s3-bucket' \
--patch-document '[ { "op": "replace", "path": "Tags", "value": [ {"Key": "test-tag-key", "Value": "test-tag-new-value"} ] } ]' \
--output json | jq '.'
{
"ProgressEvent": {
"TypeName": "AWS::S3::Bucket",
"Identifier": "name-of-my-s3-bucket",
"RequestToken": "01234567-89ab-cdef-0123-456789abcdef",
"Operation": "UPDATE",
"OperationStatus": "IN_PROGRESS",
"EventTime": "2026-04-22T18:05:24.230000+00:00",
"ResourceModel": "{\"AbacStatus\":\"Enabled\",\"PublicAccessBlockConfiguration\":{\"RestrictPublicBuckets\":true,\"BlockPublicPolicy\":true,\"BlockPublicAcls\":true,\"IgnorePublicAcls\":true},\"BucketName\":\"name-of-my-s3-bucket\",\"OwnershipControls\":{\"Rules\":[{\"ObjectOwnership\":\"BucketOwnerEnforced\"}]},\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":true,\"BlockedEncryptionTypes\":{\"EncryptionType\":[\"NONE\"]},\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"AES256\"}}]},\"Tags\":[{\"Value\":\"test-tag-new-value\",\"Key\":\"test-tag-key\"}]}"
}
}
Check the status of a request
aws cloudcontrol get-resource-request-status \
--request-token "01234567-89ab-cdef-0123-456789abcdef" \
--output json | jq '.'
{
"ProgressEvent": {
"TypeName": "AWS::S3::Bucket",
"Identifier": "name-of-my-s3-bucket",
"RequestToken": "01234567-89ab-cdef-0123-456789abcdef",
"Operation": "UPDATE",
"OperationStatus": "SUCCESS",
"EventTime": "2026-04-22T18:05:44.727000+00:00"
}
}
Minimal Python example
As of April, 2026, the Lambda Python 3.14 runtime bundles boto3 v1.40.4 , which was released on 2025-08-06. This predates the introduction of attribute-based access control for S3 buckets on 2025-11-20. Rather than require users of my software to build a Lambda bundle, and give up AWS's cautious, secure, automatic runtime patching, I use CloudControl temporarily.
In this testing function, I care only about a timeout, success, or failure. Note the "gotcha" mentioned above. CloudControl's update semantics defeat permissions tests that happen to involve an idempotent update request (trying to set an attribute value that has already been set).
github.com/sqlxpert/aws-rcp-s3-require-encryption-kms/blob/495f7f6/test/tester_rcp.py
Thank you for reading. Don't hesitate to e-mail me! Feedback that improves this article will be acknowledged publicly.