From 72bbc3975603e367bb6787de77717281cbd9707d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Jan 2018 18:48:12 -0500 Subject: [PATCH 1/8] Add 'aws_dx_hosted_private_virtual_interface' resource. Add 'aws_dx_hosted_private_virtual_interface_accepter' resource. --- aws/dx_vif.go | 256 ++++++ aws/provider.go | 736 +++++++++--------- ...aws_dx_hosted_private_virtual_interface.go | 108 +++ ...sted_private_virtual_interface_accepter.go | 139 ++++ ...x_hosted_private_virtual_interface_test.go | 98 +++ aws/utils.go | 13 + aws/utils_test.go | 32 +- website/aws.erb | 6 + ...ed_private_virtual_interface.html.markdown | 62 ++ ...e_virtual_interface_accepter.html.markdown | 88 +++ 10 files changed, 1170 insertions(+), 368 deletions(-) create mode 100644 aws/dx_vif.go create mode 100644 aws/resource_aws_dx_hosted_private_virtual_interface.go create mode 100644 aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go create mode 100644 aws/resource_aws_dx_hosted_private_virtual_interface_test.go create mode 100644 website/docs/r/dx_hosted_private_virtual_interface.html.markdown create mode 100644 website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown diff --git a/aws/dx_vif.go b/aws/dx_vif.go new file mode 100644 index 000000000000..903c5ca4fed3 --- /dev/null +++ b/aws/dx_vif.go @@ -0,0 +1,256 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func isNoSuchDxVirtualInterfaceErr(err error) bool { + return isAWSErr(err, "DirectConnectClientException", "does not exist") +} + +func dxVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) (*directconnect.VirtualInterface, error) { + conn := meta.(*AWSClient).dxconn + + resp, state, err := dxVirtualInterfaceStateRefresh(conn, d.Id())() + if err != nil { + return nil, fmt.Errorf("Error reading Direct Connect virtual interface: %s", err.Error()) + } + terminalStates := map[string]bool{ + directconnect.VirtualInterfaceStateDeleted: true, + directconnect.VirtualInterfaceStateDeleting: true, + directconnect.VirtualInterfaceStateRejected: true, + } + if _, ok := terminalStates[state]; ok { + log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil, nil + } + + return resp.(*directconnect.VirtualInterface), nil +} + +func dxVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + if err := setTagsDX(conn, d, arn); err != nil { + return err + } + + return nil +} + +func dxVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + log.Printf("[DEBUG] Deleting Direct Connect virtual interface: %s", d.Id()) + _, err := conn.DeleteVirtualInterface(&directconnect.DeleteVirtualInterfaceInput{ + VirtualInterfaceId: aws.String(d.Id()), + }) + if err != nil { + if isNoSuchDxVirtualInterfaceErr(err) { + return nil + } + return fmt.Errorf("Error deleting Direct Connect virtual interface: %s", err.Error()) + } + + deleteStateConf := &resource.StateChangeConf{ + Pending: []string{ + directconnect.VirtualInterfaceStateAvailable, + directconnect.VirtualInterfaceStateConfirming, + directconnect.VirtualInterfaceStateDeleting, + directconnect.VirtualInterfaceStateDown, + directconnect.VirtualInterfaceStatePending, + directconnect.VirtualInterfaceStateRejected, + directconnect.VirtualInterfaceStateVerifying, + }, + Target: []string{ + directconnect.VirtualInterfaceStateDeleted, + }, + Refresh: dxVirtualInterfaceStateRefresh(conn, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } + _, err = deleteStateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for Direct Connect virtual interface (%s) to be deleted: %s", d.Id(), err) + } + + return nil +} + +func dxVirtualInterfaceStateRefresh(conn *directconnect.DirectConnect, vifId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ + VirtualInterfaceId: aws.String(vifId), + }) + if err != nil { + if isNoSuchDxVirtualInterfaceErr(err) { + return nil, directconnect.VirtualInterfaceStateDeleted, nil + } + return nil, "", err + } + + if len(resp.VirtualInterfaces) < 1 { + return nil, directconnect.ConnectionStateDeleted, nil + } + vif := resp.VirtualInterfaces[0] + return vif, aws.StringValue(vif.VirtualInterfaceState), nil + } +} + +func dxVirtualInterfaceWaitUntilAvailable(d *schema.ResourceData, conn *directconnect.DirectConnect, pending, target []string) error { + stateConf := &resource.StateChangeConf{ + Pending: pending, + Target: target, + Refresh: dxVirtualInterfaceStateRefresh(conn, d.Id()), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for Direct Connect virtual interface %s to become available: %s", d.Id(), err.Error()) + } + + return nil +} + +// Attributes common to public VIFs and creator side of hosted public VIFs. +func dxPublicVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { + if err := dxVirtualInterfaceAttributes(d, meta, vif); err != nil { + return err + } + d.Set("route_filter_prefixes", flattenDxRouteFilterPrefixes(vif.RouteFilterPrefixes)) + + return nil +} + +// Attributes common to private VIFs and creator side of hosted private VIFs. +func dxPrivateVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { + return dxVirtualInterfaceAttributes(d, meta, vif) +} + +// Attributes common to public/private VIFs and creator side of hosted public/private VIFs. +func dxVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { + if err := dxVirtualInterfaceArnAttribute(d, meta); err != nil { + return err + } + + d.Set("connection_id", vif.ConnectionId) + d.Set("name", vif.VirtualInterfaceName) + d.Set("vlan", vif.Vlan) + d.Set("bgp_asn", vif.Asn) + d.Set("bgp_auth_key", vif.AuthKey) + d.Set("address_family", vif.AddressFamily) + d.Set("customer_address", vif.CustomerAddress) + d.Set("amazon_address", vif.AmazonAddress) + + return nil +} + +func dxVirtualInterfaceArnAttribute(d *schema.ResourceData, meta interface{}) error { + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + + return nil +} + +func expandDxRouteFilterPrefixes(cfg []interface{}) []*directconnect.RouteFilterPrefix { + prefixes := make([]*directconnect.RouteFilterPrefix, len(cfg), len(cfg)) + for i, p := range cfg { + prefix := &directconnect.RouteFilterPrefix{ + Cidr: aws.String(p.(string)), + } + prefixes[i] = prefix + } + return prefixes +} + +func flattenDxRouteFilterPrefixes(prefixes []*directconnect.RouteFilterPrefix) *schema.Set { + out := make([]interface{}, 0) + for _, prefix := range prefixes { + out = append(out, aws.StringValue(prefix.Cidr)) + } + return schema.NewSet(schema.HashString, out) +} + +// Schemas common to all (public/private, hosted or not) virtual interfaces. +var dxVirtualInterfaceSchemaWithTags = mergeSchemas( + dxVirtualInterfaceSchema, + map[string]*schema.Schema{ + "tags": tagsSchema(), + }, +) +var dxVirtualInterfaceSchema = map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), + }, + "bgp_asn": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "bgp_auth_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "address_family": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + }, + "customer_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "amazon_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, +} diff --git a/aws/provider.go b/aws/provider.go index 1939f4dd146a..1f64f97ef3ea 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -263,373 +263,375 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "aws_acm_certificate": resourceAwsAcmCertificate(), - "aws_acm_certificate_validation": resourceAwsAcmCertificateValidation(), - "aws_acmpca_certificate_authority": resourceAwsAcmpcaCertificateAuthority(), - "aws_ami": resourceAwsAmi(), - "aws_ami_copy": resourceAwsAmiCopy(), - "aws_ami_from_instance": resourceAwsAmiFromInstance(), - "aws_ami_launch_permission": resourceAwsAmiLaunchPermission(), - "aws_api_gateway_account": resourceAwsApiGatewayAccount(), - "aws_api_gateway_api_key": resourceAwsApiGatewayApiKey(), - "aws_api_gateway_authorizer": resourceAwsApiGatewayAuthorizer(), - "aws_api_gateway_base_path_mapping": resourceAwsApiGatewayBasePathMapping(), - "aws_api_gateway_client_certificate": resourceAwsApiGatewayClientCertificate(), - "aws_api_gateway_deployment": resourceAwsApiGatewayDeployment(), - "aws_api_gateway_documentation_part": resourceAwsApiGatewayDocumentationPart(), - "aws_api_gateway_documentation_version": resourceAwsApiGatewayDocumentationVersion(), - "aws_api_gateway_domain_name": resourceAwsApiGatewayDomainName(), - "aws_api_gateway_gateway_response": resourceAwsApiGatewayGatewayResponse(), - "aws_api_gateway_integration": resourceAwsApiGatewayIntegration(), - "aws_api_gateway_integration_response": resourceAwsApiGatewayIntegrationResponse(), - "aws_api_gateway_method": resourceAwsApiGatewayMethod(), - "aws_api_gateway_method_response": resourceAwsApiGatewayMethodResponse(), - "aws_api_gateway_method_settings": resourceAwsApiGatewayMethodSettings(), - "aws_api_gateway_model": resourceAwsApiGatewayModel(), - "aws_api_gateway_request_validator": resourceAwsApiGatewayRequestValidator(), - "aws_api_gateway_resource": resourceAwsApiGatewayResource(), - "aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(), - "aws_api_gateway_stage": resourceAwsApiGatewayStage(), - "aws_api_gateway_usage_plan": resourceAwsApiGatewayUsagePlan(), - "aws_api_gateway_usage_plan_key": resourceAwsApiGatewayUsagePlanKey(), - "aws_api_gateway_vpc_link": resourceAwsApiGatewayVpcLink(), - "aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(), - "aws_appautoscaling_target": resourceAwsAppautoscalingTarget(), - "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), - "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), - "aws_appsync_datasource": resourceAwsAppsyncDatasource(), - "aws_appsync_graphql_api": resourceAwsAppsyncGraphqlApi(), - "aws_athena_database": resourceAwsAthenaDatabase(), - "aws_athena_named_query": resourceAwsAthenaNamedQuery(), - "aws_autoscaling_attachment": resourceAwsAutoscalingAttachment(), - "aws_autoscaling_group": resourceAwsAutoscalingGroup(), - "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), - "aws_autoscaling_notification": resourceAwsAutoscalingNotification(), - "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), - "aws_autoscaling_schedule": resourceAwsAutoscalingSchedule(), - "aws_budgets_budget": resourceAwsBudgetsBudget(), - "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), - "aws_cloudformation_stack": resourceAwsCloudFormationStack(), - "aws_cloudfront_distribution": resourceAwsCloudFrontDistribution(), - "aws_cloudfront_origin_access_identity": resourceAwsCloudFrontOriginAccessIdentity(), - "aws_cloudtrail": resourceAwsCloudTrail(), - "aws_cloudwatch_event_permission": resourceAwsCloudWatchEventPermission(), - "aws_cloudwatch_event_rule": resourceAwsCloudWatchEventRule(), - "aws_cloudwatch_event_target": resourceAwsCloudWatchEventTarget(), - "aws_cloudwatch_log_destination": resourceAwsCloudWatchLogDestination(), - "aws_cloudwatch_log_destination_policy": resourceAwsCloudWatchLogDestinationPolicy(), - "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), - "aws_cloudwatch_log_metric_filter": resourceAwsCloudWatchLogMetricFilter(), - "aws_cloudwatch_log_resource_policy": resourceAwsCloudWatchLogResourcePolicy(), - "aws_cloudwatch_log_stream": resourceAwsCloudWatchLogStream(), - "aws_cloudwatch_log_subscription_filter": resourceAwsCloudwatchLogSubscriptionFilter(), - "aws_config_aggregate_authorization": resourceAwsConfigAggregateAuthorization(), - "aws_config_config_rule": resourceAwsConfigConfigRule(), - "aws_config_configuration_aggregator": resourceAwsConfigConfigurationAggregator(), - "aws_config_configuration_recorder": resourceAwsConfigConfigurationRecorder(), - "aws_config_configuration_recorder_status": resourceAwsConfigConfigurationRecorderStatus(), - "aws_config_delivery_channel": resourceAwsConfigDeliveryChannel(), - "aws_cognito_identity_pool": resourceAwsCognitoIdentityPool(), - "aws_cognito_identity_pool_roles_attachment": resourceAwsCognitoIdentityPoolRolesAttachment(), - "aws_cognito_identity_provider": resourceAwsCognitoIdentityProvider(), - "aws_cognito_user_group": resourceAwsCognitoUserGroup(), - "aws_cognito_user_pool": resourceAwsCognitoUserPool(), - "aws_cognito_user_pool_client": resourceAwsCognitoUserPoolClient(), - "aws_cognito_user_pool_domain": resourceAwsCognitoUserPoolDomain(), - "aws_cognito_resource_server": resourceAwsCognitoResourceServer(), - "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), - "aws_cloudwatch_dashboard": resourceAwsCloudWatchDashboard(), - "aws_codedeploy_app": resourceAwsCodeDeployApp(), - "aws_codedeploy_deployment_config": resourceAwsCodeDeployDeploymentConfig(), - "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), - "aws_codecommit_repository": resourceAwsCodeCommitRepository(), - "aws_codecommit_trigger": resourceAwsCodeCommitTrigger(), - "aws_codebuild_project": resourceAwsCodeBuildProject(), - "aws_codebuild_webhook": resourceAwsCodeBuildWebhook(), - "aws_codepipeline": resourceAwsCodePipeline(), - "aws_customer_gateway": resourceAwsCustomerGateway(), - "aws_dax_cluster": resourceAwsDaxCluster(), - "aws_dax_parameter_group": resourceAwsDaxParameterGroup(), - "aws_dax_subnet_group": resourceAwsDaxSubnetGroup(), - "aws_db_event_subscription": resourceAwsDbEventSubscription(), - "aws_db_instance": resourceAwsDbInstance(), - "aws_db_option_group": resourceAwsDbOptionGroup(), - "aws_db_parameter_group": resourceAwsDbParameterGroup(), - "aws_db_security_group": resourceAwsDbSecurityGroup(), - "aws_db_snapshot": resourceAwsDbSnapshot(), - "aws_db_subnet_group": resourceAwsDbSubnetGroup(), - "aws_devicefarm_project": resourceAwsDevicefarmProject(), - "aws_directory_service_directory": resourceAwsDirectoryServiceDirectory(), - "aws_directory_service_conditional_forwarder": resourceAwsDirectoryServiceConditionalForwarder(), - "aws_dms_certificate": resourceAwsDmsCertificate(), - "aws_dms_endpoint": resourceAwsDmsEndpoint(), - "aws_dms_replication_instance": resourceAwsDmsReplicationInstance(), - "aws_dms_replication_subnet_group": resourceAwsDmsReplicationSubnetGroup(), - "aws_dms_replication_task": resourceAwsDmsReplicationTask(), - "aws_dx_lag": resourceAwsDxLag(), - "aws_dx_connection": resourceAwsDxConnection(), - "aws_dx_connection_association": resourceAwsDxConnectionAssociation(), - "aws_dx_gateway": resourceAwsDxGateway(), - "aws_dx_gateway_association": resourceAwsDxGatewayAssociation(), - "aws_dynamodb_table": resourceAwsDynamoDbTable(), - "aws_dynamodb_table_item": resourceAwsDynamoDbTableItem(), - "aws_dynamodb_global_table": resourceAwsDynamoDbGlobalTable(), - "aws_ebs_snapshot": resourceAwsEbsSnapshot(), - "aws_ebs_volume": resourceAwsEbsVolume(), - "aws_ecr_lifecycle_policy": resourceAwsEcrLifecyclePolicy(), - "aws_ecr_repository": resourceAwsEcrRepository(), - "aws_ecr_repository_policy": resourceAwsEcrRepositoryPolicy(), - "aws_ecs_cluster": resourceAwsEcsCluster(), - "aws_ecs_service": resourceAwsEcsService(), - "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), - "aws_efs_file_system": resourceAwsEfsFileSystem(), - "aws_efs_mount_target": resourceAwsEfsMountTarget(), - "aws_egress_only_internet_gateway": resourceAwsEgressOnlyInternetGateway(), - "aws_eip": resourceAwsEip(), - "aws_eip_association": resourceAwsEipAssociation(), - "aws_eks_cluster": resourceAwsEksCluster(), - "aws_elasticache_cluster": resourceAwsElasticacheCluster(), - "aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(), - "aws_elasticache_replication_group": resourceAwsElasticacheReplicationGroup(), - "aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(), - "aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(), - "aws_elastic_beanstalk_application": resourceAwsElasticBeanstalkApplication(), - "aws_elastic_beanstalk_application_version": resourceAwsElasticBeanstalkApplicationVersion(), - "aws_elastic_beanstalk_configuration_template": resourceAwsElasticBeanstalkConfigurationTemplate(), - "aws_elastic_beanstalk_environment": resourceAwsElasticBeanstalkEnvironment(), - "aws_elasticsearch_domain": resourceAwsElasticSearchDomain(), - "aws_elasticsearch_domain_policy": resourceAwsElasticSearchDomainPolicy(), - "aws_elastictranscoder_pipeline": resourceAwsElasticTranscoderPipeline(), - "aws_elastictranscoder_preset": resourceAwsElasticTranscoderPreset(), - "aws_elb": resourceAwsElb(), - "aws_elb_attachment": resourceAwsElbAttachment(), - "aws_emr_cluster": resourceAwsEMRCluster(), - "aws_emr_instance_group": resourceAwsEMRInstanceGroup(), - "aws_emr_security_configuration": resourceAwsEMRSecurityConfiguration(), - "aws_flow_log": resourceAwsFlowLog(), - "aws_gamelift_alias": resourceAwsGameliftAlias(), - "aws_gamelift_build": resourceAwsGameliftBuild(), - "aws_gamelift_fleet": resourceAwsGameliftFleet(), - "aws_glacier_vault": resourceAwsGlacierVault(), - "aws_glue_catalog_database": resourceAwsGlueCatalogDatabase(), - "aws_glue_catalog_table": resourceAwsGlueCatalogTable(), - "aws_glue_classifier": resourceAwsGlueClassifier(), - "aws_glue_connection": resourceAwsGlueConnection(), - "aws_glue_crawler": resourceAwsGlueCrawler(), - "aws_glue_job": resourceAwsGlueJob(), - "aws_glue_trigger": resourceAwsGlueTrigger(), - "aws_guardduty_detector": resourceAwsGuardDutyDetector(), - "aws_guardduty_ipset": resourceAwsGuardDutyIpset(), - "aws_guardduty_member": resourceAwsGuardDutyMember(), - "aws_guardduty_threatintelset": resourceAwsGuardDutyThreatintelset(), - "aws_iam_access_key": resourceAwsIamAccessKey(), - "aws_iam_account_alias": resourceAwsIamAccountAlias(), - "aws_iam_account_password_policy": resourceAwsIamAccountPasswordPolicy(), - "aws_iam_group_policy": resourceAwsIamGroupPolicy(), - "aws_iam_group": resourceAwsIamGroup(), - "aws_iam_group_membership": resourceAwsIamGroupMembership(), - "aws_iam_group_policy_attachment": resourceAwsIamGroupPolicyAttachment(), - "aws_iam_instance_profile": resourceAwsIamInstanceProfile(), - "aws_iam_openid_connect_provider": resourceAwsIamOpenIDConnectProvider(), - "aws_iam_policy": resourceAwsIamPolicy(), - "aws_iam_policy_attachment": resourceAwsIamPolicyAttachment(), - "aws_iam_role_policy_attachment": resourceAwsIamRolePolicyAttachment(), - "aws_iam_role_policy": resourceAwsIamRolePolicy(), - "aws_iam_role": resourceAwsIamRole(), - "aws_iam_saml_provider": resourceAwsIamSamlProvider(), - "aws_iam_server_certificate": resourceAwsIAMServerCertificate(), - "aws_iam_service_linked_role": resourceAwsIamServiceLinkedRole(), - "aws_iam_user_group_membership": resourceAwsIamUserGroupMembership(), - "aws_iam_user_policy_attachment": resourceAwsIamUserPolicyAttachment(), - "aws_iam_user_policy": resourceAwsIamUserPolicy(), - "aws_iam_user_ssh_key": resourceAwsIamUserSshKey(), - "aws_iam_user": resourceAwsIamUser(), - "aws_iam_user_login_profile": resourceAwsIamUserLoginProfile(), - "aws_inspector_assessment_target": resourceAWSInspectorAssessmentTarget(), - "aws_inspector_assessment_template": resourceAWSInspectorAssessmentTemplate(), - "aws_inspector_resource_group": resourceAWSInspectorResourceGroup(), - "aws_instance": resourceAwsInstance(), - "aws_internet_gateway": resourceAwsInternetGateway(), - "aws_iot_certificate": resourceAwsIotCertificate(), - "aws_iot_policy": resourceAwsIotPolicy(), - "aws_iot_thing": resourceAwsIotThing(), - "aws_iot_thing_type": resourceAwsIotThingType(), - "aws_iot_topic_rule": resourceAwsIotTopicRule(), - "aws_key_pair": resourceAwsKeyPair(), - "aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(), - "aws_kinesis_stream": resourceAwsKinesisStream(), - "aws_kms_alias": resourceAwsKmsAlias(), - "aws_kms_grant": resourceAwsKmsGrant(), - "aws_kms_key": resourceAwsKmsKey(), - "aws_lambda_function": resourceAwsLambdaFunction(), - "aws_lambda_event_source_mapping": resourceAwsLambdaEventSourceMapping(), - "aws_lambda_alias": resourceAwsLambdaAlias(), - "aws_lambda_permission": resourceAwsLambdaPermission(), - "aws_launch_configuration": resourceAwsLaunchConfiguration(), - "aws_launch_template": resourceAwsLaunchTemplate(), - "aws_lightsail_domain": resourceAwsLightsailDomain(), - "aws_lightsail_instance": resourceAwsLightsailInstance(), - "aws_lightsail_key_pair": resourceAwsLightsailKeyPair(), - "aws_lightsail_static_ip": resourceAwsLightsailStaticIp(), - "aws_lightsail_static_ip_attachment": resourceAwsLightsailStaticIpAttachment(), - "aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(), - "aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(), - "aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(), - "aws_load_balancer_listener_policy": resourceAwsLoadBalancerListenerPolicies(), - "aws_lb_ssl_negotiation_policy": resourceAwsLBSSLNegotiationPolicy(), - "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), - "aws_mq_broker": resourceAwsMqBroker(), - "aws_mq_configuration": resourceAwsMqConfiguration(), - "aws_media_store_container": resourceAwsMediaStoreContainer(), - "aws_nat_gateway": resourceAwsNatGateway(), - "aws_network_acl": resourceAwsNetworkAcl(), - "aws_default_network_acl": resourceAwsDefaultNetworkAcl(), - "aws_neptune_parameter_group": resourceAwsNeptuneParameterGroup(), - "aws_network_acl_rule": resourceAwsNetworkAclRule(), - "aws_network_interface": resourceAwsNetworkInterface(), - "aws_network_interface_attachment": resourceAwsNetworkInterfaceAttachment(), - "aws_opsworks_application": resourceAwsOpsworksApplication(), - "aws_opsworks_stack": resourceAwsOpsworksStack(), - "aws_opsworks_java_app_layer": resourceAwsOpsworksJavaAppLayer(), - "aws_opsworks_haproxy_layer": resourceAwsOpsworksHaproxyLayer(), - "aws_opsworks_static_web_layer": resourceAwsOpsworksStaticWebLayer(), - "aws_opsworks_php_app_layer": resourceAwsOpsworksPhpAppLayer(), - "aws_opsworks_rails_app_layer": resourceAwsOpsworksRailsAppLayer(), - "aws_opsworks_nodejs_app_layer": resourceAwsOpsworksNodejsAppLayer(), - "aws_opsworks_memcached_layer": resourceAwsOpsworksMemcachedLayer(), - "aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(), - "aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(), - "aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(), - "aws_opsworks_instance": resourceAwsOpsworksInstance(), - "aws_opsworks_user_profile": resourceAwsOpsworksUserProfile(), - "aws_opsworks_permission": resourceAwsOpsworksPermission(), - "aws_opsworks_rds_db_instance": resourceAwsOpsworksRdsDbInstance(), - "aws_organizations_organization": resourceAwsOrganizationsOrganization(), - "aws_organizations_account": resourceAwsOrganizationsAccount(), - "aws_organizations_policy": resourceAwsOrganizationsPolicy(), - "aws_organizations_policy_attachment": resourceAwsOrganizationsPolicyAttachment(), - "aws_placement_group": resourceAwsPlacementGroup(), - "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), - "aws_rds_cluster": resourceAwsRDSCluster(), - "aws_rds_cluster_instance": resourceAwsRDSClusterInstance(), - "aws_rds_cluster_parameter_group": resourceAwsRDSClusterParameterGroup(), - "aws_redshift_cluster": resourceAwsRedshiftCluster(), - "aws_redshift_security_group": resourceAwsRedshiftSecurityGroup(), - "aws_redshift_parameter_group": resourceAwsRedshiftParameterGroup(), - "aws_redshift_subnet_group": resourceAwsRedshiftSubnetGroup(), - "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), - "aws_route53_query_log": resourceAwsRoute53QueryLog(), - "aws_route53_record": resourceAwsRoute53Record(), - "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), - "aws_route53_zone": resourceAwsRoute53Zone(), - "aws_route53_health_check": resourceAwsRoute53HealthCheck(), - "aws_route": resourceAwsRoute(), - "aws_route_table": resourceAwsRouteTable(), - "aws_default_route_table": resourceAwsDefaultRouteTable(), - "aws_route_table_association": resourceAwsRouteTableAssociation(), - "aws_secretsmanager_secret": resourceAwsSecretsManagerSecret(), - "aws_secretsmanager_secret_version": resourceAwsSecretsManagerSecretVersion(), - "aws_ses_active_receipt_rule_set": resourceAwsSesActiveReceiptRuleSet(), - "aws_ses_domain_identity": resourceAwsSesDomainIdentity(), - "aws_ses_domain_identity_verification": resourceAwsSesDomainIdentityVerification(), - "aws_ses_domain_dkim": resourceAwsSesDomainDkim(), - "aws_ses_domain_mail_from": resourceAwsSesDomainMailFrom(), - "aws_ses_receipt_filter": resourceAwsSesReceiptFilter(), - "aws_ses_receipt_rule": resourceAwsSesReceiptRule(), - "aws_ses_receipt_rule_set": resourceAwsSesReceiptRuleSet(), - "aws_ses_configuration_set": resourceAwsSesConfigurationSet(), - "aws_ses_event_destination": resourceAwsSesEventDestination(), - "aws_ses_identity_notification_topic": resourceAwsSesNotificationTopic(), - "aws_ses_template": resourceAwsSesTemplate(), - "aws_s3_bucket": resourceAwsS3Bucket(), - "aws_s3_bucket_policy": resourceAwsS3BucketPolicy(), - "aws_s3_bucket_object": resourceAwsS3BucketObject(), - "aws_s3_bucket_notification": resourceAwsS3BucketNotification(), - "aws_s3_bucket_metric": resourceAwsS3BucketMetric(), - "aws_security_group": resourceAwsSecurityGroup(), - "aws_network_interface_sg_attachment": resourceAwsNetworkInterfaceSGAttachment(), - "aws_default_security_group": resourceAwsDefaultSecurityGroup(), - "aws_security_group_rule": resourceAwsSecurityGroupRule(), - "aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(), - "aws_service_discovery_private_dns_namespace": resourceAwsServiceDiscoveryPrivateDnsNamespace(), - "aws_service_discovery_public_dns_namespace": resourceAwsServiceDiscoveryPublicDnsNamespace(), - "aws_service_discovery_service": resourceAwsServiceDiscoveryService(), - "aws_simpledb_domain": resourceAwsSimpleDBDomain(), - "aws_ssm_activation": resourceAwsSsmActivation(), - "aws_ssm_association": resourceAwsSsmAssociation(), - "aws_ssm_document": resourceAwsSsmDocument(), - "aws_ssm_maintenance_window": resourceAwsSsmMaintenanceWindow(), - "aws_ssm_maintenance_window_target": resourceAwsSsmMaintenanceWindowTarget(), - "aws_ssm_maintenance_window_task": resourceAwsSsmMaintenanceWindowTask(), - "aws_ssm_patch_baseline": resourceAwsSsmPatchBaseline(), - "aws_ssm_patch_group": resourceAwsSsmPatchGroup(), - "aws_ssm_parameter": resourceAwsSsmParameter(), - "aws_ssm_resource_data_sync": resourceAwsSsmResourceDataSync(), - "aws_spot_datafeed_subscription": resourceAwsSpotDataFeedSubscription(), - "aws_spot_instance_request": resourceAwsSpotInstanceRequest(), - "aws_spot_fleet_request": resourceAwsSpotFleetRequest(), - "aws_sqs_queue": resourceAwsSqsQueue(), - "aws_sqs_queue_policy": resourceAwsSqsQueuePolicy(), - "aws_snapshot_create_volume_permission": resourceAwsSnapshotCreateVolumePermission(), - "aws_sns_platform_application": resourceAwsSnsPlatformApplication(), - "aws_sns_sms_preferences": resourceAwsSnsSmsPreferences(), - "aws_sns_topic": resourceAwsSnsTopic(), - "aws_sns_topic_policy": resourceAwsSnsTopicPolicy(), - "aws_sns_topic_subscription": resourceAwsSnsTopicSubscription(), - "aws_sfn_activity": resourceAwsSfnActivity(), - "aws_sfn_state_machine": resourceAwsSfnStateMachine(), - "aws_default_subnet": resourceAwsDefaultSubnet(), - "aws_subnet": resourceAwsSubnet(), - "aws_volume_attachment": resourceAwsVolumeAttachment(), - "aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(), - "aws_default_vpc_dhcp_options": resourceAwsDefaultVpcDhcpOptions(), - "aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(), - "aws_vpc_peering_connection": resourceAwsVpcPeeringConnection(), - "aws_vpc_peering_connection_accepter": resourceAwsVpcPeeringConnectionAccepter(), - "aws_vpc_peering_connection_options": resourceAwsVpcPeeringConnectionOptions(), - "aws_default_vpc": resourceAwsDefaultVpc(), - "aws_vpc": resourceAwsVpc(), - "aws_vpc_endpoint": resourceAwsVpcEndpoint(), - "aws_vpc_endpoint_connection_notification": resourceAwsVpcEndpointConnectionNotification(), - "aws_vpc_endpoint_route_table_association": resourceAwsVpcEndpointRouteTableAssociation(), - "aws_vpc_endpoint_subnet_association": resourceAwsVpcEndpointSubnetAssociation(), - "aws_vpc_endpoint_service": resourceAwsVpcEndpointService(), - "aws_vpc_endpoint_service_allowed_principal": resourceAwsVpcEndpointServiceAllowedPrincipal(), - "aws_vpn_connection": resourceAwsVpnConnection(), - "aws_vpn_connection_route": resourceAwsVpnConnectionRoute(), - "aws_vpn_gateway": resourceAwsVpnGateway(), - "aws_vpn_gateway_attachment": resourceAwsVpnGatewayAttachment(), - "aws_vpn_gateway_route_propagation": resourceAwsVpnGatewayRoutePropagation(), - "aws_waf_byte_match_set": resourceAwsWafByteMatchSet(), - "aws_waf_ipset": resourceAwsWafIPSet(), - "aws_waf_rate_based_rule": resourceAwsWafRateBasedRule(), - "aws_waf_regex_match_set": resourceAwsWafRegexMatchSet(), - "aws_waf_regex_pattern_set": resourceAwsWafRegexPatternSet(), - "aws_waf_rule": resourceAwsWafRule(), - "aws_waf_rule_group": resourceAwsWafRuleGroup(), - "aws_waf_size_constraint_set": resourceAwsWafSizeConstraintSet(), - "aws_waf_web_acl": resourceAwsWafWebAcl(), - "aws_waf_xss_match_set": resourceAwsWafXssMatchSet(), - "aws_waf_sql_injection_match_set": resourceAwsWafSqlInjectionMatchSet(), - "aws_waf_geo_match_set": resourceAwsWafGeoMatchSet(), - "aws_wafregional_byte_match_set": resourceAwsWafRegionalByteMatchSet(), - "aws_wafregional_geo_match_set": resourceAwsWafRegionalGeoMatchSet(), - "aws_wafregional_ipset": resourceAwsWafRegionalIPSet(), - "aws_wafregional_rate_based_rule": resourceAwsWafRegionalRateBasedRule(), - "aws_wafregional_regex_match_set": resourceAwsWafRegionalRegexMatchSet(), - "aws_wafregional_regex_pattern_set": resourceAwsWafRegionalRegexPatternSet(), - "aws_wafregional_rule": resourceAwsWafRegionalRule(), - "aws_wafregional_rule_group": resourceAwsWafRegionalRuleGroup(), - "aws_wafregional_size_constraint_set": resourceAwsWafRegionalSizeConstraintSet(), - "aws_wafregional_sql_injection_match_set": resourceAwsWafRegionalSqlInjectionMatchSet(), - "aws_wafregional_xss_match_set": resourceAwsWafRegionalXssMatchSet(), - "aws_wafregional_web_acl": resourceAwsWafRegionalWebAcl(), - "aws_wafregional_web_acl_association": resourceAwsWafRegionalWebAclAssociation(), - "aws_batch_compute_environment": resourceAwsBatchComputeEnvironment(), - "aws_batch_job_definition": resourceAwsBatchJobDefinition(), - "aws_batch_job_queue": resourceAwsBatchJobQueue(), - "aws_neptune_subnet_group": resourceAwsNeptuneSubnetGroup(), - "aws_neptune_cluster_parameter_group": resourceAwsNeptuneClusterParameterGroup(), + "aws_acm_certificate": resourceAwsAcmCertificate(), + "aws_acm_certificate_validation": resourceAwsAcmCertificateValidation(), + "aws_acmpca_certificate_authority": resourceAwsAcmpcaCertificateAuthority(), + "aws_ami": resourceAwsAmi(), + "aws_ami_copy": resourceAwsAmiCopy(), + "aws_ami_from_instance": resourceAwsAmiFromInstance(), + "aws_ami_launch_permission": resourceAwsAmiLaunchPermission(), + "aws_api_gateway_account": resourceAwsApiGatewayAccount(), + "aws_api_gateway_api_key": resourceAwsApiGatewayApiKey(), + "aws_api_gateway_authorizer": resourceAwsApiGatewayAuthorizer(), + "aws_api_gateway_base_path_mapping": resourceAwsApiGatewayBasePathMapping(), + "aws_api_gateway_client_certificate": resourceAwsApiGatewayClientCertificate(), + "aws_api_gateway_deployment": resourceAwsApiGatewayDeployment(), + "aws_api_gateway_documentation_part": resourceAwsApiGatewayDocumentationPart(), + "aws_api_gateway_documentation_version": resourceAwsApiGatewayDocumentationVersion(), + "aws_api_gateway_domain_name": resourceAwsApiGatewayDomainName(), + "aws_api_gateway_gateway_response": resourceAwsApiGatewayGatewayResponse(), + "aws_api_gateway_integration": resourceAwsApiGatewayIntegration(), + "aws_api_gateway_integration_response": resourceAwsApiGatewayIntegrationResponse(), + "aws_api_gateway_method": resourceAwsApiGatewayMethod(), + "aws_api_gateway_method_response": resourceAwsApiGatewayMethodResponse(), + "aws_api_gateway_method_settings": resourceAwsApiGatewayMethodSettings(), + "aws_api_gateway_model": resourceAwsApiGatewayModel(), + "aws_api_gateway_request_validator": resourceAwsApiGatewayRequestValidator(), + "aws_api_gateway_resource": resourceAwsApiGatewayResource(), + "aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(), + "aws_api_gateway_stage": resourceAwsApiGatewayStage(), + "aws_api_gateway_usage_plan": resourceAwsApiGatewayUsagePlan(), + "aws_api_gateway_usage_plan_key": resourceAwsApiGatewayUsagePlanKey(), + "aws_api_gateway_vpc_link": resourceAwsApiGatewayVpcLink(), + "aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(), + "aws_appautoscaling_target": resourceAwsAppautoscalingTarget(), + "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), + "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), + "aws_appsync_datasource": resourceAwsAppsyncDatasource(), + "aws_appsync_graphql_api": resourceAwsAppsyncGraphqlApi(), + "aws_athena_database": resourceAwsAthenaDatabase(), + "aws_athena_named_query": resourceAwsAthenaNamedQuery(), + "aws_autoscaling_attachment": resourceAwsAutoscalingAttachment(), + "aws_autoscaling_group": resourceAwsAutoscalingGroup(), + "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), + "aws_autoscaling_notification": resourceAwsAutoscalingNotification(), + "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), + "aws_autoscaling_schedule": resourceAwsAutoscalingSchedule(), + "aws_budgets_budget": resourceAwsBudgetsBudget(), + "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), + "aws_cloudformation_stack": resourceAwsCloudFormationStack(), + "aws_cloudfront_distribution": resourceAwsCloudFrontDistribution(), + "aws_cloudfront_origin_access_identity": resourceAwsCloudFrontOriginAccessIdentity(), + "aws_cloudtrail": resourceAwsCloudTrail(), + "aws_cloudwatch_event_permission": resourceAwsCloudWatchEventPermission(), + "aws_cloudwatch_event_rule": resourceAwsCloudWatchEventRule(), + "aws_cloudwatch_event_target": resourceAwsCloudWatchEventTarget(), + "aws_cloudwatch_log_destination": resourceAwsCloudWatchLogDestination(), + "aws_cloudwatch_log_destination_policy": resourceAwsCloudWatchLogDestinationPolicy(), + "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), + "aws_cloudwatch_log_metric_filter": resourceAwsCloudWatchLogMetricFilter(), + "aws_cloudwatch_log_resource_policy": resourceAwsCloudWatchLogResourcePolicy(), + "aws_cloudwatch_log_stream": resourceAwsCloudWatchLogStream(), + "aws_cloudwatch_log_subscription_filter": resourceAwsCloudwatchLogSubscriptionFilter(), + "aws_config_aggregate_authorization": resourceAwsConfigAggregateAuthorization(), + "aws_config_config_rule": resourceAwsConfigConfigRule(), + "aws_config_configuration_aggregator": resourceAwsConfigConfigurationAggregator(), + "aws_config_configuration_recorder": resourceAwsConfigConfigurationRecorder(), + "aws_config_configuration_recorder_status": resourceAwsConfigConfigurationRecorderStatus(), + "aws_config_delivery_channel": resourceAwsConfigDeliveryChannel(), + "aws_cognito_identity_pool": resourceAwsCognitoIdentityPool(), + "aws_cognito_identity_pool_roles_attachment": resourceAwsCognitoIdentityPoolRolesAttachment(), + "aws_cognito_identity_provider": resourceAwsCognitoIdentityProvider(), + "aws_cognito_user_group": resourceAwsCognitoUserGroup(), + "aws_cognito_user_pool": resourceAwsCognitoUserPool(), + "aws_cognito_user_pool_client": resourceAwsCognitoUserPoolClient(), + "aws_cognito_user_pool_domain": resourceAwsCognitoUserPoolDomain(), + "aws_cognito_resource_server": resourceAwsCognitoResourceServer(), + "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), + "aws_cloudwatch_dashboard": resourceAwsCloudWatchDashboard(), + "aws_codedeploy_app": resourceAwsCodeDeployApp(), + "aws_codedeploy_deployment_config": resourceAwsCodeDeployDeploymentConfig(), + "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), + "aws_codecommit_repository": resourceAwsCodeCommitRepository(), + "aws_codecommit_trigger": resourceAwsCodeCommitTrigger(), + "aws_codebuild_project": resourceAwsCodeBuildProject(), + "aws_codebuild_webhook": resourceAwsCodeBuildWebhook(), + "aws_codepipeline": resourceAwsCodePipeline(), + "aws_customer_gateway": resourceAwsCustomerGateway(), + "aws_dax_cluster": resourceAwsDaxCluster(), + "aws_dax_parameter_group": resourceAwsDaxParameterGroup(), + "aws_dax_subnet_group": resourceAwsDaxSubnetGroup(), + "aws_db_event_subscription": resourceAwsDbEventSubscription(), + "aws_db_instance": resourceAwsDbInstance(), + "aws_db_option_group": resourceAwsDbOptionGroup(), + "aws_db_parameter_group": resourceAwsDbParameterGroup(), + "aws_db_security_group": resourceAwsDbSecurityGroup(), + "aws_db_snapshot": resourceAwsDbSnapshot(), + "aws_db_subnet_group": resourceAwsDbSubnetGroup(), + "aws_devicefarm_project": resourceAwsDevicefarmProject(), + "aws_directory_service_directory": resourceAwsDirectoryServiceDirectory(), + "aws_directory_service_conditional_forwarder": resourceAwsDirectoryServiceConditionalForwarder(), + "aws_dms_certificate": resourceAwsDmsCertificate(), + "aws_dms_endpoint": resourceAwsDmsEndpoint(), + "aws_dms_replication_instance": resourceAwsDmsReplicationInstance(), + "aws_dms_replication_subnet_group": resourceAwsDmsReplicationSubnetGroup(), + "aws_dms_replication_task": resourceAwsDmsReplicationTask(), + "aws_dx_lag": resourceAwsDxLag(), + "aws_dx_connection": resourceAwsDxConnection(), + "aws_dx_connection_association": resourceAwsDxConnectionAssociation(), + "aws_dx_gateway": resourceAwsDxGateway(), + "aws_dx_gateway_association": resourceAwsDxGatewayAssociation(), + "aws_dx_hosted_private_virtual_interface": resourceAwsDxHostedPrivateVirtualInterface(), + "aws_dx_hosted_private_virtual_interface_accepter": resourceAwsDxHostedPrivateVirtualInterfaceAccepter(), + "aws_dynamodb_table": resourceAwsDynamoDbTable(), + "aws_dynamodb_table_item": resourceAwsDynamoDbTableItem(), + "aws_dynamodb_global_table": resourceAwsDynamoDbGlobalTable(), + "aws_ebs_snapshot": resourceAwsEbsSnapshot(), + "aws_ebs_volume": resourceAwsEbsVolume(), + "aws_ecr_lifecycle_policy": resourceAwsEcrLifecyclePolicy(), + "aws_ecr_repository": resourceAwsEcrRepository(), + "aws_ecr_repository_policy": resourceAwsEcrRepositoryPolicy(), + "aws_ecs_cluster": resourceAwsEcsCluster(), + "aws_ecs_service": resourceAwsEcsService(), + "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), + "aws_efs_file_system": resourceAwsEfsFileSystem(), + "aws_efs_mount_target": resourceAwsEfsMountTarget(), + "aws_egress_only_internet_gateway": resourceAwsEgressOnlyInternetGateway(), + "aws_eip": resourceAwsEip(), + "aws_eip_association": resourceAwsEipAssociation(), + "aws_eks_cluster": resourceAwsEksCluster(), + "aws_elasticache_cluster": resourceAwsElasticacheCluster(), + "aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(), + "aws_elasticache_replication_group": resourceAwsElasticacheReplicationGroup(), + "aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(), + "aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(), + "aws_elastic_beanstalk_application": resourceAwsElasticBeanstalkApplication(), + "aws_elastic_beanstalk_application_version": resourceAwsElasticBeanstalkApplicationVersion(), + "aws_elastic_beanstalk_configuration_template": resourceAwsElasticBeanstalkConfigurationTemplate(), + "aws_elastic_beanstalk_environment": resourceAwsElasticBeanstalkEnvironment(), + "aws_elasticsearch_domain": resourceAwsElasticSearchDomain(), + "aws_elasticsearch_domain_policy": resourceAwsElasticSearchDomainPolicy(), + "aws_elastictranscoder_pipeline": resourceAwsElasticTranscoderPipeline(), + "aws_elastictranscoder_preset": resourceAwsElasticTranscoderPreset(), + "aws_elb": resourceAwsElb(), + "aws_elb_attachment": resourceAwsElbAttachment(), + "aws_emr_cluster": resourceAwsEMRCluster(), + "aws_emr_instance_group": resourceAwsEMRInstanceGroup(), + "aws_emr_security_configuration": resourceAwsEMRSecurityConfiguration(), + "aws_flow_log": resourceAwsFlowLog(), + "aws_gamelift_alias": resourceAwsGameliftAlias(), + "aws_gamelift_build": resourceAwsGameliftBuild(), + "aws_gamelift_fleet": resourceAwsGameliftFleet(), + "aws_glacier_vault": resourceAwsGlacierVault(), + "aws_glue_catalog_database": resourceAwsGlueCatalogDatabase(), + "aws_glue_catalog_table": resourceAwsGlueCatalogTable(), + "aws_glue_classifier": resourceAwsGlueClassifier(), + "aws_glue_connection": resourceAwsGlueConnection(), + "aws_glue_crawler": resourceAwsGlueCrawler(), + "aws_glue_job": resourceAwsGlueJob(), + "aws_glue_trigger": resourceAwsGlueTrigger(), + "aws_guardduty_detector": resourceAwsGuardDutyDetector(), + "aws_guardduty_ipset": resourceAwsGuardDutyIpset(), + "aws_guardduty_member": resourceAwsGuardDutyMember(), + "aws_guardduty_threatintelset": resourceAwsGuardDutyThreatintelset(), + "aws_iam_access_key": resourceAwsIamAccessKey(), + "aws_iam_account_alias": resourceAwsIamAccountAlias(), + "aws_iam_account_password_policy": resourceAwsIamAccountPasswordPolicy(), + "aws_iam_group_policy": resourceAwsIamGroupPolicy(), + "aws_iam_group": resourceAwsIamGroup(), + "aws_iam_group_membership": resourceAwsIamGroupMembership(), + "aws_iam_group_policy_attachment": resourceAwsIamGroupPolicyAttachment(), + "aws_iam_instance_profile": resourceAwsIamInstanceProfile(), + "aws_iam_openid_connect_provider": resourceAwsIamOpenIDConnectProvider(), + "aws_iam_policy": resourceAwsIamPolicy(), + "aws_iam_policy_attachment": resourceAwsIamPolicyAttachment(), + "aws_iam_role_policy_attachment": resourceAwsIamRolePolicyAttachment(), + "aws_iam_role_policy": resourceAwsIamRolePolicy(), + "aws_iam_role": resourceAwsIamRole(), + "aws_iam_saml_provider": resourceAwsIamSamlProvider(), + "aws_iam_server_certificate": resourceAwsIAMServerCertificate(), + "aws_iam_service_linked_role": resourceAwsIamServiceLinkedRole(), + "aws_iam_user_group_membership": resourceAwsIamUserGroupMembership(), + "aws_iam_user_policy_attachment": resourceAwsIamUserPolicyAttachment(), + "aws_iam_user_policy": resourceAwsIamUserPolicy(), + "aws_iam_user_ssh_key": resourceAwsIamUserSshKey(), + "aws_iam_user": resourceAwsIamUser(), + "aws_iam_user_login_profile": resourceAwsIamUserLoginProfile(), + "aws_inspector_assessment_target": resourceAWSInspectorAssessmentTarget(), + "aws_inspector_assessment_template": resourceAWSInspectorAssessmentTemplate(), + "aws_inspector_resource_group": resourceAWSInspectorResourceGroup(), + "aws_instance": resourceAwsInstance(), + "aws_internet_gateway": resourceAwsInternetGateway(), + "aws_iot_certificate": resourceAwsIotCertificate(), + "aws_iot_policy": resourceAwsIotPolicy(), + "aws_iot_thing": resourceAwsIotThing(), + "aws_iot_thing_type": resourceAwsIotThingType(), + "aws_iot_topic_rule": resourceAwsIotTopicRule(), + "aws_key_pair": resourceAwsKeyPair(), + "aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(), + "aws_kinesis_stream": resourceAwsKinesisStream(), + "aws_kms_alias": resourceAwsKmsAlias(), + "aws_kms_grant": resourceAwsKmsGrant(), + "aws_kms_key": resourceAwsKmsKey(), + "aws_lambda_function": resourceAwsLambdaFunction(), + "aws_lambda_event_source_mapping": resourceAwsLambdaEventSourceMapping(), + "aws_lambda_alias": resourceAwsLambdaAlias(), + "aws_lambda_permission": resourceAwsLambdaPermission(), + "aws_launch_configuration": resourceAwsLaunchConfiguration(), + "aws_launch_template": resourceAwsLaunchTemplate(), + "aws_lightsail_domain": resourceAwsLightsailDomain(), + "aws_lightsail_instance": resourceAwsLightsailInstance(), + "aws_lightsail_key_pair": resourceAwsLightsailKeyPair(), + "aws_lightsail_static_ip": resourceAwsLightsailStaticIp(), + "aws_lightsail_static_ip_attachment": resourceAwsLightsailStaticIpAttachment(), + "aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(), + "aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(), + "aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(), + "aws_load_balancer_listener_policy": resourceAwsLoadBalancerListenerPolicies(), + "aws_lb_ssl_negotiation_policy": resourceAwsLBSSLNegotiationPolicy(), + "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), + "aws_mq_broker": resourceAwsMqBroker(), + "aws_mq_configuration": resourceAwsMqConfiguration(), + "aws_media_store_container": resourceAwsMediaStoreContainer(), + "aws_nat_gateway": resourceAwsNatGateway(), + "aws_network_acl": resourceAwsNetworkAcl(), + "aws_default_network_acl": resourceAwsDefaultNetworkAcl(), + "aws_neptune_parameter_group": resourceAwsNeptuneParameterGroup(), + "aws_network_acl_rule": resourceAwsNetworkAclRule(), + "aws_network_interface": resourceAwsNetworkInterface(), + "aws_network_interface_attachment": resourceAwsNetworkInterfaceAttachment(), + "aws_opsworks_application": resourceAwsOpsworksApplication(), + "aws_opsworks_stack": resourceAwsOpsworksStack(), + "aws_opsworks_java_app_layer": resourceAwsOpsworksJavaAppLayer(), + "aws_opsworks_haproxy_layer": resourceAwsOpsworksHaproxyLayer(), + "aws_opsworks_static_web_layer": resourceAwsOpsworksStaticWebLayer(), + "aws_opsworks_php_app_layer": resourceAwsOpsworksPhpAppLayer(), + "aws_opsworks_rails_app_layer": resourceAwsOpsworksRailsAppLayer(), + "aws_opsworks_nodejs_app_layer": resourceAwsOpsworksNodejsAppLayer(), + "aws_opsworks_memcached_layer": resourceAwsOpsworksMemcachedLayer(), + "aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(), + "aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(), + "aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(), + "aws_opsworks_instance": resourceAwsOpsworksInstance(), + "aws_opsworks_user_profile": resourceAwsOpsworksUserProfile(), + "aws_opsworks_permission": resourceAwsOpsworksPermission(), + "aws_opsworks_rds_db_instance": resourceAwsOpsworksRdsDbInstance(), + "aws_organizations_organization": resourceAwsOrganizationsOrganization(), + "aws_organizations_account": resourceAwsOrganizationsAccount(), + "aws_organizations_policy": resourceAwsOrganizationsPolicy(), + "aws_organizations_policy_attachment": resourceAwsOrganizationsPolicyAttachment(), + "aws_placement_group": resourceAwsPlacementGroup(), + "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), + "aws_rds_cluster": resourceAwsRDSCluster(), + "aws_rds_cluster_instance": resourceAwsRDSClusterInstance(), + "aws_rds_cluster_parameter_group": resourceAwsRDSClusterParameterGroup(), + "aws_redshift_cluster": resourceAwsRedshiftCluster(), + "aws_redshift_security_group": resourceAwsRedshiftSecurityGroup(), + "aws_redshift_parameter_group": resourceAwsRedshiftParameterGroup(), + "aws_redshift_subnet_group": resourceAwsRedshiftSubnetGroup(), + "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), + "aws_route53_query_log": resourceAwsRoute53QueryLog(), + "aws_route53_record": resourceAwsRoute53Record(), + "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), + "aws_route53_zone": resourceAwsRoute53Zone(), + "aws_route53_health_check": resourceAwsRoute53HealthCheck(), + "aws_route": resourceAwsRoute(), + "aws_route_table": resourceAwsRouteTable(), + "aws_default_route_table": resourceAwsDefaultRouteTable(), + "aws_route_table_association": resourceAwsRouteTableAssociation(), + "aws_secretsmanager_secret": resourceAwsSecretsManagerSecret(), + "aws_secretsmanager_secret_version": resourceAwsSecretsManagerSecretVersion(), + "aws_ses_active_receipt_rule_set": resourceAwsSesActiveReceiptRuleSet(), + "aws_ses_domain_identity": resourceAwsSesDomainIdentity(), + "aws_ses_domain_identity_verification": resourceAwsSesDomainIdentityVerification(), + "aws_ses_domain_dkim": resourceAwsSesDomainDkim(), + "aws_ses_domain_mail_from": resourceAwsSesDomainMailFrom(), + "aws_ses_receipt_filter": resourceAwsSesReceiptFilter(), + "aws_ses_receipt_rule": resourceAwsSesReceiptRule(), + "aws_ses_receipt_rule_set": resourceAwsSesReceiptRuleSet(), + "aws_ses_configuration_set": resourceAwsSesConfigurationSet(), + "aws_ses_event_destination": resourceAwsSesEventDestination(), + "aws_ses_identity_notification_topic": resourceAwsSesNotificationTopic(), + "aws_ses_template": resourceAwsSesTemplate(), + "aws_s3_bucket": resourceAwsS3Bucket(), + "aws_s3_bucket_policy": resourceAwsS3BucketPolicy(), + "aws_s3_bucket_object": resourceAwsS3BucketObject(), + "aws_s3_bucket_notification": resourceAwsS3BucketNotification(), + "aws_s3_bucket_metric": resourceAwsS3BucketMetric(), + "aws_security_group": resourceAwsSecurityGroup(), + "aws_network_interface_sg_attachment": resourceAwsNetworkInterfaceSGAttachment(), + "aws_default_security_group": resourceAwsDefaultSecurityGroup(), + "aws_security_group_rule": resourceAwsSecurityGroupRule(), + "aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(), + "aws_service_discovery_private_dns_namespace": resourceAwsServiceDiscoveryPrivateDnsNamespace(), + "aws_service_discovery_public_dns_namespace": resourceAwsServiceDiscoveryPublicDnsNamespace(), + "aws_service_discovery_service": resourceAwsServiceDiscoveryService(), + "aws_simpledb_domain": resourceAwsSimpleDBDomain(), + "aws_ssm_activation": resourceAwsSsmActivation(), + "aws_ssm_association": resourceAwsSsmAssociation(), + "aws_ssm_document": resourceAwsSsmDocument(), + "aws_ssm_maintenance_window": resourceAwsSsmMaintenanceWindow(), + "aws_ssm_maintenance_window_target": resourceAwsSsmMaintenanceWindowTarget(), + "aws_ssm_maintenance_window_task": resourceAwsSsmMaintenanceWindowTask(), + "aws_ssm_patch_baseline": resourceAwsSsmPatchBaseline(), + "aws_ssm_patch_group": resourceAwsSsmPatchGroup(), + "aws_ssm_parameter": resourceAwsSsmParameter(), + "aws_ssm_resource_data_sync": resourceAwsSsmResourceDataSync(), + "aws_spot_datafeed_subscription": resourceAwsSpotDataFeedSubscription(), + "aws_spot_instance_request": resourceAwsSpotInstanceRequest(), + "aws_spot_fleet_request": resourceAwsSpotFleetRequest(), + "aws_sqs_queue": resourceAwsSqsQueue(), + "aws_sqs_queue_policy": resourceAwsSqsQueuePolicy(), + "aws_snapshot_create_volume_permission": resourceAwsSnapshotCreateVolumePermission(), + "aws_sns_platform_application": resourceAwsSnsPlatformApplication(), + "aws_sns_sms_preferences": resourceAwsSnsSmsPreferences(), + "aws_sns_topic": resourceAwsSnsTopic(), + "aws_sns_topic_policy": resourceAwsSnsTopicPolicy(), + "aws_sns_topic_subscription": resourceAwsSnsTopicSubscription(), + "aws_sfn_activity": resourceAwsSfnActivity(), + "aws_sfn_state_machine": resourceAwsSfnStateMachine(), + "aws_default_subnet": resourceAwsDefaultSubnet(), + "aws_subnet": resourceAwsSubnet(), + "aws_volume_attachment": resourceAwsVolumeAttachment(), + "aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(), + "aws_default_vpc_dhcp_options": resourceAwsDefaultVpcDhcpOptions(), + "aws_vpc_dhcp_options": resourceAwsVpcDhcpOptions(), + "aws_vpc_peering_connection": resourceAwsVpcPeeringConnection(), + "aws_vpc_peering_connection_accepter": resourceAwsVpcPeeringConnectionAccepter(), + "aws_vpc_peering_connection_options": resourceAwsVpcPeeringConnectionOptions(), + "aws_default_vpc": resourceAwsDefaultVpc(), + "aws_vpc": resourceAwsVpc(), + "aws_vpc_endpoint": resourceAwsVpcEndpoint(), + "aws_vpc_endpoint_connection_notification": resourceAwsVpcEndpointConnectionNotification(), + "aws_vpc_endpoint_route_table_association": resourceAwsVpcEndpointRouteTableAssociation(), + "aws_vpc_endpoint_subnet_association": resourceAwsVpcEndpointSubnetAssociation(), + "aws_vpc_endpoint_service": resourceAwsVpcEndpointService(), + "aws_vpc_endpoint_service_allowed_principal": resourceAwsVpcEndpointServiceAllowedPrincipal(), + "aws_vpn_connection": resourceAwsVpnConnection(), + "aws_vpn_connection_route": resourceAwsVpnConnectionRoute(), + "aws_vpn_gateway": resourceAwsVpnGateway(), + "aws_vpn_gateway_attachment": resourceAwsVpnGatewayAttachment(), + "aws_vpn_gateway_route_propagation": resourceAwsVpnGatewayRoutePropagation(), + "aws_waf_byte_match_set": resourceAwsWafByteMatchSet(), + "aws_waf_ipset": resourceAwsWafIPSet(), + "aws_waf_rate_based_rule": resourceAwsWafRateBasedRule(), + "aws_waf_regex_match_set": resourceAwsWafRegexMatchSet(), + "aws_waf_regex_pattern_set": resourceAwsWafRegexPatternSet(), + "aws_waf_rule": resourceAwsWafRule(), + "aws_waf_rule_group": resourceAwsWafRuleGroup(), + "aws_waf_size_constraint_set": resourceAwsWafSizeConstraintSet(), + "aws_waf_web_acl": resourceAwsWafWebAcl(), + "aws_waf_xss_match_set": resourceAwsWafXssMatchSet(), + "aws_waf_sql_injection_match_set": resourceAwsWafSqlInjectionMatchSet(), + "aws_waf_geo_match_set": resourceAwsWafGeoMatchSet(), + "aws_wafregional_byte_match_set": resourceAwsWafRegionalByteMatchSet(), + "aws_wafregional_geo_match_set": resourceAwsWafRegionalGeoMatchSet(), + "aws_wafregional_ipset": resourceAwsWafRegionalIPSet(), + "aws_wafregional_rate_based_rule": resourceAwsWafRegionalRateBasedRule(), + "aws_wafregional_regex_match_set": resourceAwsWafRegionalRegexMatchSet(), + "aws_wafregional_regex_pattern_set": resourceAwsWafRegionalRegexPatternSet(), + "aws_wafregional_rule": resourceAwsWafRegionalRule(), + "aws_wafregional_rule_group": resourceAwsWafRegionalRuleGroup(), + "aws_wafregional_size_constraint_set": resourceAwsWafRegionalSizeConstraintSet(), + "aws_wafregional_sql_injection_match_set": resourceAwsWafRegionalSqlInjectionMatchSet(), + "aws_wafregional_xss_match_set": resourceAwsWafRegionalXssMatchSet(), + "aws_wafregional_web_acl": resourceAwsWafRegionalWebAcl(), + "aws_wafregional_web_acl_association": resourceAwsWafRegionalWebAclAssociation(), + "aws_batch_compute_environment": resourceAwsBatchComputeEnvironment(), + "aws_batch_job_definition": resourceAwsBatchJobDefinition(), + "aws_batch_job_queue": resourceAwsBatchJobQueue(), + "aws_neptune_subnet_group": resourceAwsNeptuneSubnetGroup(), + "aws_neptune_cluster_parameter_group": resourceAwsNeptuneClusterParameterGroup(), // ALBs are actually LBs because they can be type `network` or `application` // To avoid regressions, we will add a new resource for each and they both point diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface.go b/aws/resource_aws_dx_hosted_private_virtual_interface.go new file mode 100644 index 000000000000..716f322b668f --- /dev/null +++ b/aws/resource_aws_dx_hosted_private_virtual_interface.go @@ -0,0 +1,108 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDxHostedPrivateVirtualInterfaceCreate, + Read: resourceAwsDxHostedPrivateVirtualInterfaceRead, + Delete: resourceAwsDxHostedPrivateVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: mergeSchemas( + dxVirtualInterfaceSchema, + map[string]*schema.Schema{ + "owner_account_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAwsAccountId, + }, + }, + ), + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + } +} + +func resourceAwsDxHostedPrivateVirtualInterfaceCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + req := &directconnect.AllocatePrivateVirtualInterfaceInput{ + ConnectionId: aws.String(d.Get("connection_id").(string)), + OwnerAccount: aws.String(d.Get("owner_account_id").(string)), + NewPrivateVirtualInterfaceAllocation: &directconnect.NewPrivateVirtualInterfaceAllocation{ + VirtualInterfaceName: aws.String(d.Get("name").(string)), + Vlan: aws.Int64(int64(d.Get("vlan").(int))), + Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), + AddressFamily: aws.String(d.Get("address_family").(string)), + }, + } + if v, ok := d.GetOk("bgp_auth_key"); ok { + req.NewPrivateVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) + } + if v, ok := d.GetOk("customer_address"); ok { + req.NewPrivateVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) + } + if v, ok := d.GetOk("amazon_address"); ok { + req.NewPrivateVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Creating Direct Connect hosted private virtual interface: %#v", req) + resp, err := conn.AllocatePrivateVirtualInterface(req) + if err != nil { + return fmt.Errorf("Error creating Direct Connect hosted private virtual interface: %s", err.Error()) + } + + d.SetId(aws.StringValue(resp.VirtualInterfaceId)) + + if err := dxHostedPrivateVirtualInterfaceWaitUntilAvailable(d, conn); err != nil { + return err + } + + return resourceAwsDxHostedPrivateVirtualInterfaceRead(d, meta) +} + +func resourceAwsDxHostedPrivateVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { + vif, err := dxVirtualInterfaceRead(d, meta) + if err != nil { + return err + } + if vif == nil { + return nil + } + + d.Set("owner_account_id", vif.OwnerAccount) + return dxPrivateVirtualInterfaceAttributes(d, meta, vif) +} + +func resourceAwsDxHostedPrivateVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { + return dxVirtualInterfaceDelete(d, meta) +} + +func dxHostedPrivateVirtualInterfaceWaitUntilAvailable(d *schema.ResourceData, conn *directconnect.DirectConnect) error { + return dxVirtualInterfaceWaitUntilAvailable( + d, + conn, + []string{ + directconnect.VirtualInterfaceStatePending, + }, + []string{ + directconnect.VirtualInterfaceStateAvailable, + directconnect.VirtualInterfaceStateConfirming, + directconnect.VirtualInterfaceStateDown, + }) +} diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go new file mode 100644 index 000000000000..b3370a830635 --- /dev/null +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go @@ -0,0 +1,139 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsDxHostedPrivateVirtualInterfaceAccepter() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDxHostedPrivateVirtualInterfaceAccepterCreate, + Read: resourceAwsDxHostedPrivateVirtualInterfaceAccepterRead, + Update: resourceAwsDxHostedPrivateVirtualInterfaceAccepterUpdate, + Delete: resourceAwsDxHostedPrivateVirtualInterfaceAccepterDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "virtual_interface_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vpn_gateway_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"dx_gateway_id"}, + }, + "dx_gateway_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"vpn_gateway_id"}, + }, + "tags": tagsSchema(), + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + } +} + +func resourceAwsDxHostedPrivateVirtualInterfaceAccepterCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + vgwIdRaw, vgwOk := d.GetOk("vpn_gateway_id") + dxgwIdRaw, dxgwOk := d.GetOk("dx_gateway_id") + if vgwOk == dxgwOk { + return fmt.Errorf( + "One of ['vpn_gateway_id', 'dx_gateway_id'] must be set to create a Direct Connect private virtual interface accepter") + } + + vifId := d.Get("virtual_interface_id").(string) + req := &directconnect.ConfirmPrivateVirtualInterfaceInput{ + VirtualInterfaceId: aws.String(vifId), + } + if vgwOk { + req.VirtualGatewayId = aws.String(vgwIdRaw.(string)) + } + if dxgwOk { + req.DirectConnectGatewayId = aws.String(dxgwIdRaw.(string)) + } + + log.Printf("[DEBUG] Accepting Direct Connect hosted private virtual interface: %#v", req) + _, err := conn.ConfirmPrivateVirtualInterface(req) + if err != nil { + return fmt.Errorf("Error accepting Direct Connect hosted private virtual interface: %s", err.Error()) + } + + d.SetId(vifId) + + if err := dxHostedPrivateVirtualInterfaceAccepterWaitUntilAvailable(d, conn); err != nil { + return err + } + + return resourceAwsDxHostedPrivateVirtualInterfaceAccepterUpdate(d, meta) +} + +func resourceAwsDxHostedPrivateVirtualInterfaceAccepterRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d, meta) + if err != nil { + return err + } + if vif == nil { + return nil + } + + if err := dxVirtualInterfaceArnAttribute(d, meta); err != nil { + return err + } + d.Set("virtual_interface_id", vif.VirtualInterfaceId) + d.Set("vpn_gateway_id", vif.VirtualGatewayId) + d.Set("dx_gateway_id", vif.DirectConnectGatewayId) + if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { + return err + } + + return nil +} + +func resourceAwsDxHostedPrivateVirtualInterfaceAccepterUpdate(d *schema.ResourceData, meta interface{}) error { + if err := dxVirtualInterfaceUpdate(d, meta); err != nil { + return err + } + + return resourceAwsDxHostedPrivateVirtualInterfaceAccepterRead(d, meta) +} + +func resourceAwsDxHostedPrivateVirtualInterfaceAccepterDelete(d *schema.ResourceData, meta interface{}) error { + return dxVirtualInterfaceDelete(d, meta) +} + +func dxHostedPrivateVirtualInterfaceAccepterWaitUntilAvailable(d *schema.ResourceData, conn *directconnect.DirectConnect) error { + return dxVirtualInterfaceWaitUntilAvailable( + d, + conn, + []string{ + directconnect.VirtualInterfaceStateConfirming, + directconnect.VirtualInterfaceStatePending, + }, + []string{ + directconnect.VirtualInterfaceStateAvailable, + directconnect.VirtualInterfaceStateDown, + }) +} diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_test.go b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go new file mode 100644 index 000000000000..25ed5dce7110 --- /dev/null +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go @@ -0,0 +1,98 @@ +package aws + +import ( + "fmt" + "os" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsDxHostedPrivateVirtualInterface_basic(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) + } + key = "DX_HOSTED_VIF_OWNER_ACCOUNT" + ownerAccountId := os.Getenv(key) + if ownerAccountId == "" { + t.Skipf("Environment variable %s is not set", key) + } + vifName := fmt.Sprintf("tf-dx-vif-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsDxHostedPrivateVirtualInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxHostedPrivateVirtualInterfaceConfig_basic(connectionId, ownerAccountId, vifName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedPrivateVirtualInterfaceExists("aws_dx_hosted_private_virtual_interface.foo"), + resource.TestCheckResourceAttr("aws_dx_hosted_private_virtual_interface.foo", "name", vifName), + ), + }, + // Test import. + { + ResourceName: "aws_dx_hosted_private_virtual_interface.foo", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAwsDxHostedPrivateVirtualInterfaceDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).dxconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_dx_hosted_private_virtual_interface" { + continue + } + + input := &directconnect.DescribeVirtualInterfacesInput{ + VirtualInterfaceId: aws.String(rs.Primary.ID), + } + + resp, err := conn.DescribeVirtualInterfaces(input) + if err != nil { + return err + } + for _, v := range resp.VirtualInterfaces { + if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { + return fmt.Errorf("[DESTROY ERROR] Dx Public VIF (%s) not deleted", rs.Primary.ID) + } + } + } + return nil +} + +func testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + return nil + } +} + +func testAccDxHostedPrivateVirtualInterfaceConfig_basic(cid, ownerAcctId, n string) string { + return fmt.Sprintf(` +resource "aws_dx_hosted_private_virtual_interface" "foo" { + connection_id = "%s" + owner_account_id = "%s" + + name = "%s" + vlan = 4094 + address_family = "ipv4" + bgp_asn = 65352 +} + `, cid, ownerAcctId, n) +} diff --git a/aws/utils.go b/aws/utils.go index 21c3aab7ef3d..6a120cccff8c 100644 --- a/aws/utils.go +++ b/aws/utils.go @@ -7,6 +7,7 @@ import ( "regexp" "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" ) // Base64Encode encodes data if the input isn't already encoded using base64.StdEncoding.EncodeToString. @@ -47,3 +48,15 @@ func isResourceNotFoundError(err error) bool { _, ok := err.(*resource.NotFoundError) return ok } + +func mergeSchemas(schemas ...map[string]*schema.Schema) map[string]*schema.Schema { + merged := make(map[string]*schema.Schema) + + for _, schema := range schemas { + for k, v := range schema { + merged[k] = v + } + } + + return merged +} diff --git a/aws/utils_test.go b/aws/utils_test.go index 8248f4384d27..a5854e7ef4b0 100644 --- a/aws/utils_test.go +++ b/aws/utils_test.go @@ -1,6 +1,10 @@ package aws -import "testing" +import ( + "testing" + + "github.com/hashicorp/terraform/helper/schema" +) var base64encodingTests = []struct { in []byte @@ -70,3 +74,29 @@ func TestJsonBytesEqualWhitespaceAndNoWhitespace(t *testing.T) { t.Errorf("Expected jsonBytesEqual to return false for %s == %s", noWhitespaceDiff, whitespaceDiff) } } + +func TestMergeSchemas(t *testing.T) { + s1 := map[string]*schema.Schema{ + "aaa": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "zzz": { + Type: schema.TypeInt, + Optional: true, + Default: 42, + }, + } + s2 := map[string]*schema.Schema{ + "xxx": { + Type: schema.TypeFloat, + Computed: true, + }, + } + + s3 := mergeSchemas(s1, s2) + if len(s3) != 3 { + t.Errorf("Expected merge schema to be of size 3, got %d", len(s3)) + } +} diff --git a/website/aws.erb b/website/aws.erb index 0e07c6a0f181..1fe25fe01645 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -773,6 +773,12 @@ > aws_dx_gateway_association + > + aws_dx_hosted_private_virtual_interface + + > + aws_dx_hosted_private_virtual_interface_accepter + > aws_dx_lag diff --git a/website/docs/r/dx_hosted_private_virtual_interface.html.markdown b/website/docs/r/dx_hosted_private_virtual_interface.html.markdown new file mode 100644 index 000000000000..85b0cf79db9c --- /dev/null +++ b/website/docs/r/dx_hosted_private_virtual_interface.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "aws" +page_title: "AWS: aws_dx_hosted_private_virtual_interface" +sidebar_current: "docs-aws-resource-dx-hosted-private-virtual-interface" +description: |- + Provides a Direct Connect hosted private virtual interface resource. +--- + +# aws_dx_hosted_private_virtual_interface + +Provides a Direct Connect hosted private virtual interface resource. +A hosted virtual interface is a virtual interface that is owned by another AWS account. + +## Example Usage + +```hcl +resource "aws_dx_hosted_private_virtual_interface" "foo" { + connection_id = "dxcon-zzzzzzzz" + + name = "vif-foo" + vlan = 4094 + address_family = "ipv4" + bgp_asn = 65352 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `connection_id` - (Required) The ID of the Direct Connect connection (or LAG) on which to create the virtual interface. +* `owner_account_id` - (Required) The AWS account that will own the new virtual interface. +* `name` - (Required) The name for the virtual interface. +* `vlan` - (Required) The VLAN ID. +* `bgp_asn` - (Required) The autonomous system (AS) number for Border Gateway Protocol (BGP) configuration. +* `bgp_auth_key` - (Optional) The authentication key for BGP configuration. +* `address_family` - (Required) The address family for the BGP peer. `ipv4 ` or `ipv6`. +* `customer_address` - (Optional) The IPv4 CIDR destination address to which Amazon should send traffic. Required for IPv4 BGP peers. +* `amazon_address` - (Optional) The IPv4 CIDR address to use to send traffic to Amazon. Required for IPv4 BGP peers. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the virtual interface. +* `arn` - The ARN of the virtual interface. + +## Timeouts + +`aws_dx_hosted_private_virtual_interface` provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - (Default `10 minutes`) Used for creating virtual interface +- `delete` - (Default `10 minutes`) Used for destroying virtual interface + +## Import + +Direct Connect hosted private virtual interfaces can be imported using the `vif id`, e.g. + +``` +$ terraform import aws_dx_hosted_private_virtual_interface.test dxvif-33cc44dd +``` diff --git a/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown b/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown new file mode 100644 index 000000000000..d346a24a62c7 --- /dev/null +++ b/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown @@ -0,0 +1,88 @@ +--- +layout: "aws" +page_title: "AWS: aws_dx_hosted_private_virtual_interface_accepter" +sidebar_current: "docs-aws-resource-dx-hosted-private-virtual-interface-accepter" +description: |- + Provides a resource to manage the accepter's side of a Direct Connect hosted private virtual interface. +--- + +# aws_dx_hosted_private_virtual_interface_accepter + +Provides a resource to manage the accepter's side of a Direct Connect hosted private virtual interface. +This resource accepts ownership of a private virtual interface created by another AWS account. + +## Example Usage + +```hcl +provider "aws" { + # Creator's credentials. +} + +provider "aws" { + alias = "accepter" + + # Accepter's credentials. +} + +data "aws_caller_identity" "accepter" { + provider = "aws.accepter" +} + +# Creator's side of the VIF +resource "aws_dx_hosted_private_virtual_interface" "creator" { + connection_id = "dxcon-zzzzzzzz" + owner_account_id = "${data.aws_caller_identity.accepter.account_id}" + + name = "vif-foo" + vlan = 4094 + address_family = "ipv4" + bgp_asn = 65352 +} + +# Accepter's side of the VIF. +resource "aws_vpn_gateway" "vpn_gw" { + provider = "aws.accepter" +} + +resource "aws_dx_hosted_private_virtual_interface_accepter" "accepter" { + provider = "aws.accepter" + virtual_interface_id = "${aws_dx_hosted_private_virtual_interface.creator.id}" + vpn_gateway_id = "${aws_vpn_gateway.vpn_gw.id}" + + tags { + Side = "Accepter" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `virtual_interface_id` - (Required) The ID of the Direct Connect virtual interface to accept. +* `vpn_gateway_id` - (Optional) The ID of the [virtual private gateway](vpn_gateway.html) to which to connect the virtual interface. +* `dx_gateway_id` - (Optional) The ID of the Direct Connect gateway to which to connect the virtual interface. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the virtual interface. +* `arn` - The ARN of the virtual interface. + +## Timeouts + +`aws_dx_hosted_private_virtual_interface_accepter` provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - (Default `10 minutes`) Used for creating virtual interface +- `delete` - (Default `10 minutes`) Used for destroying virtual interface + +## Import + +Direct Connect hosted private virtual interfaces can be imported using the `vif id`, e.g. + +``` +$ terraform import aws_dx_hosted_private_virtual_interface_accepter.test dxvif-33cc44dd +``` From 884a706289dd26ed13590f65fb5d9a70c283df05 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Feb 2018 11:54:33 -0500 Subject: [PATCH 2/8] Fix indentation for configuration generation. --- aws/resource_aws_dx_hosted_private_virtual_interface_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_test.go b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go index 25ed5dce7110..451bd87ed7cf 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface_test.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go @@ -94,5 +94,5 @@ resource "aws_dx_hosted_private_virtual_interface" "foo" { address_family = "ipv4" bgp_asn = 65352 } - `, cid, ownerAcctId, n) +`, cid, ownerAcctId, n) } From e70337c9e4fe57f8a9cf10312f8a9248d0078ddf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Feb 2018 11:56:45 -0500 Subject: [PATCH 3/8] Change interface to 'dxVirtualInterfaceRead'. --- aws/dx_vif.go | 133 ++++++++---------- ...aws_dx_hosted_private_virtual_interface.go | 6 +- ...sted_private_virtual_interface_accepter.go | 4 +- 3 files changed, 70 insertions(+), 73 deletions(-) diff --git a/aws/dx_vif.go b/aws/dx_vif.go index 903c5ca4fed3..27a83780c503 100644 --- a/aws/dx_vif.go +++ b/aws/dx_vif.go @@ -13,25 +13,75 @@ import ( "github.com/hashicorp/terraform/helper/validation" ) +// Schemas common to all (public/private, hosted or not) virtual interfaces. +var dxVirtualInterfaceSchemaWithTags = mergeSchemas( + dxVirtualInterfaceSchema, + map[string]*schema.Schema{ + "tags": tagsSchema(), + }, +) +var dxVirtualInterfaceSchema = map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), + }, + "bgp_asn": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "bgp_auth_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "address_family": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + }, + "customer_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "amazon_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, +} + func isNoSuchDxVirtualInterfaceErr(err error) bool { return isAWSErr(err, "DirectConnectClientException", "does not exist") } -func dxVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) (*directconnect.VirtualInterface, error) { - conn := meta.(*AWSClient).dxconn - - resp, state, err := dxVirtualInterfaceStateRefresh(conn, d.Id())() +func dxVirtualInterfaceRead(id string, conn *directconnect.DirectConnect) (*directconnect.VirtualInterface, error) { + resp, state, err := dxVirtualInterfaceStateRefresh(conn, id)() if err != nil { return nil, fmt.Errorf("Error reading Direct Connect virtual interface: %s", err.Error()) } - terminalStates := map[string]bool{ - directconnect.VirtualInterfaceStateDeleted: true, - directconnect.VirtualInterfaceStateDeleting: true, - directconnect.VirtualInterfaceStateRejected: true, - } - if _, ok := terminalStates[state]; ok { - log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) - d.SetId("") + if state == directconnect.VirtualInterfaceStateDeleted { return nil, nil } @@ -195,62 +245,3 @@ func flattenDxRouteFilterPrefixes(prefixes []*directconnect.RouteFilterPrefix) * } return schema.NewSet(schema.HashString, out) } - -// Schemas common to all (public/private, hosted or not) virtual interfaces. -var dxVirtualInterfaceSchemaWithTags = mergeSchemas( - dxVirtualInterfaceSchema, - map[string]*schema.Schema{ - "tags": tagsSchema(), - }, -) -var dxVirtualInterfaceSchema = map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "connection_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "vlan": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntBetween(1, 4094), - }, - "bgp_asn": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "bgp_auth_key": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), - }, - "customer_address": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - "amazon_address": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, -} diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface.go b/aws/resource_aws_dx_hosted_private_virtual_interface.go index 716f322b668f..b45b63cdf929 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface.go @@ -77,11 +77,15 @@ func resourceAwsDxHostedPrivateVirtualInterfaceCreate(d *schema.ResourceData, me } func resourceAwsDxHostedPrivateVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { - vif, err := dxVirtualInterfaceRead(d, meta) + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) if err != nil { return err } if vif == nil { + log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + d.SetId("") return nil } diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go index b3370a830635..cea7d588b1bd 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go @@ -91,11 +91,13 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterCreate(d *schema.Resource func resourceAwsDxHostedPrivateVirtualInterfaceAccepterRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - vif, err := dxVirtualInterfaceRead(d, meta) + vif, err := dxVirtualInterfaceRead(d.Id(), conn) if err != nil { return err } if vif == nil { + log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + d.SetId("") return nil } From 654b5b83aad5d66cf5f4169dbc2a62e7456c3332 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 21 Jun 2018 19:13:01 -0400 Subject: [PATCH 4/8] Copy changes to 'dx_vif.go' and 'structure.go' from the 'issue-3248' branch. --- aws/dx_vif.go | 49 +++++++++++++++--------------------------------- aws/structure.go | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/aws/dx_vif.go b/aws/dx_vif.go index 27a83780c503..0898583598cf 100644 --- a/aws/dx_vif.go +++ b/aws/dx_vif.go @@ -72,14 +72,10 @@ var dxVirtualInterfaceSchema = map[string]*schema.Schema{ }, } -func isNoSuchDxVirtualInterfaceErr(err error) bool { - return isAWSErr(err, "DirectConnectClientException", "does not exist") -} - func dxVirtualInterfaceRead(id string, conn *directconnect.DirectConnect) (*directconnect.VirtualInterface, error) { resp, state, err := dxVirtualInterfaceStateRefresh(conn, id)() if err != nil { - return nil, fmt.Errorf("Error reading Direct Connect virtual interface: %s", err.Error()) + return nil, fmt.Errorf("Error reading Direct Connect virtual interface: %s", err) } if state == directconnect.VirtualInterfaceStateDeleted { return nil, nil @@ -113,10 +109,10 @@ func dxVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { VirtualInterfaceId: aws.String(d.Id()), }) if err != nil { - if isNoSuchDxVirtualInterfaceErr(err) { + if isAWSErr(err, "DirectConnectClientException", "does not exist") { return nil } - return fmt.Errorf("Error deleting Direct Connect virtual interface: %s", err.Error()) + return fmt.Errorf("Error deleting Direct Connect virtual interface: %s", err) } deleteStateConf := &resource.StateChangeConf{ @@ -151,17 +147,21 @@ func dxVirtualInterfaceStateRefresh(conn *directconnect.DirectConnect, vifId str VirtualInterfaceId: aws.String(vifId), }) if err != nil { - if isNoSuchDxVirtualInterfaceErr(err) { - return nil, directconnect.VirtualInterfaceStateDeleted, nil - } return nil, "", err } - if len(resp.VirtualInterfaces) < 1 { - return nil, directconnect.ConnectionStateDeleted, nil + n := len(resp.VirtualInterfaces) + switch n { + case 0: + return "", directconnect.VirtualInterfaceStateDeleted, nil + + case 1: + vif := resp.VirtualInterfaces[0] + return vif, aws.StringValue(vif.VirtualInterfaceState), nil + + default: + return nil, "", fmt.Errorf("Found %d Direct Connect virtual interfaces for %s, expected 1", n, vifId) } - vif := resp.VirtualInterfaces[0] - return vif, aws.StringValue(vif.VirtualInterfaceState), nil } } @@ -175,7 +175,7 @@ func dxVirtualInterfaceWaitUntilAvailable(d *schema.ResourceData, conn *directco MinTimeout: 5 * time.Second, } if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf("Error waiting for Direct Connect virtual interface %s to become available: %s", d.Id(), err.Error()) + return fmt.Errorf("Error waiting for Direct Connect virtual interface (%s) to become available: %s", d.Id(), err) } return nil @@ -226,22 +226,3 @@ func dxVirtualInterfaceArnAttribute(d *schema.ResourceData, meta interface{}) er return nil } - -func expandDxRouteFilterPrefixes(cfg []interface{}) []*directconnect.RouteFilterPrefix { - prefixes := make([]*directconnect.RouteFilterPrefix, len(cfg), len(cfg)) - for i, p := range cfg { - prefix := &directconnect.RouteFilterPrefix{ - Cidr: aws.String(p.(string)), - } - prefixes[i] = prefix - } - return prefixes -} - -func flattenDxRouteFilterPrefixes(prefixes []*directconnect.RouteFilterPrefix) *schema.Set { - out := make([]interface{}, 0) - for _, prefix := range prefixes { - out = append(out, aws.StringValue(prefix.Cidr)) - } - return schema.NewSet(schema.HashString, out) -} diff --git a/aws/structure.go b/aws/structure.go index 11eca3b1efb2..9370d54e4701 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -20,6 +20,7 @@ import ( "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" "github.com/aws/aws-sdk-go/service/configservice" "github.com/aws/aws-sdk-go/service/dax" + "github.com/aws/aws-sdk-go/service/directconnect" "github.com/aws/aws-sdk-go/service/directoryservice" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/ec2" @@ -4268,3 +4269,22 @@ func expandVpcPeeringConnectionOptions(m map[string]interface{}) *ec2.PeeringCon return options } + +func expandDxRouteFilterPrefixes(cfg []interface{}) []*directconnect.RouteFilterPrefix { + prefixes := make([]*directconnect.RouteFilterPrefix, len(cfg), len(cfg)) + for i, p := range cfg { + prefix := &directconnect.RouteFilterPrefix{ + Cidr: aws.String(p.(string)), + } + prefixes[i] = prefix + } + return prefixes +} + +func flattenDxRouteFilterPrefixes(prefixes []*directconnect.RouteFilterPrefix) *schema.Set { + out := make([]interface{}, 0) + for _, prefix := range prefixes { + out = append(out, aws.StringValue(prefix.Cidr)) + } + return schema.NewSet(schema.HashString, out) +} From 025d799ebf3510b3c085d47dd53fb735ef6a9c84 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 22 Jun 2018 12:58:29 -0400 Subject: [PATCH 5/8] Remove 'mergeSchemas'. --- aws/dx_vif.go | 60 ----------------- ...aws_dx_hosted_private_virtual_interface.go | 67 ++++++++++++++++--- aws/utils.go | 13 ---- aws/utils_test.go | 28 -------- 4 files changed, 57 insertions(+), 111 deletions(-) diff --git a/aws/dx_vif.go b/aws/dx_vif.go index 0898583598cf..92ee5358cc73 100644 --- a/aws/dx_vif.go +++ b/aws/dx_vif.go @@ -10,68 +10,8 @@ import ( "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" ) -// Schemas common to all (public/private, hosted or not) virtual interfaces. -var dxVirtualInterfaceSchemaWithTags = mergeSchemas( - dxVirtualInterfaceSchema, - map[string]*schema.Schema{ - "tags": tagsSchema(), - }, -) -var dxVirtualInterfaceSchema = map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "connection_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "vlan": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntBetween(1, 4094), - }, - "bgp_asn": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "bgp_auth_key": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), - }, - "customer_address": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - "amazon_address": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, -} - func dxVirtualInterfaceRead(id string, conn *directconnect.DirectConnect) (*directconnect.VirtualInterface, error) { resp, state, err := dxVirtualInterfaceStateRefresh(conn, id)() if err != nil { diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface.go b/aws/resource_aws_dx_hosted_private_virtual_interface.go index b45b63cdf929..c793bbbe98d6 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { @@ -19,17 +20,63 @@ func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { State: schema.ImportStatePassthrough, }, - Schema: mergeSchemas( - dxVirtualInterfaceSchema, - map[string]*schema.Schema{ - "owner_account_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validateAwsAccountId, - }, + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, }, - ), + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), + }, + "bgp_asn": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "bgp_auth_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "address_family": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + }, + "customer_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "amazon_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "owner_account_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAwsAccountId, + }, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), diff --git a/aws/utils.go b/aws/utils.go index 6a120cccff8c..21c3aab7ef3d 100644 --- a/aws/utils.go +++ b/aws/utils.go @@ -7,7 +7,6 @@ import ( "regexp" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/helper/schema" ) // Base64Encode encodes data if the input isn't already encoded using base64.StdEncoding.EncodeToString. @@ -48,15 +47,3 @@ func isResourceNotFoundError(err error) bool { _, ok := err.(*resource.NotFoundError) return ok } - -func mergeSchemas(schemas ...map[string]*schema.Schema) map[string]*schema.Schema { - merged := make(map[string]*schema.Schema) - - for _, schema := range schemas { - for k, v := range schema { - merged[k] = v - } - } - - return merged -} diff --git a/aws/utils_test.go b/aws/utils_test.go index a5854e7ef4b0..785ad141ed33 100644 --- a/aws/utils_test.go +++ b/aws/utils_test.go @@ -2,8 +2,6 @@ package aws import ( "testing" - - "github.com/hashicorp/terraform/helper/schema" ) var base64encodingTests = []struct { @@ -74,29 +72,3 @@ func TestJsonBytesEqualWhitespaceAndNoWhitespace(t *testing.T) { t.Errorf("Expected jsonBytesEqual to return false for %s == %s", noWhitespaceDiff, whitespaceDiff) } } - -func TestMergeSchemas(t *testing.T) { - s1 := map[string]*schema.Schema{ - "aaa": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "zzz": { - Type: schema.TypeInt, - Optional: true, - Default: 42, - }, - } - s2 := map[string]*schema.Schema{ - "xxx": { - Type: schema.TypeFloat, - Computed: true, - }, - } - - s3 := mergeSchemas(s1, s2) - if len(s3) != 3 { - t.Errorf("Expected merge schema to be of size 3, got %d", len(s3)) - } -} From 4af87fd7244ed0356dc7c96d7bec6c68025227cf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 22 Jun 2018 16:07:29 -0400 Subject: [PATCH 6/8] Changes after code review on https://github.com/terraform-providers/terraform-provider-aws/pull/3253. --- aws/dx_vif.go | 56 +------------------ ...aws_dx_hosted_private_virtual_interface.go | 41 ++++++++++++-- ...sted_private_virtual_interface_accepter.go | 31 ++++++++-- ...x_hosted_private_virtual_interface_test.go | 13 +++-- ...ed_private_virtual_interface.html.markdown | 8 +-- ...e_virtual_interface_accepter.html.markdown | 2 +- 6 files changed, 74 insertions(+), 77 deletions(-) diff --git a/aws/dx_vif.go b/aws/dx_vif.go index 92ee5358cc73..941c21c134b7 100644 --- a/aws/dx_vif.go +++ b/aws/dx_vif.go @@ -6,7 +6,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" @@ -27,14 +26,7 @@ func dxVirtualInterfaceRead(id string, conn *directconnect.DirectConnect) (*dire func dxVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - if err := setTagsDX(conn, d, arn); err != nil { + if err := setTagsDX(conn, d, d.Get("arn").(string)); err != nil { return err } @@ -120,49 +112,3 @@ func dxVirtualInterfaceWaitUntilAvailable(d *schema.ResourceData, conn *directco return nil } - -// Attributes common to public VIFs and creator side of hosted public VIFs. -func dxPublicVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { - if err := dxVirtualInterfaceAttributes(d, meta, vif); err != nil { - return err - } - d.Set("route_filter_prefixes", flattenDxRouteFilterPrefixes(vif.RouteFilterPrefixes)) - - return nil -} - -// Attributes common to private VIFs and creator side of hosted private VIFs. -func dxPrivateVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { - return dxVirtualInterfaceAttributes(d, meta, vif) -} - -// Attributes common to public/private VIFs and creator side of hosted public/private VIFs. -func dxVirtualInterfaceAttributes(d *schema.ResourceData, meta interface{}, vif *directconnect.VirtualInterface) error { - if err := dxVirtualInterfaceArnAttribute(d, meta); err != nil { - return err - } - - d.Set("connection_id", vif.ConnectionId) - d.Set("name", vif.VirtualInterfaceName) - d.Set("vlan", vif.Vlan) - d.Set("bgp_asn", vif.Asn) - d.Set("bgp_auth_key", vif.AuthKey) - d.Set("address_family", vif.AddressFamily) - d.Set("customer_address", vif.CustomerAddress) - d.Set("amazon_address", vif.AmazonAddress) - - return nil -} - -func dxVirtualInterfaceArnAttribute(d *schema.ResourceData, meta interface{}) error { - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) - - return nil -} diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface.go b/aws/resource_aws_dx_hosted_private_virtual_interface.go index c793bbbe98d6..f0ce581ff788 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface.go @@ -6,6 +6,7 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -17,7 +18,7 @@ func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { Read: resourceAwsDxHostedPrivateVirtualInterfaceRead, Delete: resourceAwsDxHostedPrivateVirtualInterfaceDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceAwsDxHostedPrivateVirtualInterfaceImport, }, Schema: map[string]*schema.Schema{ @@ -98,13 +99,13 @@ func resourceAwsDxHostedPrivateVirtualInterfaceCreate(d *schema.ResourceData, me AddressFamily: aws.String(d.Get("address_family").(string)), }, } - if v, ok := d.GetOk("bgp_auth_key"); ok { + if v, ok := d.GetOk("bgp_auth_key"); ok && v.(string) != "" { req.NewPrivateVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) } - if v, ok := d.GetOk("customer_address"); ok { + if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { req.NewPrivateVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) } - if v, ok := d.GetOk("amazon_address"); ok { + if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { req.NewPrivateVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) } @@ -115,6 +116,14 @@ func resourceAwsDxHostedPrivateVirtualInterfaceCreate(d *schema.ResourceData, me } d.SetId(aws.StringValue(resp.VirtualInterfaceId)) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) if err := dxHostedPrivateVirtualInterfaceWaitUntilAvailable(d, conn); err != nil { return err @@ -136,14 +145,36 @@ func resourceAwsDxHostedPrivateVirtualInterfaceRead(d *schema.ResourceData, meta return nil } + d.Set("connection_id", vif.ConnectionId) + d.Set("name", vif.VirtualInterfaceName) + d.Set("vlan", vif.Vlan) + d.Set("bgp_asn", vif.Asn) + d.Set("bgp_auth_key", vif.AuthKey) + d.Set("address_family", vif.AddressFamily) + d.Set("customer_address", vif.CustomerAddress) + d.Set("amazon_address", vif.AmazonAddress) d.Set("owner_account_id", vif.OwnerAccount) - return dxPrivateVirtualInterfaceAttributes(d, meta, vif) + + return nil } func resourceAwsDxHostedPrivateVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { return dxVirtualInterfaceDelete(d, meta) } +func resourceAwsDxHostedPrivateVirtualInterfaceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + + return []*schema.ResourceData{d}, nil +} + func dxHostedPrivateVirtualInterfaceWaitUntilAvailable(d *schema.ResourceData, conn *directconnect.DirectConnect) error { return dxVirtualInterfaceWaitUntilAvailable( d, diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go index cea7d588b1bd..77479b4859be 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go @@ -6,6 +6,7 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/directconnect" "github.com/hashicorp/terraform/helper/schema" ) @@ -17,7 +18,7 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepter() *schema.Resource { Update: resourceAwsDxHostedPrivateVirtualInterfaceAccepterUpdate, Delete: resourceAwsDxHostedPrivateVirtualInterfaceAccepterDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceAwsDxHostedPrivateVirtualInterfaceAccepterImport, }, Schema: map[string]*schema.Schema{ @@ -66,10 +67,10 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterCreate(d *schema.Resource req := &directconnect.ConfirmPrivateVirtualInterfaceInput{ VirtualInterfaceId: aws.String(vifId), } - if vgwOk { + if vgwOk && vgwIdRaw.(string) != "" { req.VirtualGatewayId = aws.String(vgwIdRaw.(string)) } - if dxgwOk { + if dxgwOk && dxgwIdRaw.(string) != "" { req.DirectConnectGatewayId = aws.String(dxgwIdRaw.(string)) } @@ -80,6 +81,14 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterCreate(d *schema.Resource } d.SetId(vifId) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) if err := dxHostedPrivateVirtualInterfaceAccepterWaitUntilAvailable(d, conn); err != nil { return err @@ -101,9 +110,6 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterRead(d *schema.ResourceDa return nil } - if err := dxVirtualInterfaceArnAttribute(d, meta); err != nil { - return err - } d.Set("virtual_interface_id", vif.VirtualInterfaceId) d.Set("vpn_gateway_id", vif.VirtualGatewayId) d.Set("dx_gateway_id", vif.DirectConnectGatewayId) @@ -126,6 +132,19 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterDelete(d *schema.Resource return dxVirtualInterfaceDelete(d, meta) } +func resourceAwsDxHostedPrivateVirtualInterfaceAccepterImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + + return []*schema.ResourceData{d}, nil +} + func dxHostedPrivateVirtualInterfaceAccepterWaitUntilAvailable(d *schema.ResourceData, conn *directconnect.DirectConnect) error { return dxVirtualInterfaceWaitUntilAvailable( d, diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_test.go b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go index 451bd87ed7cf..a7ddff6003fc 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface_test.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go @@ -23,7 +23,8 @@ func TestAccAwsDxHostedPrivateVirtualInterface_basic(t *testing.T) { if ownerAccountId == "" { t.Skipf("Environment variable %s is not set", key) } - vifName := fmt.Sprintf("tf-dx-vif-%s", acctest.RandString(5)) + vifName := fmt.Sprintf("terraform-testacc-dxvif-%s", acctest.RandString(5)) + bgpAsn := randIntRange(64512, 65534) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -31,7 +32,7 @@ func TestAccAwsDxHostedPrivateVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckAwsDxHostedPrivateVirtualInterfaceDestroy, Steps: []resource.TestStep{ { - Config: testAccDxHostedPrivateVirtualInterfaceConfig_basic(connectionId, ownerAccountId, vifName), + Config: testAccDxHostedPrivateVirtualInterfaceConfig_basic(connectionId, ownerAccountId, vifName, bgpAsn), Check: resource.ComposeTestCheckFunc( testAccCheckAwsDxHostedPrivateVirtualInterfaceExists("aws_dx_hosted_private_virtual_interface.foo"), resource.TestCheckResourceAttr("aws_dx_hosted_private_virtual_interface.foo", "name", vifName), @@ -65,7 +66,7 @@ func testAccCheckAwsDxHostedPrivateVirtualInterfaceDestroy(s *terraform.State) e } for _, v := range resp.VirtualInterfaces { if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { - return fmt.Errorf("[DESTROY ERROR] Dx Public VIF (%s) not deleted", rs.Primary.ID) + return fmt.Errorf("[DESTROY ERROR] Dx Private VIF (%s) not deleted", rs.Primary.ID) } } } @@ -83,7 +84,7 @@ func testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(name string) resource. } } -func testAccDxHostedPrivateVirtualInterfaceConfig_basic(cid, ownerAcctId, n string) string { +func testAccDxHostedPrivateVirtualInterfaceConfig_basic(cid, ownerAcctId, n string, bgpAsn int) string { return fmt.Sprintf(` resource "aws_dx_hosted_private_virtual_interface" "foo" { connection_id = "%s" @@ -92,7 +93,7 @@ resource "aws_dx_hosted_private_virtual_interface" "foo" { name = "%s" vlan = 4094 address_family = "ipv4" - bgp_asn = 65352 + bgp_asn = %d } -`, cid, ownerAcctId, n) +`, cid, ownerAcctId, n, bgpAsn) } diff --git a/website/docs/r/dx_hosted_private_virtual_interface.html.markdown b/website/docs/r/dx_hosted_private_virtual_interface.html.markdown index 85b0cf79db9c..9953d85d2493 100644 --- a/website/docs/r/dx_hosted_private_virtual_interface.html.markdown +++ b/website/docs/r/dx_hosted_private_virtual_interface.html.markdown @@ -28,15 +28,15 @@ resource "aws_dx_hosted_private_virtual_interface" "foo" { The following arguments are supported: +* `address_family` - (Required) The address family for the BGP peer. `ipv4 ` or `ipv6`. +* `bgp_asn` - (Required) The autonomous system (AS) number for Border Gateway Protocol (BGP) configuration. * `connection_id` - (Required) The ID of the Direct Connect connection (or LAG) on which to create the virtual interface. -* `owner_account_id` - (Required) The AWS account that will own the new virtual interface. * `name` - (Required) The name for the virtual interface. +* `owner_account_id` - (Required) The AWS account that will own the new virtual interface. * `vlan` - (Required) The VLAN ID. -* `bgp_asn` - (Required) The autonomous system (AS) number for Border Gateway Protocol (BGP) configuration. +* `amazon_address` - (Optional) The IPv4 CIDR address to use to send traffic to Amazon. Required for IPv4 BGP peers. * `bgp_auth_key` - (Optional) The authentication key for BGP configuration. -* `address_family` - (Required) The address family for the BGP peer. `ipv4 ` or `ipv6`. * `customer_address` - (Optional) The IPv4 CIDR destination address to which Amazon should send traffic. Required for IPv4 BGP peers. -* `amazon_address` - (Optional) The IPv4 CIDR address to use to send traffic to Amazon. Required for IPv4 BGP peers. ## Attributes Reference diff --git a/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown b/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown index d346a24a62c7..ef0ebd23c2d6 100644 --- a/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown +++ b/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown @@ -60,9 +60,9 @@ resource "aws_dx_hosted_private_virtual_interface_accepter" "accepter" { The following arguments are supported: * `virtual_interface_id` - (Required) The ID of the Direct Connect virtual interface to accept. -* `vpn_gateway_id` - (Optional) The ID of the [virtual private gateway](vpn_gateway.html) to which to connect the virtual interface. * `dx_gateway_id` - (Optional) The ID of the Direct Connect gateway to which to connect the virtual interface. * `tags` - (Optional) A mapping of tags to assign to the resource. +* `vpn_gateway_id` - (Optional) The ID of the [virtual private gateway](vpn_gateway.html) to which to connect the virtual interface. ## Attributes Reference From eb5f5bb1bd0a2debe344ed4eb683b5bcd1569d14 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 22 Jun 2018 16:19:35 -0400 Subject: [PATCH 7/8] Use 'directconnect.ErrCodeClientException'. --- aws/dx_vif.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/dx_vif.go b/aws/dx_vif.go index 941c21c134b7..ea659c7b6c99 100644 --- a/aws/dx_vif.go +++ b/aws/dx_vif.go @@ -41,7 +41,7 @@ func dxVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { VirtualInterfaceId: aws.String(d.Id()), }) if err != nil { - if isAWSErr(err, "DirectConnectClientException", "does not exist") { + if isAWSErr(err, directconnect.ErrCodeClientException, "does not exist") { return nil } return fmt.Errorf("Error deleting Direct Connect virtual interface: %s", err) From 64f9a629dcae7f3c7b2bbcdcf2f4a68c479a2bce Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 22 Jun 2018 19:07:31 -0400 Subject: [PATCH 8/8] Changes after additional code review. --- ...aws_dx_hosted_private_virtual_interface_accepter.go | 10 +++++++++- .../dx_hosted_private_virtual_interface.html.markdown | 2 +- ...ed_private_virtual_interface_accepter.html.markdown | 8 ++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go index 77479b4859be..855061c9eef2 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go @@ -109,6 +109,13 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterRead(d *schema.ResourceDa d.SetId("") return nil } + vifState := aws.StringValue(vif.VirtualInterfaceState) + if vifState != directconnect.VirtualInterfaceStateAvailable && + vifState != directconnect.VirtualInterfaceStateDown { + log.Printf("[WARN] Direct Connect virtual interface (%s) is '%s', removing from state", vifState, d.Id()) + d.SetId("") + return nil + } d.Set("virtual_interface_id", vif.VirtualInterfaceId) d.Set("vpn_gateway_id", vif.VirtualGatewayId) @@ -129,7 +136,8 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterUpdate(d *schema.Resource } func resourceAwsDxHostedPrivateVirtualInterfaceAccepterDelete(d *schema.ResourceData, meta interface{}) error { - return dxVirtualInterfaceDelete(d, meta) + log.Printf("[WARN] Will not delete Direct Connect virtual interface. Terraform will remove this resource from the state file, however resources may remain.") + return nil } func resourceAwsDxHostedPrivateVirtualInterfaceAccepterImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { diff --git a/website/docs/r/dx_hosted_private_virtual_interface.html.markdown b/website/docs/r/dx_hosted_private_virtual_interface.html.markdown index 9953d85d2493..9c95cd6be615 100644 --- a/website/docs/r/dx_hosted_private_virtual_interface.html.markdown +++ b/website/docs/r/dx_hosted_private_virtual_interface.html.markdown @@ -8,7 +8,7 @@ description: |- # aws_dx_hosted_private_virtual_interface -Provides a Direct Connect hosted private virtual interface resource. +Provides a Direct Connect hosted private virtual interface resource. This resource represents the allocator's side of the hosted virtual interface. A hosted virtual interface is a virtual interface that is owned by another AWS account. ## Example Usage diff --git a/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown b/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown index ef0ebd23c2d6..ef2301d58f6e 100644 --- a/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown +++ b/website/docs/r/dx_hosted_private_virtual_interface_accepter.html.markdown @@ -64,6 +64,14 @@ The following arguments are supported: * `tags` - (Optional) A mapping of tags to assign to the resource. * `vpn_gateway_id` - (Optional) The ID of the [virtual private gateway](vpn_gateway.html) to which to connect the virtual interface. +### Removing `aws_dx_hosted_private_virtual_interface_accepter` from your configuration + +AWS allows a Direct Connect hosted private virtual interface to be deleted from either the allocator's or accepter's side. +However, Terraform only allows the Direct Connect hosted private virtual interface to be deleted from the allocator's side +by removing the corresponding `aws_dx_hosted_private_virtual_interface` resource from your configuration. +Removing a `aws_dx_hosted_private_virtual_interface_accepter` resource from your configuration will remove it +from your statefile and management, **but will not delete the Direct Connect virtual interface.** + ## Attributes Reference The following attributes are exported: