cdk parameter ref as json during synthesize

Viewed 306

I am using AWS Amplify along with a custom stack generated with the AWS CDK. In this stack, I am attempting to use the env parameter supplied by the Amplify CLI when pushing my cloud formation templates. For example, I am trying to make the ServerlessCluster clusterIdentifier property dynamic based on the env parameter.

I have tried several variations but I can't seem to get around receiving tokens rather than the desired output of:

{
  "Ref": "env"
}

Is there any escape hatch to have the CDK output the above Ref JSON during synthesizing time?

Note: I have to run cdk synth -o amplify/backend/custom/cdk in order to let amplify continue to control deployment.

1 Answers

Rather than try to use the env parameter supplied by amplify directly in the cdk stack. I ended up creating a backend stack per environment like this

new CdkBackendStack(app, "prod-stack", {
  stackName: "prod-stack",
  deploymentEnvironment: "prod",
});

new CdkBackendStack(app, "dev-stack", {
  stackName: "dev-stack",
  deploymentEnvironment: "dev",
});

I can then use this deploymentEnvironment prop to conditionally create the stack and each stack can produce the needed output like this:

new cdk.CfnOutput(this, "ClusterSecretArn", {
  value: serverlessCluster.secret?.secretArn || "",
});

Now I can synthesize both stacks and output to a custom amplify stack like this

cdk synth dev-stack > ../amplify/backend/cdk/dev/dev-cloudformation-template.yml

cdk synth prod-stack > ../amplify/backend/cdk/prod/prod-cloudformation-template.yml

Following amplify docs to add a custom stack, both the dev and prod stack are added to backend-config.json:

  "cdk": {
    "dev": {
      "providerPlugin": "awscloudformation"
    },
    "prod": {
      "providerPlugin": "awscloudformation"
    }
  },

And then I can use the output in other amplify stacks by modifying backend-config.json like this:

"dependsOn": [
        {
          "category": "cdk",
          "resourceName": "dev",
          "attributes": [
            "DatabaseName",
            "SchemaName",
            "ClusterArn",
            "ClusterSecretArn"
          ]
        },
        {
          "category": "cdk",
          "resourceName": "prod",
          "attributes": [
            "DatabaseName",
            "SchemaName",
            "ClusterArn",
            "ClusterSecretArn"
          ]
        }
      ],

I can define the new parameters in the stack like

  "DevDatabaseName": {
    "Fn::GetAtt": [
      "cdkdev",
      "Outputs.DatabaseName"
    ]
  },
  "DevSchemaName": {
    "Fn::GetAtt": [
      "cdkdev",
      "Outputs.SchemaName"
    ]
  },
  "DevClusterArn": {
    "Fn::GetAtt": [
      "cdkdev",
      "Outputs.ClusterArn"
    ]
  },
  "DevClusterSecretArn": {
    "Fn::GetAtt": [
      "cdkdev",
      "Outputs.ClusterSecretArn"
    ]
  },
  "ProdDatabaseName": {
    "Fn::GetAtt": [
      "cdkprod",
      "Outputs.DatabaseName"
    ]
  }
  ...

And then in the cloudformation template I have a condition defined:

  "Conditions": {
    "IsDevEnvironment": {
      "Fn::Equals": [
        {
          "Ref": "env"
        },
        "dev"
      ]
    }
  }

and finally, where I need these parameters I have:

"DbClusterIdentifier": {
  "Fn::If": [
    "IsDevEnvironment",
    {
      "Ref": "DevClusterArn"
    },
    {
      "Ref": "ProdClusterArn"
    }
  ]
},

I was hoping for a more elegant solution but this was the best I could manage.

Related