Now I show you how to use a json file for the additional AnalyticsConfigurationProperty object.
All CloudFormation Resources are defined within the CDK itself. The AnalyticsConfigurationProperty in GO CDK is defined as:
type CfnBucket_AnalyticsConfigurationProperty struct {
Id *string `json:"id" yaml:"id"`
StorageClassAnalysis interface{} `json:"storageClassAnalysis" yaml:"storageClassAnalysis"`
Prefix *string `json:"prefix" yaml:"prefix"`
TagFilters interface{} `json:"tagFilters" yaml:"tagFilters"`
}
Because the type of StorageClassAnalysis is interface, which means “any type”, the StorageClassAnalysis property will not be typechecked.
So for example if you make an error and do not define AnalyticsConfigurationProperty as a list, the rendered CloudFormation will be like:
buckyE21EA0FF:
Type: AWS::S3::Bucket
Properties:
AnalyticsConfigurations:
Id: AnalyticsConfigurationId
StorageClassAnalysis:
CDK will synth and deploy, but the CloudFormation will give you an error at deploy time:
Value of property AnalyticsConfigurations must be of type List
Because what it should be is:
buckyE21EA0FF:
Type: AWS::S3::Bucket
Properties:
AnalyticsConfigurations:
- Id: AnalyticsConfigurationId
Prefix: AnalyticsConfigurationPrefix
StorageClassAnalysis:
So you have wasted about a minute each time you change your code. We don`t want that.
You could use cfn-lint before you deploy.
When you change to the base directory of this chapter, * infrastructure-as-go/cdk-go/escape-hatch*, you would type this to chexk the *string* stack:
cfn-lint cdk.out/string.template.json
Which will detect the error:
cfn-lint cdk.out/string.template.json
E3002 Property is an object instead of List at Resources/buckyE21EA0FF/Properties/AnalyticsConfigurations
cdk.out/string.template.json:14:9
To prevent this kind if error, we use strong typing support.
In goformation (4:) all CloudFormation schemata are generated as GO structs.
type Bucket struct {
// ...
AnalyticsConfigurations []Bucket_AnalyticsConfiguration `json:"AnalyticsConfigurations,omitempty"`
As you see AnalyticsConfigurations is not an interface{} type, but has its own struct. So we have the type information.
In type Bucket_AnalyticsConfiguration you see:
type Bucket_AnalyticsConfiguration struct {
Id string `json:"Id,omitempty"`
Prefix string `json:"Prefix,omitempty"`
StorageClassAnalysis *Bucket_StorageClassAnalysis `json:"StorageClassAnalysis,omitempty"`
//...
The tagged JSON information will be used to render CloudFormation. So you could call the variable Id also AnalyticsID and the CloudFormation json will render Id, because of
AnalyticsID string `json:"Id,omitempty"`
Now we can use this type definition of GOformation in the CDK app ( see escape-hatch-file.go):
import (
"github.com/awslabs/goformation/v5/cloudformation/s3"
)
[
{
"Id": "AnalyticsConfigurationId",
"StorageClassAnalysis": {
"DataExport": {
"Destination": {
"BucketArn": {
"Fn::GetAtt": [
"Helper",
"Arn"
]
},
"Format": "CSV",
"Prefix": "AnalyticsDestinationPrefix"
},
"OutputSchemaVersion": "V_1"
}
},
"Prefix": "AnalyticsConfigurationPrefix",
"TagFilters": [
{
"Key": "AnalyticsTagKey",
"Value": "AnalyticsTagValue"
}
]
}
]
var analyticsConfigurationFromFile []s3.Bucket_AnalyticsConfiguration
data, err := os.ReadFile("testdata/analyticsconfig.json")
json.Unmarshal(data, &analyticsConfigurationFromFile)
The helper bucket is dynamically generated so you have to set this reference:
analyticsConfigurationFromFile[0].StorageClassAnalysis.DataExport.Destination.BucketArn = *helper.BucketArn()
Now you can deploy to CloudFormation without errors.
With this approach you can support strong typing on all CloudFormation resources.
If you have Upper/lowercase errors in names like storageclassAnalysis instead of StorageClassAnalysis, the umarshal process will ignore the case and render the correct name defined in the json tag json:"StorageClassAnalysis,omitempty"
of the GO structure.
If you add additional, not cfn-supported objects in the json file, they will be ignored. See file testdata/analyticsconfig-bad.json for an example
Read about how to directly create the structure in the next chapter.
See the full source on github.