From 0178d30948826d9e720517ef54ddf3b85684a22b Mon Sep 17 00:00:00 2001 From: Matt Bush Date: Sun, 17 Mar 2024 18:01:05 -0700 Subject: [PATCH 01/18] Add providerconfig field for specifying aws partition Signed-off-by: Matt Bush # Conflicts: # internal/clients/aws.go --- apis/v1beta1/types.go | 4 ++++ internal/clients/aws.go | 1 + package/crds/aws.upbound.io_providerconfigs.yaml | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/apis/v1beta1/types.go b/apis/v1beta1/types.go index 3f856cfe95..ffd29991b0 100644 --- a/apis/v1beta1/types.go +++ b/apis/v1beta1/types.go @@ -21,6 +21,10 @@ type ProviderConfigSpec struct { // of AWS calls made by the provider. // +optional Endpoint *EndpointConfig `json:"endpoint,omitempty"` + // Partition defines the aws partition id to use when constructing ARNs. Most AWS regions use the commercial + // partition, simply named aws. + // +kubebuilder:default=aws + Partition string `json:"partition,omitempty"` // Whether to skip credentials validation via the STS API. // This can be useful for testing and for AWS API implementations that do not have STS available. // +optional diff --git a/internal/clients/aws.go b/internal/clients/aws.go index 5613722529..f89a1ad8cf 100644 --- a/internal/clients/aws.go +++ b/internal/clients/aws.go @@ -30,6 +30,7 @@ import ( const ( keyAccountID = "account_id" keyRegion = "region" + keyPartition = "partition" localstackAccountID = "000000000" ) diff --git a/package/crds/aws.upbound.io_providerconfigs.yaml b/package/crds/aws.upbound.io_providerconfigs.yaml index 15d1c5ffa6..be2ddd7323 100644 --- a/package/crds/aws.upbound.io_providerconfigs.yaml +++ b/package/crds/aws.upbound.io_providerconfigs.yaml @@ -401,6 +401,12 @@ spec: required: - url type: object + partition: + default: aws + description: |- + Partition defines the aws partition id to use when constructing ARNs. Most AWS regions use the commercial + partition, simply named aws. + type: string s3_use_path_style: description: Whether to enable the request to use path-style addressing, i.e., https://s3.amazonaws.com/BUCKET/KEY. From 51ff6cd48955e008ed9237e21e9ffeb099ba4781 Mon Sep 17 00:00:00 2001 From: Matt Bush Date: Sun, 17 Mar 2024 18:20:35 -0700 Subject: [PATCH 02/18] Refactor arns built through string interpolations to consider the partition Signed-off-by: Matt Bush # Conflicts: # config/externalname.go --- config/externalname.go | 84 +++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/config/externalname.go b/config/externalname.go index d01dada3f2..389ee6eda8 100644 --- a/config/externalname.go +++ b/config/externalname.go @@ -160,7 +160,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // Uses the ID of workspace, workspace_id parameter. "aws_prometheus_alert_manager_definition": config.IdentifierFromProvider, // - "aws_prometheus_rule_group_namespace": config.TemplatedStringAsIdentifier("name", "arn:aws:aps:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:rulegroupsnamespace/{{ .parameters.workspace_id }}/{{ .external_name }}"), + "aws_prometheus_rule_group_namespace": config.TemplatedStringAsIdentifier("name", fullArnTemplate("aps", "rulegroupsnamespace/{{ .parameters.workspace_id }}/{{ .external_name }}")), // ID is a random UUID. "aws_prometheus_workspace": config.IdentifierFromProvider, @@ -285,7 +285,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // appflow // // arn:aws:appflow:us-west-2:123456789012:flow/example-flow - "aws_appflow_flow": config.TemplatedStringAsIdentifier("name", "arn:aws:appflow:{{ .setup.configuration.region }}:{{ .client_metadata.account_id }}:flow/{{ .external_name }}"), + "aws_appflow_flow": config.TemplatedStringAsIdentifier("name", fullArnTemplate("appflow", "flow/{{ .external_name }}")), // appintegrations // @@ -425,7 +425,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // arn:aws:batch:us-east-1:123456789012:job-definition/sample:1 "aws_batch_job_definition": config.IdentifierFromProvider, // Batch Scheduling Policy can be imported using the arn: arn:aws:batch:us-east-1:123456789012:scheduling-policy/sample - "aws_batch_scheduling_policy": config.TemplatedStringAsIdentifier("name", "arn:aws:batch:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:scheduling-policy/{{ .external_name }}"), + "aws_batch_scheduling_policy": config.TemplatedStringAsIdentifier("name", fullArnTemplate("batch", "scheduling-policy/{{ .external_name }}")), // budgets // @@ -472,7 +472,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // // config.NameAsIdentifier did not work, the identifier for the resource turned out to be an ARN // arn:aws:cloudformation:us-west-1:123456789123:stack/networking-stack/1e691240-6f2c-11ed-8f91-06094dc221f3 - "aws_cloudformation_stack": TemplatedStringAsIdentifierWithNoName("arn:aws:cloudformation:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:stack/{{ .parameters.name }}/{{ .external_name }}"), + "aws_cloudformation_stack": TemplatedStringAsIdentifierWithNoName(fullArnTemplate("cloudformation", "stack/{{ .parameters.name }}/{{ .external_name }}")), // CloudFormation StackSets can be imported using the name "aws_cloudformation_stack_set": config.NameAsIdentifier, // Cloudformation Stacks Instances imported using the StackSet name, target @@ -520,7 +520,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // cloudtrail // // Cloudtrails can be imported using the name arn:aws:cloudtrail:us-west-1:153891904029:trail/foobar - "aws_cloudtrail": config.TemplatedStringAsIdentifier("name", "arn:aws:cloudtrail:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:trail/{{ .external_name }}"), + "aws_cloudtrail": config.TemplatedStringAsIdentifier("name", fullArnTemplate("cloudtrail", "trail/{{ .external_name }}")), // Event data stores can be imported using their arn "aws_cloudtrail_event_data_store": config.IdentifierFromProvider, @@ -602,7 +602,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // CodeDeploy CustomActionType can be imported using the id "aws_codepipeline_custom_action_type": config.IdentifierFromProvider, // CodePipeline Webhooks can be imported by their ARN: arn:aws:codepipeline:us-west-2:123456789012:webhook:example - "aws_codepipeline_webhook": config.TemplatedStringAsIdentifier("name", "arn:aws:codepipeline:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:webhook:{{ .external_name }}"), + "aws_codepipeline_webhook": config.TemplatedStringAsIdentifier("name", fullArnTemplate("codepipeline", "webhook:{{ .external_name }}")), // codestarconnections // @@ -1115,7 +1115,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // ECS Account Setting defaults can be imported using the name "aws_ecs_account_setting_default": config.IdentifierFromProvider, // - "aws_ecs_capacity_provider": config.TemplatedStringAsIdentifier("name", "arn:aws:ecs:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:capacity-provider/{{ .external_name }}"), + "aws_ecs_capacity_provider": config.TemplatedStringAsIdentifier("name", fullArnTemplate("ecs", "capacity-provider/{{ .external_name }}")), // "aws_ecs_cluster": config.TemplatedStringAsIdentifier("name", "arn:aws:ecs:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:cluster/{{ .external_name }}"), // ECS cluster capacity providers can be imported using the cluster_name attribute @@ -1262,7 +1262,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_evidently_project": config.IdentifierFromProvider, // CloudWatch Evidently Segment can be imported using the arn // Example: arn:aws:evidently:us-west-2:123456789012:segment/example - "aws_evidently_segment": config.TemplatedStringAsIdentifier("name", "arn:aws:evidently:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:segment/{{ .external_name }}"), + "aws_evidently_segment": config.TemplatedStringAsIdentifier("name", fullArnTemplate("evidently", "segment/{{ .external_name }}")), // firehose // @@ -1333,7 +1333,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // "aws_glue_job": config.NameAsIdentifier, // Imported using ARN: arn:aws:glue:us-west-2:123456789012:registry/example - "aws_glue_registry": config.TemplatedStringAsIdentifier("registry_name", "arn:aws:glue:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:registry/{{ .external_name }}"), + "aws_glue_registry": config.TemplatedStringAsIdentifier("registry_name", fullArnTemplate("glue", "registry/{{ .external_name }}")), // Imported using the account ID: 12356789012 "aws_glue_resource_policy": config.IdentifierFromProvider, // Glue Registries can be imported using arn @@ -1402,7 +1402,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // test-role/arn:aws:iam::xxxxxxxxxxxx:policy/test-policy "aws_iam_role_policy_attachment": config.IdentifierFromProvider, // IAM SAML Providers can be imported using the arn - "aws_iam_saml_provider": config.TemplatedStringAsIdentifier("name", "arn:aws:iam::{{ .setup.client_metadata.account_id }}:saml-provider/{{ .external_name }}"), + "aws_iam_saml_provider": config.TemplatedStringAsIdentifier("name", regionlessArnTemplate("iam", "saml-provider/{{ .external_name }}")), // IAM Server Certificates can be imported using the name "aws_iam_server_certificate": config.NameAsIdentifier, // IAM service-linked roles can be imported using role ARN that contains the @@ -1521,7 +1521,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_iot_topic_rule": config.NameAsIdentifier, // IoT topic rule destinations can be imported using the arn // arn:aws:iot:us-west-2:123456789012:ruledestination/vpc/2ce781c8-68a6-4c52-9c62-63fe489ecc60 - "aws_iot_topic_rule_destination": TemplatedStringAsProviderDefinedIdentifier("arn:aws:iot:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:ruledestination/vpc/{{ .external_name }}"), + "aws_iot_topic_rule_destination": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("iot", "ruledestination/vpc/{{ .external_name }}")), // ivs // @@ -1586,18 +1586,18 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // kinesis // // Even though the documentation says the ID is name, it uses ARN.. - "aws_kinesis_stream": config.TemplatedStringAsIdentifier("name", "arn:aws:kinesis:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:stream/{{ .external_name }}"), + "aws_kinesis_stream": config.TemplatedStringAsIdentifier("name", fullArnTemplate("kinesis", "stream/{{ .external_name }}")), // Kinesis Stream Consumers can be imported using the Amazon Resource Name (ARN) // that has a random substring. "aws_kinesis_stream_consumer": config.IdentifierFromProvider, // kinesisanalytics // - "aws_kinesis_analytics_application": config.TemplatedStringAsIdentifier("name", "arn:aws:kinesisanalytics:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:application/{{ .external_name }}"), + "aws_kinesis_analytics_application": config.TemplatedStringAsIdentifier("name", fullArnTemplate("kinesisanalytics", "application/{{ .external_name }}")), // kinesisanalyticsv2 // - "aws_kinesisanalyticsv2_application": config.TemplatedStringAsIdentifier("name", "arn:aws:kinesisanalytics:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:application/{{ .external_name }}"), + "aws_kinesisanalyticsv2_application": config.TemplatedStringAsIdentifier("name", fullArnTemplate("kinesisanalytics", "application/{{ .external_name }}")), // aws_kinesisanalyticsv2_application can be imported by using application_name together with snapshot_name // e.g. example-application/example-snapshot "aws_kinesisanalyticsv2_application_snapshot": FormattedIdentifierUserDefinedNameLast("snapshot_name", "/", "application_name"), @@ -1638,7 +1638,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // lambda // // Lambda Function Aliases are identified by their ARN, like arn:aws:lambda:eu-west-1:123456789012:function:lambda-function:alias - "aws_lambda_alias": config.TemplatedStringAsIdentifier("name", "arn:aws:lambda:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:function:{{ .parameters.function_name }}:{{ .external_name }}"), + "aws_lambda_alias": config.TemplatedStringAsIdentifier("name", fullArnTemplate("lambda", "function:{{ .parameters.function_name }}:{{ .external_name }}")), // Code Signing Configs can be imported using their ARN that has a random // substring in the end. // arn:aws:lambda:us-west-2:123456789012:code-signing-config:csc-0f6c334abcdea4d8b @@ -1833,13 +1833,13 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_networkfirewall_firewall": config.IdentifierFromProvider, // Network Firewall Policies can be imported using their ARN // Example: arn:aws:network-firewall:us-west-1:123456789012:firewall-policy/example - "aws_networkfirewall_firewall_policy": config.TemplatedStringAsIdentifier("name", "arn:aws:network-firewall:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:firewall-policy/{{ .external_name }}"), + "aws_networkfirewall_firewall_policy": config.TemplatedStringAsIdentifier("name", fullArnTemplate("network-firewall", "firewall-policy/{{ .external_name }}")), // Network Firewall Logging Configurations can be imported using the firewall_arn // Example: arn:aws:network-firewall:us-west-1:123456789012:firewall/example "aws_networkfirewall_logging_configuration": config.IdentifierFromProvider, // Network Firewall Rule Groups can be imported using their ARN // Example: arn:aws:network-firewall:us-west-1:123456789012:stateful-rulegroup/example - "aws_networkfirewall_rule_group": config.TemplatedStringAsIdentifier("", "arn:aws:network-firewall:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:{{ .parameters.type | ToLower }}-rulegroup/{{ .external_name }}"), + "aws_networkfirewall_rule_group": config.TemplatedStringAsIdentifier("", fullArnTemplate("network-firewall", "{{ .parameters.type | ToLower }}-rulegroup/{{ .external_name }}")), // networkmanager // @@ -2348,7 +2348,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // arn:aws:securityhub:eu-west-1:312940875350:action/custom/a // TODO: following configuration assumes the `a` in the above ARN // is the security hub custom action identifier - "aws_securityhub_action_target": config.TemplatedStringAsIdentifier("identifier", "arn:aws:securityhub:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:action/custom/{{ .external_name }}"), + "aws_securityhub_action_target": config.TemplatedStringAsIdentifier("identifier", fullArnTemplate("securityhub", "action/custom/{{ .external_name }}")), // imported using the arn that has a random substring: // arn:aws:securityhub:eu-west-1:123456789098:finding-aggregator/abcd1234-abcd-1234-1234-abcdef123456 "aws_securityhub_finding_aggregator": config.IdentifierFromProvider, @@ -2515,9 +2515,9 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // sfn // - "aws_sfn_activity": config.TemplatedStringAsIdentifier("name", "arn:aws:states:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:activity/{{ .external_name }}"), + "aws_sfn_activity": config.TemplatedStringAsIdentifier("name", fullArnTemplate("states", "activity/{{ .external_name }}")), // - "aws_sfn_state_machine": config.TemplatedStringAsIdentifier("name", "arn:aws:states:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:stateMachine:{{ .external_name }}"), + "aws_sfn_state_machine": config.TemplatedStringAsIdentifier("name", fullArnTemplate("states", "stateMachine:{{ .external_name }}")), // signer // @@ -2533,12 +2533,12 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // // SNS platform applications can be imported using the ARN: // arn:aws:sns:us-west-2:0123456789012:app/GCM/gcm_application - "aws_sns_platform_application": config.TemplatedStringAsIdentifier("name", "arn:aws:sns:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:app/GCM/{{ .external_name }}"), + "aws_sns_platform_application": config.TemplatedStringAsIdentifier("name", fullArnTemplate("sns", "app/GCM/{{ .external_name }}")), // no import documentation is provided // TODO: we will need to check if normalization is possible "aws_sns_sms_preferences": config.IdentifierFromProvider, // SNS Topics can be imported using the topic arn - "aws_sns_topic": config.TemplatedStringAsIdentifier("name", "arn:aws:sns:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:{{ .external_name }}"), + "aws_sns_topic": config.TemplatedStringAsIdentifier("name", fullArnTemplate("sns", "{{ .external_name }}")), // SNS Topic Policy can be imported using the topic ARN: // arn:aws:sns:us-west-2:0123456789012:my-topic "aws_sns_topic_policy": FormattedIdentifierFromProvider("", "arn"), @@ -3245,3 +3245,43 @@ func apiGatewayAccount() config.ExternalName { } return e } + +// fullArnTemplate builds a templated string for constructing a terraform id component which is an ARN, which includes +// the aws partition, service, region, account id, and resource. This is by far the most common form of ARN. +// e.g. arn:aws:ec2:ap-south-1:123456789012:instance/i-1234567890ab +func fullArnTemplate(service string, resource string) string { + return genericArnTemplate(service, resource, false, false) + +} + +// globalArnTemplate builds a templated string for constructing a terraform id component which is an ARN of a resource +// which is global to the entire AWS partition, including only the partition, service, and resource. +// e.g. arn:aws:s3:::name-of-my-bucket +func globalArnTemplate(service string, resource string) string { + return genericArnTemplate(service, resource, true, true) +} + +// regionlessArnTemplate builds a templated string for constructing a terraform id component which is an ARN of a +// resource which is regionless, but specific to your account id. It includes the partition, service, account id, and +// resource. +// e.g. arn:aws:iam::123456789012:role/example +func regionlessArnTemplate(service string, resource string) string { + return genericArnTemplate(service, resource, false, true) +} + +// genericArnTemplate builds a templated string for constructing a terraform id component which is an ARN of any format. +// It always includes the aws partition, service, and resource. Unless you specify to elide them, it will also include +// templates which resolve to the region (from the spec.forProvider) and the account id (calculated from the provider +// config). +func genericArnTemplate(service string, resource string, elideAccountId bool, elideRegion bool) string { + region := "{{ .setup.configuration.region }}" + if elideRegion { + region = "" + } + accountId := "{{ .setup.client_metadata.account_id }}" + if elideAccountId { + accountId = "" + } + partition := "{{ .setup.configuration.partition }}" + return fmt.Sprintf("arn:%s:%s:%s:%s:%s", partition, service, region, accountId, resource) +} From 0d457b36ab4df92fcdd687e4fe30f2faec736543 Mon Sep 17 00:00:00 2001 From: Matt Bush Date: Sun, 17 Mar 2024 23:35:15 -0700 Subject: [PATCH 03/18] Use existing partition id Signed-off-by: Matt Bush --- apis/v1beta1/types.go | 4 ---- internal/clients/aws.go | 6 ++++++ package/crds/aws.upbound.io_providerconfigs.yaml | 6 ------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/apis/v1beta1/types.go b/apis/v1beta1/types.go index ffd29991b0..3f856cfe95 100644 --- a/apis/v1beta1/types.go +++ b/apis/v1beta1/types.go @@ -21,10 +21,6 @@ type ProviderConfigSpec struct { // of AWS calls made by the provider. // +optional Endpoint *EndpointConfig `json:"endpoint,omitempty"` - // Partition defines the aws partition id to use when constructing ARNs. Most AWS regions use the commercial - // partition, simply named aws. - // +kubebuilder:default=aws - Partition string `json:"partition,omitempty"` // Whether to skip credentials validation via the STS API. // This can be useful for testing and for AWS API implementations that do not have STS available. // +optional diff --git a/internal/clients/aws.go b/internal/clients/aws.go index f89a1ad8cf..1b7b70b63d 100644 --- a/internal/clients/aws.go +++ b/internal/clients/aws.go @@ -94,7 +94,13 @@ func SelectTerraformSetup(config *SetupConfig) terraform.SetupFn { // nolint:goc } ps.ClientMetadata = map[string]string{ keyAccountID: credCache.accountID, + keyPartition: "aws", } + + if pc.Spec.Endpoint != nil && pc.Spec.Endpoint.PartitionID != nil { + ps.ClientMetadata[keyPartition] = *pc.Spec.Endpoint.PartitionID + } + // several external name configs depend on the setup.Configuration for templating region ps.Configuration = map[string]any{ keyRegion: awsCfg.Region, diff --git a/package/crds/aws.upbound.io_providerconfigs.yaml b/package/crds/aws.upbound.io_providerconfigs.yaml index be2ddd7323..15d1c5ffa6 100644 --- a/package/crds/aws.upbound.io_providerconfigs.yaml +++ b/package/crds/aws.upbound.io_providerconfigs.yaml @@ -401,12 +401,6 @@ spec: required: - url type: object - partition: - default: aws - description: |- - Partition defines the aws partition id to use when constructing ARNs. Most AWS regions use the commercial - partition, simply named aws. - type: string s3_use_path_style: description: Whether to enable the request to use path-style addressing, i.e., https://s3.amazonaws.com/BUCKET/KEY. From 7884709da57ad5ebdd0e377651cf8285f032e659 Mon Sep 17 00:00:00 2001 From: Matt Bush Date: Sun, 17 Mar 2024 23:36:21 -0700 Subject: [PATCH 04/18] Use appropriate IAM signing region for non-commercial partitions Signed-off-by: Matt Bush --- internal/clients/aws.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/internal/clients/aws.go b/internal/clients/aws.go index 1b7b70b63d..38353789ca 100644 --- a/internal/clients/aws.go +++ b/internal/clients/aws.go @@ -39,6 +39,15 @@ type SetupConfig struct { Logger logging.Logger } +// iamRegions holds the region used for signing IAM credentials for each AWS partition. +var iamRegions = map[string]string{ + "aws": "us-east-1", + "aws-gov": "us-gov-west-1", + "aws-cn": "cn-northeast-1", + "aws-iso": "us-iso-east-1", + "aws-iosb": "us-isob-east-1", +} + func SelectTerraformSetup(config *SetupConfig) terraform.SetupFn { // nolint:gocyclo credsCache := NewAWSCredentialsProviderCache(WithCacheLogger(config.Logger)) return func(ctx context.Context, c client.Client, mg resource.Managed) (terraform.Setup, error) { @@ -134,11 +143,22 @@ func getAWSConfigWithDefaultRegion(ctx context.Context, c client.Client, obj run return nil, err } if cfg.Region == "" && obj.GetObjectKind().GroupVersionKind().Group == "iam.aws.upbound.io" { - cfg.Region = "us-east-1" + cfg.Region = getIAMRegion(pc) } return cfg, nil } +func getIAMRegion(pc *v1beta1.ProviderConfig) string { + defaultRegion := "us-east-1" + if pc == nil || pc.Spec.Endpoint == nil || pc.Spec.Endpoint.PartitionID == nil { + return defaultRegion + } + if region, ok := iamRegions[*pc.Spec.Endpoint.PartitionID]; ok { + return region + } + return defaultRegion +} + type metaOnlyPrimary struct { meta any } From bae302ef5e431ba75811c4d357edd1f52c6c9b2e Mon Sep 17 00:00:00 2001 From: Matt Bush Date: Mon, 18 Mar 2024 11:21:53 -0700 Subject: [PATCH 05/18] remove unused function Signed-off-by: Matt Bush --- config/externalname.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/config/externalname.go b/config/externalname.go index 389ee6eda8..a238df3306 100644 --- a/config/externalname.go +++ b/config/externalname.go @@ -3254,13 +3254,6 @@ func fullArnTemplate(service string, resource string) string { } -// globalArnTemplate builds a templated string for constructing a terraform id component which is an ARN of a resource -// which is global to the entire AWS partition, including only the partition, service, and resource. -// e.g. arn:aws:s3:::name-of-my-bucket -func globalArnTemplate(service string, resource string) string { - return genericArnTemplate(service, resource, true, true) -} - // regionlessArnTemplate builds a templated string for constructing a terraform id component which is an ARN of a // resource which is regionless, but specific to your account id. It includes the partition, service, account id, and // resource. From 638e9466d105a529ec633b9320c4130df979faf4 Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Mon, 4 Nov 2024 13:48:54 +0300 Subject: [PATCH 06/18] add aws-iso-e partition & fix aws-us-gov and aws-iso-b name Signed-off-by: Erhan Cagirici --- internal/clients/aws.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/clients/aws.go b/internal/clients/aws.go index 38353789ca..26e15a2fa1 100644 --- a/internal/clients/aws.go +++ b/internal/clients/aws.go @@ -41,11 +41,12 @@ type SetupConfig struct { // iamRegions holds the region used for signing IAM credentials for each AWS partition. var iamRegions = map[string]string{ - "aws": "us-east-1", - "aws-gov": "us-gov-west-1", - "aws-cn": "cn-northeast-1", - "aws-iso": "us-iso-east-1", - "aws-iosb": "us-isob-east-1", + "aws": "us-east-1", + "aws-us-gov": "us-gov-west-1", + "aws-cn": "cn-northeast-1", + "aws-iso": "us-iso-east-1", + "aws-iso-b": "us-isob-east-1", + "aws-iso-e": "eu-isoe-west-1", } func SelectTerraformSetup(config *SetupConfig) terraform.SetupFn { // nolint:gocyclo From 7bbd178ffb98dde248f9ffd50d4da0a8fea65d5a Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Tue, 5 Nov 2024 00:48:04 +0300 Subject: [PATCH 07/18] fix partition templating Signed-off-by: Erhan Cagirici --- config/externalname.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/externalname.go b/config/externalname.go index a238df3306..6b07856251 100644 --- a/config/externalname.go +++ b/config/externalname.go @@ -3275,6 +3275,6 @@ func genericArnTemplate(service string, resource string, elideAccountId bool, el if elideAccountId { accountId = "" } - partition := "{{ .setup.configuration.partition }}" + partition := "{{ .setup.client_metadata.partition }}" return fmt.Sprintf("arn:%s:%s:%s:%s:%s", partition, service, region, accountId, resource) } From 8a6ffb5c52178834af94f57a2229b05e782671df Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Tue, 5 Nov 2024 01:06:42 +0300 Subject: [PATCH 08/18] add missing partition templates for resources Signed-off-by: Erhan Cagirici --- config/externalname.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/config/externalname.go b/config/externalname.go index 6b07856251..47f9ba47b9 100644 --- a/config/externalname.go +++ b/config/externalname.go @@ -36,7 +36,7 @@ var TerraformPluginFrameworkExternalNameConfigs = map[string]config.ExternalName // batch // AWS Batch job queue can be imported using the name - "aws_batch_job_queue": config.TemplatedStringAsIdentifier("name", "arn:aws:batch:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:job-queue/{{ .external_name }}"), + "aws_batch_job_queue": config.TemplatedStringAsIdentifier("name", fullArnTemplate("batch", "job-queue/{{ .external_name }}")), // bedrockagent // @@ -576,13 +576,13 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // codeartifact // // CodeArtifact Domain can be imported using the CodeArtifact Domain arn - "aws_codeartifact_domain": config.TemplatedStringAsIdentifier("", "arn:aws:codeartifact:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:domain/{{ .external_name }}"), + "aws_codeartifact_domain": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "domain/{{ .external_name }}")), // CodeArtifact Domain Permissions Policies can be imported using the CodeArtifact Domain ARN - "aws_codeartifact_domain_permissions_policy": config.TemplatedStringAsIdentifier("", "arn:aws:codeartifact:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:domain/{{ .parameters.domain }}"), + "aws_codeartifact_domain_permissions_policy": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "domain/{{ .parameters.domain }}")), // CodeArtifact Repository can be imported using the CodeArtifact Repository ARN - "aws_codeartifact_repository": config.TemplatedStringAsIdentifier("", "arn:aws:codeartifact:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:repository/{{ .parameters.domain }}/{{ .external_name }}"), + "aws_codeartifact_repository": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "repository/{{ .parameters.domain }}/{{ .external_name }}")), // CodeArtifact Repository Permissions Policies can be imported using the CodeArtifact Repository ARN - "aws_codeartifact_repository_permissions_policy": config.TemplatedStringAsIdentifier("", "arn:aws:codeartifact:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:repository/{{ .parameters.domain }}/{{ .parameters.repository }}"), + "aws_codeartifact_repository_permissions_policy": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "repository/{{ .parameters.domain }}/{{ .parameters.repository }}")), // codecommit // @@ -1117,7 +1117,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // "aws_ecs_capacity_provider": config.TemplatedStringAsIdentifier("name", fullArnTemplate("ecs", "capacity-provider/{{ .external_name }}")), // - "aws_ecs_cluster": config.TemplatedStringAsIdentifier("name", "arn:aws:ecs:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:cluster/{{ .external_name }}"), + "aws_ecs_cluster": config.TemplatedStringAsIdentifier("name", fullArnTemplate("ecs", "cluster/{{ .external_name }}")), // ECS cluster capacity providers can be imported using the cluster_name attribute "aws_ecs_cluster_capacity_providers": config.IdentifierFromProvider, // @@ -1183,7 +1183,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // elasticsearch // // Elasticsearch domains can be imported using the domain_name - "aws_elasticsearch_domain": config.TemplatedStringAsIdentifier("domain_name", "arn:aws:es:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:domain/{{ .external_name }}"), + "aws_elasticsearch_domain": config.TemplatedStringAsIdentifier("domain_name", fullArnTemplate("es", "domain/{{ .external_name }}")), // No import "aws_elasticsearch_domain_policy": config.IdentifierFromProvider, // Elasticsearch domains can be imported using the domain_name @@ -1552,13 +1552,13 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // // MSK Connect Connector can be imported using the connector's arn // Example: arn:aws:kafkaconnect:eu-central-1:123456789012:connector/example/264edee4-17a3-412e-bd76-6681cfc93805-3 - "aws_mskconnect_connector": TemplatedStringAsProviderDefinedIdentifier("arn:aws:kafkaconnect:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:connector/{{ .parameters.name }}/{{ .external_name }}"), + "aws_mskconnect_connector": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("kafkaconnect", "connector/{{ .parameters.name }}/{{ .external_name }}")), // MSK Connect Custom Plugin can be imported using the plugin's arn // Example: arn:aws:kafkaconnect:eu-central-1:123456789012:custom-plugin/debezium-example/abcdefgh-1234-5678-9abc-defghijklmno-4 - "aws_mskconnect_custom_plugin": TemplatedStringAsProviderDefinedIdentifier("arn:aws:kafkaconnect:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:custom-plugin/{{ .parameters.name }}/{{ .external_name }}"), + "aws_mskconnect_custom_plugin": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("kafkaconnect", "custom-plugin/{{ .parameters.name }}/{{ .external_name }}")), // MSK Connect Worker Configuration can be imported using the worker configuration's arn // Example: arn:aws:kafkaconnect:eu-central-1:123456789012:worker-configuration/example/8848493b-7fcc-478c-a646-4a52634e3378-4 - "aws_mskconnect_worker_configuration": TemplatedStringAsProviderDefinedIdentifier("arn:aws:kafkaconnect:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:worker-configuration/{{ .parameters.name }}/{{ .external_name }}"), + "aws_mskconnect_worker_configuration": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("kafkaconnect", "worker-configuration/{{ .parameters.name }}/{{ .external_name }}")), // kendra // @@ -1855,7 +1855,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_networkmanager_core_network": config.IdentifierFromProvider, // aws_networkmanager_customer_gateway_association can be imported using the global network ID and customer gateway ARN // Example: global-network-0d47f6t230mz46dy4,arn:aws:ec2:us-west-2:123456789012:customer-gateway/cgw-123abc05e04123abc - "aws_networkmanager_customer_gateway_association": config.TemplatedStringAsIdentifier("", "{{ .parameters.global_network_id }},arn:aws:ec2:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:customer-gateway/{{ .parameters.customer_gateway_arn }}"), + "aws_networkmanager_customer_gateway_association": config.TemplatedStringAsIdentifier("", "{{ .parameters.global_network_id }},arn:{{ .setup.client_metadata.partition }}:ec2:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:customer-gateway/{{ .parameters.customer_gateway_arn }}"), // aws_networkmanager_device can be imported using the device ARN // Example: arn:aws:networkmanager::123456789012:device/global-network-0d47f6t230mz46dy4/device-07f6fd08867abc123 "aws_networkmanager_device": config.IdentifierFromProvider, @@ -1872,7 +1872,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_networkmanager_site": config.IdentifierFromProvider, // aws_networkmanager_transit_gateway_connect_peer_association can be imported using the global network ID and customer gateway ARN // Example: global-network-0d47f6t230mz46dy4,arn:aws:ec2:us-west-2:123456789012:transit-gateway-connect-peer/tgw-connect-peer-12345678 - "aws_networkmanager_transit_gateway_connect_peer_association": config.TemplatedStringAsIdentifier("", "{{ .parameters.global_network_id }},arn:aws:ec2:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:transit-gateway-connect-peer/{{ .parameters.transit_gateway_connect_peer_arn }}"), + "aws_networkmanager_transit_gateway_connect_peer_association": config.TemplatedStringAsIdentifier("", "{{ .parameters.global_network_id }},arn:{{ .setup.client_metadata.partition }}:ec2:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:transit-gateway-connect-peer/{{ .parameters.transit_gateway_connect_peer_arn }}"), // aws_networkmanager_transit_gateway_registration can be imported using the global network ID and transit gateway ARN // Example: global-network-0d47f6t230mz46dy4,arn:aws:ec2:us-west-2:123456789012:transit-gateway/tgw-123abc05e04123abc "aws_networkmanager_transit_gateway_registration": config.TemplatedStringAsIdentifier("", "{{ .parameters.global_network_id }},{{ .parameters.transit_gateway_arn }}"), @@ -2826,7 +2826,8 @@ func iamPolicy() config.ExternalName { path = "/" } accountID := setup["client_metadata"].(map[string]string)["account_id"] - return fmt.Sprintf("arn:aws:iam::%s:policy%s%s", accountID, path, externalName), nil + partition := setup["client_metadata"].(map[string]string)["partition"] + return fmt.Sprintf("arn:%s:iam::%s:policy%s%s", partition, accountID, path, externalName), nil } e.GetExternalNameFn = func(tfstate map[string]any) (string, error) { @@ -3001,7 +3002,8 @@ func PermissionSetIdAsExternalName() config.ExternalName { } instanceId := strings.Split(iaStr, "/")[1] - return fmt.Sprintf("arn:aws:sso:::permissionSet/%s/%s,%s", instanceId, externalName, iaStr), nil + partition := setup["client_metadata"].(map[string]string)["partition"] + return fmt.Sprintf("arn:%s:sso:::permissionSet/%s/%s,%s", partition, instanceId, externalName, iaStr), nil }, DisableNameInitializer: true, } From cdd2eb06edd8a9be40057e20f5473504dcc2ca70 Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Tue, 5 Nov 2024 01:25:54 +0300 Subject: [PATCH 09/18] fix aws-cn default region to cn-north-1 Signed-off-by: Erhan Cagirici --- internal/clients/aws.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/clients/aws.go b/internal/clients/aws.go index 26e15a2fa1..022f2f6a54 100644 --- a/internal/clients/aws.go +++ b/internal/clients/aws.go @@ -43,7 +43,7 @@ type SetupConfig struct { var iamRegions = map[string]string{ "aws": "us-east-1", "aws-us-gov": "us-gov-west-1", - "aws-cn": "cn-northeast-1", + "aws-cn": "cn-north-1", "aws-iso": "us-iso-east-1", "aws-iso-b": "us-isob-east-1", "aws-iso-e": "eu-isoe-west-1", From 05c570e77145b015f2c2f44e27f32330b9d0b4ce Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Tue, 5 Nov 2024 01:26:37 +0300 Subject: [PATCH 10/18] refactor ARN template function names Signed-off-by: Erhan Cagirici --- config/externalname.go | 80 +++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/config/externalname.go b/config/externalname.go index 47f9ba47b9..4fa31b47b8 100644 --- a/config/externalname.go +++ b/config/externalname.go @@ -36,7 +36,7 @@ var TerraformPluginFrameworkExternalNameConfigs = map[string]config.ExternalName // batch // AWS Batch job queue can be imported using the name - "aws_batch_job_queue": config.TemplatedStringAsIdentifier("name", fullArnTemplate("batch", "job-queue/{{ .external_name }}")), + "aws_batch_job_queue": config.TemplatedStringAsIdentifier("name", fullARNTemplate("batch", "job-queue/{{ .external_name }}")), // bedrockagent // @@ -160,7 +160,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // Uses the ID of workspace, workspace_id parameter. "aws_prometheus_alert_manager_definition": config.IdentifierFromProvider, // - "aws_prometheus_rule_group_namespace": config.TemplatedStringAsIdentifier("name", fullArnTemplate("aps", "rulegroupsnamespace/{{ .parameters.workspace_id }}/{{ .external_name }}")), + "aws_prometheus_rule_group_namespace": config.TemplatedStringAsIdentifier("name", fullARNTemplate("aps", "rulegroupsnamespace/{{ .parameters.workspace_id }}/{{ .external_name }}")), // ID is a random UUID. "aws_prometheus_workspace": config.IdentifierFromProvider, @@ -285,7 +285,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // appflow // // arn:aws:appflow:us-west-2:123456789012:flow/example-flow - "aws_appflow_flow": config.TemplatedStringAsIdentifier("name", fullArnTemplate("appflow", "flow/{{ .external_name }}")), + "aws_appflow_flow": config.TemplatedStringAsIdentifier("name", fullARNTemplate("appflow", "flow/{{ .external_name }}")), // appintegrations // @@ -425,7 +425,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // arn:aws:batch:us-east-1:123456789012:job-definition/sample:1 "aws_batch_job_definition": config.IdentifierFromProvider, // Batch Scheduling Policy can be imported using the arn: arn:aws:batch:us-east-1:123456789012:scheduling-policy/sample - "aws_batch_scheduling_policy": config.TemplatedStringAsIdentifier("name", fullArnTemplate("batch", "scheduling-policy/{{ .external_name }}")), + "aws_batch_scheduling_policy": config.TemplatedStringAsIdentifier("name", fullARNTemplate("batch", "scheduling-policy/{{ .external_name }}")), // budgets // @@ -472,7 +472,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // // config.NameAsIdentifier did not work, the identifier for the resource turned out to be an ARN // arn:aws:cloudformation:us-west-1:123456789123:stack/networking-stack/1e691240-6f2c-11ed-8f91-06094dc221f3 - "aws_cloudformation_stack": TemplatedStringAsIdentifierWithNoName(fullArnTemplate("cloudformation", "stack/{{ .parameters.name }}/{{ .external_name }}")), + "aws_cloudformation_stack": TemplatedStringAsIdentifierWithNoName(fullARNTemplate("cloudformation", "stack/{{ .parameters.name }}/{{ .external_name }}")), // CloudFormation StackSets can be imported using the name "aws_cloudformation_stack_set": config.NameAsIdentifier, // Cloudformation Stacks Instances imported using the StackSet name, target @@ -520,7 +520,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // cloudtrail // // Cloudtrails can be imported using the name arn:aws:cloudtrail:us-west-1:153891904029:trail/foobar - "aws_cloudtrail": config.TemplatedStringAsIdentifier("name", fullArnTemplate("cloudtrail", "trail/{{ .external_name }}")), + "aws_cloudtrail": config.TemplatedStringAsIdentifier("name", fullARNTemplate("cloudtrail", "trail/{{ .external_name }}")), // Event data stores can be imported using their arn "aws_cloudtrail_event_data_store": config.IdentifierFromProvider, @@ -576,13 +576,13 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // codeartifact // // CodeArtifact Domain can be imported using the CodeArtifact Domain arn - "aws_codeartifact_domain": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "domain/{{ .external_name }}")), + "aws_codeartifact_domain": config.TemplatedStringAsIdentifier("", fullARNTemplate("codeartifact", "domain/{{ .external_name }}")), // CodeArtifact Domain Permissions Policies can be imported using the CodeArtifact Domain ARN - "aws_codeartifact_domain_permissions_policy": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "domain/{{ .parameters.domain }}")), + "aws_codeartifact_domain_permissions_policy": config.TemplatedStringAsIdentifier("", fullARNTemplate("codeartifact", "domain/{{ .parameters.domain }}")), // CodeArtifact Repository can be imported using the CodeArtifact Repository ARN - "aws_codeartifact_repository": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "repository/{{ .parameters.domain }}/{{ .external_name }}")), + "aws_codeartifact_repository": config.TemplatedStringAsIdentifier("", fullARNTemplate("codeartifact", "repository/{{ .parameters.domain }}/{{ .external_name }}")), // CodeArtifact Repository Permissions Policies can be imported using the CodeArtifact Repository ARN - "aws_codeartifact_repository_permissions_policy": config.TemplatedStringAsIdentifier("", fullArnTemplate("codeartifact", "repository/{{ .parameters.domain }}/{{ .parameters.repository }}")), + "aws_codeartifact_repository_permissions_policy": config.TemplatedStringAsIdentifier("", fullARNTemplate("codeartifact", "repository/{{ .parameters.domain }}/{{ .parameters.repository }}")), // codecommit // @@ -602,7 +602,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // CodeDeploy CustomActionType can be imported using the id "aws_codepipeline_custom_action_type": config.IdentifierFromProvider, // CodePipeline Webhooks can be imported by their ARN: arn:aws:codepipeline:us-west-2:123456789012:webhook:example - "aws_codepipeline_webhook": config.TemplatedStringAsIdentifier("name", fullArnTemplate("codepipeline", "webhook:{{ .external_name }}")), + "aws_codepipeline_webhook": config.TemplatedStringAsIdentifier("name", fullARNTemplate("codepipeline", "webhook:{{ .external_name }}")), // codestarconnections // @@ -1115,9 +1115,9 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // ECS Account Setting defaults can be imported using the name "aws_ecs_account_setting_default": config.IdentifierFromProvider, // - "aws_ecs_capacity_provider": config.TemplatedStringAsIdentifier("name", fullArnTemplate("ecs", "capacity-provider/{{ .external_name }}")), + "aws_ecs_capacity_provider": config.TemplatedStringAsIdentifier("name", fullARNTemplate("ecs", "capacity-provider/{{ .external_name }}")), // - "aws_ecs_cluster": config.TemplatedStringAsIdentifier("name", fullArnTemplate("ecs", "cluster/{{ .external_name }}")), + "aws_ecs_cluster": config.TemplatedStringAsIdentifier("name", fullARNTemplate("ecs", "cluster/{{ .external_name }}")), // ECS cluster capacity providers can be imported using the cluster_name attribute "aws_ecs_cluster_capacity_providers": config.IdentifierFromProvider, // @@ -1183,7 +1183,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // elasticsearch // // Elasticsearch domains can be imported using the domain_name - "aws_elasticsearch_domain": config.TemplatedStringAsIdentifier("domain_name", fullArnTemplate("es", "domain/{{ .external_name }}")), + "aws_elasticsearch_domain": config.TemplatedStringAsIdentifier("domain_name", fullARNTemplate("es", "domain/{{ .external_name }}")), // No import "aws_elasticsearch_domain_policy": config.IdentifierFromProvider, // Elasticsearch domains can be imported using the domain_name @@ -1262,7 +1262,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_evidently_project": config.IdentifierFromProvider, // CloudWatch Evidently Segment can be imported using the arn // Example: arn:aws:evidently:us-west-2:123456789012:segment/example - "aws_evidently_segment": config.TemplatedStringAsIdentifier("name", fullArnTemplate("evidently", "segment/{{ .external_name }}")), + "aws_evidently_segment": config.TemplatedStringAsIdentifier("name", fullARNTemplate("evidently", "segment/{{ .external_name }}")), // firehose // @@ -1333,7 +1333,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // "aws_glue_job": config.NameAsIdentifier, // Imported using ARN: arn:aws:glue:us-west-2:123456789012:registry/example - "aws_glue_registry": config.TemplatedStringAsIdentifier("registry_name", fullArnTemplate("glue", "registry/{{ .external_name }}")), + "aws_glue_registry": config.TemplatedStringAsIdentifier("registry_name", fullARNTemplate("glue", "registry/{{ .external_name }}")), // Imported using the account ID: 12356789012 "aws_glue_resource_policy": config.IdentifierFromProvider, // Glue Registries can be imported using arn @@ -1402,7 +1402,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // test-role/arn:aws:iam::xxxxxxxxxxxx:policy/test-policy "aws_iam_role_policy_attachment": config.IdentifierFromProvider, // IAM SAML Providers can be imported using the arn - "aws_iam_saml_provider": config.TemplatedStringAsIdentifier("name", regionlessArnTemplate("iam", "saml-provider/{{ .external_name }}")), + "aws_iam_saml_provider": config.TemplatedStringAsIdentifier("name", regionlessARNTemplate("iam", "saml-provider/{{ .external_name }}")), // IAM Server Certificates can be imported using the name "aws_iam_server_certificate": config.NameAsIdentifier, // IAM service-linked roles can be imported using role ARN that contains the @@ -1521,7 +1521,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_iot_topic_rule": config.NameAsIdentifier, // IoT topic rule destinations can be imported using the arn // arn:aws:iot:us-west-2:123456789012:ruledestination/vpc/2ce781c8-68a6-4c52-9c62-63fe489ecc60 - "aws_iot_topic_rule_destination": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("iot", "ruledestination/vpc/{{ .external_name }}")), + "aws_iot_topic_rule_destination": TemplatedStringAsProviderDefinedIdentifier(fullARNTemplate("iot", "ruledestination/vpc/{{ .external_name }}")), // ivs // @@ -1552,13 +1552,13 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // // MSK Connect Connector can be imported using the connector's arn // Example: arn:aws:kafkaconnect:eu-central-1:123456789012:connector/example/264edee4-17a3-412e-bd76-6681cfc93805-3 - "aws_mskconnect_connector": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("kafkaconnect", "connector/{{ .parameters.name }}/{{ .external_name }}")), + "aws_mskconnect_connector": TemplatedStringAsProviderDefinedIdentifier(fullARNTemplate("kafkaconnect", "connector/{{ .parameters.name }}/{{ .external_name }}")), // MSK Connect Custom Plugin can be imported using the plugin's arn // Example: arn:aws:kafkaconnect:eu-central-1:123456789012:custom-plugin/debezium-example/abcdefgh-1234-5678-9abc-defghijklmno-4 - "aws_mskconnect_custom_plugin": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("kafkaconnect", "custom-plugin/{{ .parameters.name }}/{{ .external_name }}")), + "aws_mskconnect_custom_plugin": TemplatedStringAsProviderDefinedIdentifier(fullARNTemplate("kafkaconnect", "custom-plugin/{{ .parameters.name }}/{{ .external_name }}")), // MSK Connect Worker Configuration can be imported using the worker configuration's arn // Example: arn:aws:kafkaconnect:eu-central-1:123456789012:worker-configuration/example/8848493b-7fcc-478c-a646-4a52634e3378-4 - "aws_mskconnect_worker_configuration": TemplatedStringAsProviderDefinedIdentifier(fullArnTemplate("kafkaconnect", "worker-configuration/{{ .parameters.name }}/{{ .external_name }}")), + "aws_mskconnect_worker_configuration": TemplatedStringAsProviderDefinedIdentifier(fullARNTemplate("kafkaconnect", "worker-configuration/{{ .parameters.name }}/{{ .external_name }}")), // kendra // @@ -1586,18 +1586,18 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // kinesis // // Even though the documentation says the ID is name, it uses ARN.. - "aws_kinesis_stream": config.TemplatedStringAsIdentifier("name", fullArnTemplate("kinesis", "stream/{{ .external_name }}")), + "aws_kinesis_stream": config.TemplatedStringAsIdentifier("name", fullARNTemplate("kinesis", "stream/{{ .external_name }}")), // Kinesis Stream Consumers can be imported using the Amazon Resource Name (ARN) // that has a random substring. "aws_kinesis_stream_consumer": config.IdentifierFromProvider, // kinesisanalytics // - "aws_kinesis_analytics_application": config.TemplatedStringAsIdentifier("name", fullArnTemplate("kinesisanalytics", "application/{{ .external_name }}")), + "aws_kinesis_analytics_application": config.TemplatedStringAsIdentifier("name", fullARNTemplate("kinesisanalytics", "application/{{ .external_name }}")), // kinesisanalyticsv2 // - "aws_kinesisanalyticsv2_application": config.TemplatedStringAsIdentifier("name", fullArnTemplate("kinesisanalytics", "application/{{ .external_name }}")), + "aws_kinesisanalyticsv2_application": config.TemplatedStringAsIdentifier("name", fullARNTemplate("kinesisanalytics", "application/{{ .external_name }}")), // aws_kinesisanalyticsv2_application can be imported by using application_name together with snapshot_name // e.g. example-application/example-snapshot "aws_kinesisanalyticsv2_application_snapshot": FormattedIdentifierUserDefinedNameLast("snapshot_name", "/", "application_name"), @@ -1638,7 +1638,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // lambda // // Lambda Function Aliases are identified by their ARN, like arn:aws:lambda:eu-west-1:123456789012:function:lambda-function:alias - "aws_lambda_alias": config.TemplatedStringAsIdentifier("name", fullArnTemplate("lambda", "function:{{ .parameters.function_name }}:{{ .external_name }}")), + "aws_lambda_alias": config.TemplatedStringAsIdentifier("name", fullARNTemplate("lambda", "function:{{ .parameters.function_name }}:{{ .external_name }}")), // Code Signing Configs can be imported using their ARN that has a random // substring in the end. // arn:aws:lambda:us-west-2:123456789012:code-signing-config:csc-0f6c334abcdea4d8b @@ -1833,13 +1833,13 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ "aws_networkfirewall_firewall": config.IdentifierFromProvider, // Network Firewall Policies can be imported using their ARN // Example: arn:aws:network-firewall:us-west-1:123456789012:firewall-policy/example - "aws_networkfirewall_firewall_policy": config.TemplatedStringAsIdentifier("name", fullArnTemplate("network-firewall", "firewall-policy/{{ .external_name }}")), + "aws_networkfirewall_firewall_policy": config.TemplatedStringAsIdentifier("name", fullARNTemplate("network-firewall", "firewall-policy/{{ .external_name }}")), // Network Firewall Logging Configurations can be imported using the firewall_arn // Example: arn:aws:network-firewall:us-west-1:123456789012:firewall/example "aws_networkfirewall_logging_configuration": config.IdentifierFromProvider, // Network Firewall Rule Groups can be imported using their ARN // Example: arn:aws:network-firewall:us-west-1:123456789012:stateful-rulegroup/example - "aws_networkfirewall_rule_group": config.TemplatedStringAsIdentifier("", fullArnTemplate("network-firewall", "{{ .parameters.type | ToLower }}-rulegroup/{{ .external_name }}")), + "aws_networkfirewall_rule_group": config.TemplatedStringAsIdentifier("", fullARNTemplate("network-firewall", "{{ .parameters.type | ToLower }}-rulegroup/{{ .external_name }}")), // networkmanager // @@ -2348,7 +2348,7 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // arn:aws:securityhub:eu-west-1:312940875350:action/custom/a // TODO: following configuration assumes the `a` in the above ARN // is the security hub custom action identifier - "aws_securityhub_action_target": config.TemplatedStringAsIdentifier("identifier", fullArnTemplate("securityhub", "action/custom/{{ .external_name }}")), + "aws_securityhub_action_target": config.TemplatedStringAsIdentifier("identifier", fullARNTemplate("securityhub", "action/custom/{{ .external_name }}")), // imported using the arn that has a random substring: // arn:aws:securityhub:eu-west-1:123456789098:finding-aggregator/abcd1234-abcd-1234-1234-abcdef123456 "aws_securityhub_finding_aggregator": config.IdentifierFromProvider, @@ -2515,9 +2515,9 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // sfn // - "aws_sfn_activity": config.TemplatedStringAsIdentifier("name", fullArnTemplate("states", "activity/{{ .external_name }}")), + "aws_sfn_activity": config.TemplatedStringAsIdentifier("name", fullARNTemplate("states", "activity/{{ .external_name }}")), // - "aws_sfn_state_machine": config.TemplatedStringAsIdentifier("name", fullArnTemplate("states", "stateMachine:{{ .external_name }}")), + "aws_sfn_state_machine": config.TemplatedStringAsIdentifier("name", fullARNTemplate("states", "stateMachine:{{ .external_name }}")), // signer // @@ -2533,12 +2533,12 @@ var TerraformPluginSDKExternalNameConfigs = map[string]config.ExternalName{ // // SNS platform applications can be imported using the ARN: // arn:aws:sns:us-west-2:0123456789012:app/GCM/gcm_application - "aws_sns_platform_application": config.TemplatedStringAsIdentifier("name", fullArnTemplate("sns", "app/GCM/{{ .external_name }}")), + "aws_sns_platform_application": config.TemplatedStringAsIdentifier("name", fullARNTemplate("sns", "app/GCM/{{ .external_name }}")), // no import documentation is provided // TODO: we will need to check if normalization is possible "aws_sns_sms_preferences": config.IdentifierFromProvider, // SNS Topics can be imported using the topic arn - "aws_sns_topic": config.TemplatedStringAsIdentifier("name", fullArnTemplate("sns", "{{ .external_name }}")), + "aws_sns_topic": config.TemplatedStringAsIdentifier("name", fullARNTemplate("sns", "{{ .external_name }}")), // SNS Topic Policy can be imported using the topic ARN: // arn:aws:sns:us-west-2:0123456789012:my-topic "aws_sns_topic_policy": FormattedIdentifierFromProvider("", "arn"), @@ -3248,27 +3248,27 @@ func apiGatewayAccount() config.ExternalName { return e } -// fullArnTemplate builds a templated string for constructing a terraform id component which is an ARN, which includes +// fullARNTemplate builds a templated string for constructing a terraform id component which is an ARN, which includes // the aws partition, service, region, account id, and resource. This is by far the most common form of ARN. // e.g. arn:aws:ec2:ap-south-1:123456789012:instance/i-1234567890ab -func fullArnTemplate(service string, resource string) string { - return genericArnTemplate(service, resource, false, false) +func fullARNTemplate(service string, resource string) string { + return genericARNTemplate(service, resource, false, false) } -// regionlessArnTemplate builds a templated string for constructing a terraform id component which is an ARN of a +// regionlessARNTemplate builds a templated string for constructing a terraform id component which is an ARN of a // resource which is regionless, but specific to your account id. It includes the partition, service, account id, and // resource. // e.g. arn:aws:iam::123456789012:role/example -func regionlessArnTemplate(service string, resource string) string { - return genericArnTemplate(service, resource, false, true) +func regionlessARNTemplate(service string, resource string) string { + return genericARNTemplate(service, resource, false, true) } -// genericArnTemplate builds a templated string for constructing a terraform id component which is an ARN of any format. +// genericARNTemplate builds a templated string for constructing a terraform id component which is an ARN of any format. // It always includes the aws partition, service, and resource. Unless you specify to elide them, it will also include // templates which resolve to the region (from the spec.forProvider) and the account id (calculated from the provider // config). -func genericArnTemplate(service string, resource string, elideAccountId bool, elideRegion bool) string { +func genericARNTemplate(service string, resource string, elideAccountId bool, elideRegion bool) string { region := "{{ .setup.configuration.region }}" if elideRegion { region = "" From 8bb8cca0fe33fdc6ba79cc022483e464ed26a915 Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Tue, 5 Nov 2024 01:30:33 +0300 Subject: [PATCH 11/18] remove unused elideAccountId option from genericARNTemplate Signed-off-by: Erhan Cagirici --- config/externalname.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/config/externalname.go b/config/externalname.go index 4fa31b47b8..5444250043 100644 --- a/config/externalname.go +++ b/config/externalname.go @@ -3252,7 +3252,7 @@ func apiGatewayAccount() config.ExternalName { // the aws partition, service, region, account id, and resource. This is by far the most common form of ARN. // e.g. arn:aws:ec2:ap-south-1:123456789012:instance/i-1234567890ab func fullARNTemplate(service string, resource string) string { - return genericARNTemplate(service, resource, false, false) + return genericARNTemplate(service, resource, false) } @@ -3261,22 +3261,17 @@ func fullARNTemplate(service string, resource string) string { // resource. // e.g. arn:aws:iam::123456789012:role/example func regionlessARNTemplate(service string, resource string) string { - return genericARNTemplate(service, resource, false, true) + return genericARNTemplate(service, resource, true) } // genericARNTemplate builds a templated string for constructing a terraform id component which is an ARN of any format. // It always includes the aws partition, service, and resource. Unless you specify to elide them, it will also include // templates which resolve to the region (from the spec.forProvider) and the account id (calculated from the provider // config). -func genericARNTemplate(service string, resource string, elideAccountId bool, elideRegion bool) string { +func genericARNTemplate(service string, resource string, elideRegion bool) string { region := "{{ .setup.configuration.region }}" if elideRegion { region = "" } - accountId := "{{ .setup.client_metadata.account_id }}" - if elideAccountId { - accountId = "" - } - partition := "{{ .setup.client_metadata.partition }}" - return fmt.Sprintf("arn:%s:%s:%s:%s:%s", partition, service, region, accountId, resource) + return fmt.Sprintf("arn:{{ .setup.client_metadata.partition }}:%s:%s:{{ .setup.client_metadata.account_id }}:%s", service, region, resource) } From c18925eed239a67351453c2e1c0af7fe7a5445c9 Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Tue, 3 Dec 2024 11:41:14 +0300 Subject: [PATCH 12/18] generate default IAM regions per partition Signed-off-by: Erhan Cagirici --- apis/v1beta1/types.go | 2 +- cmd/partitiongen/main.go | 220 ++++++++++++++++++ cmd/partitiongen/partitions_gen.go.tmpl | 24 ++ go.mod | 2 +- internal/clients/aws.go | 45 +++- internal/clients/generate.go | 2 + internal/clients/partitions.go | 29 +++ internal/clients/partitions_test.go | 113 +++++++++ internal/clients/provider_config.go | 15 +- internal/clients/zz_partitions_gen.go | 99 ++++++++ .../crds/aws.upbound.io_providerconfigs.yaml | 1 + 11 files changed, 536 insertions(+), 16 deletions(-) create mode 100644 cmd/partitiongen/main.go create mode 100644 cmd/partitiongen/partitions_gen.go.tmpl create mode 100644 internal/clients/generate.go create mode 100644 internal/clients/partitions.go create mode 100644 internal/clients/partitions_test.go create mode 100644 internal/clients/zz_partitions_gen.go diff --git a/apis/v1beta1/types.go b/apis/v1beta1/types.go index 3f856cfe95..bac5521731 100644 --- a/apis/v1beta1/types.go +++ b/apis/v1beta1/types.go @@ -183,7 +183,7 @@ type URLConfig struct { // and region by choosing Static type. Alternatively, you can provide // configuration for dynamically resolving the URL with the config you provide // once you set the type as Dynamic. - // +kubebuilder:validation:Enum=Static;Dynamic + // +kubebuilder:validation:Enum=Static;Dynamic;Auto Type string `json:"type"` // Static is the full URL you'd like the AWS SDK to use. diff --git a/cmd/partitiongen/main.go b/cmd/partitiongen/main.go new file mode 100644 index 0000000000..8f459cd804 --- /dev/null +++ b/cmd/partitiongen/main.go @@ -0,0 +1,220 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package main + +import ( + "bytes" + _ "embed" + "encoding/json" + "flag" + "fmt" + "go/format" + "html/template" + "io" + "log" + "net/http" + "os" + "sort" + + "github.com/crossplane/crossplane-runtime/pkg/errors" +) + +//go:embed partitions_gen.go.tmpl +var templateBody string + +const documentVersion = 3 + +type PartitionDatum struct { + ID string + Name string + DNSSuffix string + RegionRegex string + CredentialScopeRegion string + IAMRegions map[string]string + GlobalServiceSigningRegions map[string]string + Regions []RegionDatum +} + +type RegionDatum struct { + ID string + Description string +} + +type TemplateData struct { + Partitions []PartitionDatum +} + +type EndpointsDocument struct { + Partitions []PartitionModel `json:"partitions"` + Version uint64 `json:"version"` +} + +type PartitionModel struct { + Defaults DefaultsModel `json:"defaults"` + DnsSuffix string `json:"dnsSuffix"` + Partition string `json:"partition"` + PartitionName string `json:"partitionName"` + RegionRegex string `json:"regionRegex"` + Regions map[string]RegionModel `json:"regions"` + Services map[string]ServiceModel `json:"services"` +} + +type DefaultsModel struct { + Hostname string `json:"hostname"` + Protocols []string `json:"protocols"` + SignatureVersions []string `json:"signatureVersions"` + Variants []VariantModel `json:"variants"` +} + +type VariantModel struct { + DnsSuffix string `json:"dnsSuffix"` + Hostname string `json:"hostname"` + Tags []string `json:"tags"` +} + +type RegionModel struct { + Description string `json:"description"` +} + +type ServiceModel struct { + Endpoints map[string]EndpointModel `json:"endpoints"` + IsRegionalized bool `json:"isRegionalized,omitempty"` + PartitionEndpoint string `json:"partitionEndpoint,omitempty"` + Defaults *DefaultsModel `json:"defaults,omitempty"` +} + +type EndpointModel struct { + CredentialScope *CredentialScopeModel `json:"credentialScope,omitempty"` + Hostname string `json:"hostname"` + Protocols []string `json:"protocols,omitempty"` + Variants []VariantModel `json:"variants,omitempty"` + Deprecated bool `json:"deprecated,omitempty"` +} + +type CredentialScopeModel struct { + Region string `json:"region,omitempty"` + Service string `json:"service,omitempty"` +} + +func usage() { + fmt.Fprintf(os.Stderr, "Usage:\n") + fmt.Fprintf(os.Stderr, "\tmain.go \n\n") +} + +func main() { + flag.Usage = usage + flag.Parse() + + args := flag.Args() + + if len(args) < 1 { + flag.Usage() + os.Exit(2) + } + + inputURL := args[0] + targetFilename := `zz_partitions_gen.go` + var endpointDocu EndpointsDocument + + log.Println("Generating AWS partition definitions file", targetFilename) + + if err := readEndpointsDocumentFromURL(inputURL, &endpointDocu); err != nil { + log.Fatalf("error reading JSON from %s: %s", inputURL, err) + } + + templateData := TemplateData{} + + if endpointDocu.Version != documentVersion { + log.Fatalf("unsupported endpoints document version: %d, expected version: %d", endpointDocu.Version, documentVersion) + } + + for _, partition := range endpointDocu.Partitions { + partitionDatum := PartitionDatum{ + GlobalServiceSigningRegions: make(map[string]string), + } + partitionDatum.ID = partition.Partition + partitionDatum.Name = partition.PartitionName + partitionDatum.DNSSuffix = partition.DnsSuffix + partitionDatum.RegionRegex = partition.RegionRegex + for id, region := range partition.Regions { + regionDatum := RegionDatum{ + ID: id, + Description: region.Description, + } + partitionDatum.Regions = append(partitionDatum.Regions, regionDatum) + } + + for svcName, svc := range partition.Services { + if svc.PartitionEndpoint != "" { + defaultEndpoint, ok := svc.Endpoints[svc.PartitionEndpoint] + if !ok { + log.Fatalf("partition endpoint %q not found for service %q in partition %q", svc.PartitionEndpoint, svcName, partition.Partition) + } + if defaultEndpoint.CredentialScope == nil { + continue + } + partitionDatum.GlobalServiceSigningRegions[svcName] = defaultEndpoint.CredentialScope.Region + } + } + templateData.Partitions = append(templateData.Partitions, partitionDatum) + + } + + sort.SliceStable(templateData.Partitions, func(i, j int) bool { + return templateData.Partitions[i].ID < templateData.Partitions[j].ID + }) + + for i := 0; i < len(templateData.Partitions); i++ { + sort.SliceStable(templateData.Partitions[i].Regions, func(j, k int) bool { + return templateData.Partitions[i].Regions[j].ID < templateData.Partitions[i].Regions[k].ID + }) + } + + tmpl, err := template.New("partitions").Parse(templateBody) + + if err != nil { + log.Fatalf("parsing function template: %v", err) + } + + targetFile, err := os.OpenFile(targetFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatalf("failed to open file %q for write: %v", targetFilename, err) + } + defer func() { + if err := targetFile.Close(); err != nil { + log.Fatalf("Failed to close the templated main %q: %s", targetFilename, err.Error()) + } + }() + + buf := &bytes.Buffer{} + if err := tmpl.Execute(buf, templateData); err != nil { + log.Fatalf("cannot execute template: %v", err) + } + gofmtFormattedBytes, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatalf("cannot gofmt generated partitions file: %v", err) + } + if _, err := targetFile.Write(gofmtFormattedBytes); err != nil { + log.Fatalf("cannot write generated file: %v", err) + } + + log.Println("Successfully generated AWS partition definitions file", targetFilename) +} + +func readEndpointsDocumentFromURL(url string, to *EndpointsDocument) error { + r, err := http.Get(url) + if err != nil { + return errors.Wrap(err, "cannot fetch remote endpoints document") + } + if r.StatusCode < 200 || r.StatusCode > 299 { + return errors.Errorf("fetching endpoints document returned non-2xx HTTP status code: %s", r.Status) + } + defer r.Body.Close() + + epDocumentRaw, err := io.ReadAll(r.Body) + if err != nil { + return errors.Wrap(err, "cannot read HTTP body") + } + return errors.Wrap(json.Unmarshal(epDocumentRaw, to), "cannot unmarshal endpoints document") +} diff --git a/cmd/partitiongen/partitions_gen.go.tmpl b/cmd/partitiongen/partitions_gen.go.tmpl new file mode 100644 index 0000000000..c8bf46dddc --- /dev/null +++ b/cmd/partitiongen/partitions_gen.go.tmpl @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 The Crossplane Authors +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by cmd/endpoints/main.go; DO NOT EDIT. + +package clients + +var ( + partitions = map[string]awsPartition{ +{{- range .Partitions }} + "{{ .ID }}": { + id: "{{ .ID }}", + name: "{{ .Name }}", + dnsSuffix: "{{ .DNSSuffix }}", + serviceToDefaultRegions: map[string]string{ + {{- range $svc, $region := .GlobalServiceSigningRegions }} + "{{ $svc }}": "{{ $region }}", + {{- end }} + }, + }, +{{- end }} + } +) \ No newline at end of file diff --git a/go.mod b/go.mod index 534fa30652..b441cf9525 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/crossplane/upjet v1.4.1-0.20241115173542-ba35c31702ca github.com/go-ini/ini v1.46.0 github.com/google/go-cmp v0.6.0 + github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.59 github.com/hashicorp/awspolicyequivalence v1.6.0 github.com/hashicorp/terraform-json v0.24.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 @@ -335,7 +336,6 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/aws-cloudformation-resource-schema-sdk-go v0.23.0 // indirect - github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.59 // indirect github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2 v2.0.0-beta.60 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect diff --git a/internal/clients/aws.go b/internal/clients/aws.go index 022f2f6a54..3780806974 100644 --- a/internal/clients/aws.go +++ b/internal/clients/aws.go @@ -17,6 +17,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/upjet/pkg/metrics" "github.com/crossplane/upjet/pkg/terraform" + "github.com/hashicorp/aws-sdk-go-base/v2/endpoints" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/xpprovider" "github.com/pkg/errors" @@ -40,14 +41,7 @@ type SetupConfig struct { } // iamRegions holds the region used for signing IAM credentials for each AWS partition. -var iamRegions = map[string]string{ - "aws": "us-east-1", - "aws-us-gov": "us-gov-west-1", - "aws-cn": "cn-north-1", - "aws-iso": "us-iso-east-1", - "aws-iso-b": "us-isob-east-1", - "aws-iso-e": "eu-isoe-west-1", -} +var iamRegions = getIAMDefaultSigningRegions() func SelectTerraformSetup(config *SetupConfig) terraform.SetupFn { // nolint:gocyclo credsCache := NewAWSCredentialsProviderCache(WithCacheLogger(config.Logger)) @@ -107,8 +101,8 @@ func SelectTerraformSetup(config *SetupConfig) terraform.SetupFn { // nolint:goc keyPartition: "aws", } - if pc.Spec.Endpoint != nil && pc.Spec.Endpoint.PartitionID != nil { - ps.ClientMetadata[keyPartition] = *pc.Spec.Endpoint.PartitionID + if err := setPartition(awsCfg, pc, &ps); err != nil { + return terraform.Setup{}, errors.Wrap(err, "cannot configure AWS partition") } // several external name configs depend on the setup.Configuration for templating region @@ -122,6 +116,37 @@ func SelectTerraformSetup(config *SetupConfig) terraform.SetupFn { // nolint:goc } } +func setPartition(awsCfg *aws.Config, pc *v1beta1.ProviderConfig, ps *terraform.Setup) error { + var partitionFromConfig string + if pc.Spec.Endpoint != nil && pc.Spec.Endpoint.PartitionID != nil { + partitionFromConfig = *pc.Spec.Endpoint.PartitionID + ps.ClientMetadata[keyPartition] = partitionFromConfig + } + // region should never be empty, but defensively code to preserve existing behavior + if awsCfg.Region == "" { + return nil + } + + // TODO(erhan): localstack environments with ALLOW_NONSTANDARD_REGIONS configuration + // might fail this check. Consider introducing a config that opt-out from partition + // resolution + partitionFromRegion, ok := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), awsCfg.Region) + if !ok || partitionFromRegion.ID() == "" { + // tolerate unknown region and honor when explicit partition config exists + if partitionFromConfig != "" { + return nil + } + return errors.Errorf("managed resource region %q does not belong to a known partition", awsCfg.Region) + } + + if partitionFromConfig != "" && partitionFromConfig != partitionFromRegion.ID() { + return errors.Errorf("conflicting partition config: managed resource region %q does not belong to configured partition %q at provider config", awsCfg.Region, *pc.Spec.Endpoint.PartitionID) + } + + ps.ClientMetadata[keyPartition] = partitionFromRegion.ID() + return nil +} + // getAccountId retrieves the account ID associated with the given credentials. // Results are cached. func getAccountId(ctx context.Context, cfg *aws.Config, creds aws.Credentials) (string, error) { diff --git a/internal/clients/generate.go b/internal/clients/generate.go new file mode 100644 index 0000000000..56d4c0d4cf --- /dev/null +++ b/internal/clients/generate.go @@ -0,0 +1,2 @@ +//go:generate go run ../../cmd/partitiongen/main.go -- https://raw.githubusercontent.com/aws/aws-sdk-go-v2/main/codegen/smithy-aws-go-codegen/src/main/resources/software/amazon/smithy/aws/go/codegen/endpoints.json +package clients diff --git a/internal/clients/partitions.go b/internal/clients/partitions.go new file mode 100644 index 0000000000..93cb4af920 --- /dev/null +++ b/internal/clients/partitions.go @@ -0,0 +1,29 @@ +package clients + +import "regexp" + +type awsPartition struct { + id string + name string + regionRegex *regexp.Regexp + dnsSuffix string + serviceToDefaultRegions map[string]string + regions map[string]awsRegion +} + +type awsRegion struct { + id string + description string +} + +func getIAMDefaultSigningRegions() map[string]string { + var partitionToDefaultRegion = map[string]string{} + for name, partition := range partitions { + reg, ok := partition.serviceToDefaultRegions["iam"] + if !ok { + continue + } + partitionToDefaultRegion[name] = reg + } + return partitionToDefaultRegion +} diff --git a/internal/clients/partitions_test.go b/internal/clients/partitions_test.go new file mode 100644 index 0000000000..d470ded08d --- /dev/null +++ b/internal/clients/partitions_test.go @@ -0,0 +1,113 @@ +package clients + +import "testing" + +func TestPartitions(t *testing.T) { + cases := map[string]struct { + partition string + servicesToRegion map[string]string + wantOk bool + }{ + "AWSDefaultPartition": { + partition: "aws", + servicesToRegion: map[string]string{ + "iam": "us-east-1", + }, + wantOk: true, + }, + "AWSChina": { + partition: "aws-cn", + servicesToRegion: map[string]string{ + "iam": "cn-north-1", + }, + wantOk: true, + }, + "AWSISO": { + partition: "aws-iso", + servicesToRegion: map[string]string{ + "iam": "us-iso-east-1", + }, + wantOk: true}, + "AWSISOB": { + partition: "aws-iso-b", + servicesToRegion: map[string]string{}, + wantOk: true, + }, + "AWSISOE": { + partition: "aws-iso-e", + servicesToRegion: map[string]string{}, + wantOk: true, + }, + "AWSISOF": { + partition: "aws-iso-f", + servicesToRegion: map[string]string{}, + wantOk: true, + }, + "AWSUSGov": { + partition: "aws-us-gov", + servicesToRegion: map[string]string{ + "iam": "us-gov-west-1", + }, + wantOk: true, + }, + "NonExistentPartition": { + partition: "aws-foo", + servicesToRegion: map[string]string{}, + wantOk: false, + }, + } + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + partition, ok := partitions[tc.partition] + if ok != tc.wantOk { + t.Errorf("expected partition existence: got %v, want %v", ok, tc.wantOk) + } + for wantSvc, wantRegion := range tc.servicesToRegion { + reg, okSvc := partition.serviceToDefaultRegions[wantSvc] + if !okSvc { + t.Errorf("expected service %q to exist in partition %q", wantSvc, tc.partition) + } + if reg != wantRegion { + t.Errorf("expected default region %q for service %q in partition %q, got region %q", wantRegion, wantSvc, tc.partition, reg) + } + } + }) + } + var defaultRegions = map[string]string{} + for name, partition := range partitions { + reg, ok := partition.serviceToDefaultRegions["iam"] + if !ok { + continue + } + defaultRegions[name] = reg + } +} + +func TestGetIAMDefaultSigningRegions(t *testing.T) { + cases := map[string]struct { + partition string + region string + wantOk bool + }{ + "AWSDefaultPartition": {"aws", "us-east-1", true}, + "AWSChina": {"aws-cn", "cn-north-1", true}, + "AWSISO": {"aws-iso", "us-iso-east-1", true}, + "AWSISOB": {"aws-iso-b", "us-isob-east-1", true}, + "AWSISOE": {"aws-iso-e", "", false}, + "AWSISOF": {"aws-iso-f", "", false}, + "AWSUSGov": {"aws-us-gov", "us-gov-west-1", true}, + "NonExistentPartition": {"aws-foo", "", false}, + } + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + defaultRegions := getIAMDefaultSigningRegions() + region, ok := defaultRegions[tc.partition] + if ok != tc.wantOk { + t.Errorf("expected partition existence: got %v, want %v", ok, tc.wantOk) + } + if region != tc.region { + t.Errorf("expected default region for partition %v: got %v, want %v", tc.partition, region, tc.region) + } + }) + } +} diff --git a/internal/clients/provider_config.go b/internal/clients/provider_config.go index 1c6661776c..6936e2e8d6 100644 --- a/internal/clients/provider_config.go +++ b/internal/clients/provider_config.go @@ -64,6 +64,7 @@ const GlobalRegion = "aws-global" const ( URLConfigTypeStatic = "Static" URLConfigTypeDynamic = "Dynamic" + URLConfigTypeAuto = "Auto" ) // userAgentV2 constructs the Crossplane user agent for AWS v2 clients @@ -133,7 +134,7 @@ func GetAWSConfigWithoutTracking(ctx context.Context, c client.Client, obj runti if err != nil { return nil, errors.Wrap(err, "cannot get credentials") } - return SetResolver(pc, cfg), nil + return SetResolver(pc, cfg) } // GetAWSConfigWithTracking obtains the provider config referenced by the @@ -165,9 +166,15 @@ func (a awsEndpointResolverAdaptorWithOptions) ResolveEndpoint(service, region s // SetResolver parses annotations from the managed resource // and returns a configuration accordingly. -func SetResolver(pc *v1beta1.ProviderConfig, cfg *aws.Config) *aws.Config { // nolint:gocyclo +func SetResolver(pc *v1beta1.ProviderConfig, cfg *aws.Config) (*aws.Config, error) { // nolint:gocyclo if pc.Spec.Endpoint == nil { - return cfg + return cfg, nil + } + if pc.Spec.Endpoint.URL.Type == URLConfigTypeAuto { + if pc.Spec.Endpoint.PartitionID == nil || *pc.Spec.Endpoint.PartitionID == "" { + return nil, errors.Errorf("partitionId is required when the Endpoint URL type is %q", URLConfigTypeAuto) + } + return cfg, nil } cfg.EndpointResolverWithOptions = awsEndpointResolverAdaptorWithOptions(func(service, region string, options interface{}) (aws.Endpoint, error) { //nolint:staticcheck fullURL := "" @@ -219,7 +226,7 @@ func SetResolver(pc *v1beta1.ProviderConfig, cfg *aws.Config) *aws.Config { // n } return e, nil }) - return cfg + return cfg, nil } // CredentialsIDSecret retrieves AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from the data which contains diff --git a/internal/clients/zz_partitions_gen.go b/internal/clients/zz_partitions_gen.go new file mode 100644 index 0000000000..a1e8ac2d85 --- /dev/null +++ b/internal/clients/zz_partitions_gen.go @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2024 The Crossplane Authors +// +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by cmd/endpoints/main.go; DO NOT EDIT. + +package clients + +var ( + partitions = map[string]awsPartition{ + "aws": { + id: "aws", + name: "AWS Standard", + dnsSuffix: "amazonaws.com", + serviceToDefaultRegions: map[string]string{ + "account": "us-east-1", + "billingconductor": "us-east-1", + "budgets": "us-east-1", + "ce": "us-east-1", + "chime": "us-east-1", + "cloudfront": "us-east-1", + "health": "us-east-1", + "iam": "us-east-1", + "importexport": "us-east-1", + "networkmanager": "us-west-2", + "notifications-contacts": "us-east-1", + "organizations": "us-east-1", + "route53": "us-east-1", + "s3": "us-east-1", + "savingsplans": "us-east-1", + "shield": "us-east-1", + "sts": "us-east-1", + "support": "us-east-1", + "tax": "us-east-1", + "waf": "us-east-1", + }, + }, + "aws-cn": { + id: "aws-cn", + name: "AWS China", + dnsSuffix: "amazonaws.com.cn", + serviceToDefaultRegions: map[string]string{ + "account": "cn-northwest-1", + "budgets": "cn-northwest-1", + "ce": "cn-northwest-1", + "cloudfront": "cn-northwest-1", + "health": "cn-northwest-1", + "iam": "cn-north-1", + "organizations": "cn-northwest-1", + "route53": "cn-northwest-1", + "support": "cn-north-1", + }, + }, + "aws-iso": { + id: "aws-iso", + name: "AWS ISO (US)", + dnsSuffix: "c2s.ic.gov", + serviceToDefaultRegions: map[string]string{ + "iam": "us-iso-east-1", + "route53": "us-iso-east-1", + "support": "us-iso-east-1", + }, + }, + "aws-iso-b": { + id: "aws-iso-b", + name: "AWS ISOB (US)", + dnsSuffix: "sc2s.sgov.gov", + serviceToDefaultRegions: map[string]string{ + "iam": "us-isob-east-1", + "route53": "us-isob-east-1", + "support": "us-isob-east-1", + }, + }, + "aws-iso-e": { + id: "aws-iso-e", + name: "AWS ISOE (Europe)", + dnsSuffix: "cloud.adc-e.uk", + serviceToDefaultRegions: map[string]string{}, + }, + "aws-iso-f": { + id: "aws-iso-f", + name: "AWS ISOF", + dnsSuffix: "csp.hci.ic.gov", + serviceToDefaultRegions: map[string]string{}, + }, + "aws-us-gov": { + id: "aws-us-gov", + name: "AWS GovCloud (US)", + dnsSuffix: "amazonaws.com", + serviceToDefaultRegions: map[string]string{ + "iam": "us-gov-west-1", + "networkmanager": "us-gov-west-1", + "organizations": "us-gov-west-1", + "route53": "us-gov-west-1", + "support": "us-gov-west-1", + }, + }, + } +) diff --git a/package/crds/aws.upbound.io_providerconfigs.yaml b/package/crds/aws.upbound.io_providerconfigs.yaml index 15d1c5ffa6..4a9505e111 100644 --- a/package/crds/aws.upbound.io_providerconfigs.yaml +++ b/package/crds/aws.upbound.io_providerconfigs.yaml @@ -394,6 +394,7 @@ spec: enum: - Static - Dynamic + - Auto type: string required: - type From 38eec9f2de97940e8dd9e00bf05099816f293109 Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Fri, 24 Jan 2025 12:42:49 +0300 Subject: [PATCH 13/18] update generated partitions Signed-off-by: Erhan Cagirici --- internal/clients/zz_partitions_gen.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/internal/clients/zz_partitions_gen.go b/internal/clients/zz_partitions_gen.go index a1e8ac2d85..5f981be3da 100644 --- a/internal/clients/zz_partitions_gen.go +++ b/internal/clients/zz_partitions_gen.go @@ -56,9 +56,11 @@ var ( name: "AWS ISO (US)", dnsSuffix: "c2s.ic.gov", serviceToDefaultRegions: map[string]string{ - "iam": "us-iso-east-1", - "route53": "us-iso-east-1", - "support": "us-iso-east-1", + "budgets": "us-iso-east-1", + "iam": "us-iso-east-1", + "organizations": "us-iso-east-1", + "route53": "us-iso-east-1", + "support": "us-iso-east-1", }, }, "aws-iso-b": { @@ -66,9 +68,11 @@ var ( name: "AWS ISOB (US)", dnsSuffix: "sc2s.sgov.gov", serviceToDefaultRegions: map[string]string{ - "iam": "us-isob-east-1", - "route53": "us-isob-east-1", - "support": "us-isob-east-1", + "budgets": "us-isob-east-1", + "iam": "us-isob-east-1", + "organizations": "us-isob-east-1", + "route53": "us-isob-east-1", + "support": "us-isob-east-1", }, }, "aws-iso-e": { From a0ded51a49e5d959d6f42399a42536b2449fbeed Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Fri, 24 Jan 2025 14:28:54 +0300 Subject: [PATCH 14/18] add linter exceptions Signed-off-by: Erhan Cagirici --- cmd/partitiongen/main.go | 8 ++++---- internal/clients/partitions.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/partitiongen/main.go b/cmd/partitiongen/main.go index 8f459cd804..b7a8506763 100644 --- a/cmd/partitiongen/main.go +++ b/cmd/partitiongen/main.go @@ -102,7 +102,7 @@ func usage() { fmt.Fprintf(os.Stderr, "\tmain.go \n\n") } -func main() { +func main() { //nolint:gocyclo flag.Usage = usage flag.Parse() @@ -183,13 +183,13 @@ func main() { } defer func() { if err := targetFile.Close(); err != nil { - log.Fatalf("Failed to close the templated main %q: %s", targetFilename, err.Error()) + log.Fatalf("Failed to close the file %q: %s", targetFilename, err.Error()) } }() buf := &bytes.Buffer{} if err := tmpl.Execute(buf, templateData); err != nil { - log.Fatalf("cannot execute template: %v", err) + log.Fatalf("cannot execute template: %v", err) //nolint:gocritic } gofmtFormattedBytes, err := format.Source(buf.Bytes()) if err != nil { @@ -203,7 +203,7 @@ func main() { } func readEndpointsDocumentFromURL(url string, to *EndpointsDocument) error { - r, err := http.Get(url) + r, err := http.Get(url) //nolint:gosec only for endpoint generation, with a fixed AWS url if err != nil { return errors.Wrap(err, "cannot fetch remote endpoints document") } diff --git a/internal/clients/partitions.go b/internal/clients/partitions.go index 93cb4af920..08174b5d84 100644 --- a/internal/clients/partitions.go +++ b/internal/clients/partitions.go @@ -5,13 +5,13 @@ import "regexp" type awsPartition struct { id string name string - regionRegex *regexp.Regexp + regionRegex *regexp.Regexp //nolint:unused dnsSuffix string serviceToDefaultRegions map[string]string - regions map[string]awsRegion + regions map[string]awsRegion //nolint:unused } -type awsRegion struct { +type awsRegion struct { //nolint:unused id string description string } From 8ac393023b5838bcc50727a9377c26e5d0a93ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20T=C3=BCrken?= Date: Fri, 24 Jan 2025 21:29:52 +0300 Subject: [PATCH 15/18] Fix example manifests and make uptestable: - OpenIDConnectProvider.iam - InternetGateway.ec2 - Subnet.ec2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fatih Türken --- examples/ec2/v1beta1/internetgateway.yaml | 13 ++++++++++++- examples/ec2/v1beta1/subnet.yaml | 11 +++++------ examples/iam/v1beta1/openidconnectprovider.yaml | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/examples/ec2/v1beta1/internetgateway.yaml b/examples/ec2/v1beta1/internetgateway.yaml index 1c20a378e0..5f7ca99edd 100644 --- a/examples/ec2/v1beta1/internetgateway.yaml +++ b/examples/ec2/v1beta1/internetgateway.yaml @@ -12,4 +12,15 @@ spec: tags: Name: main vpcIdRef: - name: sample-vpc + name: sample-vpc2 +--- +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: VPC +metadata: + name: sample-vpc2 +spec: + forProvider: + region: us-west-1 + cidrBlock: 172.16.0.0/16 + tags: + Name: DemoVpc \ No newline at end of file diff --git a/examples/ec2/v1beta1/subnet.yaml b/examples/ec2/v1beta1/subnet.yaml index 5d09886f22..a4179e4a4c 100644 --- a/examples/ec2/v1beta1/subnet.yaml +++ b/examples/ec2/v1beta1/subnet.yaml @@ -15,13 +15,12 @@ spec: cidrBlock: 172.16.10.0/24 --- apiVersion: ec2.aws.upbound.io/v1beta1 -kind: Subnet +kind: VPC metadata: - name: sample-subnet2 + name: sample-vpc spec: forProvider: region: us-west-1 - availabilityZone: us-west-1c - vpcIdRef: - name: sample-vpc - cidrBlock: 172.16.11.0/24 + cidrBlock: 172.16.0.0/16 + tags: + Name: DemoVpc diff --git a/examples/iam/v1beta1/openidconnectprovider.yaml b/examples/iam/v1beta1/openidconnectprovider.yaml index 98c5b55a4b..8cfc60efd6 100644 --- a/examples/iam/v1beta1/openidconnectprovider.yaml +++ b/examples/iam/v1beta1/openidconnectprovider.yaml @@ -14,4 +14,4 @@ spec: - some-audience thumbprintList: - 15e29108718111e59b3dad31954647e3c344a231 - url: https://token.actions.githubusercontent.com + url: https://accounts.google.com From 088f34a129e896f1c442d2418f230de27fe63d49 Mon Sep 17 00:00:00 2001 From: Erhan Cagirici Date: Fri, 24 Jan 2025 21:54:08 +0300 Subject: [PATCH 16/18] fix linter Signed-off-by: Erhan Cagirici --- cmd/partitiongen/main.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/partitiongen/main.go b/cmd/partitiongen/main.go index b7a8506763..9561e8b897 100644 --- a/cmd/partitiongen/main.go +++ b/cmd/partitiongen/main.go @@ -203,14 +203,19 @@ func main() { //nolint:gocyclo } func readEndpointsDocumentFromURL(url string, to *EndpointsDocument) error { - r, err := http.Get(url) //nolint:gosec only for endpoint generation, with a fixed AWS url + r, err := http.Get(url) //nolint only for endpoint generation, with a fixed AWS url if err != nil { return errors.Wrap(err, "cannot fetch remote endpoints document") } if r.StatusCode < 200 || r.StatusCode > 299 { return errors.Errorf("fetching endpoints document returned non-2xx HTTP status code: %s", r.Status) } - defer r.Body.Close() + + defer func() { + if err := r.Body.Close(); err != nil { + log.Printf("error closing response body: %v", err) + } + }() epDocumentRaw, err := io.ReadAll(r.Body) if err != nil { From 8329b5eb775924692ca14ccd29cfd0b4e89ad11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20T=C3=BCrken?= Date: Mon, 27 Jan 2025 20:53:16 +0300 Subject: [PATCH 17/18] Fix example manifests and make uptestable for ec2 EIP resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fatih Türken --- examples/ec2/v1beta1/eip.yaml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/ec2/v1beta1/eip.yaml b/examples/ec2/v1beta1/eip.yaml index 54a4602e7f..d95d365b32 100644 --- a/examples/ec2/v1beta1/eip.yaml +++ b/examples/ec2/v1beta1/eip.yaml @@ -5,12 +5,31 @@ apiVersion: ec2.aws.upbound.io/v1beta1 kind: EIP metadata: + annotations: + meta.upbound.io/example-id: ec2/v1beta1/eip labels: testing.upbound.io/example-name: lb name: lb spec: forProvider: - instanceRef: - name: sample-instance - vpc: true + instanceSelector: + matchLabels: + testing.upbound.io/example-name: web + domain: vpc region: us-west-1 +--- +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +metadata: + annotations: + meta.upbound.io/example-id: ec2/v1beta1/eip + labels: + testing.upbound.io/example-name: web + name: web +spec: + forProvider: + ami: ami-07b068f843ec78e72 + instanceType: t3.micro + region: us-west-1 + tags: + Name: HelloWorld From b1d2f308e6f991f76bd735d1f5f357bdba61f536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20T=C3=BCrken?= Date: Tue, 28 Jan 2025 22:07:24 +0300 Subject: [PATCH 18/18] Fix example manifests for elasticsearch Domain resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fatih Türken --- examples/elasticsearch/v1beta1/domain.yaml | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/examples/elasticsearch/v1beta1/domain.yaml b/examples/elasticsearch/v1beta1/domain.yaml index c58c612851..1db3dd68a8 100644 --- a/examples/elasticsearch/v1beta1/domain.yaml +++ b/examples/elasticsearch/v1beta1/domain.yaml @@ -10,27 +10,13 @@ metadata: uptest.upbound.io/timeout: "3600" labels: testing.upbound.io/example-name: example - name: example + name: uptest-example spec: forProvider: - accessPolicies: | - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "AWS": "*" - }, - "Action": "es:*", - "Resource": "arn:aws:es:us-west-1:domain/example/*" - } - ] - } clusterConfig: - instanceType: r4.large.elasticsearch ebsOptions: - ebsEnabled: true volumeSize: 30 - elasticsearchVersion: "5.3" + elasticsearchVersion: "7.1" region: us-west-1