aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.41k stars 3.8k forks source link

codepipeline-actions: RuntimeError: Error: The 'account' property must be a concrete value #29361

Open subhashisbhowmik opened 4 months ago

subhashisbhowmik commented 4 months ago

Describe the bug

I am seeing RuntimeError: Error: The 'account' property must be a concrete value during cdk synth

Expected Behavior

cdk synth succeeds

Current Behavior

cdk synth fails with RuntimeError: Error: The 'account' property must be a concrete value

During cdk synth --profile <profile> I get:

(.venv) (py311) PS C:\Users\Subhashis\Desktop\DHCDK> cdk synth --profile <profile>
123456789
ap-south-1
jsii.errors.JavaScriptError:
  @jsii/kernel.RuntimeError: Error: The 'account' property must be a concrete value (action: 'DeployBeta')
      at Kernel._Kernel_ensureSync (C:\Users\SUBHAS~1\AppData\Local\Temp\tmpllzgwr8m\lib\program.js:10491:23)
      at Kernel.invoke (C:\Users\SUBHAS~1\AppData\Local\Temp\tmpllzgwr8m\lib\program.js:9855:102)
      at KernelHost.processRequest (C:\Users\SUBHAS~1\AppData\Local\Temp\tmpllzgwr8m\lib\program.js:11696:36)
      at KernelHost.run (C:\Users\SUBHAS~1\AppData\Local\Temp\tmpllzgwr8m\lib\program.js:11656:22)
      at Immediate._onImmediate (C:\Users\SUBHAS~1\AppData\Local\Temp\tmpllzgwr8m\lib\program.js:11657:46)
      at process.processImmediate (node:internal/timers:476:21)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\Subhashis\Desktop\DHCDK\app.py", line 22, in <module>
    DhAppPipelineStack(app, "DhAppPipelineStack", cdk_pipeline=cdk_pipeline, env=env)
  File "C:\Users\Subhashis\Desktop\DHCDK\.venv\Lib\site-packages\jsii\_runtime.py", line 118, in __call__
    inst = super(JSIIMeta, cast(JSIIMeta, cls)).__call__(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Subhashis\Desktop\DHCDK\dhcdk\application_pipeline\dh_app_pipeline_stack.py", line 75, in __init__
    beta_ecs_stage = self.create_deploy_stage("Beta", pipeline, build_output, kwargs["env"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Subhashis\Desktop\DHCDK\dhcdk\application_pipeline\dh_app_pipeline_stack.py", line 107, in create_deploy_stage
    stage.add_action(deploy_action)
  File "C:\Users\Subhashis\Desktop\DHCDK\.venv\Lib\site-packages\aws_cdk\aws_codepipeline\__init__.py", line 5814, in add_action
    return typing.cast(None, jsii.invoke(self, "addAction", [action]))
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Subhashis\Desktop\DHCDK\.venv\Lib\site-packages\jsii\_kernel\__init__.py", line 149, in wrapped
    return _recursize_dereference(kernel, fn(kernel, *args, **kwargs))
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Subhashis\Desktop\DHCDK\.venv\Lib\site-packages\jsii\_kernel\__init__.py", line 399, in invoke
    response = self.provider.invoke(
               ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Subhashis\Desktop\DHCDK\.venv\Lib\site-packages\jsii\_kernel\providers\process.py", line 380, in invoke
    return self._process.send(request, InvokeResponse)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Subhashis\Desktop\DHCDK\.venv\Lib\site-packages\jsii\_kernel\providers\process.py", line 342, in send
    raise RuntimeError(resp.error) from JavaScriptError(resp.stack)
RuntimeError: Error: The 'account' property must be a concrete value (action: 'DeployBeta')

Reproduction Steps

app.py

#!/usr/bin/env python3
import aws_cdk as cdk

from dhcdk.infra_pipeline.dh_infra_pipeline_stack import DhInfraPipelineStack
from dhcdk.application_pipeline.dh_app_pipeline_stack import DhAppPipelineStack

account_id = '123456789'
region = 'ap-south-1'
env = cdk.Environment(account=account_id, region=region)

app = cdk.App()

DhInfraPipelineStack(app, "DhcdkPipelineStack", env=env)
DhAppPipelineStack(app, "DhAppPipelineStack", env=env)

app.synth()

dh_app_pipeline_stack.py:

from aws_cdk import (
    Stack,
    aws_codebuild as codebuild,
    aws_codepipeline as codepipeline,
    aws_codepipeline_actions as codepipeline_actions,
    aws_ecs as ecs,
    aws_ssm as ssm,
)
from constructs import Construct
from aws_cdk import aws_ecr as ecr
import aws_cdk

class DhAppPipelineStack(Stack):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        repository = ecr.Repository.from_repository_name(self, "drh_core_application", "drh_core_application")

        pipeline = codepipeline.Pipeline(
            self, "MyPipeline",
            pipeline_name="MyPipeline"
        )

        # Define the source stage
        source_output = codepipeline.Artifact()
        source_action = codepipeline_actions.CodeStarConnectionsSourceAction(
            connection_arn="arn:aws:codestar-connections:ap-south-1:123456789:connection/abcde",
            action_name="Source",
            output=source_output,
            owner="Artemis-Technologies",
            repo="CoreService",
            branch="main"
        )
        pipeline.add_stage(
            stage_name="Source",
            actions=[source_action]
        )

        # Define the build stage
        build_output = codepipeline.Artifact()
        build_action = codepipeline_actions.CodeBuildAction(
            action_name="Build",
            project=codebuild.PipelineProject(
                self, "CoreService",
                build_spec=codebuild.BuildSpec.from_source_filename("buildspec.yml"),
                environment=codebuild.BuildEnvironment(
                    build_image=codebuild.LinuxBuildImage.STANDARD_7_0
                )
            ),
            input=source_output,
            outputs=[build_output]
        )
        pipeline.add_stage(
            stage_name="Build",
            actions=[build_action]
        )

        # Define the deploy stages
        beta_ecs_stage = self.create_deploy_stage("Beta", pipeline, build_output, kwargs["env"])
        prod_ecs_stage = self.create_deploy_stage("Prod", pipeline, build_output, kwargs["env"])
        #
        # # Add manual approval action between beta and prod stages
        # manual_approval_action = codepipeline_actions.ManualApprovalAction(
        #     action_name="Approve",
        #     run_order=1
        # )
        # prod_ecs_stage.add_action(manual_approval_action)

    def create_deploy_stage(self, stage: str, pipeline: codepipeline.Pipeline,
                            build_output: codepipeline.Artifact, env) -> codepipeline.IStage:

        print(env.account)
        print(env.region)
        ecs_cluster_arn = ssm.StringParameter.value_for_string_parameter(self, f"/deploymentInfo/{stage}/DrhClusterArn")
        ecs_service_arn = ssm.StringParameter.value_for_string_parameter(self, f"/deploymentInfo/{stage}/DrhApplicationArn")
        ecs_cluster = ecs.Cluster.from_cluster_arn(self, f"{stage}-DrhCluster", cluster_arn=ecs_cluster_arn)
        ecs_service = ecs.FargateService.from_fargate_service_attributes(self, f"{stage}-DrhApplication", service_arn=ecs_service_arn, cluster=ecs_cluster)

        # print(ecs_service)
        deploy_action = codepipeline_actions.EcsDeployAction(
            action_name=f"Deploy{stage}",
            service=ecs_service,
            image_file=build_output.at_path("imageDetail.json"),
            run_order=1,
            deployment_timeout=aws_cdk.Duration.minutes(60),
        )

        stage = pipeline.add_stage(
            stage_name=stage
        )
        stage.add_action(deploy_action)

        return stage

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.82.0 (build 3a8648a)

Framework Version

No response

Node.js Version

v18.16.0

OS

Windows 10

Language

Python

Language Version

3.11

Other information

No response

pahud commented 4 months ago

Hi

2.82.0 was pretty old version. Can you try the latest CDK version v2.131.0 from a clean repo and see if this issue still exists?

subhashisbhowmik commented 4 months ago

Hi,

Had some issues with updating CDK, so moved to wsl2. Still seeing the same issue with the updated CDK

(.venv) (base) root@Subhashis-Desktop:/mnt/c/Users/Subhashis/Desktop/DHCDK# cdk synth
123456789
ap-south-1
jsii.errors.JavaScriptError:
  @jsii/kernel.RuntimeError: Error: The 'account' property must be a concrete value (action: 'DeployBeta')
      at Kernel._Kernel_ensureSync (/tmp/tmp0upb_l2n/lib/program.js:10491:23)
      at Kernel.invoke (/tmp/tmp0upb_l2n/lib/program.js:9855:102)
      at KernelHost.processRequest (/tmp/tmp0upb_l2n/lib/program.js:11696:36)
      at KernelHost.run (/tmp/tmp0upb_l2n/lib/program.js:11656:22)
      at Immediate._onImmediate (/tmp/tmp0upb_l2n/lib/program.js:11657:46)
      at process.processImmediate (node:internal/timers:478:21)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/mnt/c/Users/Subhashis/Desktop/DHCDK/app.py", line 14, in <module>
    DhAppPipelineStack(app, "DhAppPipelineStack", env=env)
  File "/root/miniconda3/lib/python3.11/site-packages/jsii/_runtime.py", line 118, in __call__
    inst = super(JSIIMeta, cast(JSIIMeta, cls)).__call__(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Subhashis/Desktop/DHCDK/dhcdk/application_pipeline/dh_app_pipeline_stack.py", line 59, in __init__
    beta_ecs_stage = self.create_deploy_stage("Beta", pipeline, build_output, kwargs["env"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/mnt/c/Users/Subhashis/Desktop/DHCDK/dhcdk/application_pipeline/dh_app_pipeline_stack.py", line 91, in create_deploy_stage
    stage.add_action(deploy_action)
  File "/root/miniconda3/lib/python3.11/site-packages/aws_cdk/aws_codepipeline/__init__.py", line 5814, in add_action
    return typing.cast(None, jsii.invoke(self, "addAction", [action]))
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/miniconda3/lib/python3.11/site-packages/jsii/_kernel/__init__.py", line 149, in wrapped
    return _recursize_dereference(kernel, fn(kernel, *args, **kwargs))
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/miniconda3/lib/python3.11/site-packages/jsii/_kernel/__init__.py", line 399, in invoke
    response = self.provider.invoke(
               ^^^^^^^^^^^^^^^^^^^^^
  File "/root/miniconda3/lib/python3.11/site-packages/jsii/_kernel/providers/process.py", line 380, in invoke
    return self._process.send(request, InvokeResponse)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/miniconda3/lib/python3.11/site-packages/jsii/_kernel/providers/process.py", line 342, in send
    raise RuntimeError(resp.error) from JavaScriptError(resp.stack)
RuntimeError: Error: The 'account' property must be a concrete value (action: 'DeployBeta')

Subprocess exited with error 1
(.venv) (base) root@Subhashis-Desktop:/mnt/c/Users/Subhashis/Desktop/DHCDK# cdk --version
2.131.0 (build 92b912d)
subhashisbhowmik commented 4 months ago

Even if I use CfnOutput + Fn.import_value, the result is the same I suppose the issue is with codepipeline_actions.EcsDeployAction

        ecs_cluster = ecs.Cluster.from_cluster_arn(self, f"{stage}-DrhCluster", cluster_arn=ecs_cluster_arn)
        ecs_service = ecs.FargateService.from_fargate_service_attributes(self, f"{stage}-DrhApplication", service_arn=ecs_service_arn, cluster=ecs_cluster)

        # print(ecs_service)
        deploy_action = codepipeline_actions.EcsDeployAction(
            action_name=f"Deploy{stage}",
            service=ecs_service,
            image_file=build_output.at_path("imageDetail.json"),
            run_order=1,
            deployment_timeout=aws_cdk.Duration.minutes(60),
        )

        stage = pipeline.add_stage(
            stage_name=stage
        )
        stage.add_action(deploy_action)
pahud commented 4 months ago

Thank you for the report.

Let's simplify the provided code snippets.

Are you able to just create the EcsDeployAction by passing a static serviceArn like this?

This is a typescript sample that I can cdk synth with no error and I guess cdk python should work too.

export class DummyStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const vpc = getDefaultVpc(this);
    const cluster = new ecs.Cluster(this, 'Cluster', { vpc });

    const sourceOutput = new codepipeline.Artifact();

    const accountId = 'YOUR_ACCOUNT_ID';

    // create a dummy sourceAction
    const sourceAction = new codepipelineActions.CodeStarConnectionsSourceAction({
      actionName: 'BitBucket_Source',
      owner: 'aws',
      repo: 'aws-cdk',
      output: sourceOutput,
      connectionArn: `arn:aws:codestar-connections:us-east-1:${accountId}:connection/12345678-abcd-12ab-34cdef5678gh`,
      variablesNamespace: 'SomeSpace',
    });

    const deployAction = new codepipelineActions.EcsDeployAction({
      actionName: 'foo',
      service: ecs.FargateService.fromFargateServiceAttributes(this, 'FGService', {
        cluster,
        serviceArn: `arn:aws:ecs:us-east-1:${accountId}:service/cluster-name/service-name`,
      }),
      input: sourceOutput,
    })

    const pipeline = new codepipeline.Pipeline(this, 'MyPipeline');
    pipeline.addStage({
      stageName: 'Source',
      actions: [sourceAction],
    });
    pipeline.addStage({
      stageName: 'Deploy',
      actions: [deployAction],
    });
  }
}
pahud commented 4 months ago

I am guessing as you are using ssm.StringParameter.value_for_string_parameter to represent the ecs_service_arn, which will be passed here as resource prop and might be having issue to determine the account in the synth time as it's a token but I am not 100% sure. Let's first check if passing a full static serviceArn works as my provided sample above.

subhashisbhowmik commented 4 months ago

Hi @pahud, yes, passing the arn directly works, but even if I use Fn.import it doesn't work This works:

        ecs_cluster_arn = "arn:aws:ecs:ap-south-1:123456789:cluster/beta-DhcdkStack-betaDrhCluster86916E42-r9fuNYW5ZfTd"
        ecs_service_arn = "arn:aws:ecs:ap-south-1:123456789:service/beta-DhcdkStack-betaDrhCluster86916E42-r9fuNYW5ZfTd/beta-DrhApplication"

This doesn't:

        ecs_cluster_arn = Fn.import_value(f"{stage}-DrhClusterArn")
        ecs_service_arn = Fn.import_value(f"{stage}-DrhApplicationArn")
moniyazi1 commented 3 months ago

Facing the same issue for exactly the same usecase @subhashisbhowmik described above. Any suggestions on how to proceed?

subhashisbhowmik commented 3 months ago

Hi team, any update on this?