I'm attempting to create an ACM certificate along with the domain validation records in Python using cdktf without success. I'm not sure if the problem is with the result of the 'domain_validation_options' for the certificate is correct or with the for_each looping. In either case the example code is incorrect.
from constructs import Construct
from cdktf import App, TerraformStack, Token, TerraformIterator, Fn
from imports.aws.acm_certificate import AcmCertificate
from imports.aws.acm_certificate_validation import AcmCertificateValidation
from imports.aws.data_aws_route53_zone import DataAwsRoute53Zone
from imports.aws.provider import AwsProvider
class CertificateExampleStack(TerraformStack):
def __init__(self, scope, name):
super().__init__(scope, name)
domain_name = "<domain name goes here>"
zone_id = "<zone id goes here>" # hard coded to simplify example
AwsProvider(self, "aws-provider", region="eu-west-1")
example_cert = AcmCertificate(self, "example",
domain_name=domain_name,
validation_method="DNS"
)
# this line is where the error occurs, the string interpolation
# in the Token.as_any isn't correct
example_for_each_iterator = TerraformIterator.from_list(
Token.as_any(
"${{ for dvo in ${"
+ example_cert.domain_validation_options
+ "} : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}}"))
aws_route53_record_example = Route53Record(self, "example_2",
allow_overwrite=True,
name=Token.as_string(
Fn.lookup_nested(example_for_each_iterator.value, ["name"])),
records=[
Token.as_string(
Fn.lookup_nested(example_for_each_iterator.value, ["record"]))
],
ttl=60,
type=Token.as_string(
Fn.lookup_nested(example_for_each_iterator.value, ["type"])),
zone_id=zone_id,
for_each=example_for_each_iterator
)
aws_route53_record_example.override_logical_id("example")
# this expression also has incorrect string interpolation.
AcmCertificateValidation(self, "example_3",
certificate_arn=example_cert.arn,
validation_record_fqdns=Token.as_list("${[ for record in ${" + aws_route53_record_example.fqn + "} : record.fqdn]}")
)
app = App()
CertificateExampleStack(app, "tf-test-python")
app.synth()
On running;
cdktf deploy
I get the following results;
Traceback (most recent call last):
File "/Users/drepik/workspaces/tf-test-python/main.py", line 56, in <module>
CertificateExampleStack(app, "tf-test-python")
File "/Users/drepik/.local/share/virtualenvs/tf-test-python-xvBxJD8u/lib/python3.11/site-packages/jsii/_runtime.py", line 118, in __call__
inst = super(JSIIMeta, cast(JSIIMeta, cls)).__call__(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/drepik/workspaces/tf-test-python/main.py", line 32, in __init__
Token.as_any("${{ for dvo in ${" + example_cert.domain_validation_options + "} : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}}"))
~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TypeError: can only concatenate str (not "AcmCertificateDomainValidationOptionsList") to str
The original code source of the example posted in the documentation is generated by some tool and doesn't seem to handle converting string interpolations between Typescript and Python.
When I change line 24 from:
example_for_each_iterator = TerraformIterator.from_list(
Token.as_any(
"${{ for dvo in ${"
+ example_cert.domain_validation_options
+ "} : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}}"))
To:
example_for_each_iterator = TerraformIterator.from_list(
Token.as_any(
"{{ for dvo in "
+ "example.domain_validation_options"
+ " : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}}")
)
Where the difference is;
The '$'s have been removed to address 'Invalid character' errors.
The domain_validation_options has been changed to string referencing the certificate created.
Additionally I made similar changes to line 47. Originally it was;
AcmCertificateValidation(self, "example_3",
certificate_arn=example_cert.arn,
validation_record_fqdns=Token.as_list("${[ for record in ${" + aws_route53_record_example.fqn + "} : record.fqdn]}")
To:
AcmCertificateValidation(self, "example_3",
certificate_arn=example_cert.arn,
validation_record_fqdns=Token.as_list("{[ for record in example_2 : record.fqdn]}")
)
Now the results of running 'cdktf deploy' with these changes results with following error;
│ Error: Incorrect attribute value type
│
│ on cdk.tf.json line 40, in resource.aws_acm_certificate_validation.example_3 (example_3):
│ 40: "validation_record_fqdns": "{[ for record in example.fqdn : record.fqdn]}"
│
│ Inappropriate value for attribute "validation_record_fqdns": set of string
│ required.
╵
tf-test-python ╷
│ Error: Missing key/value separator
│
│ on cdk.tf.json line 52, in resource.aws_route53_record.example (example_2):
│ 52: "for_each": "${toset({{ for dvo in {example.domain_validation_options} : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}})}",
│
│ Expected an equals sign ("=") to mark the beginning of the attribute value.
At this point I'm stuck and not sure if I'm making a progress. Neither have I found a way to effectively debug this issue further.
Expected Behavior
A Route53 domain validation record to be created.
Actual Behavior
Deployment generates the following error;
│ Error: Incorrect attribute value type
│
│ on cdk.tf.json line 40, in resource.aws_acm_certificate_validation.example_3 (example_3):
│ 40: "validation_record_fqdns": "{[ for record in example.fqdn : record.fqdn]}"
│
│ Inappropriate value for attribute "validation_record_fqdns": set of string
│ required.
╵
tf-test-python ╷
│ Error: Missing key/value separator
│
│ on cdk.tf.json line 52, in resource.aws_route53_record.example (example_2):
│ 52: "for_each": "${toset({{ for dvo in {example.domain_validation_options} : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}})}",
│
│ Expected an equals sign ("=") to mark the beginning of the attribute value.
Relevant Error/Panic Output Snippet
cdktf version: 0.18.0
python version
Here's the cdktf output of the provider versions;
~/workspaces/tf-test-python > cdktf provider list
┌───────────────┬──────────────────┬─────────┬────────────┬──────────────────────────┬─────────────────┐
│ Provider Name │ Provider Version │ CDKTF │ Constraint │ Package Name │ Package Version │
├───────────────┼──────────────────┼─────────┼────────────┼──────────────────────────┼─────────────────┤
│ aws │ 4.67.0 │ │ ~>4.0 │ │ │
├───────────────┼──────────────────┼─────────┼────────────┼──────────────────────────┼─────────────────┤
│ aws │ 5.17.0 │ ^0.18.0 │ │ cdktf-cdktf-provider-aws │ 17.0.6 │
└───────────────┴──────────────────┴─────────┴────────────┴──────────────────────────┴─────────────────┘
from cdktf import App, TerraformStack, Token, TerraformIterator, Fn
from imports.aws.acm_certificate import AcmCertificate
from imports.aws.acm_certificate_validation import AcmCertificateValidation
from imports.aws.route53_record import Route53Record
from imports.aws.provider import AwsProvider
class CertificateExampleStack(TerraformStack):
def __init__(self, scope, name):
super().__init__(scope, name)
domain_name = "<domain name goes here"
zone_id = "<hosted zone Id for the domain goes here>" # hard coded to simplify example
AwsProvider(self, "aws-provider", region="eu-west-1")
example_cert = AcmCertificate(self, "example",
domain_name=domain_name,
validation_method="DNS"
)
example_for_each_iterator = TerraformIterator.from_list(
Token.as_any(
"{{ for dvo in "
+ "example.domain_validation_options"
+ " : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}}")
)
aws_route53_record_example = Route53Record(self, "example_2",
allow_overwrite=True,
name=Token.as_string(
Fn.lookup_nested(example_for_each_iterator.value, ["resource_record_name"])),
records=[
Token.as_string(
Fn.lookup_nested(example_for_each_iterator.value, ["resource_record_value"]))
],
ttl=60,
type=Token.as_string(
Fn.lookup_nested(example_for_each_iterator.value, ["resource_record_type"])),
zone_id=zone_id,
for_each=example_for_each_iterator
)
# This allows the Terraform resource name to match the original name. You can remove the call if you don't need them to match.
aws_route53_record_example.override_logical_id("example")
AcmCertificateValidation(self, "example_3",
certificate_arn=example_cert.arn,
validation_record_fqdns=Token.as_list("{[ for record in example_2 : record.fqdn]}")
)
app = App()
CertificateExampleStack(app, "tf-test-python")
app.synth()
set the domain name and hosted zone id to correct values.
Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request.
Volunteering to Work on This Issue
If you are interested in working on this issue, please leave a comment.
If this would be your first contribution, please review the contribution guide.
Terraform Core Version
1.5.5
AWS Provider Version
4.67.0
Affected Resource(s)
I'm attempting to create an ACM certificate along with the domain validation records in Python using cdktf without success. I'm not sure if the problem is with the result of the 'domain_validation_options' for the certificate is correct or with the for_each looping. In either case the example code is incorrect.
Starting with the example code found at (https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation?lang=python) with non-relevant code removed, I have the following code that illustrates the problem:
On running;
I get the following results;
The original code source of the example posted in the documentation is generated by some tool and doesn't seem to handle converting string interpolations between Typescript and Python.
When I change line 24 from:
To:
Where the difference is;
Additionally I made similar changes to line 47. Originally it was;
To:
Now the results of running 'cdktf deploy' with these changes results with following error;
At this point I'm stuck and not sure if I'm making a progress. Neither have I found a way to effectively debug this issue further.
Expected Behavior
A Route53 domain validation record to be created.
Actual Behavior
Deployment generates the following error;
tf-test-python ╷ │ Error: Missing key/value separator │ │ on cdk.tf.json line 52, in resource.aws_route53_record.example (example_2): │ 52: "for_each": "${toset({{ for dvo in {example.domain_validation_options} : dvo.domain_name => {\n name = dvo.resource_record_name\n record = dvo.resource_record_value\n type = dvo.resource_record_type\n }}})}", │ │ Expected an equals sign ("=") to mark the beginning of the attribute value.
Relevant Error/Panic Output Snippet
Terraform Configuration Files
Here's the cdktf.json file;
Steps to Reproduce
init the project
cdktf init --template=typescript --providers=hashicorp/aws --local
paste the following into main.py
set the domain name and hosted zone id to correct values.
run the deployment
cdktf deploy
Debug Output
No response
Panic Output
No response
Important Factoids
Here is the cdk.tf.json file generated;
References
No response
Would you like to implement a fix?
None