People keep twisting themselves into knots trying to fit things into these labels, but the net effect is unrelated to the description.
Using a "declarative language":
my_bucket = aws_s3_bucket(aws_region, bucket_name)
Using an "imperative language":
if aws_s3_bucket.exists(aws_region, bucket_name):
my_bucket = aws_s3_bucket.update(aws_region, bucket_name)
else:
my_bucket = aws_s3_bucket.create(aws_region, bucket_name)
I prefer the latter. In every DSL I've ever used, people end up needing to handle weird edge cases, and it's extremely hard to do that with a "declarative-only" DSL, so they end up adding imperative-ness to the DSL. Give me a regular "imperative" programming language and lots of convenience functions that do black magic behind the scenes, and I'll do regular programming when the black magic falls short.
This is basically why AWS CDK / Terraform CDK / Pulumi exist.