Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Documentation]: Python examples for aws_acm_certificate do not work #33613

Open
DanielRepik opened this issue Sep 25, 2023 · 1 comment
Open
Labels
documentation Introduces or discusses updates to documentation. service/acm Issues and PRs that pertain to the acm service.

Comments

@DanielRepik
Copy link

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:

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          │
└───────────────┴──────────────────┴─────────┴────────────┴──────────────────────────┴─────────────────┘

Terraform Configuration Files

Here's the cdktf.json file;

{
  "language": "python",
  "app": "pipenv run python main.py",
  "projectId": "0a834de4-5397-4b3c-b822-0c7983809785",
  "sendCrashReports": "true",
  "terraformProviders": [
    "hashicorp/aws@~>4.0"
  ],
  "terraformModules": [],
  "codeMakerOutput": "imports",
  "context": {}
}

Steps to Reproduce

init the project

cdktf init --template=typescript --providers=hashicorp/aws --local

paste the following into main.py

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.

run the deployment

cdktf deploy

Debug Output

No response

Panic Output

No response

Important Factoids

Here is the cdk.tf.json file generated;

{
  "//": {
    "metadata": {
      "backend": "local",
      "stackName": "tf-test-python",
      "version": "0.18.0"
    },
    "outputs": {
    }
  },
  "provider": {
    "aws": [
      {
        "region": "eu-west-1"
      }
    ]
  },
  "resource": {
    "aws_acm_certificate": {
      "example": {
        "//": {
          "metadata": {
            "path": "tf-test-python/example",
            "uniqueId": "example"
          }
        },
        "domain_name": "<domain-name-goes-here>",
        "validation_method": "DNS"
      }
    },
    "aws_acm_certificate_validation": {
      "example_3": {
        "//": {
          "metadata": {
            "path": "tf-test-python/example_3",
            "uniqueId": "example_3"
          }
        },
        "certificate_arn": "${aws_acm_certificate.example.arn}",
        "validation_record_fqdns": "{[ for record in example_2 : record.fqdn]}"
      }
    },
    "aws_route53_record": {
      "example": {
        "//": {
          "metadata": {
            "path": "tf-test-python/example_2",
            "uniqueId": "example"
          }
        },
        "allow_overwrite": true,
        "for_each": "${toset({{ for dvo in example.domain_validation_options : dvo.domain_name => dvo}})}",
        "name": "${each.value.resource_record_name}",
        "records": [
          "${each.value.resource_record_value}"
        ],
        "ttl": 60,
        "type": "${each.value.resource_record_type}",
        "zone_id": "<zone_id_goes_here>"
      }
    }
  },
  "terraform": {
    "backend": {
      "local": {
        "path": "/Users/drepik/workspaces/tf-test-python/terraform.tf-test-python.tfstate"
      }
    },
    "required_providers": {
      "aws": {
        "source": "hashicorp/aws",
        "version": "4.67.0"
      }
    }
  }
}

References

No response

Would you like to implement a fix?

None

@DanielRepik DanielRepik added the bug Addresses a defect in current functionality. label Sep 25, 2023
@github-actions
Copy link

Community Note

Voting for Prioritization

  • Please vote on this issue by adding a 👍 reaction to the original post to help the community and maintainers prioritize this request.
  • Please see our prioritization guide for information on how we prioritize.
  • 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-aws-provider terraform-aws-provider bot added the needs-triage Waiting for first response or review from a maintainer. label Sep 25, 2023
@justinretzolk justinretzolk added documentation Introduces or discusses updates to documentation. service/acm Issues and PRs that pertain to the acm service. and removed bug Addresses a defect in current functionality. needs-triage Waiting for first response or review from a maintainer. labels Nov 8, 2023
@justinretzolk justinretzolk changed the title [Bug]: [Documentation]: Python examples for aws_acm_certificate do not work Nov 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Introduces or discusses updates to documentation. service/acm Issues and PRs that pertain to the acm service.
Projects
None yet
Development

No branches or pull requests

2 participants