From 6db3626f36297580bb8c6ebb67dc18fd92fd2890 Mon Sep 17 00:00:00 2001 From: Daniel Messias Date: Tue, 25 May 2021 22:11:32 +0100 Subject: [PATCH 01/10] New resource: aws_lakeformation_policy_tag --- aws/provider.go | 1658 +++++++++++++++++ resource_aws_lakeformation_policy_tag.go | 177 ++ resource_aws_lakeformation_policy_tag_test.go | 188 ++ .../r/lakeformation_policy_tag.html.markdown | 43 + 4 files changed, 2066 insertions(+) create mode 100644 aws/provider.go create mode 100644 resource_aws_lakeformation_policy_tag.go create mode 100644 resource_aws_lakeformation_policy_tag_test.go create mode 100644 website/docs/r/lakeformation_policy_tag.html.markdown diff --git a/aws/provider.go b/aws/provider.go new file mode 100644 index 000000000000..9d24a17f8a59 --- /dev/null +++ b/aws/provider.go @@ -0,0 +1,1658 @@ +package aws + +import ( + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/mutexkv" +) + +// Provider returns a *schema.Provider. +func Provider() *schema.Provider { + // TODO: Move the validation to this, requires conditional schemas + // TODO: Move the configuration to this, requires validation + + // The actual provider + provider := &schema.Provider{ + Schema: map[string]*schema.Schema{ + "access_key": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: descriptions["access_key"], + }, + + "secret_key": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: descriptions["secret_key"], + }, + + "profile": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: descriptions["profile"], + }, + + "assume_role": assumeRoleSchema(), + + "shared_credentials_file": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: descriptions["shared_credentials_file"], + }, + + "token": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: descriptions["token"], + }, + + "region": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "AWS_REGION", + "AWS_DEFAULT_REGION", + }, nil), + Description: descriptions["region"], + InputDefault: "us-east-1", // lintignore:AWSAT003 + }, + + "max_retries": { + Type: schema.TypeInt, + Optional: true, + Default: 25, + Description: descriptions["max_retries"], + }, + + "allowed_account_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + ConflictsWith: []string{"forbidden_account_ids"}, + Set: schema.HashString, + }, + + "forbidden_account_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + ConflictsWith: []string{"allowed_account_ids"}, + Set: schema.HashString, + }, + + "default_tags": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Configuration block with settings to default resource tags across all resources.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tags": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Resource tags to default across all resources", + }, + }, + }, + }, + + "endpoints": endpointsSchema(), + + "ignore_tags": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Configuration block with settings to ignore resource tags across all resources.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "keys": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Description: "Resource tag keys to ignore across all resources.", + }, + "key_prefixes": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Description: "Resource tag key prefixes to ignore across all resources.", + }, + }, + }, + }, + + "insecure": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["insecure"], + }, + + "skip_credentials_validation": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["skip_credentials_validation"], + }, + + "skip_get_ec2_platforms": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["skip_get_ec2_platforms"], + }, + + "skip_region_validation": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["skip_region_validation"], + }, + + "skip_requesting_account_id": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["skip_requesting_account_id"], + }, + + "skip_metadata_api_check": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["skip_metadata_api_check"], + }, + + "s3_force_path_style": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["s3_force_path_style"], + }, + }, + + DataSourcesMap: map[string]*schema.Resource{ + "aws_acm_certificate": dataSourceAwsAcmCertificate(), + "aws_acmpca_certificate_authority": dataSourceAwsAcmpcaCertificateAuthority(), + "aws_acmpca_certificate": dataSourceAwsAcmpcaCertificate(), + "aws_ami": dataSourceAwsAmi(), + "aws_ami_ids": dataSourceAwsAmiIds(), + "aws_api_gateway_api_key": dataSourceAwsApiGatewayApiKey(), + "aws_api_gateway_domain_name": dataSourceAwsApiGatewayDomainName(), + "aws_api_gateway_resource": dataSourceAwsApiGatewayResource(), + "aws_api_gateway_rest_api": dataSourceAwsApiGatewayRestApi(), + "aws_api_gateway_vpc_link": dataSourceAwsApiGatewayVpcLink(), + "aws_apigatewayv2_api": dataSourceAwsApiGatewayV2Api(), + "aws_apigatewayv2_apis": dataSourceAwsApiGatewayV2Apis(), + "aws_arn": dataSourceAwsArn(), + "aws_autoscaling_group": dataSourceAwsAutoscalingGroup(), + "aws_autoscaling_groups": dataSourceAwsAutoscalingGroups(), + "aws_availability_zone": dataSourceAwsAvailabilityZone(), + "aws_availability_zones": dataSourceAwsAvailabilityZones(), + "aws_backup_plan": dataSourceAwsBackupPlan(), + "aws_backup_selection": dataSourceAwsBackupSelection(), + "aws_backup_vault": dataSourceAwsBackupVault(), + "aws_batch_compute_environment": dataSourceAwsBatchComputeEnvironment(), + "aws_batch_job_queue": dataSourceAwsBatchJobQueue(), + "aws_billing_service_account": dataSourceAwsBillingServiceAccount(), + "aws_caller_identity": dataSourceAwsCallerIdentity(), + "aws_canonical_user_id": dataSourceAwsCanonicalUserId(), + "aws_cloudformation_export": dataSourceAwsCloudFormationExport(), + "aws_cloudformation_stack": dataSourceAwsCloudFormationStack(), + "aws_cloudformation_type": dataSourceAwsCloudFormationType(), + "aws_cloudfront_cache_policy": dataSourceAwsCloudFrontCachePolicy(), + "aws_cloudfront_distribution": dataSourceAwsCloudFrontDistribution(), + "aws_cloudfront_function": dataSourceAwsCloudFrontFunction(), + "aws_cloudfront_origin_request_policy": dataSourceAwsCloudFrontOriginRequestPolicy(), + "aws_cloudhsm_v2_cluster": dataSourceCloudHsmV2Cluster(), + "aws_cloudtrail_service_account": dataSourceAwsCloudTrailServiceAccount(), + "aws_cloudwatch_event_connection": dataSourceAwsCloudwatchEventConnection(), + "aws_cloudwatch_event_source": dataSourceAwsCloudWatchEventSource(), + "aws_cloudwatch_log_group": dataSourceAwsCloudwatchLogGroup(), + "aws_codeartifact_authorization_token": dataSourceAwsCodeArtifactAuthorizationToken(), + "aws_codeartifact_repository_endpoint": dataSourceAwsCodeArtifactRepositoryEndpoint(), + "aws_cognito_user_pools": dataSourceAwsCognitoUserPools(), + "aws_codecommit_repository": dataSourceAwsCodeCommitRepository(), + "aws_codestarconnections_connection": dataSourceAwsCodeStarConnectionsConnection(), + "aws_cur_report_definition": dataSourceAwsCurReportDefinition(), + "aws_default_tags": dataSourceAwsDefaultTags(), + "aws_db_cluster_snapshot": dataSourceAwsDbClusterSnapshot(), + "aws_db_event_categories": dataSourceAwsDbEventCategories(), + "aws_db_instance": dataSourceAwsDbInstance(), + "aws_db_snapshot": dataSourceAwsDbSnapshot(), + "aws_db_subnet_group": dataSourceAwsDbSubnetGroup(), + "aws_directory_service_directory": dataSourceAwsDirectoryServiceDirectory(), + "aws_docdb_engine_version": dataSourceAwsDocdbEngineVersion(), + "aws_docdb_orderable_db_instance": dataSourceAwsDocdbOrderableDbInstance(), + "aws_dx_gateway": dataSourceAwsDxGateway(), + "aws_dynamodb_table": dataSourceAwsDynamoDbTable(), + "aws_ebs_default_kms_key": dataSourceAwsEbsDefaultKmsKey(), + "aws_ebs_encryption_by_default": dataSourceAwsEbsEncryptionByDefault(), + "aws_ebs_snapshot": dataSourceAwsEbsSnapshot(), + "aws_ebs_snapshot_ids": dataSourceAwsEbsSnapshotIds(), + "aws_ebs_volume": dataSourceAwsEbsVolume(), + "aws_ebs_volumes": dataSourceAwsEbsVolumes(), + "aws_ec2_coip_pool": dataSourceAwsEc2CoipPool(), + "aws_ec2_coip_pools": dataSourceAwsEc2CoipPools(), + "aws_ec2_instance_type": dataSourceAwsEc2InstanceType(), + "aws_ec2_instance_type_offering": dataSourceAwsEc2InstanceTypeOffering(), + "aws_ec2_instance_type_offerings": dataSourceAwsEc2InstanceTypeOfferings(), + "aws_ec2_local_gateway": dataSourceAwsEc2LocalGateway(), + "aws_ec2_local_gateways": dataSourceAwsEc2LocalGateways(), + "aws_ec2_local_gateway_route_table": dataSourceAwsEc2LocalGatewayRouteTable(), + "aws_ec2_local_gateway_route_tables": dataSourceAwsEc2LocalGatewayRouteTables(), + "aws_ec2_local_gateway_virtual_interface": dataSourceAwsEc2LocalGatewayVirtualInterface(), + "aws_ec2_local_gateway_virtual_interface_group": dataSourceAwsEc2LocalGatewayVirtualInterfaceGroup(), + "aws_ec2_local_gateway_virtual_interface_groups": dataSourceAwsEc2LocalGatewayVirtualInterfaceGroups(), + "aws_ec2_managed_prefix_list": dataSourceAwsEc2ManagedPrefixList(), + "aws_ec2_spot_price": dataSourceAwsEc2SpotPrice(), + "aws_ec2_transit_gateway": dataSourceAwsEc2TransitGateway(), + "aws_ec2_transit_gateway_dx_gateway_attachment": dataSourceAwsEc2TransitGatewayDxGatewayAttachment(), + "aws_ec2_transit_gateway_peering_attachment": dataSourceAwsEc2TransitGatewayPeeringAttachment(), + "aws_ec2_transit_gateway_route_table": dataSourceAwsEc2TransitGatewayRouteTable(), + "aws_ec2_transit_gateway_route_tables": dataSourceAwsEc2TransitGatewayRouteTables(), + "aws_ec2_transit_gateway_vpc_attachment": dataSourceAwsEc2TransitGatewayVpcAttachment(), + "aws_ec2_transit_gateway_vpn_attachment": dataSourceAwsEc2TransitGatewayVpnAttachment(), + "aws_ecr_authorization_token": dataSourceAwsEcrAuthorizationToken(), + "aws_ecr_image": dataSourceAwsEcrImage(), + "aws_ecr_repository": dataSourceAwsEcrRepository(), + "aws_ecs_cluster": dataSourceAwsEcsCluster(), + "aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(), + "aws_ecs_service": dataSourceAwsEcsService(), + "aws_ecs_task_definition": dataSourceAwsEcsTaskDefinition(), + "aws_customer_gateway": dataSourceAwsCustomerGateway(), + "aws_efs_access_point": dataSourceAwsEfsAccessPoint(), + "aws_efs_access_points": dataSourceAwsEfsAccessPoints(), + "aws_efs_file_system": dataSourceAwsEfsFileSystem(), + "aws_efs_mount_target": dataSourceAwsEfsMountTarget(), + "aws_eip": dataSourceAwsEip(), + "aws_eks_addon": dataSourceAwsEksAddon(), + "aws_eks_cluster": dataSourceAwsEksCluster(), + "aws_eks_cluster_auth": dataSourceAwsEksClusterAuth(), + "aws_elastic_beanstalk_application": dataSourceAwsElasticBeanstalkApplication(), + "aws_elastic_beanstalk_hosted_zone": dataSourceAwsElasticBeanstalkHostedZone(), + "aws_elastic_beanstalk_solution_stack": dataSourceAwsElasticBeanstalkSolutionStack(), + "aws_elasticache_cluster": dataSourceAwsElastiCacheCluster(), + "aws_elasticsearch_domain": dataSourceAwsElasticSearchDomain(), + "aws_elb": dataSourceAwsElb(), + "aws_elasticache_replication_group": dataSourceAwsElasticacheReplicationGroup(), + "aws_elb_hosted_zone_id": dataSourceAwsElbHostedZoneId(), + "aws_elb_service_account": dataSourceAwsElbServiceAccount(), + "aws_glue_connection": dataSourceAwsGlueConnection(), + "aws_glue_data_catalog_encryption_settings": dataSourceAwsGlueDataCatalogEncryptionSettings(), + "aws_glue_script": dataSourceAwsGlueScript(), + "aws_guardduty_detector": dataSourceAwsGuarddutyDetector(), + "aws_iam_account_alias": dataSourceAwsIamAccountAlias(), + "aws_iam_group": dataSourceAwsIAMGroup(), + "aws_iam_instance_profile": dataSourceAwsIAMInstanceProfile(), + "aws_iam_policy": dataSourceAwsIAMPolicy(), + "aws_iam_policy_document": dataSourceAwsIamPolicyDocument(), + "aws_iam_role": dataSourceAwsIAMRole(), + "aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(), + "aws_iam_user": dataSourceAwsIAMUser(), + "aws_identitystore_group": dataSourceAwsIdentityStoreGroup(), + "aws_identitystore_user": dataSourceAwsIdentityStoreUser(), + "aws_imagebuilder_component": dataSourceAwsImageBuilderComponent(), + "aws_imagebuilder_distribution_configuration": datasourceAwsImageBuilderDistributionConfiguration(), + "aws_imagebuilder_image": dataSourceAwsImageBuilderImage(), + "aws_imagebuilder_image_pipeline": dataSourceAwsImageBuilderImagePipeline(), + "aws_imagebuilder_image_recipe": dataSourceAwsImageBuilderImageRecipe(), + "aws_imagebuilder_infrastructure_configuration": datasourceAwsImageBuilderInfrastructureConfiguration(), + "aws_inspector_rules_packages": dataSourceAwsInspectorRulesPackages(), + "aws_instance": dataSourceAwsInstance(), + "aws_instances": dataSourceAwsInstances(), + "aws_internet_gateway": dataSourceAwsInternetGateway(), + "aws_iot_endpoint": dataSourceAwsIotEndpoint(), + "aws_ip_ranges": dataSourceAwsIPRanges(), + "aws_kinesis_stream": dataSourceAwsKinesisStream(), + "aws_kinesis_stream_consumer": dataSourceAwsKinesisStreamConsumer(), + "aws_kms_alias": dataSourceAwsKmsAlias(), + "aws_kms_ciphertext": dataSourceAwsKmsCiphertext(), + "aws_kms_key": dataSourceAwsKmsKey(), + "aws_kms_public_key": dataSourceAwsKmsPublicKey(), + "aws_kms_secret": dataSourceAwsKmsSecret(), + "aws_kms_secrets": dataSourceAwsKmsSecrets(), + "aws_lakeformation_data_lake_settings": dataSourceAwsLakeFormationDataLakeSettings(), + "aws_lakeformation_permissions": dataSourceAwsLakeFormationPermissions(), + "aws_lakeformation_resource": dataSourceAwsLakeFormationResource(), + "aws_lambda_alias": dataSourceAwsLambdaAlias(), + "aws_lambda_code_signing_config": dataSourceAwsLambdaCodeSigningConfig(), + "aws_lambda_function": dataSourceAwsLambdaFunction(), + "aws_lambda_invocation": dataSourceAwsLambdaInvocation(), + "aws_lambda_layer_version": dataSourceAwsLambdaLayerVersion(), + "aws_launch_configuration": dataSourceAwsLaunchConfiguration(), + "aws_launch_template": dataSourceAwsLaunchTemplate(), + "aws_lex_bot_alias": dataSourceAwsLexBotAlias(), + "aws_lex_bot": dataSourceAwsLexBot(), + "aws_lex_intent": dataSourceAwsLexIntent(), + "aws_lex_slot_type": dataSourceAwsLexSlotType(), + "aws_mq_broker": dataSourceAwsMqBroker(), + "aws_msk_cluster": dataSourceAwsMskCluster(), + "aws_msk_configuration": dataSourceAwsMskConfiguration(), + "aws_nat_gateway": dataSourceAwsNatGateway(), + "aws_neptune_orderable_db_instance": dataSourceAwsNeptuneOrderableDbInstance(), + "aws_neptune_engine_version": dataSourceAwsNeptuneEngineVersion(), + "aws_network_acls": dataSourceAwsNetworkAcls(), + "aws_network_interface": dataSourceAwsNetworkInterface(), + "aws_network_interfaces": dataSourceAwsNetworkInterfaces(), + "aws_organizations_delegated_administrators": dataSourceAwsOrganizationsDelegatedAdministrators(), + "aws_organizations_delegated_services": dataSourceAwsOrganizationsDelegatedServices(), + "aws_organizations_organization": dataSourceAwsOrganizationsOrganization(), + "aws_organizations_organizational_units": dataSourceAwsOrganizationsOrganizationalUnits(), + "aws_outposts_outpost": dataSourceAwsOutpostsOutpost(), + "aws_outposts_outpost_instance_type": dataSourceAwsOutpostsOutpostInstanceType(), + "aws_outposts_outpost_instance_types": dataSourceAwsOutpostsOutpostInstanceTypes(), + "aws_outposts_outposts": dataSourceAwsOutpostsOutposts(), + "aws_outposts_site": dataSourceAwsOutpostsSite(), + "aws_outposts_sites": dataSourceAwsOutpostsSites(), + "aws_partition": dataSourceAwsPartition(), + "aws_prefix_list": dataSourceAwsPrefixList(), + "aws_pricing_product": dataSourceAwsPricingProduct(), + "aws_qldb_ledger": dataSourceAwsQLDBLedger(), + "aws_ram_resource_share": dataSourceAwsRamResourceShare(), + "aws_rds_certificate": dataSourceAwsRdsCertificate(), + "aws_rds_cluster": dataSourceAwsRdsCluster(), + "aws_rds_engine_version": dataSourceAwsRdsEngineVersion(), + "aws_rds_orderable_db_instance": dataSourceAwsRdsOrderableDbInstance(), + "aws_redshift_cluster": dataSourceAwsRedshiftCluster(), + "aws_redshift_orderable_cluster": dataSourceAwsRedshiftOrderableCluster(), + "aws_redshift_service_account": dataSourceAwsRedshiftServiceAccount(), + "aws_region": dataSourceAwsRegion(), + "aws_regions": dataSourceAwsRegions(), + "aws_resourcegroupstaggingapi_resources": dataSourceAwsResourceGroupsTaggingAPIResources(), + "aws_route": dataSourceAwsRoute(), + "aws_route_table": dataSourceAwsRouteTable(), + "aws_route_tables": dataSourceAwsRouteTables(), + "aws_route53_delegation_set": dataSourceAwsDelegationSet(), + "aws_route53_resolver_endpoint": dataSourceAwsRoute53ResolverEndpoint(), + "aws_route53_resolver_rule": dataSourceAwsRoute53ResolverRule(), + "aws_route53_resolver_rules": dataSourceAwsRoute53ResolverRules(), + "aws_route53_zone": dataSourceAwsRoute53Zone(), + "aws_s3_bucket": dataSourceAwsS3Bucket(), + "aws_s3_bucket_object": dataSourceAwsS3BucketObject(), + "aws_s3_bucket_objects": dataSourceAwsS3BucketObjects(), + "aws_sagemaker_prebuilt_ecr_image": dataSourceAwsSageMakerPrebuiltECRImage(), + "aws_secretsmanager_secret": dataSourceAwsSecretsManagerSecret(), + "aws_secretsmanager_secret_rotation": dataSourceAwsSecretsManagerSecretRotation(), + "aws_secretsmanager_secret_version": dataSourceAwsSecretsManagerSecretVersion(), + "aws_servicequotas_service": dataSourceAwsServiceQuotasService(), + "aws_servicequotas_service_quota": dataSourceAwsServiceQuotasServiceQuota(), + "aws_service_discovery_dns_namespace": dataSourceServiceDiscoveryDnsNamespace(), + "aws_sfn_activity": dataSourceAwsSfnActivity(), + "aws_sfn_state_machine": dataSourceAwsSfnStateMachine(), + "aws_signer_signing_job": dataSourceAwsSignerSigningJob(), + "aws_signer_signing_profile": dataSourceAwsSignerSigningProfile(), + "aws_sns_topic": dataSourceAwsSnsTopic(), + "aws_sqs_queue": dataSourceAwsSqsQueue(), + "aws_ssm_document": dataSourceAwsSsmDocument(), + "aws_ssm_parameter": dataSourceAwsSsmParameter(), + "aws_ssm_patch_baseline": dataSourceAwsSsmPatchBaseline(), + "aws_ssoadmin_instances": dataSourceAwsSsoAdminInstances(), + "aws_ssoadmin_permission_set": dataSourceAwsSsoAdminPermissionSet(), + "aws_storagegateway_local_disk": dataSourceAwsStorageGatewayLocalDisk(), + "aws_subnet": dataSourceAwsSubnet(), + "aws_subnet_ids": dataSourceAwsSubnetIDs(), + "aws_transfer_server": dataSourceAwsTransferServer(), + "aws_vpcs": dataSourceAwsVpcs(), + "aws_security_group": dataSourceAwsSecurityGroup(), + "aws_security_groups": dataSourceAwsSecurityGroups(), + "aws_vpc": dataSourceAwsVpc(), + "aws_vpc_dhcp_options": dataSourceAwsVpcDhcpOptions(), + "aws_vpc_endpoint": dataSourceAwsVpcEndpoint(), + "aws_vpc_endpoint_service": dataSourceAwsVpcEndpointService(), + "aws_vpc_peering_connection": dataSourceAwsVpcPeeringConnection(), + "aws_vpc_peering_connections": dataSourceAwsVpcPeeringConnections(), + "aws_vpn_gateway": dataSourceAwsVpnGateway(), + "aws_waf_ipset": dataSourceAwsWafIpSet(), + "aws_waf_rule": dataSourceAwsWafRule(), + "aws_waf_rate_based_rule": dataSourceAwsWafRateBasedRule(), + "aws_waf_web_acl": dataSourceAwsWafWebAcl(), + "aws_wafregional_ipset": dataSourceAwsWafRegionalIpSet(), + "aws_wafregional_rule": dataSourceAwsWafRegionalRule(), + "aws_wafregional_rate_based_rule": dataSourceAwsWafRegionalRateBasedRule(), + "aws_wafregional_web_acl": dataSourceAwsWafRegionalWebAcl(), + "aws_wafv2_ip_set": dataSourceAwsWafv2IPSet(), + "aws_wafv2_regex_pattern_set": dataSourceAwsWafv2RegexPatternSet(), + "aws_wafv2_rule_group": dataSourceAwsWafv2RuleGroup(), + "aws_wafv2_web_acl": dataSourceAwsWafv2WebACL(), + "aws_workspaces_bundle": dataSourceAwsWorkspacesBundle(), + "aws_workspaces_directory": dataSourceAwsWorkspacesDirectory(), + "aws_workspaces_image": dataSourceAwsWorkspacesImage(), + "aws_workspaces_workspace": dataSourceAwsWorkspacesWorkspace(), + + // Adding the Aliases for the ALB -> LB Rename + "aws_lb": dataSourceAwsLb(), + "aws_alb": dataSourceAwsLb(), + "aws_lb_listener": dataSourceAwsLbListener(), + "aws_alb_listener": dataSourceAwsLbListener(), + "aws_lb_target_group": dataSourceAwsLbTargetGroup(), + "aws_alb_target_group": dataSourceAwsLbTargetGroup(), + }, + + ResourcesMap: map[string]*schema.Resource{ + "aws_accessanalyzer_analyzer": resourceAwsAccessAnalyzerAnalyzer(), + "aws_acm_certificate": resourceAwsAcmCertificate(), + "aws_acm_certificate_validation": resourceAwsAcmCertificateValidation(), + "aws_acmpca_certificate_authority": resourceAwsAcmpcaCertificateAuthority(), + "aws_acmpca_certificate_authority_certificate": resourceAwsAcmpcaCertificateAuthorityCertificate(), + "aws_acmpca_certificate": resourceAwsAcmpcaCertificate(), + "aws_ami": resourceAwsAmi(), + "aws_ami_copy": resourceAwsAmiCopy(), + "aws_ami_from_instance": resourceAwsAmiFromInstance(), + "aws_ami_launch_permission": resourceAwsAmiLaunchPermission(), + "aws_amplify_app": resourceAwsAmplifyApp(), + "aws_amplify_backend_environment": resourceAwsAmplifyBackendEnvironment(), + "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_rest_api_policy": resourceAwsApiGatewayRestApiPolicy(), + "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_apigatewayv2_api": resourceAwsApiGatewayV2Api(), + "aws_apigatewayv2_api_mapping": resourceAwsApiGatewayV2ApiMapping(), + "aws_apigatewayv2_authorizer": resourceAwsApiGatewayV2Authorizer(), + "aws_apigatewayv2_deployment": resourceAwsApiGatewayV2Deployment(), + "aws_apigatewayv2_domain_name": resourceAwsApiGatewayV2DomainName(), + "aws_apigatewayv2_integration": resourceAwsApiGatewayV2Integration(), + "aws_apigatewayv2_integration_response": resourceAwsApiGatewayV2IntegrationResponse(), + "aws_apigatewayv2_model": resourceAwsApiGatewayV2Model(), + "aws_apigatewayv2_route": resourceAwsApiGatewayV2Route(), + "aws_apigatewayv2_route_response": resourceAwsApiGatewayV2RouteResponse(), + "aws_apigatewayv2_stage": resourceAwsApiGatewayV2Stage(), + "aws_apigatewayv2_vpc_link": resourceAwsApiGatewayV2VpcLink(), + "aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(), + "aws_appautoscaling_target": resourceAwsAppautoscalingTarget(), + "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), + "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), + "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), + "aws_appmesh_mesh": resourceAwsAppmeshMesh(), + "aws_appmesh_route": resourceAwsAppmeshRoute(), + "aws_appmesh_virtual_gateway": resourceAwsAppmeshVirtualGateway(), + "aws_appmesh_virtual_node": resourceAwsAppmeshVirtualNode(), + "aws_appmesh_virtual_router": resourceAwsAppmeshVirtualRouter(), + "aws_appmesh_virtual_service": resourceAwsAppmeshVirtualService(), + "aws_apprunner_auto_scaling_configuration_version": resourceAwsAppRunnerAutoScalingConfigurationVersion(), + "aws_apprunner_connection": resourceAwsAppRunnerConnection(), + "aws_apprunner_custom_domain_association": resourceAwsAppRunnerCustomDomainAssociation(), + "aws_apprunner_service": resourceAwsAppRunnerService(), + "aws_appsync_api_key": resourceAwsAppsyncApiKey(), + "aws_appsync_datasource": resourceAwsAppsyncDatasource(), + "aws_appsync_function": resourceAwsAppsyncFunction(), + "aws_appsync_graphql_api": resourceAwsAppsyncGraphqlApi(), + "aws_appsync_resolver": resourceAwsAppsyncResolver(), + "aws_athena_database": resourceAwsAthenaDatabase(), + "aws_athena_named_query": resourceAwsAthenaNamedQuery(), + "aws_athena_workgroup": resourceAwsAthenaWorkgroup(), + "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_autoscalingplans_scaling_plan": resourceAwsAutoScalingPlansScalingPlan(), + "aws_backup_global_settings": resourceAwsBackupGlobalSettings(), + "aws_backup_plan": resourceAwsBackupPlan(), + "aws_backup_region_settings": resourceAwsBackupRegionSettings(), + "aws_backup_selection": resourceAwsBackupSelection(), + "aws_backup_vault": resourceAwsBackupVault(), + "aws_backup_vault_notifications": resourceAwsBackupVaultNotifications(), + "aws_backup_vault_policy": resourceAwsBackupVaultPolicy(), + "aws_budgets_budget": resourceAwsBudgetsBudget(), + "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), + "aws_cloudformation_stack": resourceAwsCloudFormationStack(), + "aws_cloudformation_stack_set": resourceAwsCloudFormationStackSet(), + "aws_cloudformation_stack_set_instance": resourceAwsCloudFormationStackSetInstance(), + "aws_cloudformation_type": resourceAwsCloudFormationType(), + "aws_cloudfront_cache_policy": resourceAwsCloudFrontCachePolicy(), + "aws_cloudfront_distribution": resourceAwsCloudFrontDistribution(), + "aws_cloudfront_function": resourceAwsCloudFrontFunction(), + "aws_cloudfront_key_group": resourceAwsCloudFrontKeyGroup(), + "aws_cloudfront_origin_access_identity": resourceAwsCloudFrontOriginAccessIdentity(), + "aws_cloudfront_origin_request_policy": resourceAwsCloudFrontOriginRequestPolicy(), + "aws_cloudfront_public_key": resourceAwsCloudFrontPublicKey(), + "aws_cloudfront_realtime_log_config": resourceAwsCloudFrontRealtimeLogConfig(), + "aws_cloudtrail": resourceAwsCloudTrail(), + "aws_cloudwatch_event_bus": resourceAwsCloudWatchEventBus(), + "aws_cloudwatch_event_permission": resourceAwsCloudWatchEventPermission(), + "aws_cloudwatch_event_rule": resourceAwsCloudWatchEventRule(), + "aws_cloudwatch_event_target": resourceAwsCloudWatchEventTarget(), + "aws_cloudwatch_event_archive": resourceAwsCloudWatchEventArchive(), + "aws_cloudwatch_event_connection": resourceAwsCloudWatchEventConnection(), + "aws_cloudwatch_event_api_destination": resourceAwsCloudWatchEventApiDestination(), + "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_conformance_pack": resourceAwsConfigConformancePack(), + "aws_config_delivery_channel": resourceAwsConfigDeliveryChannel(), + "aws_config_organization_custom_rule": resourceAwsConfigOrganizationCustomRule(), + "aws_config_organization_managed_rule": resourceAwsConfigOrganizationManagedRule(), + "aws_config_remediation_configuration": resourceAwsConfigRemediationConfiguration(), + "aws_cognito_identity_pool": resourceAwsCognitoIdentityPool(), + "aws_cognito_identity_pool_roles_attachment": resourceAwsCognitoIdentityPoolRolesAttachment(), + "aws_cognito_identity_provider": resourceAwsCognitoIdentityProvider(), + "aws_cognito_resource_server": resourceAwsCognitoResourceServer(), + "aws_cognito_user_group": resourceAwsCognitoUserGroup(), + "aws_cognito_user_pool": resourceAwsCognitoUserPool(), + "aws_cognito_user_pool_client": resourceAwsCognitoUserPoolClient(), + "aws_cognito_user_pool_domain": resourceAwsCognitoUserPoolDomain(), + "aws_cognito_user_pool_ui_customization": resourceAwsCognitoUserPoolUICustomization(), + "aws_cloudhsm_v2_cluster": resourceAwsCloudHsmV2Cluster(), + "aws_cloudhsm_v2_hsm": resourceAwsCloudHsmV2Hsm(), + "aws_cloudwatch_composite_alarm": resourceAwsCloudWatchCompositeAlarm(), + "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), + "aws_cloudwatch_dashboard": resourceAwsCloudWatchDashboard(), + "aws_cloudwatch_metric_stream": resourceAwsCloudWatchMetricStream(), + "aws_cloudwatch_query_definition": resourceAwsCloudWatchQueryDefinition(), + "aws_codedeploy_app": resourceAwsCodeDeployApp(), + "aws_codedeploy_deployment_config": resourceAwsCodeDeployDeploymentConfig(), + "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), + "aws_codecommit_repository": resourceAwsCodeCommitRepository(), + "aws_codecommit_trigger": resourceAwsCodeCommitTrigger(), + "aws_codeartifact_domain": resourceAwsCodeArtifactDomain(), + "aws_codeartifact_domain_permissions_policy": resourceAwsCodeArtifactDomainPermissionsPolicy(), + "aws_codeartifact_repository": resourceAwsCodeArtifactRepository(), + "aws_codeartifact_repository_permissions_policy": resourceAwsCodeArtifactRepositoryPermissionsPolicy(), + "aws_codebuild_project": resourceAwsCodeBuildProject(), + "aws_codebuild_report_group": resourceAwsCodeBuildReportGroup(), + "aws_codebuild_source_credential": resourceAwsCodeBuildSourceCredential(), + "aws_codebuild_webhook": resourceAwsCodeBuildWebhook(), + "aws_codepipeline": resourceAwsCodePipeline(), + "aws_codepipeline_webhook": resourceAwsCodePipelineWebhook(), + "aws_codestarconnections_connection": resourceAwsCodeStarConnectionsConnection(), + "aws_codestarconnections_host": resourceAwsCodeStarConnectionsHost(), + "aws_codestarnotifications_notification_rule": resourceAwsCodeStarNotificationsNotificationRule(), + "aws_cur_report_definition": resourceAwsCurReportDefinition(), + "aws_customer_gateway": resourceAwsCustomerGateway(), + "aws_datapipeline_pipeline": resourceAwsDataPipelinePipeline(), + "aws_datasync_agent": resourceAwsDataSyncAgent(), + "aws_datasync_location_efs": resourceAwsDataSyncLocationEfs(), + "aws_datasync_location_fsx_windows_file_system": resourceAwsDataSyncLocationFsxWindowsFileSystem(), + "aws_datasync_location_nfs": resourceAwsDataSyncLocationNfs(), + "aws_datasync_location_s3": resourceAwsDataSyncLocationS3(), + "aws_datasync_location_smb": resourceAwsDataSyncLocationSmb(), + "aws_datasync_task": resourceAwsDataSyncTask(), + "aws_dax_cluster": resourceAwsDaxCluster(), + "aws_dax_parameter_group": resourceAwsDaxParameterGroup(), + "aws_dax_subnet_group": resourceAwsDaxSubnetGroup(), + "aws_db_cluster_snapshot": resourceAwsDbClusterSnapshot(), + "aws_db_event_subscription": resourceAwsDbEventSubscription(), + "aws_db_instance": resourceAwsDbInstance(), + "aws_db_instance_role_association": resourceAwsDbInstanceRoleAssociation(), + "aws_db_option_group": resourceAwsDbOptionGroup(), + "aws_db_parameter_group": resourceAwsDbParameterGroup(), + "aws_db_proxy": resourceAwsDbProxy(), + "aws_db_proxy_default_target_group": resourceAwsDbProxyDefaultTargetGroup(), + "aws_db_proxy_endpoint": resourceAwsDbProxyEndpoint(), + "aws_db_proxy_target": resourceAwsDbProxyTarget(), + "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_directory_service_log_subscription": resourceAwsDirectoryServiceLogSubscription(), + "aws_dlm_lifecycle_policy": resourceAwsDlmLifecyclePolicy(), + "aws_dms_certificate": resourceAwsDmsCertificate(), + "aws_dms_endpoint": resourceAwsDmsEndpoint(), + "aws_dms_event_subscription": resourceAwsDmsEventSubscription(), + "aws_dms_replication_instance": resourceAwsDmsReplicationInstance(), + "aws_dms_replication_subnet_group": resourceAwsDmsReplicationSubnetGroup(), + "aws_dms_replication_task": resourceAwsDmsReplicationTask(), + "aws_docdb_cluster": resourceAwsDocDBCluster(), + "aws_docdb_cluster_instance": resourceAwsDocDBClusterInstance(), + "aws_docdb_cluster_parameter_group": resourceAwsDocDBClusterParameterGroup(), + "aws_docdb_cluster_snapshot": resourceAwsDocDBClusterSnapshot(), + "aws_docdb_subnet_group": resourceAwsDocDBSubnetGroup(), + "aws_dx_bgp_peer": resourceAwsDxBgpPeer(), + "aws_dx_connection": resourceAwsDxConnection(), + "aws_dx_connection_association": resourceAwsDxConnectionAssociation(), + "aws_dx_gateway": resourceAwsDxGateway(), + "aws_dx_gateway_association": resourceAwsDxGatewayAssociation(), + "aws_dx_gateway_association_proposal": resourceAwsDxGatewayAssociationProposal(), + "aws_dx_hosted_private_virtual_interface": resourceAwsDxHostedPrivateVirtualInterface(), + "aws_dx_hosted_private_virtual_interface_accepter": resourceAwsDxHostedPrivateVirtualInterfaceAccepter(), + "aws_dx_hosted_public_virtual_interface": resourceAwsDxHostedPublicVirtualInterface(), + "aws_dx_hosted_public_virtual_interface_accepter": resourceAwsDxHostedPublicVirtualInterfaceAccepter(), + "aws_dx_hosted_transit_virtual_interface": resourceAwsDxHostedTransitVirtualInterface(), + "aws_dx_hosted_transit_virtual_interface_accepter": resourceAwsDxHostedTransitVirtualInterfaceAccepter(), + "aws_dx_lag": resourceAwsDxLag(), + "aws_dx_private_virtual_interface": resourceAwsDxPrivateVirtualInterface(), + "aws_dx_public_virtual_interface": resourceAwsDxPublicVirtualInterface(), + "aws_dx_transit_virtual_interface": resourceAwsDxTransitVirtualInterface(), + "aws_dynamodb_table": resourceAwsDynamoDbTable(), + "aws_dynamodb_table_item": resourceAwsDynamoDbTableItem(), + "aws_dynamodb_global_table": resourceAwsDynamoDbGlobalTable(), + "aws_dynamodb_kinesis_streaming_destination": resourceAwsDynamoDbKinesisStreamingDestination(), + "aws_ebs_default_kms_key": resourceAwsEbsDefaultKmsKey(), + "aws_ebs_encryption_by_default": resourceAwsEbsEncryptionByDefault(), + "aws_ebs_snapshot": resourceAwsEbsSnapshot(), + "aws_ebs_snapshot_copy": resourceAwsEbsSnapshotCopy(), + "aws_ebs_volume": resourceAwsEbsVolume(), + "aws_ec2_availability_zone_group": resourceAwsEc2AvailabilityZoneGroup(), + "aws_ec2_capacity_reservation": resourceAwsEc2CapacityReservation(), + "aws_ec2_carrier_gateway": resourceAwsEc2CarrierGateway(), + "aws_ec2_client_vpn_authorization_rule": resourceAwsEc2ClientVpnAuthorizationRule(), + "aws_ec2_client_vpn_endpoint": resourceAwsEc2ClientVpnEndpoint(), + "aws_ec2_client_vpn_network_association": resourceAwsEc2ClientVpnNetworkAssociation(), + "aws_ec2_client_vpn_route": resourceAwsEc2ClientVpnRoute(), + "aws_ec2_fleet": resourceAwsEc2Fleet(), + "aws_ec2_local_gateway_route": resourceAwsEc2LocalGatewayRoute(), + "aws_ec2_local_gateway_route_table_vpc_association": resourceAwsEc2LocalGatewayRouteTableVpcAssociation(), + "aws_ec2_managed_prefix_list": resourceAwsEc2ManagedPrefixList(), + "aws_ec2_tag": resourceAwsEc2Tag(), + "aws_ec2_traffic_mirror_filter": resourceAwsEc2TrafficMirrorFilter(), + "aws_ec2_traffic_mirror_filter_rule": resourceAwsEc2TrafficMirrorFilterRule(), + "aws_ec2_traffic_mirror_target": resourceAwsEc2TrafficMirrorTarget(), + "aws_ec2_traffic_mirror_session": resourceAwsEc2TrafficMirrorSession(), + "aws_ec2_transit_gateway": resourceAwsEc2TransitGateway(), + "aws_ec2_transit_gateway_peering_attachment": resourceAwsEc2TransitGatewayPeeringAttachment(), + "aws_ec2_transit_gateway_peering_attachment_accepter": resourceAwsEc2TransitGatewayPeeringAttachmentAccepter(), + "aws_ec2_transit_gateway_prefix_list_reference": resourceAwsEc2TransitGatewayPrefixListReference(), + "aws_ec2_transit_gateway_route": resourceAwsEc2TransitGatewayRoute(), + "aws_ec2_transit_gateway_route_table": resourceAwsEc2TransitGatewayRouteTable(), + "aws_ec2_transit_gateway_route_table_association": resourceAwsEc2TransitGatewayRouteTableAssociation(), + "aws_ec2_transit_gateway_route_table_propagation": resourceAwsEc2TransitGatewayRouteTablePropagation(), + "aws_ec2_transit_gateway_vpc_attachment": resourceAwsEc2TransitGatewayVpcAttachment(), + "aws_ec2_transit_gateway_vpc_attachment_accepter": resourceAwsEc2TransitGatewayVpcAttachmentAccepter(), + "aws_ecr_lifecycle_policy": resourceAwsEcrLifecyclePolicy(), + "aws_ecrpublic_repository": resourceAwsEcrPublicRepository(), + "aws_ecr_registry_policy": resourceAwsEcrRegistryPolicy(), + "aws_ecr_replication_configuration": resourceAwsEcrReplicationConfiguration(), + "aws_ecr_repository": resourceAwsEcrRepository(), + "aws_ecr_repository_policy": resourceAwsEcrRepositoryPolicy(), + "aws_ecs_capacity_provider": resourceAwsEcsCapacityProvider(), + "aws_ecs_cluster": resourceAwsEcsCluster(), + "aws_ecs_service": resourceAwsEcsService(), + "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), + "aws_efs_access_point": resourceAwsEfsAccessPoint(), + "aws_efs_file_system": resourceAwsEfsFileSystem(), + "aws_efs_file_system_policy": resourceAwsEfsFileSystemPolicy(), + "aws_efs_mount_target": resourceAwsEfsMountTarget(), + "aws_egress_only_internet_gateway": resourceAwsEgressOnlyInternetGateway(), + "aws_eip": resourceAwsEip(), + "aws_eip_association": resourceAwsEipAssociation(), + "aws_eks_cluster": resourceAwsEksCluster(), + "aws_eks_addon": resourceAwsEksAddon(), + "aws_eks_fargate_profile": resourceAwsEksFargateProfile(), + "aws_eks_node_group": resourceAwsEksNodeGroup(), + "aws_elasticache_cluster": resourceAwsElasticacheCluster(), + "aws_elasticache_global_replication_group": resourceAwsElasticacheGlobalReplicationGroup(), + "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_instance_fleet": resourceAwsEMRInstanceFleet(), + "aws_emr_managed_scaling_policy": resourceAwsEMRManagedScalingPolicy(), + "aws_emr_security_configuration": resourceAwsEMRSecurityConfiguration(), + "aws_flow_log": resourceAwsFlowLog(), + "aws_fsx_lustre_file_system": resourceAwsFsxLustreFileSystem(), + "aws_fsx_windows_file_system": resourceAwsFsxWindowsFileSystem(), + "aws_fms_admin_account": resourceAwsFmsAdminAccount(), + "aws_fms_policy": resourceAwsFmsPolicy(), + "aws_gamelift_alias": resourceAwsGameliftAlias(), + "aws_gamelift_build": resourceAwsGameliftBuild(), + "aws_gamelift_fleet": resourceAwsGameliftFleet(), + "aws_gamelift_game_session_queue": resourceAwsGameliftGameSessionQueue(), + "aws_glacier_vault": resourceAwsGlacierVault(), + "aws_glacier_vault_lock": resourceAwsGlacierVaultLock(), + "aws_globalaccelerator_accelerator": resourceAwsGlobalAcceleratorAccelerator(), + "aws_globalaccelerator_endpoint_group": resourceAwsGlobalAcceleratorEndpointGroup(), + "aws_globalaccelerator_listener": resourceAwsGlobalAcceleratorListener(), + "aws_glue_catalog_database": resourceAwsGlueCatalogDatabase(), + "aws_glue_catalog_table": resourceAwsGlueCatalogTable(), + "aws_glue_classifier": resourceAwsGlueClassifier(), + "aws_glue_connection": resourceAwsGlueConnection(), + "aws_glue_dev_endpoint": resourceAwsGlueDevEndpoint(), + "aws_glue_crawler": resourceAwsGlueCrawler(), + "aws_glue_data_catalog_encryption_settings": resourceAwsGlueDataCatalogEncryptionSettings(), + "aws_glue_job": resourceAwsGlueJob(), + "aws_glue_ml_transform": resourceAwsGlueMLTransform(), + "aws_glue_partition": resourceAwsGluePartition(), + "aws_glue_registry": resourceAwsGlueRegistry(), + "aws_glue_resource_policy": resourceAwsGlueResourcePolicy(), + "aws_glue_schema": resourceAwsGlueSchema(), + "aws_glue_security_configuration": resourceAwsGlueSecurityConfiguration(), + "aws_glue_trigger": resourceAwsGlueTrigger(), + "aws_glue_user_defined_function": resourceAwsGlueUserDefinedFunction(), + "aws_glue_workflow": resourceAwsGlueWorkflow(), + "aws_guardduty_detector": resourceAwsGuardDutyDetector(), + "aws_guardduty_filter": resourceAwsGuardDutyFilter(), + "aws_guardduty_invite_accepter": resourceAwsGuardDutyInviteAccepter(), + "aws_guardduty_ipset": resourceAwsGuardDutyIpset(), + "aws_guardduty_member": resourceAwsGuardDutyMember(), + "aws_guardduty_organization_admin_account": resourceAwsGuardDutyOrganizationAdminAccount(), + "aws_guardduty_organization_configuration": resourceAwsGuardDutyOrganizationConfiguration(), + "aws_guardduty_publishing_destination": resourceAwsGuardDutyPublishingDestination(), + "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_imagebuilder_component": resourceAwsImageBuilderComponent(), + "aws_imagebuilder_distribution_configuration": resourceAwsImageBuilderDistributionConfiguration(), + "aws_imagebuilder_image": resourceAwsImageBuilderImage(), + "aws_imagebuilder_image_pipeline": resourceAwsImageBuilderImagePipeline(), + "aws_imagebuilder_image_recipe": resourceAwsImageBuilderImageRecipe(), + "aws_imagebuilder_infrastructure_configuration": resourceAwsImageBuilderInfrastructureConfiguration(), + "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_policy_attachment": resourceAwsIotPolicyAttachment(), + "aws_iot_thing": resourceAwsIotThing(), + "aws_iot_thing_principal_attachment": resourceAwsIotThingPrincipalAttachment(), + "aws_iot_thing_type": resourceAwsIotThingType(), + "aws_iot_topic_rule": resourceAwsIotTopicRule(), + "aws_iot_role_alias": resourceAwsIotRoleAlias(), + "aws_key_pair": resourceAwsKeyPair(), + "aws_kinesis_analytics_application": resourceAwsKinesisAnalyticsApplication(), + "aws_kinesisanalyticsv2_application": resourceAwsKinesisAnalyticsV2Application(), + "aws_kinesisanalyticsv2_application_snapshot": resourceAwsKinesisAnalyticsV2ApplicationSnapshot(), + "aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(), + "aws_kinesis_stream": resourceAwsKinesisStream(), + "aws_kinesis_stream_consumer": resourceAwsKinesisStreamConsumer(), + "aws_kinesis_video_stream": resourceAwsKinesisVideoStream(), + "aws_kms_alias": resourceAwsKmsAlias(), + "aws_kms_external_key": resourceAwsKmsExternalKey(), + "aws_kms_grant": resourceAwsKmsGrant(), + "aws_kms_key": resourceAwsKmsKey(), + "aws_kms_ciphertext": resourceAwsKmsCiphertext(), + "aws_lakeformation_data_lake_settings": resourceAwsLakeFormationDataLakeSettings(), + "aws_lakeformation_permissions": resourceAwsLakeFormationPermissions(), + "aws_lakeformation_policy_tag": resourceAwsLakeFormationPolicyTag(), + "aws_lakeformation_resource": resourceAwsLakeFormationResource(), + "aws_lambda_alias": resourceAwsLambdaAlias(), + "aws_lambda_code_signing_config": resourceAwsLambdaCodeSigningConfig(), + "aws_lambda_event_source_mapping": resourceAwsLambdaEventSourceMapping(), + "aws_lambda_function_event_invoke_config": resourceAwsLambdaFunctionEventInvokeConfig(), + "aws_lambda_function": resourceAwsLambdaFunction(), + "aws_lambda_layer_version": resourceAwsLambdaLayerVersion(), + "aws_lambda_permission": resourceAwsLambdaPermission(), + "aws_lambda_provisioned_concurrency_config": resourceAwsLambdaProvisionedConcurrencyConfig(), + "aws_launch_configuration": resourceAwsLaunchConfiguration(), + "aws_launch_template": resourceAwsLaunchTemplate(), + "aws_lex_bot": resourceAwsLexBot(), + "aws_lex_bot_alias": resourceAwsLexBotAlias(), + "aws_lex_intent": resourceAwsLexIntent(), + "aws_lex_slot_type": resourceAwsLexSlotType(), + "aws_licensemanager_association": resourceAwsLicenseManagerAssociation(), + "aws_licensemanager_license_configuration": resourceAwsLicenseManagerLicenseConfiguration(), + "aws_lightsail_domain": resourceAwsLightsailDomain(), + "aws_lightsail_instance": resourceAwsLightsailInstance(), + "aws_lightsail_instance_public_ports": resourceAwsLightsailInstancePublicPorts(), + "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_macie2_account": resourceAwsMacie2Account(), + "aws_macie2_classification_job": resourceAwsMacie2ClassificationJob(), + "aws_macie2_custom_data_identifier": resourceAwsMacie2CustomDataIdentifier(), + "aws_macie2_findings_filter": resourceAwsMacie2FindingsFilter(), + "aws_macie2_invitation_accepter": resourceAwsMacie2InvitationAccepter(), + "aws_macie2_member": resourceAwsMacie2Member(), + "aws_macie2_organization_admin_account": resourceAwsMacie2OrganizationAdminAccount(), + "aws_macie_member_account_association": resourceAwsMacieMemberAccountAssociation(), + "aws_macie_s3_bucket_association": resourceAwsMacieS3BucketAssociation(), + "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), + "aws_mq_broker": resourceAwsMqBroker(), + "aws_mq_configuration": resourceAwsMqConfiguration(), + "aws_media_convert_queue": resourceAwsMediaConvertQueue(), + "aws_media_package_channel": resourceAwsMediaPackageChannel(), + "aws_media_store_container": resourceAwsMediaStoreContainer(), + "aws_media_store_container_policy": resourceAwsMediaStoreContainerPolicy(), + "aws_msk_cluster": resourceAwsMskCluster(), + "aws_msk_configuration": resourceAwsMskConfiguration(), + "aws_msk_scram_secret_association": resourceAwsMskScramSecretAssociation(), + "aws_mwaa_environment": resourceAwsMwaaEnvironment(), + "aws_nat_gateway": resourceAwsNatGateway(), + "aws_network_acl": resourceAwsNetworkAcl(), + "aws_default_network_acl": resourceAwsDefaultNetworkAcl(), + "aws_neptune_cluster": resourceAwsNeptuneCluster(), + "aws_neptune_cluster_instance": resourceAwsNeptuneClusterInstance(), + "aws_neptune_cluster_parameter_group": resourceAwsNeptuneClusterParameterGroup(), + "aws_neptune_cluster_snapshot": resourceAwsNeptuneClusterSnapshot(), + "aws_neptune_event_subscription": resourceAwsNeptuneEventSubscription(), + "aws_neptune_parameter_group": resourceAwsNeptuneParameterGroup(), + "aws_neptune_subnet_group": resourceAwsNeptuneSubnetGroup(), + "aws_network_acl_rule": resourceAwsNetworkAclRule(), + "aws_network_interface": resourceAwsNetworkInterface(), + "aws_network_interface_attachment": resourceAwsNetworkInterfaceAttachment(), + "aws_networkfirewall_firewall": resourceAwsNetworkFirewallFirewall(), + "aws_networkfirewall_firewall_policy": resourceAwsNetworkFirewallFirewallPolicy(), + "aws_networkfirewall_logging_configuration": resourceAwsNetworkFirewallLoggingConfiguration(), + "aws_networkfirewall_resource_policy": resourceAwsNetworkFirewallResourcePolicy(), + "aws_networkfirewall_rule_group": resourceAwsNetworkFirewallRuleGroup(), + "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_delegated_administrator": resourceAwsOrganizationsDelegatedAdministrator(), + "aws_organizations_policy": resourceAwsOrganizationsPolicy(), + "aws_organizations_policy_attachment": resourceAwsOrganizationsPolicyAttachment(), + "aws_organizations_organizational_unit": resourceAwsOrganizationsOrganizationalUnit(), + "aws_placement_group": resourceAwsPlacementGroup(), + "aws_prometheus_workspace": resourceAwsPrometheusWorkspace(), + "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), + "aws_qldb_ledger": resourceAwsQLDBLedger(), + "aws_quicksight_group": resourceAwsQuickSightGroup(), + "aws_quicksight_user": resourceAwsQuickSightUser(), + "aws_ram_principal_association": resourceAwsRamPrincipalAssociation(), + "aws_ram_resource_association": resourceAwsRamResourceAssociation(), + "aws_ram_resource_share": resourceAwsRamResourceShare(), + "aws_ram_resource_share_accepter": resourceAwsRamResourceShareAccepter(), + "aws_rds_cluster": resourceAwsRDSCluster(), + "aws_rds_cluster_endpoint": resourceAwsRDSClusterEndpoint(), + "aws_rds_cluster_instance": resourceAwsRDSClusterInstance(), + "aws_rds_cluster_parameter_group": resourceAwsRDSClusterParameterGroup(), + "aws_rds_global_cluster": resourceAwsRDSGlobalCluster(), + "aws_redshift_cluster": resourceAwsRedshiftCluster(), + "aws_redshift_security_group": resourceAwsRedshiftSecurityGroup(), + "aws_redshift_parameter_group": resourceAwsRedshiftParameterGroup(), + "aws_redshift_subnet_group": resourceAwsRedshiftSubnetGroup(), + "aws_redshift_snapshot_copy_grant": resourceAwsRedshiftSnapshotCopyGrant(), + "aws_redshift_snapshot_schedule": resourceAwsRedshiftSnapshotSchedule(), + "aws_redshift_snapshot_schedule_association": resourceAwsRedshiftSnapshotScheduleAssociation(), + "aws_redshift_event_subscription": resourceAwsRedshiftEventSubscription(), + "aws_resourcegroups_group": resourceAwsResourceGroupsGroup(), + "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), + "aws_route53_hosted_zone_dnssec": resourceAwsRoute53HostedZoneDnssec(), + "aws_route53_key_signing_key": resourceAwsRoute53KeySigningKey(), + "aws_route53_query_log": resourceAwsRoute53QueryLog(), + "aws_route53_record": resourceAwsRoute53Record(), + "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), + "aws_route53_vpc_association_authorization": resourceAwsRoute53VPCAssociationAuthorization(), + "aws_route53_zone": resourceAwsRoute53Zone(), + "aws_route53_health_check": resourceAwsRoute53HealthCheck(), + "aws_route53_resolver_dnssec_config": resourceAwsRoute53ResolverDnssecConfig(), + "aws_route53_resolver_endpoint": resourceAwsRoute53ResolverEndpoint(), + "aws_route53_resolver_firewall_domain_list": resourceAwsRoute53ResolverFirewallDomainList(), + "aws_route53_resolver_firewall_rule": resourceAwsRoute53ResolverFirewallRule(), + "aws_route53_resolver_firewall_rule_group": resourceAwsRoute53ResolverFirewallRuleGroup(), + "aws_route53_resolver_firewall_rule_group_association": resourceAwsRoute53ResolverFirewallRuleGroupAssociation(), + "aws_route53_resolver_query_log_config": resourceAwsRoute53ResolverQueryLogConfig(), + "aws_route53_resolver_query_log_config_association": resourceAwsRoute53ResolverQueryLogConfigAssociation(), + "aws_route53_resolver_rule_association": resourceAwsRoute53ResolverRuleAssociation(), + "aws_route53_resolver_rule": resourceAwsRoute53ResolverRule(), + "aws_route": resourceAwsRoute(), + "aws_route_table": resourceAwsRouteTable(), + "aws_default_route_table": resourceAwsDefaultRouteTable(), + "aws_route_table_association": resourceAwsRouteTableAssociation(), + "aws_sagemaker_app": resourceAwsSagemakerApp(), + "aws_sagemaker_app_image_config": resourceAwsSagemakerAppImageConfig(), + "aws_sagemaker_code_repository": resourceAwsSagemakerCodeRepository(), + "aws_sagemaker_domain": resourceAwsSagemakerDomain(), + "aws_sagemaker_endpoint": resourceAwsSagemakerEndpoint(), + "aws_sagemaker_endpoint_configuration": resourceAwsSagemakerEndpointConfiguration(), + "aws_sagemaker_feature_group": resourceAwsSagemakerFeatureGroup(), + "aws_sagemaker_image": resourceAwsSagemakerImage(), + "aws_sagemaker_image_version": resourceAwsSagemakerImageVersion(), + "aws_sagemaker_model": resourceAwsSagemakerModel(), + "aws_sagemaker_model_package_group": resourceAwsSagemakerModelPackageGroup(), + "aws_sagemaker_notebook_instance_lifecycle_configuration": resourceAwsSagemakerNotebookInstanceLifeCycleConfiguration(), + "aws_sagemaker_notebook_instance": resourceAwsSagemakerNotebookInstance(), + "aws_sagemaker_user_profile": resourceAwsSagemakerUserProfile(), + "aws_secretsmanager_secret": resourceAwsSecretsManagerSecret(), + "aws_secretsmanager_secret_policy": resourceAwsSecretsManagerSecretPolicy(), + "aws_secretsmanager_secret_version": resourceAwsSecretsManagerSecretVersion(), + "aws_secretsmanager_secret_rotation": resourceAwsSecretsManagerSecretRotation(), + "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_email_identity": resourceAwsSesEmailIdentity(), + "aws_ses_identity_policy": resourceAwsSesIdentityPolicy(), + "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_access_point": resourceAwsS3AccessPoint(), + "aws_s3_account_public_access_block": resourceAwsS3AccountPublicAccessBlock(), + "aws_s3_bucket": resourceAwsS3Bucket(), + "aws_s3_bucket_analytics_configuration": resourceAwsS3BucketAnalyticsConfiguration(), + "aws_s3_bucket_policy": resourceAwsS3BucketPolicy(), + "aws_s3_bucket_public_access_block": resourceAwsS3BucketPublicAccessBlock(), + "aws_s3_bucket_object": resourceAwsS3BucketObject(), + "aws_s3_bucket_ownership_controls": resourceAwsS3BucketOwnershipControls(), + "aws_s3_bucket_notification": resourceAwsS3BucketNotification(), + "aws_s3_bucket_metric": resourceAwsS3BucketMetric(), + "aws_s3_bucket_inventory": resourceAwsS3BucketInventory(), + "aws_s3_object_copy": resourceAwsS3ObjectCopy(), + "aws_s3control_bucket": resourceAwsS3ControlBucket(), + "aws_s3control_bucket_policy": resourceAwsS3ControlBucketPolicy(), + "aws_s3control_bucket_lifecycle_configuration": resourceAwsS3ControlBucketLifecycleConfiguration(), + "aws_s3outposts_endpoint": resourceAwsS3OutpostsEndpoint(), + "aws_security_group": resourceAwsSecurityGroup(), + "aws_network_interface_sg_attachment": resourceAwsNetworkInterfaceSGAttachment(), + "aws_default_security_group": resourceAwsDefaultSecurityGroup(), + "aws_security_group_rule": resourceAwsSecurityGroupRule(), + "aws_securityhub_account": resourceAwsSecurityHubAccount(), + "aws_securityhub_action_target": resourceAwsSecurityHubActionTarget(), + "aws_securityhub_insight": resourceAwsSecurityHubInsight(), + "aws_securityhub_invite_accepter": resourceAwsSecurityHubInviteAccepter(), + "aws_securityhub_member": resourceAwsSecurityHubMember(), + "aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(), + "aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(), + "aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(), + "aws_servicecatalog_budget_resource_association": resourceAwsServiceCatalogBudgetResourceAssociation(), + "aws_servicecatalog_constraint": resourceAwsServiceCatalogConstraint(), + "aws_servicecatalog_organizations_access": resourceAwsServiceCatalogOrganizationsAccess(), + "aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(), + "aws_servicecatalog_portfolio_share": resourceAwsServiceCatalogPortfolioShare(), + "aws_servicecatalog_product": resourceAwsServiceCatalogProduct(), + "aws_servicecatalog_service_action": resourceAwsServiceCatalogServiceAction(), + "aws_servicecatalog_tag_option": resourceAwsServiceCatalogTagOption(), + "aws_servicecatalog_tag_option_resource_association": resourceAwsServiceCatalogTagOptionResourceAssociation(), + "aws_servicecatalog_product_portfolio_association": resourceAwsServiceCatalogProductPortfolioAssociation(), + "aws_servicecatalog_provisioning_artifact": resourceAwsServiceCatalogProvisioningArtifact(), + "aws_service_discovery_http_namespace": resourceAwsServiceDiscoveryHttpNamespace(), + "aws_service_discovery_private_dns_namespace": resourceAwsServiceDiscoveryPrivateDnsNamespace(), + "aws_service_discovery_public_dns_namespace": resourceAwsServiceDiscoveryPublicDnsNamespace(), + "aws_service_discovery_service": resourceAwsServiceDiscoveryService(), + "aws_servicequotas_service_quota": resourceAwsServiceQuotasServiceQuota(), + "aws_shield_protection": resourceAwsShieldProtection(), + "aws_signer_signing_job": resourceAwsSignerSigningJob(), + "aws_signer_signing_profile": resourceAwsSignerSigningProfile(), + "aws_signer_signing_profile_permission": resourceAwsSignerSigningProfilePermission(), + "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_ssoadmin_account_assignment": resourceAwsSsoAdminAccountAssignment(), + "aws_ssoadmin_managed_policy_attachment": resourceAwsSsoAdminManagedPolicyAttachment(), + "aws_ssoadmin_permission_set": resourceAwsSsoAdminPermissionSet(), + "aws_ssoadmin_permission_set_inline_policy": resourceAwsSsoAdminPermissionSetInlinePolicy(), + "aws_storagegateway_cache": resourceAwsStorageGatewayCache(), + "aws_storagegateway_cached_iscsi_volume": resourceAwsStorageGatewayCachedIscsiVolume(), + "aws_storagegateway_gateway": resourceAwsStorageGatewayGateway(), + "aws_storagegateway_nfs_file_share": resourceAwsStorageGatewayNfsFileShare(), + "aws_storagegateway_smb_file_share": resourceAwsStorageGatewaySmbFileShare(), + "aws_storagegateway_stored_iscsi_volume": resourceAwsStorageGatewayStoredIscsiVolume(), + "aws_storagegateway_tape_pool": resourceAwsStorageGatewayTapePool(), + "aws_storagegateway_upload_buffer": resourceAwsStorageGatewayUploadBuffer(), + "aws_storagegateway_working_storage": resourceAwsStorageGatewayWorkingStorage(), + "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_swf_domain": resourceAwsSwfDomain(), + "aws_synthetics_canary": resourceAwsSyntheticsCanary(), + "aws_timestreamwrite_database": resourceAwsTimestreamWriteDatabase(), + "aws_timestreamwrite_table": resourceAwsTimestreamWriteTable(), + "aws_transfer_server": resourceAwsTransferServer(), + "aws_transfer_ssh_key": resourceAwsTransferSshKey(), + "aws_transfer_user": resourceAwsTransferUser(), + "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_vpc_ipv4_cidr_block_association": resourceAwsVpcIpv4CidrBlockAssociation(), + "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_wafv2_ip_set": resourceAwsWafv2IPSet(), + "aws_wafv2_regex_pattern_set": resourceAwsWafv2RegexPatternSet(), + "aws_wafv2_rule_group": resourceAwsWafv2RuleGroup(), + "aws_wafv2_web_acl": resourceAwsWafv2WebACL(), + "aws_wafv2_web_acl_association": resourceAwsWafv2WebACLAssociation(), + "aws_wafv2_web_acl_logging_configuration": resourceAwsWafv2WebACLLoggingConfiguration(), + "aws_worklink_fleet": resourceAwsWorkLinkFleet(), + "aws_worklink_website_certificate_authority_association": resourceAwsWorkLinkWebsiteCertificateAuthorityAssociation(), + "aws_workspaces_directory": resourceAwsWorkspacesDirectory(), + "aws_workspaces_workspace": resourceAwsWorkspacesWorkspace(), + "aws_batch_compute_environment": resourceAwsBatchComputeEnvironment(), + "aws_batch_job_definition": resourceAwsBatchJobDefinition(), + "aws_batch_job_queue": resourceAwsBatchJobQueue(), + "aws_pinpoint_app": resourceAwsPinpointApp(), + "aws_pinpoint_adm_channel": resourceAwsPinpointADMChannel(), + "aws_pinpoint_apns_channel": resourceAwsPinpointAPNSChannel(), + "aws_pinpoint_apns_sandbox_channel": resourceAwsPinpointAPNSSandboxChannel(), + "aws_pinpoint_apns_voip_channel": resourceAwsPinpointAPNSVoipChannel(), + "aws_pinpoint_apns_voip_sandbox_channel": resourceAwsPinpointAPNSVoipSandboxChannel(), + "aws_pinpoint_baidu_channel": resourceAwsPinpointBaiduChannel(), + "aws_pinpoint_email_channel": resourceAwsPinpointEmailChannel(), + "aws_pinpoint_event_stream": resourceAwsPinpointEventStream(), + "aws_pinpoint_gcm_channel": resourceAwsPinpointGCMChannel(), + "aws_pinpoint_sms_channel": resourceAwsPinpointSMSChannel(), + "aws_xray_encryption_config": resourceAwsXrayEncryptionConfig(), + "aws_xray_group": resourceAwsXrayGroup(), + "aws_xray_sampling_rule": resourceAwsXraySamplingRule(), + "aws_workspaces_ip_group": resourceAwsWorkspacesIpGroup(), + + // 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 + // back to the old ALB version. IF the Terraform supported aliases for resources + // this would be a whole lot simpler + "aws_alb": resourceAwsLb(), + "aws_lb": resourceAwsLb(), + "aws_alb_listener": resourceAwsLbListener(), + "aws_lb_listener": resourceAwsLbListener(), + "aws_alb_listener_certificate": resourceAwsLbListenerCertificate(), + "aws_lb_listener_certificate": resourceAwsLbListenerCertificate(), + "aws_alb_listener_rule": resourceAwsLbbListenerRule(), + "aws_lb_listener_rule": resourceAwsLbbListenerRule(), + "aws_alb_target_group": resourceAwsLbTargetGroup(), + "aws_lb_target_group": resourceAwsLbTargetGroup(), + "aws_alb_target_group_attachment": resourceAwsLbTargetGroupAttachment(), + "aws_lb_target_group_attachment": resourceAwsLbTargetGroupAttachment(), + }, + } + + // Avoid Go formatting churn and Git conflicts + // You probably should not do this + provider.DataSourcesMap["aws_serverlessapplicationrepository_application"] = dataSourceAwsServerlessApplicationRepositoryApplication() + provider.ResourcesMap["aws_serverlessapplicationrepository_cloudformation_stack"] = resourceAwsServerlessApplicationRepositoryCloudFormationStack() + + provider.ConfigureFunc = func(d *schema.ResourceData) (interface{}, error) { + terraformVersion := provider.TerraformVersion + if terraformVersion == "" { + // Terraform 0.12 introduced this field to the protocol + // We can therefore assume that if it's missing it's 0.10 or 0.11 + terraformVersion = "0.11+compatible" + } + return providerConfigure(d, terraformVersion) + } + + return provider +} + +var descriptions map[string]string +var endpointServiceNames []string + +func init() { + descriptions = map[string]string{ + "region": "The region where AWS operations will take place. Examples\n" + + "are us-east-1, us-west-2, etc.", // lintignore:AWSAT003 + + "access_key": "The access key for API operations. You can retrieve this\n" + + "from the 'Security & Credentials' section of the AWS console.", + + "secret_key": "The secret key for API operations. You can retrieve this\n" + + "from the 'Security & Credentials' section of the AWS console.", + + "profile": "The profile for API operations. If not set, the default profile\n" + + "created with `aws configure` will be used.", + + "shared_credentials_file": "The path to the shared credentials file. If not set\n" + + "this defaults to ~/.aws/credentials.", + + "token": "session token. A session token is only required if you are\n" + + "using temporary security credentials.", + + "max_retries": "The maximum number of times an AWS API request is\n" + + "being executed. If the API request still fails, an error is\n" + + "thrown.", + + "endpoint": "Use this to override the default service endpoint URL", + + "insecure": "Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted," + + "default value is `false`", + + "skip_credentials_validation": "Skip the credentials validation via STS API. " + + "Used for AWS API implementations that do not have STS available/implemented.", + + "skip_get_ec2_platforms": "Skip getting the supported EC2 platforms. " + + "Used by users that don't have ec2:DescribeAccountAttributes permissions.", + + "skip_region_validation": "Skip static validation of region name. " + + "Used by users of alternative AWS-like APIs or users w/ access to regions that are not public (yet).", + + "skip_requesting_account_id": "Skip requesting the account ID. " + + "Used for AWS API implementations that do not have IAM/STS API and/or metadata API.", + + "skip_medatadata_api_check": "Skip the AWS Metadata API check. " + + "Used for AWS API implementations that do not have a metadata api endpoint.", + + "s3_force_path_style": "Set this to true to force the request to use path-style addressing,\n" + + "i.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\n" + + "use virtual hosted bucket addressing when possible\n" + + "(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.", + } + + endpointServiceNames = []string{ + "accessanalyzer", + "acm", + "acmpca", + "amplify", + "apigateway", + "appconfig", + "applicationautoscaling", + "applicationinsights", + "appmesh", + "apprunner", + "appstream", + "appsync", + "athena", + "auditmanager", + "autoscaling", + "autoscalingplans", + "backup", + "batch", + "budgets", + "chime", + "cloud9", + "cloudformation", + "cloudfront", + "cloudhsm", + "cloudsearch", + "cloudtrail", + "cloudwatch", + "cloudwatchevents", + "cloudwatchlogs", + "codeartifact", + "codebuild", + "codecommit", + "codedeploy", + "codepipeline", + "codestarconnections", + "cognitoidentity", + "cognitoidp", + "configservice", + "connect", + "cur", + "dataexchange", + "datapipeline", + "datasync", + "dax", + "detective", + "devicefarm", + "directconnect", + "dlm", + "dms", + "docdb", + "ds", + "dynamodb", + "ec2", + "ecr", + "ecrpublic", + "ecs", + "efs", + "eks", + "elasticache", + "elasticbeanstalk", + "elastictranscoder", + "elb", + "emr", + "emrcontainers", + "es", + "firehose", + "fms", + "forecast", + "fsx", + "gamelift", + "glacier", + "globalaccelerator", + "glue", + "greengrass", + "guardduty", + "iam", + "identitystore", + "imagebuilder", + "inspector", + "iot", + "iotanalytics", + "iotevents", + "kafka", + "kinesis", + "kinesisanalytics", + "kinesisanalyticsv2", + "kinesisvideo", + "kms", + "lakeformation", + "lambda", + "lexmodels", + "licensemanager", + "lightsail", + "macie", + "macie2", + "managedblockchain", + "marketplacecatalog", + "mediaconnect", + "mediaconvert", + "medialive", + "mediapackage", + "mediastore", + "mediastoredata", + "mq", + "mwaa", + "neptune", + "networkfirewall", + "networkmanager", + "opsworks", + "organizations", + "outposts", + "personalize", + "pinpoint", + "pricing", + "qldb", + "quicksight", + "ram", + "rds", + "redshift", + "resourcegroups", + "resourcegroupstaggingapi", + "route53", + "route53domains", + "route53resolver", + "s3", + "s3control", + "s3outposts", + "sagemaker", + "sdb", + "secretsmanager", + "securityhub", + "serverlessrepo", + "servicecatalog", + "servicediscovery", + "servicequotas", + "ses", + "shield", + "signer", + "sns", + "sqs", + "ssm", + "ssoadmin", + "stepfunctions", + "storagegateway", + "sts", + "swf", + "synthetics", + "timestreamwrite", + "transfer", + "waf", + "wafregional", + "wafv2", + "worklink", + "workmail", + "workspaces", + "xray", + } +} + +func providerConfigure(d *schema.ResourceData, terraformVersion string) (interface{}, error) { + config := Config{ + AccessKey: d.Get("access_key").(string), + SecretKey: d.Get("secret_key").(string), + Profile: d.Get("profile").(string), + Token: d.Get("token").(string), + Region: d.Get("region").(string), + CredsFilename: d.Get("shared_credentials_file").(string), + DefaultTagsConfig: expandProviderDefaultTags(d.Get("default_tags").([]interface{})), + Endpoints: make(map[string]string), + MaxRetries: d.Get("max_retries").(int), + IgnoreTagsConfig: expandProviderIgnoreTags(d.Get("ignore_tags").([]interface{})), + Insecure: d.Get("insecure").(bool), + SkipCredsValidation: d.Get("skip_credentials_validation").(bool), + SkipGetEC2Platforms: d.Get("skip_get_ec2_platforms").(bool), + SkipRegionValidation: d.Get("skip_region_validation").(bool), + SkipRequestingAccountId: d.Get("skip_requesting_account_id").(bool), + SkipMetadataApiCheck: d.Get("skip_metadata_api_check").(bool), + S3ForcePathStyle: d.Get("s3_force_path_style").(bool), + terraformVersion: terraformVersion, + } + + if l, ok := d.Get("assume_role").([]interface{}); ok && len(l) > 0 && l[0] != nil { + m := l[0].(map[string]interface{}) + + if v, ok := m["duration_seconds"].(int); ok && v != 0 { + config.AssumeRoleDurationSeconds = v + } + + if v, ok := m["external_id"].(string); ok && v != "" { + config.AssumeRoleExternalID = v + } + + if v, ok := m["policy"].(string); ok && v != "" { + config.AssumeRolePolicy = v + } + + if policyARNSet, ok := m["policy_arns"].(*schema.Set); ok && policyARNSet.Len() > 0 { + for _, policyARNRaw := range policyARNSet.List() { + policyARN, ok := policyARNRaw.(string) + + if !ok { + continue + } + + config.AssumeRolePolicyARNs = append(config.AssumeRolePolicyARNs, policyARN) + } + } + + if v, ok := m["role_arn"].(string); ok && v != "" { + config.AssumeRoleARN = v + } + + if v, ok := m["session_name"].(string); ok && v != "" { + config.AssumeRoleSessionName = v + } + + if tagMapRaw, ok := m["tags"].(map[string]interface{}); ok && len(tagMapRaw) > 0 { + config.AssumeRoleTags = make(map[string]string) + + for k, vRaw := range tagMapRaw { + v, ok := vRaw.(string) + + if !ok { + continue + } + + config.AssumeRoleTags[k] = v + } + } + + if transitiveTagKeySet, ok := m["transitive_tag_keys"].(*schema.Set); ok && transitiveTagKeySet.Len() > 0 { + for _, transitiveTagKeyRaw := range transitiveTagKeySet.List() { + transitiveTagKey, ok := transitiveTagKeyRaw.(string) + + if !ok { + continue + } + + config.AssumeRoleTransitiveTagKeys = append(config.AssumeRoleTransitiveTagKeys, transitiveTagKey) + } + } + + log.Printf("[INFO] assume_role configuration set: (ARN: %q, SessionID: %q, ExternalID: %q)", config.AssumeRoleARN, config.AssumeRoleSessionName, config.AssumeRoleExternalID) + } + + endpointsSet := d.Get("endpoints").(*schema.Set) + + for _, endpointsSetI := range endpointsSet.List() { + endpoints := endpointsSetI.(map[string]interface{}) + for _, endpointServiceName := range endpointServiceNames { + config.Endpoints[endpointServiceName] = endpoints[endpointServiceName].(string) + } + } + + if v, ok := d.GetOk("allowed_account_ids"); ok { + for _, accountIDRaw := range v.(*schema.Set).List() { + config.AllowedAccountIds = append(config.AllowedAccountIds, accountIDRaw.(string)) + } + } + + if v, ok := d.GetOk("forbidden_account_ids"); ok { + for _, accountIDRaw := range v.(*schema.Set).List() { + config.ForbiddenAccountIds = append(config.ForbiddenAccountIds, accountIDRaw.(string)) + } + } + + return config.Client() +} + +// This is a global MutexKV for use within this plugin. +var awsMutexKV = mutexkv.NewMutexKV() + +func assumeRoleSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "duration_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: "Seconds to restrict the assume role session duration.", + }, + "external_id": { + Type: schema.TypeString, + Optional: true, + Description: "Unique identifier that might be required for assuming a role in another account.", + }, + "policy": { + Type: schema.TypeString, + Optional: true, + Description: "IAM Policy JSON describing further restricting permissions for the IAM Role being assumed.", + ValidateFunc: validation.StringIsJSON, + }, + "policy_arns": { + Type: schema.TypeSet, + Optional: true, + Description: "Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateArn, + }, + }, + "role_arn": { + Type: schema.TypeString, + Optional: true, + Description: "Amazon Resource Name of an IAM Role to assume prior to making API calls.", + ValidateFunc: validateArn, + }, + "session_name": { + Type: schema.TypeString, + Optional: true, + Description: "Identifier for the assumed role session.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "Assume role session tags.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "transitive_tag_keys": { + Type: schema.TypeSet, + Optional: true, + Description: "Assume role session tag keys to pass to any subsequent sessions.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + } +} + +func endpointsSchema() *schema.Schema { + endpointsAttributes := make(map[string]*schema.Schema) + + for _, endpointServiceName := range endpointServiceNames { + endpointsAttributes[endpointServiceName] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "", + Description: descriptions["endpoint"], + } + } + + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: endpointsAttributes, + }, + } +} + +func expandProviderDefaultTags(l []interface{}) *keyvaluetags.DefaultConfig { + if len(l) == 0 || l[0] == nil { + return nil + } + + defaultConfig := &keyvaluetags.DefaultConfig{} + m := l[0].(map[string]interface{}) + + if v, ok := m["tags"].(map[string]interface{}); ok { + defaultConfig.Tags = keyvaluetags.New(v) + } + return defaultConfig +} + +func expandProviderIgnoreTags(l []interface{}) *keyvaluetags.IgnoreConfig { + if len(l) == 0 || l[0] == nil { + return nil + } + + ignoreConfig := &keyvaluetags.IgnoreConfig{} + m := l[0].(map[string]interface{}) + + if v, ok := m["keys"].(*schema.Set); ok { + ignoreConfig.Keys = keyvaluetags.New(v.List()) + } + + if v, ok := m["key_prefixes"].(*schema.Set); ok { + ignoreConfig.KeyPrefixes = keyvaluetags.New(v.List()) + } + + return ignoreConfig +} + +// ReverseDns switches a DNS hostname to reverse DNS and vice-versa. +func ReverseDns(hostname string) string { + parts := strings.Split(hostname, ".") + + for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 { + parts[i], parts[j] = parts[j], parts[i] + } + + return strings.Join(parts, ".") +} diff --git a/resource_aws_lakeformation_policy_tag.go b/resource_aws_lakeformation_policy_tag.go new file mode 100644 index 000000000000..9ba5eee57ba8 --- /dev/null +++ b/resource_aws_lakeformation_policy_tag.go @@ -0,0 +1,177 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceAwsLakeFormationPolicyTag() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLakeFormationPolicyTagCreate, + Read: resourceAwsLakeFormationPolicyTagRead, + Update: resourceAwsLakeFormationPolicyTagUpdate, + Delete: resourceAwsLakeFormationPolicyTagDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "values": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 15, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 255), + validation.StringMatch(regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:\*\/=+\-@%]*)$`), ""), + ), + }, + Set: schema.HashString, + }, + "catalog_id": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAwsLakeFormationPolicyTagCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + tagKey := d.Get("key").(string) + tagValues := d.Get("values").(*schema.Set) + + var catalogID string + if v, ok := d.GetOk("catalog_id"); ok { + catalogID = v.(string) + } else { + catalogID = meta.(*AWSClient).accountid + } + + input := &lakeformation.CreateLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + TagValues: expandStringSet(tagValues), + } + + _, err := conn.CreateLFTag(input) + if err != nil { + return fmt.Errorf("Error creating Lake Formation Policy Tag: %w", err) + } + + d.SetId(fmt.Sprintf("%s:%s", catalogID, tagKey)) + + return resourceAwsLakeFormationPolicyTagRead(d, meta) +} + +func resourceAwsLakeFormationPolicyTagRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + catalogID, tagKey, err := readPolicyTagID(d.Id()) + if err != nil { + return err + } + + input := &lakeformation.GetLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + + output, err := conn.GetLFTag(input) + if err != nil { + if isAWSErr(err, lakeformation.ErrCodeEntityNotFoundException, "") { + log.Printf("[WARN] Lake Formation Policy Tag (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading Lake Formation Policy Tag: %s", err.Error()) + } + + d.Set("key", output.TagKey) + d.Set("values", flattenStringList(output.TagValues)) + d.Set("catalog_id", output.CatalogId) + + return nil +} + +func resourceAwsLakeFormationPolicyTagUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + catalogID, tagKey, err := readPolicyTagID(d.Id()) + if err != nil { + return err + } + + o, n := d.GetChange("values") + os := o.(*schema.Set) + ns := n.(*schema.Set) + toAdd := expandStringSet(ns.Difference(os)) + toDelete := expandStringSet(os.Difference(ns)) + + input := &lakeformation.UpdateLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + + if len(toAdd) > 0 { + input.TagValuesToAdd = toAdd + } + + if len(toDelete) > 0 { + input.TagValuesToDelete = toDelete + } + + _, err = conn.UpdateLFTag(input) + if err != nil { + return fmt.Errorf("Error updating Lake Formation Policy Tag (%s): %w", d.Id(), err) + } + + return resourceAwsLakeFormationPolicyTagRead(d, meta) +} + +func resourceAwsLakeFormationPolicyTagDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + catalogID, tagKey, err := readPolicyTagID(d.Id()) + if err != nil { + return err + } + + input := &lakeformation.DeleteLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + + _, err = conn.DeleteLFTag(input) + if err != nil { + return fmt.Errorf("Error deleting Lake Formation Policy Tag (%s): %w", d.Id(), err) + } + + return nil +} + +func readPolicyTagID(id string) (catalogID string, tagKey string, err error) { + idParts := strings.Split(id, ":") + if len(idParts) != 2 { + return "", "", fmt.Errorf("Unexpected format of ID (%q), expected CATALOG-ID:TAG-KEY", id) + } + return idParts[0], idParts[1], nil +} diff --git a/resource_aws_lakeformation_policy_tag_test.go b/resource_aws_lakeformation_policy_tag_test.go new file mode 100644 index 000000000000..2277aa05969a --- /dev/null +++ b/resource_aws_lakeformation_policy_tag_test.go @@ -0,0 +1,188 @@ +package aws + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAWSLakeFormationPolicyTag_basic(t *testing.T) { + resourceName := "aws_lakeformation_policy_tag.test" + rKey := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPolicyTagsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPolicyTagConfig_basic(rKey), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPolicyTagExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key", rKey), + resource.TestCheckResourceAttr(resourceName, "values.0", "value"), + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSLakeFormationPolicyTag_disappears(t *testing.T) { + resourceName := "aws_lakeformation_policy_tag.test" + rKey := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPolicyTagsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPolicyTagConfig_basic(rKey), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPolicyTagExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationPolicyTag(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSLakeFormationPolicyTag_values(t *testing.T) { + resourceName := "aws_lakeformation_policy_tag.test" + rKey := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPolicyTagsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPolicyTagConfig_values(rKey, []string{"value1", "value2"}), + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPolicyTagExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key", rKey), + resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test an update that adds, removes and retains a tag value + Config: testAccAWSLakeFormationPolicyTagConfig_values(rKey, []string{"value1", "value3"}), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPolicyTagExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key", rKey), + resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), + resource.TestCheckResourceAttr(resourceName, "values.1", "value3"), + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + ), + }, + }, + }) +} + +func testAccCheckAWSLakeFormationPolicyTagsDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lakeformation_policy_tag" { + continue + } + + catalogID, tagKey, err := readPolicyTagID(rs.Primary.ID) + if err != nil { + return err + } + + input := &lakeformation.GetLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + if _, err := conn.GetLFTag(input); err != nil { + if isAWSErr(err, lakeformation.ErrCodeEntityNotFoundException, "") { + continue + } + return err + } + return fmt.Errorf("Lake Formation Policy Tag (%s) still exists", rs.Primary.ID) + } + + return nil +} + +func testAccCheckAWSLakeFormationPolicyTagExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + catalogID, tagKey, err := readPolicyTagID(rs.Primary.ID) + if err != nil { + return err + } + + input := &lakeformation.GetLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + _, err = conn.GetLFTag(input) + + if err != nil { + return err + } + + return nil + } +} + +func testAccAWSLakeFormationPolicyTagConfig_basic(rKey string) string { + return fmt.Sprintf(` +resource "aws_lakeformation_policy_tag" "test" { + key = %[1]q + values = ["value"] +} +`, rKey) +} + +func testAccAWSLakeFormationPolicyTagConfig_values(rKey string, values []string) string { + quotedValues := make([]string, len(values)) + for i, v := range values { + quotedValues[i] = strconv.Quote(v) + } + + return fmt.Sprintf(` +resource "aws_lakeformation_policy_tag" "test" { + key = %[1]q + values = [%s] +} +`, rKey, strings.Join(quotedValues, ",")) +} diff --git a/website/docs/r/lakeformation_policy_tag.html.markdown b/website/docs/r/lakeformation_policy_tag.html.markdown new file mode 100644 index 000000000000..ba20eaac8cdd --- /dev/null +++ b/website/docs/r/lakeformation_policy_tag.html.markdown @@ -0,0 +1,43 @@ +--- +subcategory: "Lake Formation" +layout: "aws" +page_title: "AWS: aws_lakeformation_policy_tag" +description: |- + Creates a tag with the specified name and values. +--- + +# Resource: aws_lakeformation_policy_tag + +Creates a policy tag with the specified name and values. Each key must have at least one value. The maximum number of values permitted is 15. + +## Example Usage + +```terraform +resource "aws_lakeformation_policy_tag" "example" { + key = "module" + values = ["Orders", "Sales", "Customers"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `catalog_id` - (Optional) ID of the Data Catalog to create the tag in. If omitted, this defaults to the AWS Account ID. +* `key` - (Required) The key-name for the tag. +* `values` - (Required) A list of possible values an attribute can take. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Catalog ID and key-name of the tag + +## Import + +Lake Formation Policy Tags can be imported using the `catalog_id:key`. If you have not set a Catalog ID specify the AWS Account ID that the database is in, e.g. + +``` +$ terraform import aws_lakeformation_policy_tag.example 123456789012:some_key +``` + From aa5fef7b8da846d8149dab9989f0e68a17ca7699 Mon Sep 17 00:00:00 2001 From: Daniel Messias Date: Tue, 25 May 2021 22:55:21 +0100 Subject: [PATCH 02/10] New resource: aws_lakeformation_permissions --- .changelog/19648.txt | 4 + ...ta_source_aws_lakeformation_permissions.go | 462 +++++ ...urce_aws_lakeformation_permissions_test.go | 663 ++++++++ aws/provider.go | 2 +- aws/resource_aws_lakeformation_lf_tag.go | 182 ++ aws/resource_aws_lakeformation_lf_tag_test.go | 211 +++ aws/resource_aws_lakeformation_permissions.go | 1301 ++++++++++++++ ...urce_aws_lakeformation_permissions_test.go | 1500 +++++++++++++++++ aws/resource_aws_lakeformation_test.go | 59 + resource_aws_lakeformation_policy_tag.go | 61 +- resource_aws_lakeformation_policy_tag_test.go | 83 +- .../d/lakeformation_permissions.html.markdown | 53 +- ...down => lakeformation_lf_tag.html.markdow} | 12 +- .../r/lakeformation_permissions.html.markdown | 60 +- 14 files changed, 4586 insertions(+), 67 deletions(-) create mode 100644 .changelog/19648.txt create mode 100644 aws/data_source_aws_lakeformation_permissions.go create mode 100644 aws/data_source_aws_lakeformation_permissions_test.go create mode 100644 aws/resource_aws_lakeformation_lf_tag.go create mode 100644 aws/resource_aws_lakeformation_lf_tag_test.go create mode 100644 aws/resource_aws_lakeformation_permissions.go create mode 100644 aws/resource_aws_lakeformation_permissions_test.go create mode 100644 aws/resource_aws_lakeformation_test.go rename website/docs/r/{lakeformation_policy_tag.html.markdown => lakeformation_lf_tag.html.markdow} (57%) diff --git a/.changelog/19648.txt b/.changelog/19648.txt new file mode 100644 index 000000000000..6c734dd5da32 --- /dev/null +++ b/.changelog/19648.txt @@ -0,0 +1,4 @@ +```release-note:note:enhancement +resource/aws_lakeformation_permissions: Add LF-tag support +datasource/aws_lakeformation_permissions: Add LF-tag support +``` \ No newline at end of file diff --git a/aws/data_source_aws_lakeformation_permissions.go b/aws/data_source_aws_lakeformation_permissions.go new file mode 100644 index 000000000000..1f362bc1f96d --- /dev/null +++ b/aws/data_source_aws_lakeformation_permissions.go @@ -0,0 +1,462 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func dataSourceAwsLakeFormationPermissions() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsLakeFormationPermissionsRead, + + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateAwsAccountId, + }, + "catalog_resource": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "data_location": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + }, + }, + }, + "database": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "permissions": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "permissions_with_grant_option": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "lf_tag": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "values": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 15, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateLFTagValues(), + }, + Set: schema.HashString, + }, + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "lf_tag_policy": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "expression": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "values": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 15, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateLFTagValues(), + }, + Set: schema.HashString, + }, + }, + }, + }, + "resource_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(lakeformation.ResourceType_Values(), false), + }, + }, + }, + }, + "principal": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validatePrincipal, + }, + "table": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "database_name": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "wildcard": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + "table_with_columns": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "column_names": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + "database_name": { + Type: schema.TypeString, + Required: true, + }, + "excluded_column_names": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "wildcard": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + input := &lakeformation.ListPermissionsInput{ + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), + }, + Resource: &lakeformation.Resource{}, + } + + if v, ok := d.GetOk("catalog_id"); ok { + input.CatalogId = aws.String(v.(string)) + } + + if _, ok := d.GetOk("catalog_resource"); ok { + input.Resource.Catalog = expandLakeFormationCatalogResource() + } + + if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + + tableType := "" + + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) + tableType = TableTypeTable + } + + if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + // can't ListPermissions for TableWithColumns, so use Table instead + input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(v.([]interface{})[0].(map[string]interface{})) + tableType = TableTypeTableWithColumns + } + + log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) + var allPermissions []*lakeformation.PrincipalResourcePermissions + + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue + } + + allPermissions = append(allPermissions, permission) + } + return !lastPage + }) + + if err != nil { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(fmt.Errorf("error reading Lake Formation Permissions: %w", err)) + } + return nil + }) + + if tfresource.TimedOut(err) { + err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue + } + + allPermissions = append(allPermissions, permission) + } + return !lastPage + }) + } + + d.SetId(fmt.Sprintf("%d", hashcode.String(input.String()))) + + if err != nil { + return fmt.Errorf("error reading Lake Formation permissions: %w", err) + } + + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + if input.Resource.Catalog != nil { + cleanPermissions = filterLakeFormationCatalogPermissions(allPermissions) + } + + if input.Resource.DataLocation != nil { + cleanPermissions = filterLakeFormationDataLocationPermissions(allPermissions) + } + + if input.Resource.Database != nil { + cleanPermissions = filterLakeFormationDatabasePermissions(allPermissions) + } + + if input.Resource.LFTag != nil { + cleanPermissions = filterLakeFormationLFTagPermissions(allPermissions) + } + + if input.Resource.LFTagPolicy != nil { + cleanPermissions = filterLakeFormationLFTagPolicyPermissions(allPermissions) + } + + if tableType == TableTypeTable { + cleanPermissions = filterLakeFormationTablePermissions( + aws.StringValue(input.Resource.Table.Name), + input.Resource.Table.TableWildcard != nil, + allPermissions, + ) + } + + if tableType == TableTypeTableWithColumns { + cleanPermissions = filterLakeFormationTableWithColumnsPermissions( + d.Get("table_with_columns.0.database_name").(string), + d.Get("table_with_columns.0.wildcard").(bool), + expandStringList(d.Get("table_with_columns.0.column_names").([]interface{})), + expandStringList(d.Get("table_with_columns.0.excluded_column_names").([]interface{})), + allPermissions, + ) + } + + if len(cleanPermissions) != len(allPermissions) { + log.Printf("[INFO] Resource Lake Formation clean permissions (%d) and all permissions (%d) have different lengths (this is not necessarily a problem): %s", len(cleanPermissions), len(allPermissions), d.Id()) + } + + d.Set("principal", cleanPermissions[0].Principal.DataLakePrincipalIdentifier) + d.Set("permissions", flattenLakeFormationPermissions(cleanPermissions)) + d.Set("permissions_with_grant_option", flattenLakeFormationGrantPermissions(cleanPermissions)) + + if cleanPermissions[0].Resource.Catalog != nil { + d.Set("catalog_resource", true) + } + + if cleanPermissions[0].Resource.DataLocation != nil { + if err := d.Set("data_location", []interface{}{flattenLakeFormationDataLocationResource(cleanPermissions[0].Resource.DataLocation)}); err != nil { + return fmt.Errorf("error setting data_location: %w", err) + } + } else { + d.Set("data_location", nil) + } + + if cleanPermissions[0].Resource.Database != nil { + if err := d.Set("database", []interface{}{flattenLakeFormationDatabaseResource(cleanPermissions[0].Resource.Database)}); err != nil { + return fmt.Errorf("error setting database: %w", err) + } + } else { + d.Set("database", nil) + } + + if cleanPermissions[0].Resource.LFTag != nil { + if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { + return fmt.Errorf("error setting LF-tag: %w", err) + } + } else { + d.Set("lf_tag", nil) + } + + if cleanPermissions[0].Resource.LFTagPolicy != nil { + if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { + return fmt.Errorf("error setting LF-tag policy: %w", err) + } + } else { + d.Set("lf_tag_policy", nil) + } + + tableSet := false + + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { + // since perm list could include TableWithColumns, get the right one + for _, perm := range cleanPermissions { + if perm.Resource.Table != nil { + if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { + return fmt.Errorf("error setting table: %w", err) + } + tableSet = true + break + } + } + } + + if !tableSet { + d.Set("table", nil) + } + + twcSet := false + + if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 { + // since perm list could include Table, get the right one + for _, perm := range cleanPermissions { + if perm.Resource.TableWithColumns != nil { + if err := d.Set("table_with_columns", []interface{}{flattenLakeFormationTableWithColumnsResource(perm.Resource.TableWithColumns)}); err != nil { + return fmt.Errorf("error setting table_with_columns: %w", err) + } + twcSet = true + break + } + } + } + + if !twcSet { + d.Set("table_with_columns", nil) + } + + return nil +} diff --git a/aws/data_source_aws_lakeformation_permissions_test.go b/aws/data_source_aws_lakeformation_permissions_test.go new file mode 100644 index 000000000000..841cfd772e94 --- /dev/null +++ b/aws/data_source_aws_lakeformation_permissions_test.go @@ -0,0 +1,663 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func testAccAWSLakeFormationPermissionsDataSource_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "catalog_resource", dataSourceName, "catalog_resource"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_dataLocation(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_dataLocation(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.#", dataSourceName, "data_location.#"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", dataSourceName, "data_location.0.arn"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_database(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_database(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "database.#", dataSourceName, "database.#"), + resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dataSourceName, "database.0.name"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.2", dataSourceName, "permissions.2"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_lf_tag(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.#", dataSourceName, "lf_tag.#"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.key", dataSourceName, "lf_tag.0.key"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.values", dataSourceName, "lf_tag.0.values"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.1", dataSourceName, "permissions_with_grant_option.1"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_lf_tag_policy(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag_policy(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.#", dataSourceName, "lf_tag_policy.#"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.resource_type", dataSourceName, "lf_tag_policy.0.resource_type"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.#", dataSourceName, "lf_tag_policy.0.expression.#"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.key", dataSourceName, "lf_tag_policy.0.expression.0.key"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.values", dataSourceName, "lf_tag_policy.0.expression.0.values"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.2", dataSourceName, "permissions.2"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_table(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_table(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "table.#", dataSourceName, "table.#"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", dataSourceName, "table.0.database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", dataSourceName, "table.0.name"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_tableWithColumns(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_tableWithColumns(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.#", dataSourceName, "table_with_columns.#"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", dataSourceName, "table_with_columns.0.database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", dataSourceName, "table_with_columns.0.name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.#", dataSourceName, "table_with_columns.0.column_names.#"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.0", dataSourceName, "table_with_columns.0.column_names.0"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.1", dataSourceName, "table_with_columns.0.column_names.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSourceConfig_basic(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < 0 { + input.TagValuesToAdd = toAdd + } + + if len(toDelete) > 0 { + input.TagValuesToDelete = toDelete + } + + _, err = conn.UpdateLFTag(input) + if err != nil { + return fmt.Errorf("Error updating Lake Formation LF-Tag (%s): %w", d.Id(), err) + } + + return resourceAwsLakeFormationLFTagRead(d, meta) +} + +func resourceAwsLakeFormationLFTagDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + catalogID, tagKey, err := readLFTagID(d.Id()) + if err != nil { + return err + } + + input := &lakeformation.DeleteLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + + _, err = conn.DeleteLFTag(input) + if err != nil { + return fmt.Errorf("Error deleting Lake Formation LF-Tag (%s): %w", d.Id(), err) + } + + return nil +} + +func readLFTagID(id string) (catalogID string, tagKey string, err error) { + idParts := strings.Split(id, ":") + if len(idParts) != 2 { + return "", "", fmt.Errorf("Unexpected format of ID (%q), expected CATALOG-ID:TAG-KEY", id) + } + return idParts[0], idParts[1], nil +} + +func validateLFTagValues() schema.SchemaValidateFunc { + return validation.All( + validation.StringLenBetween(1, 255), + validation.StringMatch(regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:\*\/=+\-@%]*)$`), ""), + ) +} diff --git a/aws/resource_aws_lakeformation_lf_tag_test.go b/aws/resource_aws_lakeformation_lf_tag_test.go new file mode 100644 index 000000000000..aabf5488da10 --- /dev/null +++ b/aws/resource_aws_lakeformation_lf_tag_test.go @@ -0,0 +1,211 @@ +package aws + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func testAccAWSLakeFormationLFTag_basic(t *testing.T) { + resourceName := "aws_lakeformation_lf_tag.test" + rKey := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationLFTagConfig_basic(rKey), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationLFTagExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key", rKey), + resource.TestCheckResourceAttr(resourceName, "values.0", "value"), + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAWSLakeFormationLFTag_disappears(t *testing.T) { + resourceName := "aws_lakeformation_lf_tag.test" + rKey := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationLFTagConfig_basic(rKey), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationLFTagExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationLFTag(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAWSLakeFormationLFTag_values(t *testing.T) { + resourceName := "aws_lakeformation_lf_tag.test" + rKey := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationLFTagConfig_values(rKey, []string{"value1", "value2"}), + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationLFTagExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key", rKey), + resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test an update that adds, removes and retains a tag value + Config: testAccAWSLakeFormationLFTagConfig_values(rKey, []string{"value1", "value3"}), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationLFTagExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "key", rKey), + resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), + resource.TestCheckResourceAttr(resourceName, "values.1", "value3"), + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + ), + }, + }, + }) +} + +func testAccCheckAWSLakeFormationLFTagsDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lakeformation_lf_tag" { + continue + } + + catalogID, tagKey, err := readLFTagID(rs.Primary.ID) + if err != nil { + return err + } + + input := &lakeformation.GetLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + + if _, err := conn.GetLFTag(input); err != nil { + if isAWSErr(err, lakeformation.ErrCodeEntityNotFoundException, "") { + continue + } + // If the lake formation admin has been revoked, there will be access denied instead of entity not found + if isAWSErr(err, lakeformation.ErrCodeAccessDeniedException, "") { + continue + } + return err + } + return fmt.Errorf("Lake Formation LF-Tag (%s) still exists", rs.Primary.ID) + } + + return nil +} + +func testAccCheckAWSLakeFormationLFTagExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + catalogID, tagKey, err := readLFTagID(rs.Primary.ID) + if err != nil { + return err + } + + input := &lakeformation.GetLFTagInput{ + CatalogId: aws.String(catalogID), + TagKey: aws.String(tagKey), + } + + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + _, err = conn.GetLFTag(input) + + if err != nil { + return err + } + + return nil + } +} + +func testAccAWSLakeFormationLFTagConfig_basic(rKey string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_caller_identity.current.arn] +} + +resource "aws_lakeformation_lf_tag" "test" { + key = %[1]q + values = ["value"] + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rKey) +} + +func testAccAWSLakeFormationLFTagConfig_values(rKey string, values []string) string { + quotedValues := make([]string, len(values)) + for i, v := range values { + quotedValues[i] = strconv.Quote(v) + } + + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_caller_identity.current.arn] +} + +resource "aws_lakeformation_lf_tag" "test" { + key = %[1]q + values = [%s] + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rKey, strings.Join(quotedValues, ",")) +} diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go new file mode 100644 index 000000000000..914a7962a5df --- /dev/null +++ b/aws/resource_aws_lakeformation_permissions.go @@ -0,0 +1,1301 @@ +package aws + +import ( + "fmt" + "log" + "reflect" + "sort" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func resourceAwsLakeFormationPermissions() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLakeFormationPermissionsCreate, + Read: resourceAwsLakeFormationPermissionsRead, + Update: resourceAwsLakeFormationPermissionsCreate, + Delete: resourceAwsLakeFormationPermissionsDelete, + + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + ValidateFunc: validateAwsAccountId, + }, + "catalog_resource": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ExactlyOneOf: []string{ + "catalog_resource", + "data_location", + "database", + "lf_tag", + "lf_tag_policy", + "table", + "table_with_columns", + }, + }, + "data_location": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + ExactlyOneOf: []string{ + "catalog_resource", + "data_location", + "database", + "lf_tag", + "lf_tag_policy", + "table", + "table_with_columns", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + }, + }, + }, + "database": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + ExactlyOneOf: []string{ + "catalog_resource", + "data_location", + "database", + "lf_tag", + "lf_tag_policy", + "table", + "table_with_columns", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "lf_tag": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + ExactlyOneOf: []string{ + "catalog_resource", + "data_location", + "database", + "lf_tag", + "lf_tag_policy", + "table", + "table_with_columns", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "values": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 15, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateLFTagValues(), + }, + Set: schema.HashString, + }, + "catalog_id": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + }, + }, + }, + }, + "lf_tag_policy": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + ExactlyOneOf: []string{ + "catalog_resource", + "data_location", + "database", + "lf_tag", + "lf_tag_policy", + "table", + "table_with_columns", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "expression": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "values": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 15, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateLFTagValues(), + }, + Set: schema.HashString, + }, + }, + }, + }, + "resource_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(lakeformation.ResourceType_Values(), false), + }, + }, + }, + }, + "permissions": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validatePermissions(), + }, + }, + "permissions_with_grant_option": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validatePermissions(), + }, + }, + "principal": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validatePrincipal, + }, + "table": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + ExactlyOneOf: []string{ + "catalog_resource", + "data_location", + "database", + "lf_tag", + "lf_tag_policy", + "table", + "table_with_columns", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "database_name": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + AtLeastOneOf: []string{ + "table.0.name", + "table.0.wildcard", + }, + }, + "wildcard": { + Type: schema.TypeBool, + Optional: true, + Default: false, + AtLeastOneOf: []string{ + "table.0.name", + "table.0.wildcard", + }, + }, + }, + }, + }, + "table_with_columns": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + ExactlyOneOf: []string{ + "catalog_resource", + "data_location", + "database", + "lf_tag", + "lf_tag_policy", + "table", + "table_with_columns", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "column_names": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + AtLeastOneOf: []string{ + "table_with_columns.0.column_names", + "table_with_columns.0.wildcard", + }, + }, + "database_name": { + Type: schema.TypeString, + Required: true, + }, + "excluded_column_names": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "wildcard": { + Type: schema.TypeBool, + Optional: true, + Default: false, + AtLeastOneOf: []string{ + "table_with_columns.0.column_names", + "table_with_columns.0.wildcard", + }, + }, + }, + }, + }, + }, + } +} + +// The challenges with Lake Formation permissions are many. These are largely undocumented and +// discovered through trial and error. These are specific problems discovered thus far: +// 1. Implicit permissions granted by Lake Formation to data lake administrators are indistinguishable +// from explicit permissions. However, implicit permissions cannot be changed, revoked, or narrowed. +// 2. One set of permissions for one LF Resource going in, can come back from AWS as multiple sets of +// permissions for multiple LF Resources (e.g., SELECT, Table, TableWithColumns). +// 3. Valid permissions for a Table LF resource can come back in TableWithColumns and vice versa. + +// For 2 & 3, some peeking at the config (i.e., d.Get()) is necessary to filter the permissions AWS +// returns. + +func resourceAwsLakeFormationPermissionsCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + input := &lakeformation.GrantPermissionsInput{ + Permissions: expandStringList(d.Get("permissions").([]interface{})), + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), + }, + Resource: &lakeformation.Resource{}, + } + + if v, ok := d.GetOk("catalog_id"); ok { + input.CatalogId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("permissions_with_grant_option"); ok { + input.PermissionsWithGrantOption = expandStringList(v.([]interface{})) + } + + if _, ok := d.GetOk("catalog_resource"); ok { + input.Resource.Catalog = expandLakeFormationCatalogResource() + } + + if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.TableWithColumns = expandLakeFormationTableWithColumnsResource(v.([]interface{})[0].(map[string]interface{})) + } + + var output *lakeformation.GrantPermissionsOutput + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + var err error + output, err = conn.GrantPermissions(input) + if err != nil { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { + return resource.RetryableError(err) + } + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Grantee has no permissions") { + return resource.RetryableError(err) + } + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "register the S3 path") { + return resource.RetryableError(err) + } + if tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeConcurrentModificationException) { + return resource.RetryableError(err) + } + if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access requested permissions") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("error creating Lake Formation Permissions: %w", err)) + } + return nil + }) + + if tfresource.TimedOut(err) { + output, err = conn.GrantPermissions(input) + } + + if err != nil { + return fmt.Errorf("error creating Lake Formation Permissions (input: %v): %w", input, err) + } + + if output == nil { + return fmt.Errorf("error creating Lake Formation Permissions: empty response") + } + + d.SetId(fmt.Sprintf("%d", hashcode.String(input.String()))) + + return resourceAwsLakeFormationPermissionsRead(d, meta) +} + +func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + input := &lakeformation.ListPermissionsInput{ + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), + }, + Resource: &lakeformation.Resource{}, + } + + if v, ok := d.GetOk("catalog_id"); ok { + input.CatalogId = aws.String(v.(string)) + } + + if _, ok := d.GetOk("catalog_resource"); ok { + input.Resource.Catalog = expandLakeFormationCatalogResource() + } + + if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + + tableType := "" + + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) + tableType = TableTypeTable + } + + if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + // can't ListPermissions for TableWithColumns, so use Table instead + input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(v.([]interface{})[0].(map[string]interface{})) + tableType = TableTypeTableWithColumns + } + + log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) + var allPermissions []*lakeformation.PrincipalResourcePermissions + + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue + } + + allPermissions = append(allPermissions, permission) + } + return !lastPage + }) + + if err != nil { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(fmt.Errorf("error reading Lake Formation Permissions: %w", err)) + } + return nil + }) + + if tfresource.TimedOut(err) { + err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue + } + + allPermissions = append(allPermissions, permission) + } + return !lastPage + }) + } + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) { + log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if !d.IsNewResource() && tfawserr.ErrMessageContains(err, "AccessDeniedException", "Resource does not exist") { + log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state: %s", d.Id(), err) + d.SetId("") + return nil + } + + if !d.IsNewResource() && len(allPermissions) == 0 { + log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state (0 permissions)", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading Lake Formation permissions: %w", err) + } + + // clean permissions = filter out permissions that do not pertain to this specific resource + + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + if input.Resource.Catalog != nil { + cleanPermissions = filterLakeFormationCatalogPermissions(allPermissions) + } + + if input.Resource.DataLocation != nil { + cleanPermissions = filterLakeFormationDataLocationPermissions(allPermissions) + } + + if input.Resource.Database != nil { + cleanPermissions = filterLakeFormationDatabasePermissions(allPermissions) + } + + if input.Resource.LFTag != nil { + cleanPermissions = filterLakeFormationLFTagPermissions(allPermissions) + } + + if input.Resource.LFTagPolicy != nil { + cleanPermissions = filterLakeFormationLFTagPolicyPermissions(allPermissions) + } + + if tableType == TableTypeTable { + cleanPermissions = filterLakeFormationTablePermissions( + aws.StringValue(input.Resource.Table.Name), + input.Resource.Table.TableWildcard != nil, + allPermissions, + ) + } + + if tableType == TableTypeTableWithColumns { + cleanPermissions = filterLakeFormationTableWithColumnsPermissions( + d.Get("table_with_columns.0.database_name").(string), + d.Get("table_with_columns.0.wildcard").(bool), + expandStringList(d.Get("table_with_columns.0.column_names").([]interface{})), + expandStringList(d.Get("table_with_columns.0.excluded_column_names").([]interface{})), + allPermissions, + ) + } + + if len(cleanPermissions) == 0 { + log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if len(cleanPermissions) != len(allPermissions) { + log.Printf("[INFO] Resource Lake Formation clean permissions (%d) and all permissions (%d) have different lengths (this is not necessarily a problem): %s", len(cleanPermissions), len(allPermissions), d.Id()) + } + + d.Set("principal", cleanPermissions[0].Principal.DataLakePrincipalIdentifier) + d.Set("permissions", flattenLakeFormationPermissions(cleanPermissions)) + d.Set("permissions_with_grant_option", flattenLakeFormationGrantPermissions(cleanPermissions)) + + if cleanPermissions[0].Resource.Catalog != nil { + d.Set("catalog_resource", true) + } + + if cleanPermissions[0].Resource.DataLocation != nil { + if err := d.Set("data_location", []interface{}{flattenLakeFormationDataLocationResource(cleanPermissions[0].Resource.DataLocation)}); err != nil { + return fmt.Errorf("error setting data_location: %w", err) + } + } else { + d.Set("data_location", nil) + } + + if cleanPermissions[0].Resource.Database != nil { + if err := d.Set("database", []interface{}{flattenLakeFormationDatabaseResource(cleanPermissions[0].Resource.Database)}); err != nil { + return fmt.Errorf("error setting database: %w", err) + } + } else { + d.Set("database", nil) + } + + if cleanPermissions[0].Resource.LFTag != nil { + if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { + return fmt.Errorf("error setting LF-Tag: %w", err) + } + } else { + d.Set("lf_tag", nil) + } + + if cleanPermissions[0].Resource.LFTagPolicy != nil { + if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { + return fmt.Errorf("error setting LF-Tag policy: %w", err) + } + } else { + d.Set("lf_tag_policy", nil) + } + + tableSet := false + + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { + // since perm list could include TableWithColumns, get the right one + for _, perm := range cleanPermissions { + if perm.Resource.Table != nil { + if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { + return fmt.Errorf("error setting table: %w", err) + } + tableSet = true + break + } + } + } + + if !tableSet { + d.Set("table", nil) + } + + twcSet := false + + if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 { + // since perm list could include Table, get the right one + for _, perm := range cleanPermissions { + if perm.Resource.TableWithColumns != nil { + if err := d.Set("table_with_columns", []interface{}{flattenLakeFormationTableWithColumnsResource(perm.Resource.TableWithColumns)}); err != nil { + return fmt.Errorf("error setting table_with_columns: %w", err) + } + twcSet = true + break + } + } + } + + if !twcSet { + d.Set("table_with_columns", nil) + } + + return nil +} + +func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + input := &lakeformation.RevokePermissionsInput{ + Permissions: expandStringList(d.Get("permissions").([]interface{})), + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), + }, + Resource: &lakeformation.Resource{}, + } + + if v, ok := d.GetOk("catalog_id"); ok { + input.CatalogId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("permissions_with_grant_option"); ok { + input.PermissionsWithGrantOption = expandStringList(v.([]interface{})) + } + + if _, ok := d.GetOk("catalog_resource"); ok { + input.Resource.Catalog = expandLakeFormationCatalogResource() + } + + if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.TableWithColumns = expandLakeFormationTableWithColumnsResource(v.([]interface{})[0].(map[string]interface{})) + } + + if input.Resource == nil || reflect.DeepEqual(input.Resource, &lakeformation.Resource{}) { + // if resource is empty, don't delete = it won't delete anything since this is the predicate + log.Printf("[WARN] No Lake Formation Resource with permissions to revoke") + return nil + } + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var err error + _, err = conn.RevokePermissions(input) + if err != nil { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "register the S3 path") { + return resource.RetryableError(err) + } + if tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeConcurrentModificationException) { + return resource.RetryableError(err) + } + if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access requested permissions") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("unable to revoke Lake Formation Permissions: %w", err)) + } + return nil + }) + + if tfresource.TimedOut(err) { + _, err = conn.RevokePermissions(input) + } + + if err != nil { + return fmt.Errorf("unable to revoke LakeFormation Permissions (input: %v): %w", input, err) + } + + return nil +} + +const ( + TableNameAllTables = "ALL_TABLES" + TableTypeTable = "Table" + TableTypeTableWithColumns = "TableWithColumns" +) + +func filterLakeFormationTablePermissions(tableName string, tableWildcard bool, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + // CREATE PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on Table, Name = (Table Name) + // LIST PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name) + // LIST PERMS = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard + + // CREATE PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on Table, TableWildcard + // LIST PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, TableWildcard, Name = ALL_TABLES + // LIST PERMS = SELECT on TableWithColumns, Name = ALL_TABLES, ColumnWildcard + + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil { + if aws.StringValue(perm.Resource.TableWithColumns.Name) == tableName || (tableWildcard && aws.StringValue(perm.Resource.TableWithColumns.Name) == TableNameAllTables) { + if len(perm.Permissions) > 0 && aws.StringValue(perm.Permissions[0]) == lakeformation.PermissionSelect { + cleanPermissions = append(cleanPermissions, perm) + continue + } + + if len(perm.PermissionsWithGrantOption) > 0 && aws.StringValue(perm.PermissionsWithGrantOption[0]) == lakeformation.PermissionSelect { + cleanPermissions = append(cleanPermissions, perm) + continue + } + } + } + + if perm.Resource.Table != nil { + if aws.StringValue(perm.Resource.Table.Name) == tableName { + cleanPermissions = append(cleanPermissions, perm) + continue + } + + if perm.Resource.Table.TableWildcard != nil && tableWildcard { + cleanPermissions = append(cleanPermissions, perm) + continue + } + } + continue + } + + return cleanPermissions +} + +func filterLakeFormationTableWithColumnsPermissions(tableName string, columnWildcard bool, columnNames []*string, excludedColumnNames []*string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + // CREATE PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard + // LIST PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name) + // LIST PERMS = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard + + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnNames != nil { + if StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnNames, columnNames) { + cleanPermissions = append(cleanPermissions, perm) + continue + } + } + + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil && (columnWildcard || len(excludedColumnNames) > 0) { + if (perm.Resource.TableWithColumns.ColumnWildcard.ExcludedColumnNames == nil && len(excludedColumnNames) == 0) || StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnWildcard.ExcludedColumnNames, excludedColumnNames) { + cleanPermissions = append(cleanPermissions, perm) + continue + } + } + + if perm.Resource.Table != nil && aws.StringValue(perm.Resource.Table.Name) == tableName { + cleanPermissions = append(cleanPermissions, perm) + continue + } + } + + return cleanPermissions +} + +func filterLakeFormationCatalogPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if perm.Resource.Catalog != nil { + cleanPermissions = append(cleanPermissions, perm) + } + } + + return cleanPermissions +} + +func filterLakeFormationDataLocationPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if perm.Resource.DataLocation != nil { + cleanPermissions = append(cleanPermissions, perm) + } + } + + return cleanPermissions +} + +func filterLakeFormationDatabasePermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if perm.Resource.Database != nil { + cleanPermissions = append(cleanPermissions, perm) + } + } + + return cleanPermissions +} + +func filterLakeFormationLFTagPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if perm.Resource.LFTag != nil { + cleanPermissions = append(cleanPermissions, perm) + } + } + + return cleanPermissions +} + +func filterLakeFormationLFTagPolicyPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if perm.Resource.LFTagPolicy != nil { + cleanPermissions = append(cleanPermissions, perm) + } + } + + return cleanPermissions +} + +func expandLakeFormationCatalogResource() *lakeformation.CatalogResource { + return &lakeformation.CatalogResource{} +} + +func expandLakeFormationDataLocationResource(tfMap map[string]interface{}) *lakeformation.DataLocationResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.DataLocationResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["arn"].(string); ok && v != "" { + apiObject.ResourceArn = aws.String(v) + } + + return apiObject +} + +func flattenLakeFormationDataLocationResource(apiObject *lakeformation.DataLocationResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.ResourceArn; v != nil { + tfMap["arn"] = aws.StringValue(v) + } + + return tfMap +} + +func expandLakeFormationDatabaseResource(tfMap map[string]interface{}) *lakeformation.DatabaseResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.DatabaseResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + return apiObject +} + +func flattenLakeFormationDatabaseResource(apiObject *lakeformation.DatabaseResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + +func expandLakeFormationLFTagPolicyResource(tfMap map[string]interface{}) *lakeformation.LFTagPolicyResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.LFTagPolicyResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["expression"]; ok && v != nil { + apiObject.Expression = expandLakeFormationLFTagExpression(v.([]interface{})) + } + + if v, ok := tfMap["resource_type"].(string); ok && v != "" { + apiObject.ResourceType = aws.String(v) + } + + return apiObject +} + +func expandLakeFormationLFTagExpression(expression []interface{}) []*lakeformation.LFTag { + tagSlice := []*lakeformation.LFTag{} + for _, element := range expression { + elementMap := element.(map[string]interface{}) + + tag := &lakeformation.LFTag{ + TagKey: aws.String(elementMap["key"].(string)), + TagValues: expandStringSet(elementMap["values"].(*schema.Set)), + } + + tagSlice = append(tagSlice, tag) + } + + return tagSlice +} + +func flattenLakeFormationLFTagPolicyResource(apiObject *lakeformation.LFTagPolicyResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.Expression; v != nil { + tfMap["expression"] = flattenLakeFormationLFTagExpression(v) + } + + if v := apiObject.ResourceType; v != nil { + tfMap["resource_type"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenLakeFormationLFTagExpression(ts []*lakeformation.LFTag) []map[string]interface{} { + tagSlice := make([]map[string]interface{}, len(ts)) + if len(ts) > 0 { + for i, t := range ts { + tag := make(map[string]interface{}) + + if v := aws.StringValue(t.TagKey); v != "" { + tag["key"] = v + } + + if v := flattenStringList(t.TagValues); v != nil { + tag["values"] = v + } + + tagSlice[i] = tag + } + } + + return tagSlice +} + +func expandLakeFormationLFTagKeyResource(tfMap map[string]interface{}) *lakeformation.LFTagKeyResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.LFTagKeyResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["key"].(string); ok && v != "" { + apiObject.TagKey = aws.String(v) + } + + if v, ok := tfMap["values"].(*schema.Set); ok && v != nil { + apiObject.TagValues = expandStringSet(v) + } + + return apiObject +} + +func flattenLakeFormationLFTagKeyResource(apiObject *lakeformation.LFTagKeyResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.TagKey; v != nil { + tfMap["key"] = aws.StringValue(v) + } + + if v := apiObject.TagValues; v != nil { + tfMap["values"] = flattenStringSet(v) + } + + return tfMap +} + +func expandLakeFormationTableResource(tfMap map[string]interface{}) *lakeformation.TableResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.TableResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["database_name"].(string); ok && v != "" { + apiObject.DatabaseName = aws.String(v) + } + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + if v, ok := tfMap["wildcard"].(bool); ok && v { + apiObject.TableWildcard = &lakeformation.TableWildcard{} + } + + return apiObject +} + +func expandLakeFormationTableWithColumnsResourceAsTable(tfMap map[string]interface{}) *lakeformation.TableResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.TableResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["database_name"].(string); ok && v != "" { + apiObject.DatabaseName = aws.String(v) + } + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + return apiObject +} + +func flattenLakeFormationTableResource(apiObject *lakeformation.TableResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.DatabaseName; v != nil { + tfMap["database_name"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil { + if aws.StringValue(v) != TableNameAllTables || apiObject.TableWildcard == nil { + tfMap["name"] = aws.StringValue(v) + } + } + + if v := apiObject.TableWildcard; v != nil { + tfMap["wildcard"] = true + } + + return tfMap +} + +func expandLakeFormationTableWithColumnsResource(tfMap map[string]interface{}) *lakeformation.TableWithColumnsResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.TableWithColumnsResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["column_names"]; ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + apiObject.ColumnNames = expandStringList(v.([]interface{})) + } + + if v, ok := tfMap["database_name"].(string); ok && v != "" { + apiObject.DatabaseName = aws.String(v) + } + + if v, ok := tfMap["excluded_column_names"]; ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + apiObject.ColumnWildcard = &lakeformation.ColumnWildcard{ + ExcludedColumnNames: expandStringList(v.([]interface{})), + } + } + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + if v, ok := tfMap["wildcard"].(bool); ok && v && apiObject.ColumnWildcard == nil { + apiObject.ColumnWildcard = &lakeformation.ColumnWildcard{} + } + + return apiObject +} + +func flattenLakeFormationTableWithColumnsResource(apiObject *lakeformation.TableWithColumnsResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + tfMap["column_names"] = flattenStringList(apiObject.ColumnNames) + + if v := apiObject.DatabaseName; v != nil { + tfMap["database_name"] = aws.StringValue(v) + } + + if v := apiObject.ColumnWildcard; v != nil { + tfMap["wildcard"] = true + tfMap["excluded_column_names"] = flattenStringList(v.ExcludedColumnNames) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenLakeFormationPermissions(apiObjects []*lakeformation.PrincipalResourcePermissions) []string { + if apiObjects == nil { + return nil + } + + tfList := make([]string, 0) + + for _, resourcePermission := range apiObjects { + for _, permission := range resourcePermission.Permissions { + tfList = append(tfList, aws.StringValue(permission)) + } + } + + sort.Strings(tfList) + + return tfList +} + +func flattenLakeFormationGrantPermissions(apiObjects []*lakeformation.PrincipalResourcePermissions) []string { + if apiObjects == nil { + return nil + } + + tfList := make([]string, 0) + + for _, resourcePermission := range apiObjects { + for _, grantPermission := range resourcePermission.PermissionsWithGrantOption { + tfList = append(tfList, aws.StringValue(grantPermission)) + } + } + + sort.Strings(tfList) + + return tfList +} + +// lakeformation.Permission_Values() enum has tag permissions in the format ASSOCIATE_TAG, DESCRIBE_TAG, which is not valid for the API +func validatePermissions() schema.SchemaValidateFunc { + return validation.StringInSlice([]string{ + "ALL", + "ALTER", + "ASSOCIATE", + "CREATE_DATABASE", + "CREATE_TABLE", + "DATA_LOCATION_ACCESS", + "DELETE", + "DESCRIBE", + "DROP", + "INSERT", + "SELECT", + }, true) +} diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go new file mode 100644 index 000000000000..a6537828546a --- /dev/null +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -0,0 +1,1500 @@ +package aws + +import ( + "fmt" + "log" + "strconv" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func testAccAWSLakeFormationPermissions_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionCreateDatabase), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "true"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + bucketName := "aws_s3_bucket.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_dataLocation(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionDataLocationAccess), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttr(resourceName, "data_location.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", bucketName, "arn"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_database(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + dbName := "aws_glue_catalog_database.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_database(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "database.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALTER"), + resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), + resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_lf_tag(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tagName := "aws_lakeformation_lf_tag.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_lf_tag(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "lf_tag.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.key", tagName, "key"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.values", tagName, "values"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "ASSOCIATE"), + resource.TestCheckResourceAttr(resourceName, "permissions.1", "DESCRIBE"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "2"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", "ASSOCIATE"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.1", "DESCRIBE"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_lf_tag_policy(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tagName := "aws_lakeformation_lf_tag.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_lf_tag_policy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "lf_tag_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "lf_tag_policy.0.resource_type", "DATABASE"), + resource.TestCheckResourceAttr(resourceName, "lf_tag_policy.0.expression.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.key", tagName, "key"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.values", tagName, "values"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), + resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_tableName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionDelete), + resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDescribe), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableWildcard(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + databaseResourceName := "aws_glue_catalog_database.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcard(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", databaseResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "table.0.wildcard", "true"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "2"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_implicitTableWithColumnsPermissions(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.wildcard", "true"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_implicitTablePermissions(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_implicitTablePermissions(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_selectPermissions(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_selectPermissions(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardPermissions(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_columnWildcardPermissions(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_columnWildcardPermissions(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lakeformation_permissions" { + continue + } + + permCount, err := permissionCountForLakeFormationResource(conn, rs) + + if err != nil { + return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) + } + + if permCount != 0 { + return fmt.Errorf("Lake Formation permissions (%s) still exist: %d", rs.Primary.ID, permCount) + } + + return nil + } + + return nil +} + +func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + permCount, err := permissionCountForLakeFormationResource(conn, rs) + + if err != nil { + return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) + } + + if permCount == 0 { + return fmt.Errorf("Lake Formation permissions (%s) do not exist or could not be found", rs.Primary.ID) + } + + return nil + } +} + +func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, rs *terraform.ResourceState) (int, error) { + input := &lakeformation.ListPermissionsInput{ + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(rs.Primary.Attributes["principal"]), + }, + Resource: &lakeformation.Resource{}, + } + + if v, ok := rs.Primary.Attributes["catalog_id"]; ok && v != "" { + input.CatalogId = aws.String(v) + } + + if v, ok := rs.Primary.Attributes["catalog_resource"]; ok && v != "" && v == "true" { + input.Resource.Catalog = expandLakeFormationCatalogResource() + } + + if v, ok := rs.Primary.Attributes["data_location.#"]; ok && v != "" && v != "0" { + tfMap := map[string]interface{}{} + + if v := rs.Primary.Attributes["data_location.0.catalog_id"]; v != "" { + tfMap["catalog_id"] = v + } + + if v := rs.Primary.Attributes["data_location.0.arn"]; v != "" { + tfMap["arn"] = v + } + + input.Resource.DataLocation = expandLakeFormationDataLocationResource(tfMap) + } + + if v, ok := rs.Primary.Attributes["database.#"]; ok && v != "" && v != "0" { + tfMap := map[string]interface{}{} + + if v := rs.Primary.Attributes["database.0.catalog_id"]; v != "" { + tfMap["catalog_id"] = v + } + + if v := rs.Primary.Attributes["database.0.name"]; v != "" { + tfMap["name"] = v + } + + input.Resource.Database = expandLakeFormationDatabaseResource(tfMap) + } + + if v, ok := rs.Primary.Attributes["lf_tag.#"]; ok && v != "" && v != "0" { + tfMap := map[string]interface{}{} + + if v := rs.Primary.Attributes["lf_tag.0.catalog_id"]; v != "" { + tfMap["catalog_id"] = v + } + + if v := rs.Primary.Attributes["lf_tag.0.key"]; v != "" { + tfMap["key"] = v + } + + if count, err := strconv.Atoi(rs.Primary.Attributes["lf_tag.0.values.#"]); err == nil { + var tagValues []string + for i := 0; i < count; i++ { + tagValues = append(tagValues, rs.Primary.Attributes[fmt.Sprintf("lf_tag.0.values.%d", i)]) + } + tfMap["values"] = flattenStringSet(aws.StringSlice(tagValues)) + } + + input.Resource.LFTag = expandLakeFormationLFTagKeyResource(tfMap) + } + + if v, ok := rs.Primary.Attributes["lf_tag_policy.#"]; ok && v != "" && v != "0" { + tfMap := map[string]interface{}{} + + if v := rs.Primary.Attributes["lf_tag_policy.0.catalog_id"]; v != "" { + tfMap["catalog_id"] = v + } + + if v := rs.Primary.Attributes["lf_tag_policy.0.resource_type"]; v != "" { + tfMap["resource_type"] = v + } + + if expressionCount, err := strconv.Atoi(rs.Primary.Attributes["lf_tag_policy.0.expression.#"]); err == nil { + expressionSlice := make([]interface{}, expressionCount) + for i := 0; i < expressionCount; i++ { + expression := make(map[string]interface{}) + + if v := rs.Primary.Attributes[fmt.Sprintf("lf_tag_policy.0.expression.%d.key", i)]; v != "" { + expression["key"] = v + } + + if expressionValueCount, err := strconv.Atoi(rs.Primary.Attributes[fmt.Sprintf("lf_tag_policy.0.expression.%d.values.#", i)]); err == nil { + valueSlice := make([]string, expressionValueCount) + for j := 0; j < expressionValueCount; j++ { + valueSlice[j] = rs.Primary.Attributes[fmt.Sprintf("lf_tag_policy.0.expression.%d.values.%d", i, j)] + } + expression["values"] = flattenStringSet(aws.StringSlice(valueSlice)) + } + expressionSlice[i] = expression + } + tfMap["expression"] = expressionSlice + } + + input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(tfMap) + } + + tableType := "" + + if v, ok := rs.Primary.Attributes["table.#"]; ok && v != "" && v != "0" { + tableType = TableTypeTable + + tfMap := map[string]interface{}{} + + if v := rs.Primary.Attributes["table.0.catalog_id"]; v != "" { + tfMap["catalog_id"] = v + } + + if v := rs.Primary.Attributes["table.0.database_name"]; v != "" { + tfMap["database_name"] = v + } + + if v := rs.Primary.Attributes["table.0.name"]; v != "" && v != TableNameAllTables { + tfMap["name"] = v + } + + if v := rs.Primary.Attributes["table.0.wildcard"]; v != "" && v == "true" { + tfMap["wildcard"] = true + } + + input.Resource.Table = expandLakeFormationTableResource(tfMap) + } + + if v, ok := rs.Primary.Attributes["table_with_columns.#"]; ok && v != "" && v != "0" { + tableType = TableTypeTableWithColumns + + tfMap := map[string]interface{}{} + + if v := rs.Primary.Attributes["table_with_columns.0.catalog_id"]; v != "" { + tfMap["catalog_id"] = v + } + + if v := rs.Primary.Attributes["table_with_columns.0.database_name"]; v != "" { + tfMap["database_name"] = v + } + + if v := rs.Primary.Attributes["table_with_columns.0.name"]; v != "" { + tfMap["name"] = v + } + + input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(tfMap) + } + + log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) + var allPermissions []*lakeformation.PrincipalResourcePermissions + + err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { + err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue + } + + allPermissions = append(allPermissions, permission) + } + return !lastPage + }) + + if err != nil { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("error listing Lake Formation Permissions: %w", err)) + } + return nil + }) + + if tfresource.TimedOut(err) { + err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue + } + + allPermissions = append(allPermissions, permission) + } + return !lastPage + }) + } + + if tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) { + return 0, nil + } + + if tfawserr.ErrMessageContains(err, "AccessDeniedException", "Resource does not exist") { + return 0, nil + } + + if err != nil { + return 0, fmt.Errorf("error listing Lake Formation permissions after retry: %w", err) + } + + // clean permissions = filter out permissions that do not pertain to this specific resource + + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + if input.Resource.Catalog != nil { + cleanPermissions = filterLakeFormationCatalogPermissions(allPermissions) + } + + if input.Resource.DataLocation != nil { + cleanPermissions = filterLakeFormationDataLocationPermissions(allPermissions) + } + + if input.Resource.Database != nil { + cleanPermissions = filterLakeFormationDatabasePermissions(allPermissions) + } + + if input.Resource.LFTag != nil { + cleanPermissions = filterLakeFormationLFTagPermissions(allPermissions) + } + + if input.Resource.LFTagPolicy != nil { + cleanPermissions = filterLakeFormationLFTagPolicyPermissions(allPermissions) + } + + if tableType == TableTypeTable { + cleanPermissions = filterLakeFormationTablePermissions( + aws.StringValue(input.Resource.Table.Name), + input.Resource.Table.TableWildcard != nil, + allPermissions, + ) + } + + var columnNames []string + if cols, err := strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.column_names.#"]); err == nil { + for i := 0; i < cols; i++ { + columnNames = append(columnNames, rs.Primary.Attributes[fmt.Sprintf("table_with_columns.0.column_names.%d", i)]) + } + } + + var excludedColumnNames []string + if cols, err := strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"]); err == nil { + for i := 0; i < cols; i++ { + excludedColumnNames = append(excludedColumnNames, rs.Primary.Attributes[fmt.Sprintf("table_with_columns.0.excluded_column_names.%d", i)]) + } + } + + if tableType == TableTypeTableWithColumns { + cleanPermissions = filterLakeFormationTableWithColumnsPermissions( + rs.Primary.Attributes["table_with_columns.0.database_name"], + rs.Primary.Attributes["table_with_columns.0.wildcard"] == "true", + aws.StringSlice(columnNames), + aws.StringSlice(excludedColumnNames), + allPermissions, + ) + } + + return len(cleanPermissions), nil +} + +func testAccAWSLakeFormationPermissionsConfig_basic(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < **NOTE:** This data source deals with explicitly granted permissions. Lake Formation grants implicit permissions to data lake administrators, database creators, and table creators. For more information, see [Implicit Lake Formation Permissions](https://docs.aws.amazon.com/lake-formation/latest/dg/implicit-permissions.html). @@ -39,6 +39,28 @@ data "aws_lakeformation_permissions" "test" { } ``` +### Permissions For Tag-Based Access Control + +```terraform +data "aws_lakeformation_permissions" "test" { + principal = aws_iam_role.workflow_role.arn + + lf_tag_policy { + resource_type = "DATABASE" + + expression { + key = "Team" + values = ["Sales"] + } + + expression { + key = "Environment" + values = ["Dev", "Production"] + } + } +} +``` + ## Argument Reference The following arguments are required: @@ -50,6 +72,8 @@ One of the following is required: * `catalog_resource` - Whether the permissions are to be granted for the Data Catalog. Defaults to `false`. * `data_location` - Configuration block for a data location resource. Detailed below. * `database` - Configuration block for a database resource. Detailed below. +* `lf_tag` - (Optional) Configuration block for an LF-tag resource. Detailed below. +* `lf_tag_policy` - (Optional) Configuration block for an LF-tag policy resource. Detailed below. * `table` - Configuration block for a table resource. Detailed below. * `table_with_columns` - Configuration block for a table with columns resource. Detailed below. @@ -77,6 +101,33 @@ The following argument is optional: * `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. +### lf_tag + +The following arguments are required: + +* `key` – (Required) The key-name for the tag. +* `values` - (Required) A list of possible values an attribute can take. + +The following argument is optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. + +### lf_tag_policy + +The following arguments are required: + +* `resource_type` – (Required) The resource type for which the tag policy applies. Valid values are `DATABASE` and `TABLE`. +* `expression` - (Required) A list of tag conditions that apply to the resource's tag policy. Configuration block for tag conditions that apply to the policy. See [`expression`](#expression) below. + +The following argument is optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. + +#### expression + +* `key` – (Required) The key-name of an LF-Tag. +* `values` - (Required) A list of possible values of an LF-Tag. + ### table The following argument is required: diff --git a/website/docs/r/lakeformation_policy_tag.html.markdown b/website/docs/r/lakeformation_lf_tag.html.markdow similarity index 57% rename from website/docs/r/lakeformation_policy_tag.html.markdown rename to website/docs/r/lakeformation_lf_tag.html.markdow index ba20eaac8cdd..b01be8397894 100644 --- a/website/docs/r/lakeformation_policy_tag.html.markdown +++ b/website/docs/r/lakeformation_lf_tag.html.markdow @@ -1,19 +1,19 @@ --- subcategory: "Lake Formation" layout: "aws" -page_title: "AWS: aws_lakeformation_policy_tag" +page_title: "AWS: aws_lakeformation_lf_tag" description: |- Creates a tag with the specified name and values. --- -# Resource: aws_lakeformation_policy_tag +# Resource: aws_lakeformation_lf_tag -Creates a policy tag with the specified name and values. Each key must have at least one value. The maximum number of values permitted is 15. +Creates an LF-Tag with the specified name and values. Each key must have at least one value. The maximum number of values permitted is 15. ## Example Usage ```terraform -resource "aws_lakeformation_policy_tag" "example" { +resource "aws_lakeformation_lf_tag" "example" { key = "module" values = ["Orders", "Sales", "Customers"] } @@ -35,9 +35,9 @@ In addition to all arguments above, the following attributes are exported: ## Import -Lake Formation Policy Tags can be imported using the `catalog_id:key`. If you have not set a Catalog ID specify the AWS Account ID that the database is in, e.g. +Lake Formation LF-Tags can be imported using the `catalog_id:key`. If you have not set a Catalog ID specify the AWS Account ID that the database is in, e.g. ``` -$ terraform import aws_lakeformation_policy_tag.example 123456789012:some_key +$ terraform import aws_lakeformation_lf_tag.example 123456789012:some_key ``` diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 2051a1e37c32..4e41270e445d 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -8,7 +8,7 @@ description: |- # Resource: aws_lakeformation_permissions -Grants permissions to the principal to access metadata in the Data Catalog and data organized in underlying data storage such as Amazon S3. Permissions are granted to a principal, in a Data Catalog, relative to a Lake Formation resource, which includes the Data Catalog, databases, and tables. For more information, see [Security and Access Control to Metadata and Data in Lake Formation](https://docs.aws.amazon.com/lake-formation/latest/dg/security-data-access.html). +Grants permissions to the principal to access metadata in the Data Catalog and data organized in underlying data storage such as Amazon S3. Permissions are granted to a principal, in a Data Catalog, relative to a Lake Formation resource, which includes the Data Catalog, databases, tables, LF-tags, and LF-tag policies. For more information, see [Security and Access Control to Metadata and Data in Lake Formation](https://docs.aws.amazon.com/lake-formation/latest/dg/security-data-access.html). !> **WARNING:** Lake Formation permissions are not in effect by default within AWS. Using this resource will not secure your data and will result in errors if you do not change the security settings for existing resources and the default security settings for new resources. See [Default Behavior and `IAMAllowedPrincipals`](#default-behavior-and-iamallowedprincipals) for additional details. @@ -124,12 +124,41 @@ resource "aws_lakeformation_permissions" "example" { } ``` +### Grant Permissions Using Tag-Based Access Control + +```terraform +resource "aws_lakeformation_permissions" "test" { + role = aws_iam_role.sales_role.arn + permissions = ["CREATE_TABLE", "ALTER", "DROP"] + + lf_tag_policy { + resource_type = "DATABASE" + + expression { + key = "Team" + values = ["Sales"] + } + + expression { + key = "Environment" + values = ["Dev", "Production"] + } + } +} +``` + + ## Argument Reference The following arguments are required: +<<<<<<< HEAD * `permissions` – (Required) List of permissions granted to the principal. Valid values may include `ALL`, `ALTER`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). * `principal` – (Required) Principal to be granted the permissions on the resource. Supported principals include `IAM_ALLOWED_PRINCIPALS` (see [Default Behavior and `IAMAllowedPrincipals`](#default-behavior-and-iamallowedprincipals) above), IAM roles, users, groups, SAML groups and users, QuickSight groups, OUs, and organizations as well as AWS account IDs for cross-account permissions. For more information, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +======= +* `permissions` – (Required) List of permissions granted to the principal. Valid values may include `ALL`, `ALTER`, `ASSOCIATE`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +* `principal` – (Required) Principal to be granted the permissions on the resource. Supported principals include IAM roles, users, groups, SAML groups and users, QuickSight groups, OUs, and organizations as well as AWS account IDs for cross-account permissions. For more information, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +>>>>>>> 5e8d0b964d (New resource: aws_lakeformation_permissions) ~> **NOTE:** We highly recommend that the `principal` _NOT_ be a Lake Formation administrator (granted using `aws_lakeformation_data_lake_settings`). The entity (e.g., IAM role) running Terraform will most likely need to be a Lake Formation administrator. As such, the entity will have implicit permissions and does not need permissions granted through this resource. @@ -138,6 +167,8 @@ One of the following is required: * `catalog_resource` - (Optional) Whether the permissions are to be granted for the Data Catalog. Defaults to `false`. * `data_location` - (Optional) Configuration block for a data location resource. Detailed below. * `database` - (Optional) Configuration block for a database resource. Detailed below. +* `lf_tag` - (Optional) Configuration block for an LF-tag resource. Detailed below. +* `lf_tag_policy` - (Optional) Configuration block for an LF-tag policy resource. Detailed below. * `table` - (Optional) Configuration block for a table resource. Detailed below. * `table_with_columns` - (Optional) Configuration block for a table with columns resource. Detailed below. @@ -166,6 +197,33 @@ The following argument is optional: * `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. +### lf_tag + +The following arguments are required: + +* `key` – (Required) The key-name for the tag. +* `values` - (Required) A list of possible values an attribute can take. + +The following argument is optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. + +### lf_tag_policy + +The following arguments are required: + +* `resource_type` – (Required) The resource type for which the tag policy applies. Valid values are `DATABASE` and `TABLE`. +* `expression` - (Required) A list of tag conditions that apply to the resource's tag policy. Configuration block for tag conditions that apply to the policy. See [`expression`](#expression) below. + +The following argument is optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. + +#### expression + +* `key` – (Required) The key-name of an LF-Tag. +* `values` - (Required) A list of possible values of an LF-Tag. + ### table The following argument is required: From dd0bfbbce2230c1820ebb981019280685c55bda5 Mon Sep 17 00:00:00 2001 From: Daniel Messias Date: Wed, 20 Apr 2022 16:16:17 +0100 Subject: [PATCH 03/10] Service package refactor --- .changelog/19523.txt | 3 - .changelog/19648.txt | 4 - ...ta_source_aws_lakeformation_permissions.go | 462 ----- ...urce_aws_lakeformation_permissions_test.go | 663 ------- aws/provider.go | 1658 ----------------- aws/resource_aws_lakeformation_lf_tag.go | 182 -- aws/resource_aws_lakeformation_lf_tag_test.go | 211 --- aws/resource_aws_lakeformation_permissions.go | 1301 ------------- ...urce_aws_lakeformation_permissions_test.go | 1500 --------------- aws/resource_aws_lakeformation_test.go | 59 - internal/service/lakeformation/permissions.go | 280 +++ .../lakeformation/permissions_data_source.go | 102 + .../permissions_data_source_test.go | 193 ++ .../service/lakeformation/permissions_test.go | 245 +++ resource_aws_lakeformation_policy_tag.go | 182 -- resource_aws_lakeformation_policy_tag_test.go | 211 --- .../docs/r/lakeformation_lf_tag.html.markdow | 43 - 17 files changed, 820 insertions(+), 6479 deletions(-) delete mode 100644 .changelog/19523.txt delete mode 100644 .changelog/19648.txt delete mode 100644 aws/data_source_aws_lakeformation_permissions.go delete mode 100644 aws/data_source_aws_lakeformation_permissions_test.go delete mode 100644 aws/provider.go delete mode 100644 aws/resource_aws_lakeformation_lf_tag.go delete mode 100644 aws/resource_aws_lakeformation_lf_tag_test.go delete mode 100644 aws/resource_aws_lakeformation_permissions.go delete mode 100644 aws/resource_aws_lakeformation_permissions_test.go delete mode 100644 aws/resource_aws_lakeformation_test.go delete mode 100644 resource_aws_lakeformation_policy_tag.go delete mode 100644 resource_aws_lakeformation_policy_tag_test.go delete mode 100644 website/docs/r/lakeformation_lf_tag.html.markdow diff --git a/.changelog/19523.txt b/.changelog/19523.txt deleted file mode 100644 index 92e459a2de23..000000000000 --- a/.changelog/19523.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:new-resource -aws_lakeformation_lf_tag -``` \ No newline at end of file diff --git a/.changelog/19648.txt b/.changelog/19648.txt deleted file mode 100644 index 6c734dd5da32..000000000000 --- a/.changelog/19648.txt +++ /dev/null @@ -1,4 +0,0 @@ -```release-note:note:enhancement -resource/aws_lakeformation_permissions: Add LF-tag support -datasource/aws_lakeformation_permissions: Add LF-tag support -``` \ No newline at end of file diff --git a/aws/data_source_aws_lakeformation_permissions.go b/aws/data_source_aws_lakeformation_permissions.go deleted file mode 100644 index 1f362bc1f96d..000000000000 --- a/aws/data_source_aws_lakeformation_permissions.go +++ /dev/null @@ -1,462 +0,0 @@ -package aws - -import ( - "fmt" - "log" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" - iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" -) - -func dataSourceAwsLakeFormationPermissions() *schema.Resource { - return &schema.Resource{ - Read: dataSourceAwsLakeFormationPermissionsRead, - - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validateAwsAccountId, - }, - "catalog_resource": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "data_location": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateArn, - }, - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - }, - }, - }, - "database": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "name": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - "permissions": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "permissions_with_grant_option": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "lf_tag": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - "values": { - Type: schema.TypeSet, - Required: true, - MinItems: 1, - MaxItems: 15, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateLFTagValues(), - }, - Set: schema.HashString, - }, - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - "lf_tag_policy": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "expression": { - Type: schema.TypeList, - Required: true, - MinItems: 1, - MaxItems: 5, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - "values": { - Type: schema.TypeSet, - Required: true, - MinItems: 1, - MaxItems: 15, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateLFTagValues(), - }, - Set: schema.HashString, - }, - }, - }, - }, - "resource_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(lakeformation.ResourceType_Values(), false), - }, - }, - }, - }, - "principal": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validatePrincipal, - }, - "table": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "database_name": { - Type: schema.TypeString, - Required: true, - }, - "name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "wildcard": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - }, - }, - }, - "table_with_columns": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "column_names": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.NoZeroValues, - }, - }, - "database_name": { - Type: schema.TypeString, - Required: true, - }, - "excluded_column_names": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.NoZeroValues, - }, - }, - "name": { - Type: schema.TypeString, - Required: true, - }, - "wildcard": { - Type: schema.TypeBool, - Optional: true, - }, - }, - }, - }, - }, - } -} - -func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - input := &lakeformation.ListPermissionsInput{ - Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), - }, - Resource: &lakeformation.Resource{}, - } - - if v, ok := d.GetOk("catalog_id"); ok { - input.CatalogId = aws.String(v.(string)) - } - - if _, ok := d.GetOk("catalog_resource"); ok { - input.Resource.Catalog = expandLakeFormationCatalogResource() - } - - if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) - } - - tableType := "" - - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) - tableType = TableTypeTable - } - - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - // can't ListPermissions for TableWithColumns, so use Table instead - input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(v.([]interface{})[0].(map[string]interface{})) - tableType = TableTypeTableWithColumns - } - - log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) - var allPermissions []*lakeformation.PrincipalResourcePermissions - - err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { - err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { - for _, permission := range resp.PrincipalResourcePermissions { - if permission == nil { - continue - } - - allPermissions = append(allPermissions, permission) - } - return !lastPage - }) - - if err != nil { - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { - return resource.RetryableError(err) - } - return resource.NonRetryableError(fmt.Errorf("error reading Lake Formation Permissions: %w", err)) - } - return nil - }) - - if tfresource.TimedOut(err) { - err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { - for _, permission := range resp.PrincipalResourcePermissions { - if permission == nil { - continue - } - - allPermissions = append(allPermissions, permission) - } - return !lastPage - }) - } - - d.SetId(fmt.Sprintf("%d", hashcode.String(input.String()))) - - if err != nil { - return fmt.Errorf("error reading Lake Formation permissions: %w", err) - } - - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - if input.Resource.Catalog != nil { - cleanPermissions = filterLakeFormationCatalogPermissions(allPermissions) - } - - if input.Resource.DataLocation != nil { - cleanPermissions = filterLakeFormationDataLocationPermissions(allPermissions) - } - - if input.Resource.Database != nil { - cleanPermissions = filterLakeFormationDatabasePermissions(allPermissions) - } - - if input.Resource.LFTag != nil { - cleanPermissions = filterLakeFormationLFTagPermissions(allPermissions) - } - - if input.Resource.LFTagPolicy != nil { - cleanPermissions = filterLakeFormationLFTagPolicyPermissions(allPermissions) - } - - if tableType == TableTypeTable { - cleanPermissions = filterLakeFormationTablePermissions( - aws.StringValue(input.Resource.Table.Name), - input.Resource.Table.TableWildcard != nil, - allPermissions, - ) - } - - if tableType == TableTypeTableWithColumns { - cleanPermissions = filterLakeFormationTableWithColumnsPermissions( - d.Get("table_with_columns.0.database_name").(string), - d.Get("table_with_columns.0.wildcard").(bool), - expandStringList(d.Get("table_with_columns.0.column_names").([]interface{})), - expandStringList(d.Get("table_with_columns.0.excluded_column_names").([]interface{})), - allPermissions, - ) - } - - if len(cleanPermissions) != len(allPermissions) { - log.Printf("[INFO] Resource Lake Formation clean permissions (%d) and all permissions (%d) have different lengths (this is not necessarily a problem): %s", len(cleanPermissions), len(allPermissions), d.Id()) - } - - d.Set("principal", cleanPermissions[0].Principal.DataLakePrincipalIdentifier) - d.Set("permissions", flattenLakeFormationPermissions(cleanPermissions)) - d.Set("permissions_with_grant_option", flattenLakeFormationGrantPermissions(cleanPermissions)) - - if cleanPermissions[0].Resource.Catalog != nil { - d.Set("catalog_resource", true) - } - - if cleanPermissions[0].Resource.DataLocation != nil { - if err := d.Set("data_location", []interface{}{flattenLakeFormationDataLocationResource(cleanPermissions[0].Resource.DataLocation)}); err != nil { - return fmt.Errorf("error setting data_location: %w", err) - } - } else { - d.Set("data_location", nil) - } - - if cleanPermissions[0].Resource.Database != nil { - if err := d.Set("database", []interface{}{flattenLakeFormationDatabaseResource(cleanPermissions[0].Resource.Database)}); err != nil { - return fmt.Errorf("error setting database: %w", err) - } - } else { - d.Set("database", nil) - } - - if cleanPermissions[0].Resource.LFTag != nil { - if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { - return fmt.Errorf("error setting LF-tag: %w", err) - } - } else { - d.Set("lf_tag", nil) - } - - if cleanPermissions[0].Resource.LFTagPolicy != nil { - if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { - return fmt.Errorf("error setting LF-tag policy: %w", err) - } - } else { - d.Set("lf_tag_policy", nil) - } - - tableSet := false - - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { - // since perm list could include TableWithColumns, get the right one - for _, perm := range cleanPermissions { - if perm.Resource.Table != nil { - if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { - return fmt.Errorf("error setting table: %w", err) - } - tableSet = true - break - } - } - } - - if !tableSet { - d.Set("table", nil) - } - - twcSet := false - - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 { - // since perm list could include Table, get the right one - for _, perm := range cleanPermissions { - if perm.Resource.TableWithColumns != nil { - if err := d.Set("table_with_columns", []interface{}{flattenLakeFormationTableWithColumnsResource(perm.Resource.TableWithColumns)}); err != nil { - return fmt.Errorf("error setting table_with_columns: %w", err) - } - twcSet = true - break - } - } - } - - if !twcSet { - d.Set("table_with_columns", nil) - } - - return nil -} diff --git a/aws/data_source_aws_lakeformation_permissions_test.go b/aws/data_source_aws_lakeformation_permissions_test.go deleted file mode 100644 index 841cfd772e94..000000000000 --- a/aws/data_source_aws_lakeformation_permissions_test.go +++ /dev/null @@ -1,663 +0,0 @@ -package aws - -import ( - "fmt" - "testing" - - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func testAccAWSLakeFormationPermissionsDataSource_basic(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - dataSourceName := "data.aws_lakeformation_permissions.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), - resource.TestCheckResourceAttrPair(resourceName, "catalog_resource", dataSourceName, "catalog_resource"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissionsDataSource_dataLocation(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - dataSourceName := "data.aws_lakeformation_permissions.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_dataLocation(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), - resource.TestCheckResourceAttrPair(resourceName, "data_location.#", dataSourceName, "data_location.#"), - resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", dataSourceName, "data_location.0.arn"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissionsDataSource_database(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - dataSourceName := "data.aws_lakeformation_permissions.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_database(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), - resource.TestCheckResourceAttrPair(resourceName, "database.#", dataSourceName, "database.#"), - resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dataSourceName, "database.0.name"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.2", dataSourceName, "permissions.2"), - resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissionsDataSource_lf_tag(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - dataSourceName := "data.aws_lakeformation_permissions.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag.#", dataSourceName, "lf_tag.#"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.key", dataSourceName, "lf_tag.0.key"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.values", dataSourceName, "lf_tag.0.values"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), - resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), - resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.1", dataSourceName, "permissions_with_grant_option.1"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissionsDataSource_lf_tag_policy(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - dataSourceName := "data.aws_lakeformation_permissions.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag_policy(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.#", dataSourceName, "lf_tag_policy.#"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.resource_type", dataSourceName, "lf_tag_policy.0.resource_type"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.#", dataSourceName, "lf_tag_policy.0.expression.#"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.key", dataSourceName, "lf_tag_policy.0.expression.0.key"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.values", dataSourceName, "lf_tag_policy.0.expression.0.values"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.2", dataSourceName, "permissions.2"), - resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissionsDataSource_table(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - dataSourceName := "data.aws_lakeformation_permissions.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_table(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), - resource.TestCheckResourceAttrPair(resourceName, "table.#", dataSourceName, "table.#"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", dataSourceName, "table.0.database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.name", dataSourceName, "table.0.name"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissionsDataSource_tableWithColumns(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - dataSourceName := "data.aws_lakeformation_permissions.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_tableWithColumns(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.#", dataSourceName, "table_with_columns.#"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", dataSourceName, "table_with_columns.0.database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", dataSourceName, "table_with_columns.0.name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.#", dataSourceName, "table_with_columns.0.column_names.#"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.0", dataSourceName, "table_with_columns.0.column_names.0"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.1", dataSourceName, "table_with_columns.0.column_names.1"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), - resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissionsDataSourceConfig_basic(rName string) string { - return fmt.Sprintf(` -data "aws_partition" "current" {} - -resource "aws_iam_role" "test" { - name = %[1]q - - assume_role_policy = < LB Rename - "aws_lb": dataSourceAwsLb(), - "aws_alb": dataSourceAwsLb(), - "aws_lb_listener": dataSourceAwsLbListener(), - "aws_alb_listener": dataSourceAwsLbListener(), - "aws_lb_target_group": dataSourceAwsLbTargetGroup(), - "aws_alb_target_group": dataSourceAwsLbTargetGroup(), - }, - - ResourcesMap: map[string]*schema.Resource{ - "aws_accessanalyzer_analyzer": resourceAwsAccessAnalyzerAnalyzer(), - "aws_acm_certificate": resourceAwsAcmCertificate(), - "aws_acm_certificate_validation": resourceAwsAcmCertificateValidation(), - "aws_acmpca_certificate_authority": resourceAwsAcmpcaCertificateAuthority(), - "aws_acmpca_certificate_authority_certificate": resourceAwsAcmpcaCertificateAuthorityCertificate(), - "aws_acmpca_certificate": resourceAwsAcmpcaCertificate(), - "aws_ami": resourceAwsAmi(), - "aws_ami_copy": resourceAwsAmiCopy(), - "aws_ami_from_instance": resourceAwsAmiFromInstance(), - "aws_ami_launch_permission": resourceAwsAmiLaunchPermission(), - "aws_amplify_app": resourceAwsAmplifyApp(), - "aws_amplify_backend_environment": resourceAwsAmplifyBackendEnvironment(), - "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_rest_api_policy": resourceAwsApiGatewayRestApiPolicy(), - "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_apigatewayv2_api": resourceAwsApiGatewayV2Api(), - "aws_apigatewayv2_api_mapping": resourceAwsApiGatewayV2ApiMapping(), - "aws_apigatewayv2_authorizer": resourceAwsApiGatewayV2Authorizer(), - "aws_apigatewayv2_deployment": resourceAwsApiGatewayV2Deployment(), - "aws_apigatewayv2_domain_name": resourceAwsApiGatewayV2DomainName(), - "aws_apigatewayv2_integration": resourceAwsApiGatewayV2Integration(), - "aws_apigatewayv2_integration_response": resourceAwsApiGatewayV2IntegrationResponse(), - "aws_apigatewayv2_model": resourceAwsApiGatewayV2Model(), - "aws_apigatewayv2_route": resourceAwsApiGatewayV2Route(), - "aws_apigatewayv2_route_response": resourceAwsApiGatewayV2RouteResponse(), - "aws_apigatewayv2_stage": resourceAwsApiGatewayV2Stage(), - "aws_apigatewayv2_vpc_link": resourceAwsApiGatewayV2VpcLink(), - "aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(), - "aws_appautoscaling_target": resourceAwsAppautoscalingTarget(), - "aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(), - "aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(), - "aws_appmesh_gateway_route": resourceAwsAppmeshGatewayRoute(), - "aws_appmesh_mesh": resourceAwsAppmeshMesh(), - "aws_appmesh_route": resourceAwsAppmeshRoute(), - "aws_appmesh_virtual_gateway": resourceAwsAppmeshVirtualGateway(), - "aws_appmesh_virtual_node": resourceAwsAppmeshVirtualNode(), - "aws_appmesh_virtual_router": resourceAwsAppmeshVirtualRouter(), - "aws_appmesh_virtual_service": resourceAwsAppmeshVirtualService(), - "aws_apprunner_auto_scaling_configuration_version": resourceAwsAppRunnerAutoScalingConfigurationVersion(), - "aws_apprunner_connection": resourceAwsAppRunnerConnection(), - "aws_apprunner_custom_domain_association": resourceAwsAppRunnerCustomDomainAssociation(), - "aws_apprunner_service": resourceAwsAppRunnerService(), - "aws_appsync_api_key": resourceAwsAppsyncApiKey(), - "aws_appsync_datasource": resourceAwsAppsyncDatasource(), - "aws_appsync_function": resourceAwsAppsyncFunction(), - "aws_appsync_graphql_api": resourceAwsAppsyncGraphqlApi(), - "aws_appsync_resolver": resourceAwsAppsyncResolver(), - "aws_athena_database": resourceAwsAthenaDatabase(), - "aws_athena_named_query": resourceAwsAthenaNamedQuery(), - "aws_athena_workgroup": resourceAwsAthenaWorkgroup(), - "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_autoscalingplans_scaling_plan": resourceAwsAutoScalingPlansScalingPlan(), - "aws_backup_global_settings": resourceAwsBackupGlobalSettings(), - "aws_backup_plan": resourceAwsBackupPlan(), - "aws_backup_region_settings": resourceAwsBackupRegionSettings(), - "aws_backup_selection": resourceAwsBackupSelection(), - "aws_backup_vault": resourceAwsBackupVault(), - "aws_backup_vault_notifications": resourceAwsBackupVaultNotifications(), - "aws_backup_vault_policy": resourceAwsBackupVaultPolicy(), - "aws_budgets_budget": resourceAwsBudgetsBudget(), - "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), - "aws_cloudformation_stack": resourceAwsCloudFormationStack(), - "aws_cloudformation_stack_set": resourceAwsCloudFormationStackSet(), - "aws_cloudformation_stack_set_instance": resourceAwsCloudFormationStackSetInstance(), - "aws_cloudformation_type": resourceAwsCloudFormationType(), - "aws_cloudfront_cache_policy": resourceAwsCloudFrontCachePolicy(), - "aws_cloudfront_distribution": resourceAwsCloudFrontDistribution(), - "aws_cloudfront_function": resourceAwsCloudFrontFunction(), - "aws_cloudfront_key_group": resourceAwsCloudFrontKeyGroup(), - "aws_cloudfront_origin_access_identity": resourceAwsCloudFrontOriginAccessIdentity(), - "aws_cloudfront_origin_request_policy": resourceAwsCloudFrontOriginRequestPolicy(), - "aws_cloudfront_public_key": resourceAwsCloudFrontPublicKey(), - "aws_cloudfront_realtime_log_config": resourceAwsCloudFrontRealtimeLogConfig(), - "aws_cloudtrail": resourceAwsCloudTrail(), - "aws_cloudwatch_event_bus": resourceAwsCloudWatchEventBus(), - "aws_cloudwatch_event_permission": resourceAwsCloudWatchEventPermission(), - "aws_cloudwatch_event_rule": resourceAwsCloudWatchEventRule(), - "aws_cloudwatch_event_target": resourceAwsCloudWatchEventTarget(), - "aws_cloudwatch_event_archive": resourceAwsCloudWatchEventArchive(), - "aws_cloudwatch_event_connection": resourceAwsCloudWatchEventConnection(), - "aws_cloudwatch_event_api_destination": resourceAwsCloudWatchEventApiDestination(), - "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_conformance_pack": resourceAwsConfigConformancePack(), - "aws_config_delivery_channel": resourceAwsConfigDeliveryChannel(), - "aws_config_organization_custom_rule": resourceAwsConfigOrganizationCustomRule(), - "aws_config_organization_managed_rule": resourceAwsConfigOrganizationManagedRule(), - "aws_config_remediation_configuration": resourceAwsConfigRemediationConfiguration(), - "aws_cognito_identity_pool": resourceAwsCognitoIdentityPool(), - "aws_cognito_identity_pool_roles_attachment": resourceAwsCognitoIdentityPoolRolesAttachment(), - "aws_cognito_identity_provider": resourceAwsCognitoIdentityProvider(), - "aws_cognito_resource_server": resourceAwsCognitoResourceServer(), - "aws_cognito_user_group": resourceAwsCognitoUserGroup(), - "aws_cognito_user_pool": resourceAwsCognitoUserPool(), - "aws_cognito_user_pool_client": resourceAwsCognitoUserPoolClient(), - "aws_cognito_user_pool_domain": resourceAwsCognitoUserPoolDomain(), - "aws_cognito_user_pool_ui_customization": resourceAwsCognitoUserPoolUICustomization(), - "aws_cloudhsm_v2_cluster": resourceAwsCloudHsmV2Cluster(), - "aws_cloudhsm_v2_hsm": resourceAwsCloudHsmV2Hsm(), - "aws_cloudwatch_composite_alarm": resourceAwsCloudWatchCompositeAlarm(), - "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), - "aws_cloudwatch_dashboard": resourceAwsCloudWatchDashboard(), - "aws_cloudwatch_metric_stream": resourceAwsCloudWatchMetricStream(), - "aws_cloudwatch_query_definition": resourceAwsCloudWatchQueryDefinition(), - "aws_codedeploy_app": resourceAwsCodeDeployApp(), - "aws_codedeploy_deployment_config": resourceAwsCodeDeployDeploymentConfig(), - "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), - "aws_codecommit_repository": resourceAwsCodeCommitRepository(), - "aws_codecommit_trigger": resourceAwsCodeCommitTrigger(), - "aws_codeartifact_domain": resourceAwsCodeArtifactDomain(), - "aws_codeartifact_domain_permissions_policy": resourceAwsCodeArtifactDomainPermissionsPolicy(), - "aws_codeartifact_repository": resourceAwsCodeArtifactRepository(), - "aws_codeartifact_repository_permissions_policy": resourceAwsCodeArtifactRepositoryPermissionsPolicy(), - "aws_codebuild_project": resourceAwsCodeBuildProject(), - "aws_codebuild_report_group": resourceAwsCodeBuildReportGroup(), - "aws_codebuild_source_credential": resourceAwsCodeBuildSourceCredential(), - "aws_codebuild_webhook": resourceAwsCodeBuildWebhook(), - "aws_codepipeline": resourceAwsCodePipeline(), - "aws_codepipeline_webhook": resourceAwsCodePipelineWebhook(), - "aws_codestarconnections_connection": resourceAwsCodeStarConnectionsConnection(), - "aws_codestarconnections_host": resourceAwsCodeStarConnectionsHost(), - "aws_codestarnotifications_notification_rule": resourceAwsCodeStarNotificationsNotificationRule(), - "aws_cur_report_definition": resourceAwsCurReportDefinition(), - "aws_customer_gateway": resourceAwsCustomerGateway(), - "aws_datapipeline_pipeline": resourceAwsDataPipelinePipeline(), - "aws_datasync_agent": resourceAwsDataSyncAgent(), - "aws_datasync_location_efs": resourceAwsDataSyncLocationEfs(), - "aws_datasync_location_fsx_windows_file_system": resourceAwsDataSyncLocationFsxWindowsFileSystem(), - "aws_datasync_location_nfs": resourceAwsDataSyncLocationNfs(), - "aws_datasync_location_s3": resourceAwsDataSyncLocationS3(), - "aws_datasync_location_smb": resourceAwsDataSyncLocationSmb(), - "aws_datasync_task": resourceAwsDataSyncTask(), - "aws_dax_cluster": resourceAwsDaxCluster(), - "aws_dax_parameter_group": resourceAwsDaxParameterGroup(), - "aws_dax_subnet_group": resourceAwsDaxSubnetGroup(), - "aws_db_cluster_snapshot": resourceAwsDbClusterSnapshot(), - "aws_db_event_subscription": resourceAwsDbEventSubscription(), - "aws_db_instance": resourceAwsDbInstance(), - "aws_db_instance_role_association": resourceAwsDbInstanceRoleAssociation(), - "aws_db_option_group": resourceAwsDbOptionGroup(), - "aws_db_parameter_group": resourceAwsDbParameterGroup(), - "aws_db_proxy": resourceAwsDbProxy(), - "aws_db_proxy_default_target_group": resourceAwsDbProxyDefaultTargetGroup(), - "aws_db_proxy_endpoint": resourceAwsDbProxyEndpoint(), - "aws_db_proxy_target": resourceAwsDbProxyTarget(), - "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_directory_service_log_subscription": resourceAwsDirectoryServiceLogSubscription(), - "aws_dlm_lifecycle_policy": resourceAwsDlmLifecyclePolicy(), - "aws_dms_certificate": resourceAwsDmsCertificate(), - "aws_dms_endpoint": resourceAwsDmsEndpoint(), - "aws_dms_event_subscription": resourceAwsDmsEventSubscription(), - "aws_dms_replication_instance": resourceAwsDmsReplicationInstance(), - "aws_dms_replication_subnet_group": resourceAwsDmsReplicationSubnetGroup(), - "aws_dms_replication_task": resourceAwsDmsReplicationTask(), - "aws_docdb_cluster": resourceAwsDocDBCluster(), - "aws_docdb_cluster_instance": resourceAwsDocDBClusterInstance(), - "aws_docdb_cluster_parameter_group": resourceAwsDocDBClusterParameterGroup(), - "aws_docdb_cluster_snapshot": resourceAwsDocDBClusterSnapshot(), - "aws_docdb_subnet_group": resourceAwsDocDBSubnetGroup(), - "aws_dx_bgp_peer": resourceAwsDxBgpPeer(), - "aws_dx_connection": resourceAwsDxConnection(), - "aws_dx_connection_association": resourceAwsDxConnectionAssociation(), - "aws_dx_gateway": resourceAwsDxGateway(), - "aws_dx_gateway_association": resourceAwsDxGatewayAssociation(), - "aws_dx_gateway_association_proposal": resourceAwsDxGatewayAssociationProposal(), - "aws_dx_hosted_private_virtual_interface": resourceAwsDxHostedPrivateVirtualInterface(), - "aws_dx_hosted_private_virtual_interface_accepter": resourceAwsDxHostedPrivateVirtualInterfaceAccepter(), - "aws_dx_hosted_public_virtual_interface": resourceAwsDxHostedPublicVirtualInterface(), - "aws_dx_hosted_public_virtual_interface_accepter": resourceAwsDxHostedPublicVirtualInterfaceAccepter(), - "aws_dx_hosted_transit_virtual_interface": resourceAwsDxHostedTransitVirtualInterface(), - "aws_dx_hosted_transit_virtual_interface_accepter": resourceAwsDxHostedTransitVirtualInterfaceAccepter(), - "aws_dx_lag": resourceAwsDxLag(), - "aws_dx_private_virtual_interface": resourceAwsDxPrivateVirtualInterface(), - "aws_dx_public_virtual_interface": resourceAwsDxPublicVirtualInterface(), - "aws_dx_transit_virtual_interface": resourceAwsDxTransitVirtualInterface(), - "aws_dynamodb_table": resourceAwsDynamoDbTable(), - "aws_dynamodb_table_item": resourceAwsDynamoDbTableItem(), - "aws_dynamodb_global_table": resourceAwsDynamoDbGlobalTable(), - "aws_dynamodb_kinesis_streaming_destination": resourceAwsDynamoDbKinesisStreamingDestination(), - "aws_ebs_default_kms_key": resourceAwsEbsDefaultKmsKey(), - "aws_ebs_encryption_by_default": resourceAwsEbsEncryptionByDefault(), - "aws_ebs_snapshot": resourceAwsEbsSnapshot(), - "aws_ebs_snapshot_copy": resourceAwsEbsSnapshotCopy(), - "aws_ebs_volume": resourceAwsEbsVolume(), - "aws_ec2_availability_zone_group": resourceAwsEc2AvailabilityZoneGroup(), - "aws_ec2_capacity_reservation": resourceAwsEc2CapacityReservation(), - "aws_ec2_carrier_gateway": resourceAwsEc2CarrierGateway(), - "aws_ec2_client_vpn_authorization_rule": resourceAwsEc2ClientVpnAuthorizationRule(), - "aws_ec2_client_vpn_endpoint": resourceAwsEc2ClientVpnEndpoint(), - "aws_ec2_client_vpn_network_association": resourceAwsEc2ClientVpnNetworkAssociation(), - "aws_ec2_client_vpn_route": resourceAwsEc2ClientVpnRoute(), - "aws_ec2_fleet": resourceAwsEc2Fleet(), - "aws_ec2_local_gateway_route": resourceAwsEc2LocalGatewayRoute(), - "aws_ec2_local_gateway_route_table_vpc_association": resourceAwsEc2LocalGatewayRouteTableVpcAssociation(), - "aws_ec2_managed_prefix_list": resourceAwsEc2ManagedPrefixList(), - "aws_ec2_tag": resourceAwsEc2Tag(), - "aws_ec2_traffic_mirror_filter": resourceAwsEc2TrafficMirrorFilter(), - "aws_ec2_traffic_mirror_filter_rule": resourceAwsEc2TrafficMirrorFilterRule(), - "aws_ec2_traffic_mirror_target": resourceAwsEc2TrafficMirrorTarget(), - "aws_ec2_traffic_mirror_session": resourceAwsEc2TrafficMirrorSession(), - "aws_ec2_transit_gateway": resourceAwsEc2TransitGateway(), - "aws_ec2_transit_gateway_peering_attachment": resourceAwsEc2TransitGatewayPeeringAttachment(), - "aws_ec2_transit_gateway_peering_attachment_accepter": resourceAwsEc2TransitGatewayPeeringAttachmentAccepter(), - "aws_ec2_transit_gateway_prefix_list_reference": resourceAwsEc2TransitGatewayPrefixListReference(), - "aws_ec2_transit_gateway_route": resourceAwsEc2TransitGatewayRoute(), - "aws_ec2_transit_gateway_route_table": resourceAwsEc2TransitGatewayRouteTable(), - "aws_ec2_transit_gateway_route_table_association": resourceAwsEc2TransitGatewayRouteTableAssociation(), - "aws_ec2_transit_gateway_route_table_propagation": resourceAwsEc2TransitGatewayRouteTablePropagation(), - "aws_ec2_transit_gateway_vpc_attachment": resourceAwsEc2TransitGatewayVpcAttachment(), - "aws_ec2_transit_gateway_vpc_attachment_accepter": resourceAwsEc2TransitGatewayVpcAttachmentAccepter(), - "aws_ecr_lifecycle_policy": resourceAwsEcrLifecyclePolicy(), - "aws_ecrpublic_repository": resourceAwsEcrPublicRepository(), - "aws_ecr_registry_policy": resourceAwsEcrRegistryPolicy(), - "aws_ecr_replication_configuration": resourceAwsEcrReplicationConfiguration(), - "aws_ecr_repository": resourceAwsEcrRepository(), - "aws_ecr_repository_policy": resourceAwsEcrRepositoryPolicy(), - "aws_ecs_capacity_provider": resourceAwsEcsCapacityProvider(), - "aws_ecs_cluster": resourceAwsEcsCluster(), - "aws_ecs_service": resourceAwsEcsService(), - "aws_ecs_task_definition": resourceAwsEcsTaskDefinition(), - "aws_efs_access_point": resourceAwsEfsAccessPoint(), - "aws_efs_file_system": resourceAwsEfsFileSystem(), - "aws_efs_file_system_policy": resourceAwsEfsFileSystemPolicy(), - "aws_efs_mount_target": resourceAwsEfsMountTarget(), - "aws_egress_only_internet_gateway": resourceAwsEgressOnlyInternetGateway(), - "aws_eip": resourceAwsEip(), - "aws_eip_association": resourceAwsEipAssociation(), - "aws_eks_cluster": resourceAwsEksCluster(), - "aws_eks_addon": resourceAwsEksAddon(), - "aws_eks_fargate_profile": resourceAwsEksFargateProfile(), - "aws_eks_node_group": resourceAwsEksNodeGroup(), - "aws_elasticache_cluster": resourceAwsElasticacheCluster(), - "aws_elasticache_global_replication_group": resourceAwsElasticacheGlobalReplicationGroup(), - "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_instance_fleet": resourceAwsEMRInstanceFleet(), - "aws_emr_managed_scaling_policy": resourceAwsEMRManagedScalingPolicy(), - "aws_emr_security_configuration": resourceAwsEMRSecurityConfiguration(), - "aws_flow_log": resourceAwsFlowLog(), - "aws_fsx_lustre_file_system": resourceAwsFsxLustreFileSystem(), - "aws_fsx_windows_file_system": resourceAwsFsxWindowsFileSystem(), - "aws_fms_admin_account": resourceAwsFmsAdminAccount(), - "aws_fms_policy": resourceAwsFmsPolicy(), - "aws_gamelift_alias": resourceAwsGameliftAlias(), - "aws_gamelift_build": resourceAwsGameliftBuild(), - "aws_gamelift_fleet": resourceAwsGameliftFleet(), - "aws_gamelift_game_session_queue": resourceAwsGameliftGameSessionQueue(), - "aws_glacier_vault": resourceAwsGlacierVault(), - "aws_glacier_vault_lock": resourceAwsGlacierVaultLock(), - "aws_globalaccelerator_accelerator": resourceAwsGlobalAcceleratorAccelerator(), - "aws_globalaccelerator_endpoint_group": resourceAwsGlobalAcceleratorEndpointGroup(), - "aws_globalaccelerator_listener": resourceAwsGlobalAcceleratorListener(), - "aws_glue_catalog_database": resourceAwsGlueCatalogDatabase(), - "aws_glue_catalog_table": resourceAwsGlueCatalogTable(), - "aws_glue_classifier": resourceAwsGlueClassifier(), - "aws_glue_connection": resourceAwsGlueConnection(), - "aws_glue_dev_endpoint": resourceAwsGlueDevEndpoint(), - "aws_glue_crawler": resourceAwsGlueCrawler(), - "aws_glue_data_catalog_encryption_settings": resourceAwsGlueDataCatalogEncryptionSettings(), - "aws_glue_job": resourceAwsGlueJob(), - "aws_glue_ml_transform": resourceAwsGlueMLTransform(), - "aws_glue_partition": resourceAwsGluePartition(), - "aws_glue_registry": resourceAwsGlueRegistry(), - "aws_glue_resource_policy": resourceAwsGlueResourcePolicy(), - "aws_glue_schema": resourceAwsGlueSchema(), - "aws_glue_security_configuration": resourceAwsGlueSecurityConfiguration(), - "aws_glue_trigger": resourceAwsGlueTrigger(), - "aws_glue_user_defined_function": resourceAwsGlueUserDefinedFunction(), - "aws_glue_workflow": resourceAwsGlueWorkflow(), - "aws_guardduty_detector": resourceAwsGuardDutyDetector(), - "aws_guardduty_filter": resourceAwsGuardDutyFilter(), - "aws_guardduty_invite_accepter": resourceAwsGuardDutyInviteAccepter(), - "aws_guardduty_ipset": resourceAwsGuardDutyIpset(), - "aws_guardduty_member": resourceAwsGuardDutyMember(), - "aws_guardduty_organization_admin_account": resourceAwsGuardDutyOrganizationAdminAccount(), - "aws_guardduty_organization_configuration": resourceAwsGuardDutyOrganizationConfiguration(), - "aws_guardduty_publishing_destination": resourceAwsGuardDutyPublishingDestination(), - "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_imagebuilder_component": resourceAwsImageBuilderComponent(), - "aws_imagebuilder_distribution_configuration": resourceAwsImageBuilderDistributionConfiguration(), - "aws_imagebuilder_image": resourceAwsImageBuilderImage(), - "aws_imagebuilder_image_pipeline": resourceAwsImageBuilderImagePipeline(), - "aws_imagebuilder_image_recipe": resourceAwsImageBuilderImageRecipe(), - "aws_imagebuilder_infrastructure_configuration": resourceAwsImageBuilderInfrastructureConfiguration(), - "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_policy_attachment": resourceAwsIotPolicyAttachment(), - "aws_iot_thing": resourceAwsIotThing(), - "aws_iot_thing_principal_attachment": resourceAwsIotThingPrincipalAttachment(), - "aws_iot_thing_type": resourceAwsIotThingType(), - "aws_iot_topic_rule": resourceAwsIotTopicRule(), - "aws_iot_role_alias": resourceAwsIotRoleAlias(), - "aws_key_pair": resourceAwsKeyPair(), - "aws_kinesis_analytics_application": resourceAwsKinesisAnalyticsApplication(), - "aws_kinesisanalyticsv2_application": resourceAwsKinesisAnalyticsV2Application(), - "aws_kinesisanalyticsv2_application_snapshot": resourceAwsKinesisAnalyticsV2ApplicationSnapshot(), - "aws_kinesis_firehose_delivery_stream": resourceAwsKinesisFirehoseDeliveryStream(), - "aws_kinesis_stream": resourceAwsKinesisStream(), - "aws_kinesis_stream_consumer": resourceAwsKinesisStreamConsumer(), - "aws_kinesis_video_stream": resourceAwsKinesisVideoStream(), - "aws_kms_alias": resourceAwsKmsAlias(), - "aws_kms_external_key": resourceAwsKmsExternalKey(), - "aws_kms_grant": resourceAwsKmsGrant(), - "aws_kms_key": resourceAwsKmsKey(), - "aws_kms_ciphertext": resourceAwsKmsCiphertext(), - "aws_lakeformation_data_lake_settings": resourceAwsLakeFormationDataLakeSettings(), - "aws_lakeformation_permissions": resourceAwsLakeFormationPermissions(), - "aws_lakeformation_lf_tag": resourceAwsLakeFormationLFTag(), - "aws_lakeformation_resource": resourceAwsLakeFormationResource(), - "aws_lambda_alias": resourceAwsLambdaAlias(), - "aws_lambda_code_signing_config": resourceAwsLambdaCodeSigningConfig(), - "aws_lambda_event_source_mapping": resourceAwsLambdaEventSourceMapping(), - "aws_lambda_function_event_invoke_config": resourceAwsLambdaFunctionEventInvokeConfig(), - "aws_lambda_function": resourceAwsLambdaFunction(), - "aws_lambda_layer_version": resourceAwsLambdaLayerVersion(), - "aws_lambda_permission": resourceAwsLambdaPermission(), - "aws_lambda_provisioned_concurrency_config": resourceAwsLambdaProvisionedConcurrencyConfig(), - "aws_launch_configuration": resourceAwsLaunchConfiguration(), - "aws_launch_template": resourceAwsLaunchTemplate(), - "aws_lex_bot": resourceAwsLexBot(), - "aws_lex_bot_alias": resourceAwsLexBotAlias(), - "aws_lex_intent": resourceAwsLexIntent(), - "aws_lex_slot_type": resourceAwsLexSlotType(), - "aws_licensemanager_association": resourceAwsLicenseManagerAssociation(), - "aws_licensemanager_license_configuration": resourceAwsLicenseManagerLicenseConfiguration(), - "aws_lightsail_domain": resourceAwsLightsailDomain(), - "aws_lightsail_instance": resourceAwsLightsailInstance(), - "aws_lightsail_instance_public_ports": resourceAwsLightsailInstancePublicPorts(), - "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_macie2_account": resourceAwsMacie2Account(), - "aws_macie2_classification_job": resourceAwsMacie2ClassificationJob(), - "aws_macie2_custom_data_identifier": resourceAwsMacie2CustomDataIdentifier(), - "aws_macie2_findings_filter": resourceAwsMacie2FindingsFilter(), - "aws_macie2_invitation_accepter": resourceAwsMacie2InvitationAccepter(), - "aws_macie2_member": resourceAwsMacie2Member(), - "aws_macie2_organization_admin_account": resourceAwsMacie2OrganizationAdminAccount(), - "aws_macie_member_account_association": resourceAwsMacieMemberAccountAssociation(), - "aws_macie_s3_bucket_association": resourceAwsMacieS3BucketAssociation(), - "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), - "aws_mq_broker": resourceAwsMqBroker(), - "aws_mq_configuration": resourceAwsMqConfiguration(), - "aws_media_convert_queue": resourceAwsMediaConvertQueue(), - "aws_media_package_channel": resourceAwsMediaPackageChannel(), - "aws_media_store_container": resourceAwsMediaStoreContainer(), - "aws_media_store_container_policy": resourceAwsMediaStoreContainerPolicy(), - "aws_msk_cluster": resourceAwsMskCluster(), - "aws_msk_configuration": resourceAwsMskConfiguration(), - "aws_msk_scram_secret_association": resourceAwsMskScramSecretAssociation(), - "aws_mwaa_environment": resourceAwsMwaaEnvironment(), - "aws_nat_gateway": resourceAwsNatGateway(), - "aws_network_acl": resourceAwsNetworkAcl(), - "aws_default_network_acl": resourceAwsDefaultNetworkAcl(), - "aws_neptune_cluster": resourceAwsNeptuneCluster(), - "aws_neptune_cluster_instance": resourceAwsNeptuneClusterInstance(), - "aws_neptune_cluster_parameter_group": resourceAwsNeptuneClusterParameterGroup(), - "aws_neptune_cluster_snapshot": resourceAwsNeptuneClusterSnapshot(), - "aws_neptune_event_subscription": resourceAwsNeptuneEventSubscription(), - "aws_neptune_parameter_group": resourceAwsNeptuneParameterGroup(), - "aws_neptune_subnet_group": resourceAwsNeptuneSubnetGroup(), - "aws_network_acl_rule": resourceAwsNetworkAclRule(), - "aws_network_interface": resourceAwsNetworkInterface(), - "aws_network_interface_attachment": resourceAwsNetworkInterfaceAttachment(), - "aws_networkfirewall_firewall": resourceAwsNetworkFirewallFirewall(), - "aws_networkfirewall_firewall_policy": resourceAwsNetworkFirewallFirewallPolicy(), - "aws_networkfirewall_logging_configuration": resourceAwsNetworkFirewallLoggingConfiguration(), - "aws_networkfirewall_resource_policy": resourceAwsNetworkFirewallResourcePolicy(), - "aws_networkfirewall_rule_group": resourceAwsNetworkFirewallRuleGroup(), - "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_delegated_administrator": resourceAwsOrganizationsDelegatedAdministrator(), - "aws_organizations_policy": resourceAwsOrganizationsPolicy(), - "aws_organizations_policy_attachment": resourceAwsOrganizationsPolicyAttachment(), - "aws_organizations_organizational_unit": resourceAwsOrganizationsOrganizationalUnit(), - "aws_placement_group": resourceAwsPlacementGroup(), - "aws_prometheus_workspace": resourceAwsPrometheusWorkspace(), - "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), - "aws_qldb_ledger": resourceAwsQLDBLedger(), - "aws_quicksight_group": resourceAwsQuickSightGroup(), - "aws_quicksight_user": resourceAwsQuickSightUser(), - "aws_ram_principal_association": resourceAwsRamPrincipalAssociation(), - "aws_ram_resource_association": resourceAwsRamResourceAssociation(), - "aws_ram_resource_share": resourceAwsRamResourceShare(), - "aws_ram_resource_share_accepter": resourceAwsRamResourceShareAccepter(), - "aws_rds_cluster": resourceAwsRDSCluster(), - "aws_rds_cluster_endpoint": resourceAwsRDSClusterEndpoint(), - "aws_rds_cluster_instance": resourceAwsRDSClusterInstance(), - "aws_rds_cluster_parameter_group": resourceAwsRDSClusterParameterGroup(), - "aws_rds_global_cluster": resourceAwsRDSGlobalCluster(), - "aws_redshift_cluster": resourceAwsRedshiftCluster(), - "aws_redshift_security_group": resourceAwsRedshiftSecurityGroup(), - "aws_redshift_parameter_group": resourceAwsRedshiftParameterGroup(), - "aws_redshift_subnet_group": resourceAwsRedshiftSubnetGroup(), - "aws_redshift_snapshot_copy_grant": resourceAwsRedshiftSnapshotCopyGrant(), - "aws_redshift_snapshot_schedule": resourceAwsRedshiftSnapshotSchedule(), - "aws_redshift_snapshot_schedule_association": resourceAwsRedshiftSnapshotScheduleAssociation(), - "aws_redshift_event_subscription": resourceAwsRedshiftEventSubscription(), - "aws_resourcegroups_group": resourceAwsResourceGroupsGroup(), - "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), - "aws_route53_hosted_zone_dnssec": resourceAwsRoute53HostedZoneDnssec(), - "aws_route53_key_signing_key": resourceAwsRoute53KeySigningKey(), - "aws_route53_query_log": resourceAwsRoute53QueryLog(), - "aws_route53_record": resourceAwsRoute53Record(), - "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), - "aws_route53_vpc_association_authorization": resourceAwsRoute53VPCAssociationAuthorization(), - "aws_route53_zone": resourceAwsRoute53Zone(), - "aws_route53_health_check": resourceAwsRoute53HealthCheck(), - "aws_route53_resolver_dnssec_config": resourceAwsRoute53ResolverDnssecConfig(), - "aws_route53_resolver_endpoint": resourceAwsRoute53ResolverEndpoint(), - "aws_route53_resolver_firewall_domain_list": resourceAwsRoute53ResolverFirewallDomainList(), - "aws_route53_resolver_firewall_rule": resourceAwsRoute53ResolverFirewallRule(), - "aws_route53_resolver_firewall_rule_group": resourceAwsRoute53ResolverFirewallRuleGroup(), - "aws_route53_resolver_firewall_rule_group_association": resourceAwsRoute53ResolverFirewallRuleGroupAssociation(), - "aws_route53_resolver_query_log_config": resourceAwsRoute53ResolverQueryLogConfig(), - "aws_route53_resolver_query_log_config_association": resourceAwsRoute53ResolverQueryLogConfigAssociation(), - "aws_route53_resolver_rule_association": resourceAwsRoute53ResolverRuleAssociation(), - "aws_route53_resolver_rule": resourceAwsRoute53ResolverRule(), - "aws_route": resourceAwsRoute(), - "aws_route_table": resourceAwsRouteTable(), - "aws_default_route_table": resourceAwsDefaultRouteTable(), - "aws_route_table_association": resourceAwsRouteTableAssociation(), - "aws_sagemaker_app": resourceAwsSagemakerApp(), - "aws_sagemaker_app_image_config": resourceAwsSagemakerAppImageConfig(), - "aws_sagemaker_code_repository": resourceAwsSagemakerCodeRepository(), - "aws_sagemaker_domain": resourceAwsSagemakerDomain(), - "aws_sagemaker_endpoint": resourceAwsSagemakerEndpoint(), - "aws_sagemaker_endpoint_configuration": resourceAwsSagemakerEndpointConfiguration(), - "aws_sagemaker_feature_group": resourceAwsSagemakerFeatureGroup(), - "aws_sagemaker_image": resourceAwsSagemakerImage(), - "aws_sagemaker_image_version": resourceAwsSagemakerImageVersion(), - "aws_sagemaker_model": resourceAwsSagemakerModel(), - "aws_sagemaker_model_package_group": resourceAwsSagemakerModelPackageGroup(), - "aws_sagemaker_notebook_instance_lifecycle_configuration": resourceAwsSagemakerNotebookInstanceLifeCycleConfiguration(), - "aws_sagemaker_notebook_instance": resourceAwsSagemakerNotebookInstance(), - "aws_sagemaker_user_profile": resourceAwsSagemakerUserProfile(), - "aws_secretsmanager_secret": resourceAwsSecretsManagerSecret(), - "aws_secretsmanager_secret_policy": resourceAwsSecretsManagerSecretPolicy(), - "aws_secretsmanager_secret_version": resourceAwsSecretsManagerSecretVersion(), - "aws_secretsmanager_secret_rotation": resourceAwsSecretsManagerSecretRotation(), - "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_email_identity": resourceAwsSesEmailIdentity(), - "aws_ses_identity_policy": resourceAwsSesIdentityPolicy(), - "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_access_point": resourceAwsS3AccessPoint(), - "aws_s3_account_public_access_block": resourceAwsS3AccountPublicAccessBlock(), - "aws_s3_bucket": resourceAwsS3Bucket(), - "aws_s3_bucket_analytics_configuration": resourceAwsS3BucketAnalyticsConfiguration(), - "aws_s3_bucket_policy": resourceAwsS3BucketPolicy(), - "aws_s3_bucket_public_access_block": resourceAwsS3BucketPublicAccessBlock(), - "aws_s3_bucket_object": resourceAwsS3BucketObject(), - "aws_s3_bucket_ownership_controls": resourceAwsS3BucketOwnershipControls(), - "aws_s3_bucket_notification": resourceAwsS3BucketNotification(), - "aws_s3_bucket_metric": resourceAwsS3BucketMetric(), - "aws_s3_bucket_inventory": resourceAwsS3BucketInventory(), - "aws_s3_object_copy": resourceAwsS3ObjectCopy(), - "aws_s3control_bucket": resourceAwsS3ControlBucket(), - "aws_s3control_bucket_policy": resourceAwsS3ControlBucketPolicy(), - "aws_s3control_bucket_lifecycle_configuration": resourceAwsS3ControlBucketLifecycleConfiguration(), - "aws_s3outposts_endpoint": resourceAwsS3OutpostsEndpoint(), - "aws_security_group": resourceAwsSecurityGroup(), - "aws_network_interface_sg_attachment": resourceAwsNetworkInterfaceSGAttachment(), - "aws_default_security_group": resourceAwsDefaultSecurityGroup(), - "aws_security_group_rule": resourceAwsSecurityGroupRule(), - "aws_securityhub_account": resourceAwsSecurityHubAccount(), - "aws_securityhub_action_target": resourceAwsSecurityHubActionTarget(), - "aws_securityhub_insight": resourceAwsSecurityHubInsight(), - "aws_securityhub_invite_accepter": resourceAwsSecurityHubInviteAccepter(), - "aws_securityhub_member": resourceAwsSecurityHubMember(), - "aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(), - "aws_securityhub_product_subscription": resourceAwsSecurityHubProductSubscription(), - "aws_securityhub_standards_subscription": resourceAwsSecurityHubStandardsSubscription(), - "aws_servicecatalog_budget_resource_association": resourceAwsServiceCatalogBudgetResourceAssociation(), - "aws_servicecatalog_constraint": resourceAwsServiceCatalogConstraint(), - "aws_servicecatalog_organizations_access": resourceAwsServiceCatalogOrganizationsAccess(), - "aws_servicecatalog_portfolio": resourceAwsServiceCatalogPortfolio(), - "aws_servicecatalog_portfolio_share": resourceAwsServiceCatalogPortfolioShare(), - "aws_servicecatalog_product": resourceAwsServiceCatalogProduct(), - "aws_servicecatalog_service_action": resourceAwsServiceCatalogServiceAction(), - "aws_servicecatalog_tag_option": resourceAwsServiceCatalogTagOption(), - "aws_servicecatalog_tag_option_resource_association": resourceAwsServiceCatalogTagOptionResourceAssociation(), - "aws_servicecatalog_product_portfolio_association": resourceAwsServiceCatalogProductPortfolioAssociation(), - "aws_servicecatalog_provisioning_artifact": resourceAwsServiceCatalogProvisioningArtifact(), - "aws_service_discovery_http_namespace": resourceAwsServiceDiscoveryHttpNamespace(), - "aws_service_discovery_private_dns_namespace": resourceAwsServiceDiscoveryPrivateDnsNamespace(), - "aws_service_discovery_public_dns_namespace": resourceAwsServiceDiscoveryPublicDnsNamespace(), - "aws_service_discovery_service": resourceAwsServiceDiscoveryService(), - "aws_servicequotas_service_quota": resourceAwsServiceQuotasServiceQuota(), - "aws_shield_protection": resourceAwsShieldProtection(), - "aws_signer_signing_job": resourceAwsSignerSigningJob(), - "aws_signer_signing_profile": resourceAwsSignerSigningProfile(), - "aws_signer_signing_profile_permission": resourceAwsSignerSigningProfilePermission(), - "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_ssoadmin_account_assignment": resourceAwsSsoAdminAccountAssignment(), - "aws_ssoadmin_managed_policy_attachment": resourceAwsSsoAdminManagedPolicyAttachment(), - "aws_ssoadmin_permission_set": resourceAwsSsoAdminPermissionSet(), - "aws_ssoadmin_permission_set_inline_policy": resourceAwsSsoAdminPermissionSetInlinePolicy(), - "aws_storagegateway_cache": resourceAwsStorageGatewayCache(), - "aws_storagegateway_cached_iscsi_volume": resourceAwsStorageGatewayCachedIscsiVolume(), - "aws_storagegateway_gateway": resourceAwsStorageGatewayGateway(), - "aws_storagegateway_nfs_file_share": resourceAwsStorageGatewayNfsFileShare(), - "aws_storagegateway_smb_file_share": resourceAwsStorageGatewaySmbFileShare(), - "aws_storagegateway_stored_iscsi_volume": resourceAwsStorageGatewayStoredIscsiVolume(), - "aws_storagegateway_tape_pool": resourceAwsStorageGatewayTapePool(), - "aws_storagegateway_upload_buffer": resourceAwsStorageGatewayUploadBuffer(), - "aws_storagegateway_working_storage": resourceAwsStorageGatewayWorkingStorage(), - "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_swf_domain": resourceAwsSwfDomain(), - "aws_synthetics_canary": resourceAwsSyntheticsCanary(), - "aws_timestreamwrite_database": resourceAwsTimestreamWriteDatabase(), - "aws_timestreamwrite_table": resourceAwsTimestreamWriteTable(), - "aws_transfer_server": resourceAwsTransferServer(), - "aws_transfer_ssh_key": resourceAwsTransferSshKey(), - "aws_transfer_user": resourceAwsTransferUser(), - "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_vpc_ipv4_cidr_block_association": resourceAwsVpcIpv4CidrBlockAssociation(), - "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_wafv2_ip_set": resourceAwsWafv2IPSet(), - "aws_wafv2_regex_pattern_set": resourceAwsWafv2RegexPatternSet(), - "aws_wafv2_rule_group": resourceAwsWafv2RuleGroup(), - "aws_wafv2_web_acl": resourceAwsWafv2WebACL(), - "aws_wafv2_web_acl_association": resourceAwsWafv2WebACLAssociation(), - "aws_wafv2_web_acl_logging_configuration": resourceAwsWafv2WebACLLoggingConfiguration(), - "aws_worklink_fleet": resourceAwsWorkLinkFleet(), - "aws_worklink_website_certificate_authority_association": resourceAwsWorkLinkWebsiteCertificateAuthorityAssociation(), - "aws_workspaces_directory": resourceAwsWorkspacesDirectory(), - "aws_workspaces_workspace": resourceAwsWorkspacesWorkspace(), - "aws_batch_compute_environment": resourceAwsBatchComputeEnvironment(), - "aws_batch_job_definition": resourceAwsBatchJobDefinition(), - "aws_batch_job_queue": resourceAwsBatchJobQueue(), - "aws_pinpoint_app": resourceAwsPinpointApp(), - "aws_pinpoint_adm_channel": resourceAwsPinpointADMChannel(), - "aws_pinpoint_apns_channel": resourceAwsPinpointAPNSChannel(), - "aws_pinpoint_apns_sandbox_channel": resourceAwsPinpointAPNSSandboxChannel(), - "aws_pinpoint_apns_voip_channel": resourceAwsPinpointAPNSVoipChannel(), - "aws_pinpoint_apns_voip_sandbox_channel": resourceAwsPinpointAPNSVoipSandboxChannel(), - "aws_pinpoint_baidu_channel": resourceAwsPinpointBaiduChannel(), - "aws_pinpoint_email_channel": resourceAwsPinpointEmailChannel(), - "aws_pinpoint_event_stream": resourceAwsPinpointEventStream(), - "aws_pinpoint_gcm_channel": resourceAwsPinpointGCMChannel(), - "aws_pinpoint_sms_channel": resourceAwsPinpointSMSChannel(), - "aws_xray_encryption_config": resourceAwsXrayEncryptionConfig(), - "aws_xray_group": resourceAwsXrayGroup(), - "aws_xray_sampling_rule": resourceAwsXraySamplingRule(), - "aws_workspaces_ip_group": resourceAwsWorkspacesIpGroup(), - - // 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 - // back to the old ALB version. IF the Terraform supported aliases for resources - // this would be a whole lot simpler - "aws_alb": resourceAwsLb(), - "aws_lb": resourceAwsLb(), - "aws_alb_listener": resourceAwsLbListener(), - "aws_lb_listener": resourceAwsLbListener(), - "aws_alb_listener_certificate": resourceAwsLbListenerCertificate(), - "aws_lb_listener_certificate": resourceAwsLbListenerCertificate(), - "aws_alb_listener_rule": resourceAwsLbbListenerRule(), - "aws_lb_listener_rule": resourceAwsLbbListenerRule(), - "aws_alb_target_group": resourceAwsLbTargetGroup(), - "aws_lb_target_group": resourceAwsLbTargetGroup(), - "aws_alb_target_group_attachment": resourceAwsLbTargetGroupAttachment(), - "aws_lb_target_group_attachment": resourceAwsLbTargetGroupAttachment(), - }, - } - - // Avoid Go formatting churn and Git conflicts - // You probably should not do this - provider.DataSourcesMap["aws_serverlessapplicationrepository_application"] = dataSourceAwsServerlessApplicationRepositoryApplication() - provider.ResourcesMap["aws_serverlessapplicationrepository_cloudformation_stack"] = resourceAwsServerlessApplicationRepositoryCloudFormationStack() - - provider.ConfigureFunc = func(d *schema.ResourceData) (interface{}, error) { - terraformVersion := provider.TerraformVersion - if terraformVersion == "" { - // Terraform 0.12 introduced this field to the protocol - // We can therefore assume that if it's missing it's 0.10 or 0.11 - terraformVersion = "0.11+compatible" - } - return providerConfigure(d, terraformVersion) - } - - return provider -} - -var descriptions map[string]string -var endpointServiceNames []string - -func init() { - descriptions = map[string]string{ - "region": "The region where AWS operations will take place. Examples\n" + - "are us-east-1, us-west-2, etc.", // lintignore:AWSAT003 - - "access_key": "The access key for API operations. You can retrieve this\n" + - "from the 'Security & Credentials' section of the AWS console.", - - "secret_key": "The secret key for API operations. You can retrieve this\n" + - "from the 'Security & Credentials' section of the AWS console.", - - "profile": "The profile for API operations. If not set, the default profile\n" + - "created with `aws configure` will be used.", - - "shared_credentials_file": "The path to the shared credentials file. If not set\n" + - "this defaults to ~/.aws/credentials.", - - "token": "session token. A session token is only required if you are\n" + - "using temporary security credentials.", - - "max_retries": "The maximum number of times an AWS API request is\n" + - "being executed. If the API request still fails, an error is\n" + - "thrown.", - - "endpoint": "Use this to override the default service endpoint URL", - - "insecure": "Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted," + - "default value is `false`", - - "skip_credentials_validation": "Skip the credentials validation via STS API. " + - "Used for AWS API implementations that do not have STS available/implemented.", - - "skip_get_ec2_platforms": "Skip getting the supported EC2 platforms. " + - "Used by users that don't have ec2:DescribeAccountAttributes permissions.", - - "skip_region_validation": "Skip static validation of region name. " + - "Used by users of alternative AWS-like APIs or users w/ access to regions that are not public (yet).", - - "skip_requesting_account_id": "Skip requesting the account ID. " + - "Used for AWS API implementations that do not have IAM/STS API and/or metadata API.", - - "skip_medatadata_api_check": "Skip the AWS Metadata API check. " + - "Used for AWS API implementations that do not have a metadata api endpoint.", - - "s3_force_path_style": "Set this to true to force the request to use path-style addressing,\n" + - "i.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\n" + - "use virtual hosted bucket addressing when possible\n" + - "(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.", - } - - endpointServiceNames = []string{ - "accessanalyzer", - "acm", - "acmpca", - "amplify", - "apigateway", - "appconfig", - "applicationautoscaling", - "applicationinsights", - "appmesh", - "apprunner", - "appstream", - "appsync", - "athena", - "auditmanager", - "autoscaling", - "autoscalingplans", - "backup", - "batch", - "budgets", - "chime", - "cloud9", - "cloudformation", - "cloudfront", - "cloudhsm", - "cloudsearch", - "cloudtrail", - "cloudwatch", - "cloudwatchevents", - "cloudwatchlogs", - "codeartifact", - "codebuild", - "codecommit", - "codedeploy", - "codepipeline", - "codestarconnections", - "cognitoidentity", - "cognitoidp", - "configservice", - "connect", - "cur", - "dataexchange", - "datapipeline", - "datasync", - "dax", - "detective", - "devicefarm", - "directconnect", - "dlm", - "dms", - "docdb", - "ds", - "dynamodb", - "ec2", - "ecr", - "ecrpublic", - "ecs", - "efs", - "eks", - "elasticache", - "elasticbeanstalk", - "elastictranscoder", - "elb", - "emr", - "emrcontainers", - "es", - "firehose", - "fms", - "forecast", - "fsx", - "gamelift", - "glacier", - "globalaccelerator", - "glue", - "greengrass", - "guardduty", - "iam", - "identitystore", - "imagebuilder", - "inspector", - "iot", - "iotanalytics", - "iotevents", - "kafka", - "kinesis", - "kinesisanalytics", - "kinesisanalyticsv2", - "kinesisvideo", - "kms", - "lakeformation", - "lambda", - "lexmodels", - "licensemanager", - "lightsail", - "macie", - "macie2", - "managedblockchain", - "marketplacecatalog", - "mediaconnect", - "mediaconvert", - "medialive", - "mediapackage", - "mediastore", - "mediastoredata", - "mq", - "mwaa", - "neptune", - "networkfirewall", - "networkmanager", - "opsworks", - "organizations", - "outposts", - "personalize", - "pinpoint", - "pricing", - "qldb", - "quicksight", - "ram", - "rds", - "redshift", - "resourcegroups", - "resourcegroupstaggingapi", - "route53", - "route53domains", - "route53resolver", - "s3", - "s3control", - "s3outposts", - "sagemaker", - "sdb", - "secretsmanager", - "securityhub", - "serverlessrepo", - "servicecatalog", - "servicediscovery", - "servicequotas", - "ses", - "shield", - "signer", - "sns", - "sqs", - "ssm", - "ssoadmin", - "stepfunctions", - "storagegateway", - "sts", - "swf", - "synthetics", - "timestreamwrite", - "transfer", - "waf", - "wafregional", - "wafv2", - "worklink", - "workmail", - "workspaces", - "xray", - } -} - -func providerConfigure(d *schema.ResourceData, terraformVersion string) (interface{}, error) { - config := Config{ - AccessKey: d.Get("access_key").(string), - SecretKey: d.Get("secret_key").(string), - Profile: d.Get("profile").(string), - Token: d.Get("token").(string), - Region: d.Get("region").(string), - CredsFilename: d.Get("shared_credentials_file").(string), - DefaultTagsConfig: expandProviderDefaultTags(d.Get("default_tags").([]interface{})), - Endpoints: make(map[string]string), - MaxRetries: d.Get("max_retries").(int), - IgnoreTagsConfig: expandProviderIgnoreTags(d.Get("ignore_tags").([]interface{})), - Insecure: d.Get("insecure").(bool), - SkipCredsValidation: d.Get("skip_credentials_validation").(bool), - SkipGetEC2Platforms: d.Get("skip_get_ec2_platforms").(bool), - SkipRegionValidation: d.Get("skip_region_validation").(bool), - SkipRequestingAccountId: d.Get("skip_requesting_account_id").(bool), - SkipMetadataApiCheck: d.Get("skip_metadata_api_check").(bool), - S3ForcePathStyle: d.Get("s3_force_path_style").(bool), - terraformVersion: terraformVersion, - } - - if l, ok := d.Get("assume_role").([]interface{}); ok && len(l) > 0 && l[0] != nil { - m := l[0].(map[string]interface{}) - - if v, ok := m["duration_seconds"].(int); ok && v != 0 { - config.AssumeRoleDurationSeconds = v - } - - if v, ok := m["external_id"].(string); ok && v != "" { - config.AssumeRoleExternalID = v - } - - if v, ok := m["policy"].(string); ok && v != "" { - config.AssumeRolePolicy = v - } - - if policyARNSet, ok := m["policy_arns"].(*schema.Set); ok && policyARNSet.Len() > 0 { - for _, policyARNRaw := range policyARNSet.List() { - policyARN, ok := policyARNRaw.(string) - - if !ok { - continue - } - - config.AssumeRolePolicyARNs = append(config.AssumeRolePolicyARNs, policyARN) - } - } - - if v, ok := m["role_arn"].(string); ok && v != "" { - config.AssumeRoleARN = v - } - - if v, ok := m["session_name"].(string); ok && v != "" { - config.AssumeRoleSessionName = v - } - - if tagMapRaw, ok := m["tags"].(map[string]interface{}); ok && len(tagMapRaw) > 0 { - config.AssumeRoleTags = make(map[string]string) - - for k, vRaw := range tagMapRaw { - v, ok := vRaw.(string) - - if !ok { - continue - } - - config.AssumeRoleTags[k] = v - } - } - - if transitiveTagKeySet, ok := m["transitive_tag_keys"].(*schema.Set); ok && transitiveTagKeySet.Len() > 0 { - for _, transitiveTagKeyRaw := range transitiveTagKeySet.List() { - transitiveTagKey, ok := transitiveTagKeyRaw.(string) - - if !ok { - continue - } - - config.AssumeRoleTransitiveTagKeys = append(config.AssumeRoleTransitiveTagKeys, transitiveTagKey) - } - } - - log.Printf("[INFO] assume_role configuration set: (ARN: %q, SessionID: %q, ExternalID: %q)", config.AssumeRoleARN, config.AssumeRoleSessionName, config.AssumeRoleExternalID) - } - - endpointsSet := d.Get("endpoints").(*schema.Set) - - for _, endpointsSetI := range endpointsSet.List() { - endpoints := endpointsSetI.(map[string]interface{}) - for _, endpointServiceName := range endpointServiceNames { - config.Endpoints[endpointServiceName] = endpoints[endpointServiceName].(string) - } - } - - if v, ok := d.GetOk("allowed_account_ids"); ok { - for _, accountIDRaw := range v.(*schema.Set).List() { - config.AllowedAccountIds = append(config.AllowedAccountIds, accountIDRaw.(string)) - } - } - - if v, ok := d.GetOk("forbidden_account_ids"); ok { - for _, accountIDRaw := range v.(*schema.Set).List() { - config.ForbiddenAccountIds = append(config.ForbiddenAccountIds, accountIDRaw.(string)) - } - } - - return config.Client() -} - -// This is a global MutexKV for use within this plugin. -var awsMutexKV = mutexkv.NewMutexKV() - -func assumeRoleSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "duration_seconds": { - Type: schema.TypeInt, - Optional: true, - Description: "Seconds to restrict the assume role session duration.", - }, - "external_id": { - Type: schema.TypeString, - Optional: true, - Description: "Unique identifier that might be required for assuming a role in another account.", - }, - "policy": { - Type: schema.TypeString, - Optional: true, - Description: "IAM Policy JSON describing further restricting permissions for the IAM Role being assumed.", - ValidateFunc: validation.StringIsJSON, - }, - "policy_arns": { - Type: schema.TypeSet, - Optional: true, - Description: "Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.", - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateArn, - }, - }, - "role_arn": { - Type: schema.TypeString, - Optional: true, - Description: "Amazon Resource Name of an IAM Role to assume prior to making API calls.", - ValidateFunc: validateArn, - }, - "session_name": { - Type: schema.TypeString, - Optional: true, - Description: "Identifier for the assumed role session.", - }, - "tags": { - Type: schema.TypeMap, - Optional: true, - Description: "Assume role session tags.", - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "transitive_tag_keys": { - Type: schema.TypeSet, - Optional: true, - Description: "Assume role session tag keys to pass to any subsequent sessions.", - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - }, - } -} - -func endpointsSchema() *schema.Schema { - endpointsAttributes := make(map[string]*schema.Schema) - - for _, endpointServiceName := range endpointServiceNames { - endpointsAttributes[endpointServiceName] = &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", - Description: descriptions["endpoint"], - } - } - - return &schema.Schema{ - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: endpointsAttributes, - }, - } -} - -func expandProviderDefaultTags(l []interface{}) *keyvaluetags.DefaultConfig { - if len(l) == 0 || l[0] == nil { - return nil - } - - defaultConfig := &keyvaluetags.DefaultConfig{} - m := l[0].(map[string]interface{}) - - if v, ok := m["tags"].(map[string]interface{}); ok { - defaultConfig.Tags = keyvaluetags.New(v) - } - return defaultConfig -} - -func expandProviderIgnoreTags(l []interface{}) *keyvaluetags.IgnoreConfig { - if len(l) == 0 || l[0] == nil { - return nil - } - - ignoreConfig := &keyvaluetags.IgnoreConfig{} - m := l[0].(map[string]interface{}) - - if v, ok := m["keys"].(*schema.Set); ok { - ignoreConfig.Keys = keyvaluetags.New(v.List()) - } - - if v, ok := m["key_prefixes"].(*schema.Set); ok { - ignoreConfig.KeyPrefixes = keyvaluetags.New(v.List()) - } - - return ignoreConfig -} - -// ReverseDns switches a DNS hostname to reverse DNS and vice-versa. -func ReverseDns(hostname string) string { - parts := strings.Split(hostname, ".") - - for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 { - parts[i], parts[j] = parts[j], parts[i] - } - - return strings.Join(parts, ".") -} diff --git a/aws/resource_aws_lakeformation_lf_tag.go b/aws/resource_aws_lakeformation_lf_tag.go deleted file mode 100644 index 894af82d9060..000000000000 --- a/aws/resource_aws_lakeformation_lf_tag.go +++ /dev/null @@ -1,182 +0,0 @@ -package aws - -import ( - "fmt" - "log" - "regexp" - "strings" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceAwsLakeFormationLFTag() *schema.Resource { - return &schema.Resource{ - Create: resourceAwsLakeFormationLFTagCreate, - Read: resourceAwsLakeFormationLFTagRead, - Update: resourceAwsLakeFormationLFTagUpdate, - Delete: resourceAwsLakeFormationLFTagDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - "values": { - Type: schema.TypeSet, - Required: true, - MinItems: 1, - MaxItems: 15, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateLFTagValues(), - }, - Set: schema.HashString, - }, - "catalog_id": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - }, - }, - } -} - -func resourceAwsLakeFormationLFTagCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - tagKey := d.Get("key").(string) - tagValues := d.Get("values").(*schema.Set) - - var catalogID string - if v, ok := d.GetOk("catalog_id"); ok { - catalogID = v.(string) - } else { - catalogID = meta.(*AWSClient).accountid - } - - input := &lakeformation.CreateLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - TagValues: expandStringSet(tagValues), - } - - _, err := conn.CreateLFTag(input) - if err != nil { - return fmt.Errorf("Error creating Lake Formation LF-Tag: %w", err) - } - - d.SetId(fmt.Sprintf("%s:%s", catalogID, tagKey)) - - return resourceAwsLakeFormationLFTagRead(d, meta) -} - -func resourceAwsLakeFormationLFTagRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - catalogID, tagKey, err := readLFTagID(d.Id()) - if err != nil { - return err - } - - input := &lakeformation.GetLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - output, err := conn.GetLFTag(input) - if err != nil { - if isAWSErr(err, lakeformation.ErrCodeEntityNotFoundException, "") { - log.Printf("[WARN] Lake Formation LF-Tag (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - return fmt.Errorf("Error reading Lake Formation LF-Tag: %s", err.Error()) - } - - d.Set("key", output.TagKey) - d.Set("values", flattenStringList(output.TagValues)) - d.Set("catalog_id", output.CatalogId) - - return nil -} - -func resourceAwsLakeFormationLFTagUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - catalogID, tagKey, err := readLFTagID(d.Id()) - if err != nil { - return err - } - - o, n := d.GetChange("values") - os := o.(*schema.Set) - ns := n.(*schema.Set) - toAdd := expandStringSet(ns.Difference(os)) - toDelete := expandStringSet(os.Difference(ns)) - - input := &lakeformation.UpdateLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - if len(toAdd) > 0 { - input.TagValuesToAdd = toAdd - } - - if len(toDelete) > 0 { - input.TagValuesToDelete = toDelete - } - - _, err = conn.UpdateLFTag(input) - if err != nil { - return fmt.Errorf("Error updating Lake Formation LF-Tag (%s): %w", d.Id(), err) - } - - return resourceAwsLakeFormationLFTagRead(d, meta) -} - -func resourceAwsLakeFormationLFTagDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - catalogID, tagKey, err := readLFTagID(d.Id()) - if err != nil { - return err - } - - input := &lakeformation.DeleteLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - _, err = conn.DeleteLFTag(input) - if err != nil { - return fmt.Errorf("Error deleting Lake Formation LF-Tag (%s): %w", d.Id(), err) - } - - return nil -} - -func readLFTagID(id string) (catalogID string, tagKey string, err error) { - idParts := strings.Split(id, ":") - if len(idParts) != 2 { - return "", "", fmt.Errorf("Unexpected format of ID (%q), expected CATALOG-ID:TAG-KEY", id) - } - return idParts[0], idParts[1], nil -} - -func validateLFTagValues() schema.SchemaValidateFunc { - return validation.All( - validation.StringLenBetween(1, 255), - validation.StringMatch(regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:\*\/=+\-@%]*)$`), ""), - ) -} diff --git a/aws/resource_aws_lakeformation_lf_tag_test.go b/aws/resource_aws_lakeformation_lf_tag_test.go deleted file mode 100644 index aabf5488da10..000000000000 --- a/aws/resource_aws_lakeformation_lf_tag_test.go +++ /dev/null @@ -1,211 +0,0 @@ -package aws - -import ( - "fmt" - "strconv" - "strings" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" -) - -func testAccAWSLakeFormationLFTag_basic(t *testing.T) { - resourceName := "aws_lakeformation_lf_tag.test" - rKey := acctest.RandomWithPrefix("tf-acc-test") - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationLFTagConfig_basic(rKey), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "key", rKey), - resource.TestCheckResourceAttr(resourceName, "values.0", "value"), - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func testAccAWSLakeFormationLFTag_disappears(t *testing.T) { - resourceName := "aws_lakeformation_lf_tag.test" - rKey := acctest.RandomWithPrefix("tf-acc-test") - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationLFTagConfig_basic(rKey), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationLFTag(), resourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - -func testAccAWSLakeFormationLFTag_values(t *testing.T) { - resourceName := "aws_lakeformation_lf_tag.test" - rKey := acctest.RandomWithPrefix("tf-acc-test") - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationLFTagConfig_values(rKey, []string{"value1", "value2"}), - Destroy: false, - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "key", rKey), - resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - // Test an update that adds, removes and retains a tag value - Config: testAccAWSLakeFormationLFTagConfig_values(rKey, []string{"value1", "value3"}), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "key", rKey), - resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), - resource.TestCheckResourceAttr(resourceName, "values.1", "value3"), - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - ), - }, - }, - }) -} - -func testAccCheckAWSLakeFormationLFTagsDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_lakeformation_lf_tag" { - continue - } - - catalogID, tagKey, err := readLFTagID(rs.Primary.ID) - if err != nil { - return err - } - - input := &lakeformation.GetLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - if _, err := conn.GetLFTag(input); err != nil { - if isAWSErr(err, lakeformation.ErrCodeEntityNotFoundException, "") { - continue - } - // If the lake formation admin has been revoked, there will be access denied instead of entity not found - if isAWSErr(err, lakeformation.ErrCodeAccessDeniedException, "") { - continue - } - return err - } - return fmt.Errorf("Lake Formation LF-Tag (%s) still exists", rs.Primary.ID) - } - - return nil -} - -func testAccCheckAWSLakeFormationLFTagExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - catalogID, tagKey, err := readLFTagID(rs.Primary.ID) - if err != nil { - return err - } - - input := &lakeformation.GetLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn - _, err = conn.GetLFTag(input) - - if err != nil { - return err - } - - return nil - } -} - -func testAccAWSLakeFormationLFTagConfig_basic(rKey string) string { - return fmt.Sprintf(` -data "aws_caller_identity" "current" {} - -resource "aws_lakeformation_data_lake_settings" "test" { - admins = [data.aws_caller_identity.current.arn] -} - -resource "aws_lakeformation_lf_tag" "test" { - key = %[1]q - values = ["value"] - - # for consistency, ensure that admins are setup before testing - depends_on = [aws_lakeformation_data_lake_settings.test] -} -`, rKey) -} - -func testAccAWSLakeFormationLFTagConfig_values(rKey string, values []string) string { - quotedValues := make([]string, len(values)) - for i, v := range values { - quotedValues[i] = strconv.Quote(v) - } - - return fmt.Sprintf(` -data "aws_caller_identity" "current" {} - -resource "aws_lakeformation_data_lake_settings" "test" { - admins = [data.aws_caller_identity.current.arn] -} - -resource "aws_lakeformation_lf_tag" "test" { - key = %[1]q - values = [%s] - - # for consistency, ensure that admins are setup before testing - depends_on = [aws_lakeformation_data_lake_settings.test] -} -`, rKey, strings.Join(quotedValues, ",")) -} diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go deleted file mode 100644 index 914a7962a5df..000000000000 --- a/aws/resource_aws_lakeformation_permissions.go +++ /dev/null @@ -1,1301 +0,0 @@ -package aws - -import ( - "fmt" - "log" - "reflect" - "sort" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" - iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" -) - -func resourceAwsLakeFormationPermissions() *schema.Resource { - return &schema.Resource{ - Create: resourceAwsLakeFormationPermissionsCreate, - Read: resourceAwsLakeFormationPermissionsRead, - Update: resourceAwsLakeFormationPermissionsCreate, - Delete: resourceAwsLakeFormationPermissionsDelete, - - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - ValidateFunc: validateAwsAccountId, - }, - "catalog_resource": { - Type: schema.TypeBool, - Optional: true, - Default: false, - ExactlyOneOf: []string{ - "catalog_resource", - "data_location", - "database", - "lf_tag", - "lf_tag_policy", - "table", - "table_with_columns", - }, - }, - "data_location": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ExactlyOneOf: []string{ - "catalog_resource", - "data_location", - "database", - "lf_tag", - "lf_tag_policy", - "table", - "table_with_columns", - }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateArn, - }, - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - }, - }, - }, - "database": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ExactlyOneOf: []string{ - "catalog_resource", - "data_location", - "database", - "lf_tag", - "lf_tag_policy", - "table", - "table_with_columns", - }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "name": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - "lf_tag": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ExactlyOneOf: []string{ - "catalog_resource", - "data_location", - "database", - "lf_tag", - "lf_tag_policy", - "table", - "table_with_columns", - }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - "values": { - Type: schema.TypeSet, - Required: true, - MinItems: 1, - MaxItems: 15, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateLFTagValues(), - }, - Set: schema.HashString, - }, - "catalog_id": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - }, - }, - }, - }, - "lf_tag_policy": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ExactlyOneOf: []string{ - "catalog_resource", - "data_location", - "database", - "lf_tag", - "lf_tag_policy", - "table", - "table_with_columns", - }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "expression": { - Type: schema.TypeList, - Required: true, - MinItems: 1, - MaxItems: 5, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 128), - }, - "values": { - Type: schema.TypeSet, - Required: true, - MinItems: 1, - MaxItems: 15, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateLFTagValues(), - }, - Set: schema.HashString, - }, - }, - }, - }, - "resource_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(lakeformation.ResourceType_Values(), false), - }, - }, - }, - }, - "permissions": { - Type: schema.TypeList, - Required: true, - ForceNew: true, - MinItems: 1, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validatePermissions(), - }, - }, - "permissions_with_grant_option": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validatePermissions(), - }, - }, - "principal": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validatePrincipal, - }, - "table": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ExactlyOneOf: []string{ - "catalog_resource", - "data_location", - "database", - "lf_tag", - "lf_tag_policy", - "table", - "table_with_columns", - }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "database_name": { - Type: schema.TypeString, - Required: true, - }, - "name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - AtLeastOneOf: []string{ - "table.0.name", - "table.0.wildcard", - }, - }, - "wildcard": { - Type: schema.TypeBool, - Optional: true, - Default: false, - AtLeastOneOf: []string{ - "table.0.name", - "table.0.wildcard", - }, - }, - }, - }, - }, - "table_with_columns": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ExactlyOneOf: []string{ - "catalog_resource", - "data_location", - "database", - "lf_tag", - "lf_tag_policy", - "table", - "table_with_columns", - }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validateAwsAccountId, - }, - "column_names": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.NoZeroValues, - }, - AtLeastOneOf: []string{ - "table_with_columns.0.column_names", - "table_with_columns.0.wildcard", - }, - }, - "database_name": { - Type: schema.TypeString, - Required: true, - }, - "excluded_column_names": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.NoZeroValues, - }, - }, - "name": { - Type: schema.TypeString, - Required: true, - }, - "wildcard": { - Type: schema.TypeBool, - Optional: true, - Default: false, - AtLeastOneOf: []string{ - "table_with_columns.0.column_names", - "table_with_columns.0.wildcard", - }, - }, - }, - }, - }, - }, - } -} - -// The challenges with Lake Formation permissions are many. These are largely undocumented and -// discovered through trial and error. These are specific problems discovered thus far: -// 1. Implicit permissions granted by Lake Formation to data lake administrators are indistinguishable -// from explicit permissions. However, implicit permissions cannot be changed, revoked, or narrowed. -// 2. One set of permissions for one LF Resource going in, can come back from AWS as multiple sets of -// permissions for multiple LF Resources (e.g., SELECT, Table, TableWithColumns). -// 3. Valid permissions for a Table LF resource can come back in TableWithColumns and vice versa. - -// For 2 & 3, some peeking at the config (i.e., d.Get()) is necessary to filter the permissions AWS -// returns. - -func resourceAwsLakeFormationPermissionsCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - input := &lakeformation.GrantPermissionsInput{ - Permissions: expandStringList(d.Get("permissions").([]interface{})), - Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), - }, - Resource: &lakeformation.Resource{}, - } - - if v, ok := d.GetOk("catalog_id"); ok { - input.CatalogId = aws.String(v.(string)) - } - - if v, ok := d.GetOk("permissions_with_grant_option"); ok { - input.PermissionsWithGrantOption = expandStringList(v.([]interface{})) - } - - if _, ok := d.GetOk("catalog_resource"); ok { - input.Resource.Catalog = expandLakeFormationCatalogResource() - } - - if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.TableWithColumns = expandLakeFormationTableWithColumnsResource(v.([]interface{})[0].(map[string]interface{})) - } - - var output *lakeformation.GrantPermissionsOutput - err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { - var err error - output, err = conn.GrantPermissions(input) - if err != nil { - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { - return resource.RetryableError(err) - } - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Grantee has no permissions") { - return resource.RetryableError(err) - } - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "register the S3 path") { - return resource.RetryableError(err) - } - if tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeConcurrentModificationException) { - return resource.RetryableError(err) - } - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access requested permissions") { - return resource.RetryableError(err) - } - - return resource.NonRetryableError(fmt.Errorf("error creating Lake Formation Permissions: %w", err)) - } - return nil - }) - - if tfresource.TimedOut(err) { - output, err = conn.GrantPermissions(input) - } - - if err != nil { - return fmt.Errorf("error creating Lake Formation Permissions (input: %v): %w", input, err) - } - - if output == nil { - return fmt.Errorf("error creating Lake Formation Permissions: empty response") - } - - d.SetId(fmt.Sprintf("%d", hashcode.String(input.String()))) - - return resourceAwsLakeFormationPermissionsRead(d, meta) -} - -func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - input := &lakeformation.ListPermissionsInput{ - Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), - }, - Resource: &lakeformation.Resource{}, - } - - if v, ok := d.GetOk("catalog_id"); ok { - input.CatalogId = aws.String(v.(string)) - } - - if _, ok := d.GetOk("catalog_resource"); ok { - input.Resource.Catalog = expandLakeFormationCatalogResource() - } - - if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) - } - - tableType := "" - - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) - tableType = TableTypeTable - } - - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - // can't ListPermissions for TableWithColumns, so use Table instead - input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(v.([]interface{})[0].(map[string]interface{})) - tableType = TableTypeTableWithColumns - } - - log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) - var allPermissions []*lakeformation.PrincipalResourcePermissions - - err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { - err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { - for _, permission := range resp.PrincipalResourcePermissions { - if permission == nil { - continue - } - - allPermissions = append(allPermissions, permission) - } - return !lastPage - }) - - if err != nil { - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { - return resource.RetryableError(err) - } - return resource.NonRetryableError(fmt.Errorf("error reading Lake Formation Permissions: %w", err)) - } - return nil - }) - - if tfresource.TimedOut(err) { - err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { - for _, permission := range resp.PrincipalResourcePermissions { - if permission == nil { - continue - } - - allPermissions = append(allPermissions, permission) - } - return !lastPage - }) - } - - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) { - log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if !d.IsNewResource() && tfawserr.ErrMessageContains(err, "AccessDeniedException", "Resource does not exist") { - log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state: %s", d.Id(), err) - d.SetId("") - return nil - } - - if !d.IsNewResource() && len(allPermissions) == 0 { - log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state (0 permissions)", d.Id()) - d.SetId("") - return nil - } - - if err != nil { - return fmt.Errorf("error reading Lake Formation permissions: %w", err) - } - - // clean permissions = filter out permissions that do not pertain to this specific resource - - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - if input.Resource.Catalog != nil { - cleanPermissions = filterLakeFormationCatalogPermissions(allPermissions) - } - - if input.Resource.DataLocation != nil { - cleanPermissions = filterLakeFormationDataLocationPermissions(allPermissions) - } - - if input.Resource.Database != nil { - cleanPermissions = filterLakeFormationDatabasePermissions(allPermissions) - } - - if input.Resource.LFTag != nil { - cleanPermissions = filterLakeFormationLFTagPermissions(allPermissions) - } - - if input.Resource.LFTagPolicy != nil { - cleanPermissions = filterLakeFormationLFTagPolicyPermissions(allPermissions) - } - - if tableType == TableTypeTable { - cleanPermissions = filterLakeFormationTablePermissions( - aws.StringValue(input.Resource.Table.Name), - input.Resource.Table.TableWildcard != nil, - allPermissions, - ) - } - - if tableType == TableTypeTableWithColumns { - cleanPermissions = filterLakeFormationTableWithColumnsPermissions( - d.Get("table_with_columns.0.database_name").(string), - d.Get("table_with_columns.0.wildcard").(bool), - expandStringList(d.Get("table_with_columns.0.column_names").([]interface{})), - expandStringList(d.Get("table_with_columns.0.excluded_column_names").([]interface{})), - allPermissions, - ) - } - - if len(cleanPermissions) == 0 { - log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if len(cleanPermissions) != len(allPermissions) { - log.Printf("[INFO] Resource Lake Formation clean permissions (%d) and all permissions (%d) have different lengths (this is not necessarily a problem): %s", len(cleanPermissions), len(allPermissions), d.Id()) - } - - d.Set("principal", cleanPermissions[0].Principal.DataLakePrincipalIdentifier) - d.Set("permissions", flattenLakeFormationPermissions(cleanPermissions)) - d.Set("permissions_with_grant_option", flattenLakeFormationGrantPermissions(cleanPermissions)) - - if cleanPermissions[0].Resource.Catalog != nil { - d.Set("catalog_resource", true) - } - - if cleanPermissions[0].Resource.DataLocation != nil { - if err := d.Set("data_location", []interface{}{flattenLakeFormationDataLocationResource(cleanPermissions[0].Resource.DataLocation)}); err != nil { - return fmt.Errorf("error setting data_location: %w", err) - } - } else { - d.Set("data_location", nil) - } - - if cleanPermissions[0].Resource.Database != nil { - if err := d.Set("database", []interface{}{flattenLakeFormationDatabaseResource(cleanPermissions[0].Resource.Database)}); err != nil { - return fmt.Errorf("error setting database: %w", err) - } - } else { - d.Set("database", nil) - } - - if cleanPermissions[0].Resource.LFTag != nil { - if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { - return fmt.Errorf("error setting LF-Tag: %w", err) - } - } else { - d.Set("lf_tag", nil) - } - - if cleanPermissions[0].Resource.LFTagPolicy != nil { - if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { - return fmt.Errorf("error setting LF-Tag policy: %w", err) - } - } else { - d.Set("lf_tag_policy", nil) - } - - tableSet := false - - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { - // since perm list could include TableWithColumns, get the right one - for _, perm := range cleanPermissions { - if perm.Resource.Table != nil { - if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { - return fmt.Errorf("error setting table: %w", err) - } - tableSet = true - break - } - } - } - - if !tableSet { - d.Set("table", nil) - } - - twcSet := false - - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 { - // since perm list could include Table, get the right one - for _, perm := range cleanPermissions { - if perm.Resource.TableWithColumns != nil { - if err := d.Set("table_with_columns", []interface{}{flattenLakeFormationTableWithColumnsResource(perm.Resource.TableWithColumns)}); err != nil { - return fmt.Errorf("error setting table_with_columns: %w", err) - } - twcSet = true - break - } - } - } - - if !twcSet { - d.Set("table_with_columns", nil) - } - - return nil -} - -func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - input := &lakeformation.RevokePermissionsInput{ - Permissions: expandStringList(d.Get("permissions").([]interface{})), - Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), - }, - Resource: &lakeformation.Resource{}, - } - - if v, ok := d.GetOk("catalog_id"); ok { - input.CatalogId = aws.String(v.(string)) - } - - if v, ok := d.GetOk("permissions_with_grant_option"); ok { - input.PermissionsWithGrantOption = expandStringList(v.([]interface{})) - } - - if _, ok := d.GetOk("catalog_resource"); ok { - input.Resource.Catalog = expandLakeFormationCatalogResource() - } - - if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTag = expandLakeFormationLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.TableWithColumns = expandLakeFormationTableWithColumnsResource(v.([]interface{})[0].(map[string]interface{})) - } - - if input.Resource == nil || reflect.DeepEqual(input.Resource, &lakeformation.Resource{}) { - // if resource is empty, don't delete = it won't delete anything since this is the predicate - log.Printf("[WARN] No Lake Formation Resource with permissions to revoke") - return nil - } - - err := resource.Retry(2*time.Minute, func() *resource.RetryError { - var err error - _, err = conn.RevokePermissions(input) - if err != nil { - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "register the S3 path") { - return resource.RetryableError(err) - } - if tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeConcurrentModificationException) { - return resource.RetryableError(err) - } - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access requested permissions") { - return resource.RetryableError(err) - } - - return resource.NonRetryableError(fmt.Errorf("unable to revoke Lake Formation Permissions: %w", err)) - } - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.RevokePermissions(input) - } - - if err != nil { - return fmt.Errorf("unable to revoke LakeFormation Permissions (input: %v): %w", input, err) - } - - return nil -} - -const ( - TableNameAllTables = "ALL_TABLES" - TableTypeTable = "Table" - TableTypeTableWithColumns = "TableWithColumns" -) - -func filterLakeFormationTablePermissions(tableName string, tableWildcard bool, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { - // CREATE PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on Table, Name = (Table Name) - // LIST PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name) - // LIST PERMS = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard - - // CREATE PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on Table, TableWildcard - // LIST PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, TableWildcard, Name = ALL_TABLES - // LIST PERMS = SELECT on TableWithColumns, Name = ALL_TABLES, ColumnWildcard - - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - for _, perm := range allPermissions { - if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil { - if aws.StringValue(perm.Resource.TableWithColumns.Name) == tableName || (tableWildcard && aws.StringValue(perm.Resource.TableWithColumns.Name) == TableNameAllTables) { - if len(perm.Permissions) > 0 && aws.StringValue(perm.Permissions[0]) == lakeformation.PermissionSelect { - cleanPermissions = append(cleanPermissions, perm) - continue - } - - if len(perm.PermissionsWithGrantOption) > 0 && aws.StringValue(perm.PermissionsWithGrantOption[0]) == lakeformation.PermissionSelect { - cleanPermissions = append(cleanPermissions, perm) - continue - } - } - } - - if perm.Resource.Table != nil { - if aws.StringValue(perm.Resource.Table.Name) == tableName { - cleanPermissions = append(cleanPermissions, perm) - continue - } - - if perm.Resource.Table.TableWildcard != nil && tableWildcard { - cleanPermissions = append(cleanPermissions, perm) - continue - } - } - continue - } - - return cleanPermissions -} - -func filterLakeFormationTableWithColumnsPermissions(tableName string, columnWildcard bool, columnNames []*string, excludedColumnNames []*string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { - // CREATE PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard - // LIST PERMS = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name) - // LIST PERMS = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard - - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - for _, perm := range allPermissions { - if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnNames != nil { - if StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnNames, columnNames) { - cleanPermissions = append(cleanPermissions, perm) - continue - } - } - - if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil && (columnWildcard || len(excludedColumnNames) > 0) { - if (perm.Resource.TableWithColumns.ColumnWildcard.ExcludedColumnNames == nil && len(excludedColumnNames) == 0) || StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnWildcard.ExcludedColumnNames, excludedColumnNames) { - cleanPermissions = append(cleanPermissions, perm) - continue - } - } - - if perm.Resource.Table != nil && aws.StringValue(perm.Resource.Table.Name) == tableName { - cleanPermissions = append(cleanPermissions, perm) - continue - } - } - - return cleanPermissions -} - -func filterLakeFormationCatalogPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - for _, perm := range allPermissions { - if perm.Resource.Catalog != nil { - cleanPermissions = append(cleanPermissions, perm) - } - } - - return cleanPermissions -} - -func filterLakeFormationDataLocationPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - for _, perm := range allPermissions { - if perm.Resource.DataLocation != nil { - cleanPermissions = append(cleanPermissions, perm) - } - } - - return cleanPermissions -} - -func filterLakeFormationDatabasePermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - for _, perm := range allPermissions { - if perm.Resource.Database != nil { - cleanPermissions = append(cleanPermissions, perm) - } - } - - return cleanPermissions -} - -func filterLakeFormationLFTagPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - for _, perm := range allPermissions { - if perm.Resource.LFTag != nil { - cleanPermissions = append(cleanPermissions, perm) - } - } - - return cleanPermissions -} - -func filterLakeFormationLFTagPolicyPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - for _, perm := range allPermissions { - if perm.Resource.LFTagPolicy != nil { - cleanPermissions = append(cleanPermissions, perm) - } - } - - return cleanPermissions -} - -func expandLakeFormationCatalogResource() *lakeformation.CatalogResource { - return &lakeformation.CatalogResource{} -} - -func expandLakeFormationDataLocationResource(tfMap map[string]interface{}) *lakeformation.DataLocationResource { - if tfMap == nil { - return nil - } - - apiObject := &lakeformation.DataLocationResource{} - - if v, ok := tfMap["catalog_id"].(string); ok && v != "" { - apiObject.CatalogId = aws.String(v) - } - - if v, ok := tfMap["arn"].(string); ok && v != "" { - apiObject.ResourceArn = aws.String(v) - } - - return apiObject -} - -func flattenLakeFormationDataLocationResource(apiObject *lakeformation.DataLocationResource) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.CatalogId; v != nil { - tfMap["catalog_id"] = aws.StringValue(v) - } - - if v := apiObject.ResourceArn; v != nil { - tfMap["arn"] = aws.StringValue(v) - } - - return tfMap -} - -func expandLakeFormationDatabaseResource(tfMap map[string]interface{}) *lakeformation.DatabaseResource { - if tfMap == nil { - return nil - } - - apiObject := &lakeformation.DatabaseResource{} - - if v, ok := tfMap["catalog_id"].(string); ok && v != "" { - apiObject.CatalogId = aws.String(v) - } - - if v, ok := tfMap["name"].(string); ok && v != "" { - apiObject.Name = aws.String(v) - } - - return apiObject -} - -func flattenLakeFormationDatabaseResource(apiObject *lakeformation.DatabaseResource) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.CatalogId; v != nil { - tfMap["catalog_id"] = aws.StringValue(v) - } - - if v := apiObject.Name; v != nil { - tfMap["name"] = aws.StringValue(v) - } - - return tfMap -} - -func expandLakeFormationLFTagPolicyResource(tfMap map[string]interface{}) *lakeformation.LFTagPolicyResource { - if tfMap == nil { - return nil - } - - apiObject := &lakeformation.LFTagPolicyResource{} - - if v, ok := tfMap["catalog_id"].(string); ok && v != "" { - apiObject.CatalogId = aws.String(v) - } - - if v, ok := tfMap["expression"]; ok && v != nil { - apiObject.Expression = expandLakeFormationLFTagExpression(v.([]interface{})) - } - - if v, ok := tfMap["resource_type"].(string); ok && v != "" { - apiObject.ResourceType = aws.String(v) - } - - return apiObject -} - -func expandLakeFormationLFTagExpression(expression []interface{}) []*lakeformation.LFTag { - tagSlice := []*lakeformation.LFTag{} - for _, element := range expression { - elementMap := element.(map[string]interface{}) - - tag := &lakeformation.LFTag{ - TagKey: aws.String(elementMap["key"].(string)), - TagValues: expandStringSet(elementMap["values"].(*schema.Set)), - } - - tagSlice = append(tagSlice, tag) - } - - return tagSlice -} - -func flattenLakeFormationLFTagPolicyResource(apiObject *lakeformation.LFTagPolicyResource) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.CatalogId; v != nil { - tfMap["catalog_id"] = aws.StringValue(v) - } - - if v := apiObject.Expression; v != nil { - tfMap["expression"] = flattenLakeFormationLFTagExpression(v) - } - - if v := apiObject.ResourceType; v != nil { - tfMap["resource_type"] = aws.StringValue(v) - } - - return tfMap -} - -func flattenLakeFormationLFTagExpression(ts []*lakeformation.LFTag) []map[string]interface{} { - tagSlice := make([]map[string]interface{}, len(ts)) - if len(ts) > 0 { - for i, t := range ts { - tag := make(map[string]interface{}) - - if v := aws.StringValue(t.TagKey); v != "" { - tag["key"] = v - } - - if v := flattenStringList(t.TagValues); v != nil { - tag["values"] = v - } - - tagSlice[i] = tag - } - } - - return tagSlice -} - -func expandLakeFormationLFTagKeyResource(tfMap map[string]interface{}) *lakeformation.LFTagKeyResource { - if tfMap == nil { - return nil - } - - apiObject := &lakeformation.LFTagKeyResource{} - - if v, ok := tfMap["catalog_id"].(string); ok && v != "" { - apiObject.CatalogId = aws.String(v) - } - - if v, ok := tfMap["key"].(string); ok && v != "" { - apiObject.TagKey = aws.String(v) - } - - if v, ok := tfMap["values"].(*schema.Set); ok && v != nil { - apiObject.TagValues = expandStringSet(v) - } - - return apiObject -} - -func flattenLakeFormationLFTagKeyResource(apiObject *lakeformation.LFTagKeyResource) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.CatalogId; v != nil { - tfMap["catalog_id"] = aws.StringValue(v) - } - - if v := apiObject.TagKey; v != nil { - tfMap["key"] = aws.StringValue(v) - } - - if v := apiObject.TagValues; v != nil { - tfMap["values"] = flattenStringSet(v) - } - - return tfMap -} - -func expandLakeFormationTableResource(tfMap map[string]interface{}) *lakeformation.TableResource { - if tfMap == nil { - return nil - } - - apiObject := &lakeformation.TableResource{} - - if v, ok := tfMap["catalog_id"].(string); ok && v != "" { - apiObject.CatalogId = aws.String(v) - } - - if v, ok := tfMap["database_name"].(string); ok && v != "" { - apiObject.DatabaseName = aws.String(v) - } - - if v, ok := tfMap["name"].(string); ok && v != "" { - apiObject.Name = aws.String(v) - } - - if v, ok := tfMap["wildcard"].(bool); ok && v { - apiObject.TableWildcard = &lakeformation.TableWildcard{} - } - - return apiObject -} - -func expandLakeFormationTableWithColumnsResourceAsTable(tfMap map[string]interface{}) *lakeformation.TableResource { - if tfMap == nil { - return nil - } - - apiObject := &lakeformation.TableResource{} - - if v, ok := tfMap["catalog_id"].(string); ok && v != "" { - apiObject.CatalogId = aws.String(v) - } - - if v, ok := tfMap["database_name"].(string); ok && v != "" { - apiObject.DatabaseName = aws.String(v) - } - - if v, ok := tfMap["name"].(string); ok && v != "" { - apiObject.Name = aws.String(v) - } - - return apiObject -} - -func flattenLakeFormationTableResource(apiObject *lakeformation.TableResource) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.CatalogId; v != nil { - tfMap["catalog_id"] = aws.StringValue(v) - } - - if v := apiObject.DatabaseName; v != nil { - tfMap["database_name"] = aws.StringValue(v) - } - - if v := apiObject.Name; v != nil { - if aws.StringValue(v) != TableNameAllTables || apiObject.TableWildcard == nil { - tfMap["name"] = aws.StringValue(v) - } - } - - if v := apiObject.TableWildcard; v != nil { - tfMap["wildcard"] = true - } - - return tfMap -} - -func expandLakeFormationTableWithColumnsResource(tfMap map[string]interface{}) *lakeformation.TableWithColumnsResource { - if tfMap == nil { - return nil - } - - apiObject := &lakeformation.TableWithColumnsResource{} - - if v, ok := tfMap["catalog_id"].(string); ok && v != "" { - apiObject.CatalogId = aws.String(v) - } - - if v, ok := tfMap["column_names"]; ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - apiObject.ColumnNames = expandStringList(v.([]interface{})) - } - - if v, ok := tfMap["database_name"].(string); ok && v != "" { - apiObject.DatabaseName = aws.String(v) - } - - if v, ok := tfMap["excluded_column_names"]; ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - apiObject.ColumnWildcard = &lakeformation.ColumnWildcard{ - ExcludedColumnNames: expandStringList(v.([]interface{})), - } - } - - if v, ok := tfMap["name"].(string); ok && v != "" { - apiObject.Name = aws.String(v) - } - - if v, ok := tfMap["wildcard"].(bool); ok && v && apiObject.ColumnWildcard == nil { - apiObject.ColumnWildcard = &lakeformation.ColumnWildcard{} - } - - return apiObject -} - -func flattenLakeFormationTableWithColumnsResource(apiObject *lakeformation.TableWithColumnsResource) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.CatalogId; v != nil { - tfMap["catalog_id"] = aws.StringValue(v) - } - - tfMap["column_names"] = flattenStringList(apiObject.ColumnNames) - - if v := apiObject.DatabaseName; v != nil { - tfMap["database_name"] = aws.StringValue(v) - } - - if v := apiObject.ColumnWildcard; v != nil { - tfMap["wildcard"] = true - tfMap["excluded_column_names"] = flattenStringList(v.ExcludedColumnNames) - } - - if v := apiObject.Name; v != nil { - tfMap["name"] = aws.StringValue(v) - } - - return tfMap -} - -func flattenLakeFormationPermissions(apiObjects []*lakeformation.PrincipalResourcePermissions) []string { - if apiObjects == nil { - return nil - } - - tfList := make([]string, 0) - - for _, resourcePermission := range apiObjects { - for _, permission := range resourcePermission.Permissions { - tfList = append(tfList, aws.StringValue(permission)) - } - } - - sort.Strings(tfList) - - return tfList -} - -func flattenLakeFormationGrantPermissions(apiObjects []*lakeformation.PrincipalResourcePermissions) []string { - if apiObjects == nil { - return nil - } - - tfList := make([]string, 0) - - for _, resourcePermission := range apiObjects { - for _, grantPermission := range resourcePermission.PermissionsWithGrantOption { - tfList = append(tfList, aws.StringValue(grantPermission)) - } - } - - sort.Strings(tfList) - - return tfList -} - -// lakeformation.Permission_Values() enum has tag permissions in the format ASSOCIATE_TAG, DESCRIBE_TAG, which is not valid for the API -func validatePermissions() schema.SchemaValidateFunc { - return validation.StringInSlice([]string{ - "ALL", - "ALTER", - "ASSOCIATE", - "CREATE_DATABASE", - "CREATE_TABLE", - "DATA_LOCATION_ACCESS", - "DELETE", - "DESCRIBE", - "DROP", - "INSERT", - "SELECT", - }, true) -} diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go deleted file mode 100644 index a6537828546a..000000000000 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ /dev/null @@ -1,1500 +0,0 @@ -package aws - -import ( - "fmt" - "log" - "strconv" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" -) - -func testAccAWSLakeFormationPermissions_basic(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionCreateDatabase), - resource.TestCheckResourceAttr(resourceName, "catalog_resource", "true"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - bucketName := "aws_s3_bucket.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_dataLocation(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionDataLocationAccess), - resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), - resource.TestCheckResourceAttr(resourceName, "data_location.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", bucketName, "arn"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_database(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - dbName := "aws_glue_catalog_database.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_database(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "database.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALTER"), - resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), - resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_lf_tag(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - tagName := "aws_lakeformation_lf_tag.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_lf_tag(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "lf_tag.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.key", tagName, "key"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.values", tagName, "values"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", "ASSOCIATE"), - resource.TestCheckResourceAttr(resourceName, "permissions.1", "DESCRIBE"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "2"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", "ASSOCIATE"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.1", "DESCRIBE"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_lf_tag_policy(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - tagName := "aws_lakeformation_lf_tag.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_lf_tag_policy(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "lf_tag_policy.#", "1"), - resource.TestCheckResourceAttr(resourceName, "lf_tag_policy.0.resource_type", "DATABASE"), - resource.TestCheckResourceAttr(resourceName, "lf_tag_policy.0.expression.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.key", tagName, "key"), - resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.values", tagName, "values"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), - resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), - resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - tableName := "aws_glue_catalog_table.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_tableName(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), - resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionDelete), - resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDescribe), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_tableWildcard(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - databaseResourceName := "aws_glue_catalog_database.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_tableWildcard(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", databaseResourceName, "name"), - resource.TestCheckResourceAttr(resourceName, "table.0.wildcard", "true"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - tableName := "aws_glue_catalog_table.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "2"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - tableName := "aws_glue_catalog_table.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_implicitTableWithColumnsPermissions(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.wildcard", "true"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_implicitTablePermissions(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - tableName := "aws_glue_catalog_table.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_implicitTablePermissions(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_selectPermissions(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_selectPermissions(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardPermissions(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), - ), - }, - }, - }) -} - -func testAccAWSLakeFormationPermissions_columnWildcardPermissions(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - roleName := "aws_iam_role.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_columnWildcardPermissions(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), - ), - }, - }, - }) -} - -func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_lakeformation_permissions" { - continue - } - - permCount, err := permissionCountForLakeFormationResource(conn, rs) - - if err != nil { - return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) - } - - if permCount != 0 { - return fmt.Errorf("Lake Formation permissions (%s) still exist: %d", rs.Primary.ID, permCount) - } - - return nil - } - - return nil -} - -func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("resource not found: %s", resourceName) - } - - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn - - permCount, err := permissionCountForLakeFormationResource(conn, rs) - - if err != nil { - return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) - } - - if permCount == 0 { - return fmt.Errorf("Lake Formation permissions (%s) do not exist or could not be found", rs.Primary.ID) - } - - return nil - } -} - -func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, rs *terraform.ResourceState) (int, error) { - input := &lakeformation.ListPermissionsInput{ - Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(rs.Primary.Attributes["principal"]), - }, - Resource: &lakeformation.Resource{}, - } - - if v, ok := rs.Primary.Attributes["catalog_id"]; ok && v != "" { - input.CatalogId = aws.String(v) - } - - if v, ok := rs.Primary.Attributes["catalog_resource"]; ok && v != "" && v == "true" { - input.Resource.Catalog = expandLakeFormationCatalogResource() - } - - if v, ok := rs.Primary.Attributes["data_location.#"]; ok && v != "" && v != "0" { - tfMap := map[string]interface{}{} - - if v := rs.Primary.Attributes["data_location.0.catalog_id"]; v != "" { - tfMap["catalog_id"] = v - } - - if v := rs.Primary.Attributes["data_location.0.arn"]; v != "" { - tfMap["arn"] = v - } - - input.Resource.DataLocation = expandLakeFormationDataLocationResource(tfMap) - } - - if v, ok := rs.Primary.Attributes["database.#"]; ok && v != "" && v != "0" { - tfMap := map[string]interface{}{} - - if v := rs.Primary.Attributes["database.0.catalog_id"]; v != "" { - tfMap["catalog_id"] = v - } - - if v := rs.Primary.Attributes["database.0.name"]; v != "" { - tfMap["name"] = v - } - - input.Resource.Database = expandLakeFormationDatabaseResource(tfMap) - } - - if v, ok := rs.Primary.Attributes["lf_tag.#"]; ok && v != "" && v != "0" { - tfMap := map[string]interface{}{} - - if v := rs.Primary.Attributes["lf_tag.0.catalog_id"]; v != "" { - tfMap["catalog_id"] = v - } - - if v := rs.Primary.Attributes["lf_tag.0.key"]; v != "" { - tfMap["key"] = v - } - - if count, err := strconv.Atoi(rs.Primary.Attributes["lf_tag.0.values.#"]); err == nil { - var tagValues []string - for i := 0; i < count; i++ { - tagValues = append(tagValues, rs.Primary.Attributes[fmt.Sprintf("lf_tag.0.values.%d", i)]) - } - tfMap["values"] = flattenStringSet(aws.StringSlice(tagValues)) - } - - input.Resource.LFTag = expandLakeFormationLFTagKeyResource(tfMap) - } - - if v, ok := rs.Primary.Attributes["lf_tag_policy.#"]; ok && v != "" && v != "0" { - tfMap := map[string]interface{}{} - - if v := rs.Primary.Attributes["lf_tag_policy.0.catalog_id"]; v != "" { - tfMap["catalog_id"] = v - } - - if v := rs.Primary.Attributes["lf_tag_policy.0.resource_type"]; v != "" { - tfMap["resource_type"] = v - } - - if expressionCount, err := strconv.Atoi(rs.Primary.Attributes["lf_tag_policy.0.expression.#"]); err == nil { - expressionSlice := make([]interface{}, expressionCount) - for i := 0; i < expressionCount; i++ { - expression := make(map[string]interface{}) - - if v := rs.Primary.Attributes[fmt.Sprintf("lf_tag_policy.0.expression.%d.key", i)]; v != "" { - expression["key"] = v - } - - if expressionValueCount, err := strconv.Atoi(rs.Primary.Attributes[fmt.Sprintf("lf_tag_policy.0.expression.%d.values.#", i)]); err == nil { - valueSlice := make([]string, expressionValueCount) - for j := 0; j < expressionValueCount; j++ { - valueSlice[j] = rs.Primary.Attributes[fmt.Sprintf("lf_tag_policy.0.expression.%d.values.%d", i, j)] - } - expression["values"] = flattenStringSet(aws.StringSlice(valueSlice)) - } - expressionSlice[i] = expression - } - tfMap["expression"] = expressionSlice - } - - input.Resource.LFTagPolicy = expandLakeFormationLFTagPolicyResource(tfMap) - } - - tableType := "" - - if v, ok := rs.Primary.Attributes["table.#"]; ok && v != "" && v != "0" { - tableType = TableTypeTable - - tfMap := map[string]interface{}{} - - if v := rs.Primary.Attributes["table.0.catalog_id"]; v != "" { - tfMap["catalog_id"] = v - } - - if v := rs.Primary.Attributes["table.0.database_name"]; v != "" { - tfMap["database_name"] = v - } - - if v := rs.Primary.Attributes["table.0.name"]; v != "" && v != TableNameAllTables { - tfMap["name"] = v - } - - if v := rs.Primary.Attributes["table.0.wildcard"]; v != "" && v == "true" { - tfMap["wildcard"] = true - } - - input.Resource.Table = expandLakeFormationTableResource(tfMap) - } - - if v, ok := rs.Primary.Attributes["table_with_columns.#"]; ok && v != "" && v != "0" { - tableType = TableTypeTableWithColumns - - tfMap := map[string]interface{}{} - - if v := rs.Primary.Attributes["table_with_columns.0.catalog_id"]; v != "" { - tfMap["catalog_id"] = v - } - - if v := rs.Primary.Attributes["table_with_columns.0.database_name"]; v != "" { - tfMap["database_name"] = v - } - - if v := rs.Primary.Attributes["table_with_columns.0.name"]; v != "" { - tfMap["name"] = v - } - - input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(tfMap) - } - - log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) - var allPermissions []*lakeformation.PrincipalResourcePermissions - - err := resource.Retry(iamwaiter.PropagationTimeout, func() *resource.RetryError { - err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { - for _, permission := range resp.PrincipalResourcePermissions { - if permission == nil { - continue - } - - allPermissions = append(allPermissions, permission) - } - return !lastPage - }) - - if err != nil { - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { - return resource.RetryableError(err) - } - - return resource.NonRetryableError(fmt.Errorf("error listing Lake Formation Permissions: %w", err)) - } - return nil - }) - - if tfresource.TimedOut(err) { - err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { - for _, permission := range resp.PrincipalResourcePermissions { - if permission == nil { - continue - } - - allPermissions = append(allPermissions, permission) - } - return !lastPage - }) - } - - if tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) { - return 0, nil - } - - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "Resource does not exist") { - return 0, nil - } - - if err != nil { - return 0, fmt.Errorf("error listing Lake Formation permissions after retry: %w", err) - } - - // clean permissions = filter out permissions that do not pertain to this specific resource - - var cleanPermissions []*lakeformation.PrincipalResourcePermissions - - if input.Resource.Catalog != nil { - cleanPermissions = filterLakeFormationCatalogPermissions(allPermissions) - } - - if input.Resource.DataLocation != nil { - cleanPermissions = filterLakeFormationDataLocationPermissions(allPermissions) - } - - if input.Resource.Database != nil { - cleanPermissions = filterLakeFormationDatabasePermissions(allPermissions) - } - - if input.Resource.LFTag != nil { - cleanPermissions = filterLakeFormationLFTagPermissions(allPermissions) - } - - if input.Resource.LFTagPolicy != nil { - cleanPermissions = filterLakeFormationLFTagPolicyPermissions(allPermissions) - } - - if tableType == TableTypeTable { - cleanPermissions = filterLakeFormationTablePermissions( - aws.StringValue(input.Resource.Table.Name), - input.Resource.Table.TableWildcard != nil, - allPermissions, - ) - } - - var columnNames []string - if cols, err := strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.column_names.#"]); err == nil { - for i := 0; i < cols; i++ { - columnNames = append(columnNames, rs.Primary.Attributes[fmt.Sprintf("table_with_columns.0.column_names.%d", i)]) - } - } - - var excludedColumnNames []string - if cols, err := strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"]); err == nil { - for i := 0; i < cols; i++ { - excludedColumnNames = append(excludedColumnNames, rs.Primary.Attributes[fmt.Sprintf("table_with_columns.0.excluded_column_names.%d", i)]) - } - } - - if tableType == TableTypeTableWithColumns { - cleanPermissions = filterLakeFormationTableWithColumnsPermissions( - rs.Primary.Attributes["table_with_columns.0.database_name"], - rs.Primary.Attributes["table_with_columns.0.wildcard"] == "true", - aws.StringSlice(columnNames), - aws.StringSlice(excludedColumnNames), - allPermissions, - ) - } - - return len(cleanPermissions), nil -} - -func testAccAWSLakeFormationPermissionsConfig_basic(rName string) string { - return fmt.Sprintf(` -data "aws_partition" "current" {} - -resource "aws_iam_role" "test" { - name = %[1]q - - assume_role_policy = < 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = ExpandLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = ExpandLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{})) } @@ -373,6 +490,14 @@ func resourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{})) } + if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = ExpandLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = ExpandLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + tableType := "" if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -444,6 +569,8 @@ func resourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { d.Set("catalog_resource", false) d.Set("data_location", nil) d.Set("database", nil) + d.Set("lf_tag", nil) + d.Set("lf_tag_policy", nil) d.Set("table_with_columns", nil) d.Set("table", nil) return nil @@ -479,6 +606,22 @@ func resourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { d.Set("database", nil) } + if cleanPermissions[0].Resource.LFTag != nil { + if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { + return fmt.Errorf("error setting database: %w", err) + } + } else { + d.Set("lf_tag", nil) + } + + if cleanPermissions[0].Resource.LFTagPolicy != nil { + if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { + return fmt.Errorf("error setting database: %w", err) + } + } else { + d.Set("lf_tag_policy", nil) + } + tableSet := false if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { @@ -560,6 +703,14 @@ func resourcePermissionsDelete(d *schema.ResourceData, meta interface{}) error { input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{})) } + if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = ExpandLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = ExpandLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{})) } @@ -719,6 +870,131 @@ func flattenDatabaseResource(apiObject *lakeformation.DatabaseResource) map[stri return tfMap } +func ExpandLFTagPolicyResource(tfMap map[string]interface{}) *lakeformation.LFTagPolicyResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.LFTagPolicyResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["expression"]; ok && v != nil { + apiObject.Expression = ExpandLFTagExpression(v.([]interface{})) + } + + if v, ok := tfMap["resource_type"].(string); ok && v != "" { + apiObject.ResourceType = aws.String(v) + } + + return apiObject +} + +func ExpandLFTagExpression(expression []interface{}) []*lakeformation.LFTag { + tagSlice := []*lakeformation.LFTag{} + for _, element := range expression { + elementMap := element.(map[string]interface{}) + + tag := &lakeformation.LFTag{ + TagKey: aws.String(elementMap["key"].(string)), + TagValues: flex.ExpandStringSet(elementMap["values"].(*schema.Set)), + } + + tagSlice = append(tagSlice, tag) + } + + return tagSlice +} + +func flattenLakeFormationLFTagPolicyResource(apiObject *lakeformation.LFTagPolicyResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.Expression; v != nil { + tfMap["expression"] = flattenLakeFormationLFTagExpression(v) + } + + if v := apiObject.ResourceType; v != nil { + tfMap["resource_type"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenLakeFormationLFTagExpression(ts []*lakeformation.LFTag) []map[string]interface{} { + tagSlice := make([]map[string]interface{}, len(ts)) + if len(ts) > 0 { + for i, t := range ts { + tag := make(map[string]interface{}) + + if v := aws.StringValue(t.TagKey); v != "" { + tag["key"] = v + } + + if v := flex.FlattenStringList(t.TagValues); v != nil { + tag["values"] = v + } + + tagSlice[i] = tag + } + } + + return tagSlice +} + +func ExpandLFTagKeyResource(tfMap map[string]interface{}) *lakeformation.LFTagKeyResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.LFTagKeyResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["key"].(string); ok && v != "" { + apiObject.TagKey = aws.String(v) + } + + if v, ok := tfMap["values"].(*schema.Set); ok && v != nil { + apiObject.TagValues = flex.ExpandStringSet(v) + } + + return apiObject +} + +func flattenLakeFormationLFTagKeyResource(apiObject *lakeformation.LFTagKeyResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.TagKey; v != nil { + tfMap["key"] = aws.StringValue(v) + } + + if v := apiObject.TagValues; v != nil { + tfMap["values"] = flex.FlattenStringSet(v) + } + + return tfMap +} + func ExpandTableResource(tfMap map[string]interface{}) *lakeformation.TableResource { if tfMap == nil { return nil @@ -904,6 +1180,8 @@ func flattenPermissions(apiObjects []*lakeformation.PrincipalResourcePermissions } } + sort.Strings(tfList) + return tfList } @@ -920,5 +1198,7 @@ func flattenGrantPermissions(apiObjects []*lakeformation.PrincipalResourcePermis } } + sort.Strings(tfList) + return tfList } diff --git a/internal/service/lakeformation/permissions_data_source.go b/internal/service/lakeformation/permissions_data_source.go index d769d7ae6083..7610ee22f345 100644 --- a/internal/service/lakeformation/permissions_data_source.go +++ b/internal/service/lakeformation/permissions_data_source.go @@ -84,6 +84,84 @@ func DataSourcePermissions() *schema.Resource { Type: schema.TypeString, }, }, + "lf_tag": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "values": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 15, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateLFTagValues(), + }, + Set: schema.HashString, + }, + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "lf_tag_policy": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: verify.ValidAccountID, + }, + "expression": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 5, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + }, + "values": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 15, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateLFTagValues(), + }, + Set: schema.HashString, + }, + }, + }, + }, + "resource_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(lakeformation.ResourceType_Values(), false), + }, + }, + }, + }, "principal": { Type: schema.TypeString, Required: true, @@ -195,6 +273,14 @@ func dataSourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{})) } + if v, ok := d.GetOk("lf_tag"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTag = ExpandLFTagKeyResource(v.([]interface{})[0].(map[string]interface{})) + } + + if v, ok := d.GetOk("lf_tag_policy"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Resource.LFTagPolicy = ExpandLFTagPolicyResource(v.([]interface{})[0].(map[string]interface{})) + } + tableType := "" if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { @@ -273,6 +359,22 @@ func dataSourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { d.Set("database", nil) } + if cleanPermissions[0].Resource.LFTag != nil { + if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { + return fmt.Errorf("error setting LF-tag: %w", err) + } + } else { + d.Set("lf_tag", nil) + } + + if cleanPermissions[0].Resource.LFTagPolicy != nil { + if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { + return fmt.Errorf("error setting LF-tag policy: %w", err) + } + } else { + d.Set("lf_tag_policy", nil) + } + tableSet := false if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { diff --git a/internal/service/lakeformation/permissions_data_source_test.go b/internal/service/lakeformation/permissions_data_source_test.go index 45689abb3832..c40a7abf0abe 100644 --- a/internal/service/lakeformation/permissions_data_source_test.go +++ b/internal/service/lakeformation/permissions_data_source_test.go @@ -88,6 +88,68 @@ func testAccPermissionsDataSource_database(t *testing.T) { }) } +func testAccPermissionsDataSource_lf_tag(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.#", dataSourceName, "lf_tag.#"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.key", dataSourceName, "lf_tag.0.key"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag.0.values", dataSourceName, "lf_tag.0.values"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.1", dataSourceName, "permissions_with_grant_option.1"), + ), + }, + }, + }) +} + +func testAccPermissionsDataSource_lf_tag_policy(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag_policy(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.#", dataSourceName, "lf_tag_policy.#"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.resource_type", dataSourceName, "lf_tag_policy.0.resource_type"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.#", dataSourceName, "lf_tag_policy.0.expression.#"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.key", dataSourceName, "lf_tag_policy.0.expression.0.key"), + resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.0.expression.0.values", dataSourceName, "lf_tag_policy.0.expression.0.values"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.2", dataSourceName, "permissions.2"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), + ), + }, + }, + }) +} + func testAccPermissionsDataSource_table(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_lakeformation_permissions.test" @@ -316,6 +378,137 @@ data "aws_lakeformation_permissions" "test" { `, rName) } +func testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + assume_role_policy = < 0 { - input.TagValuesToAdd = toAdd - } - - if len(toDelete) > 0 { - input.TagValuesToDelete = toDelete - } - - _, err = conn.UpdateLFTag(input) - if err != nil { - return fmt.Errorf("Error updating Lake Formation LF-Tag (%s): %w", d.Id(), err) - } - - return resourceAwsLakeFormationLFTagRead(d, meta) -} - -func resourceAwsLakeFormationLFTagDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - - catalogID, tagKey, err := readLFTagID(d.Id()) - if err != nil { - return err - } - - input := &lakeformation.DeleteLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - _, err = conn.DeleteLFTag(input) - if err != nil { - return fmt.Errorf("Error deleting Lake Formation LF-Tag (%s): %w", d.Id(), err) - } - - return nil -} - -func readLFTagID(id string) (catalogID string, tagKey string, err error) { - idParts := strings.Split(id, ":") - if len(idParts) != 2 { - return "", "", fmt.Errorf("Unexpected format of ID (%q), expected CATALOG-ID:TAG-KEY", id) - } - return idParts[0], idParts[1], nil -} - -func validateLFTagValues() schema.SchemaValidateFunc { - return validation.All( - validation.StringLenBetween(1, 255), - validation.StringMatch(regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:\*\/=+\-@%]*)$`), ""), - ) -} diff --git a/resource_aws_lakeformation_policy_tag_test.go b/resource_aws_lakeformation_policy_tag_test.go deleted file mode 100644 index aabf5488da10..000000000000 --- a/resource_aws_lakeformation_policy_tag_test.go +++ /dev/null @@ -1,211 +0,0 @@ -package aws - -import ( - "fmt" - "strconv" - "strings" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" -) - -func testAccAWSLakeFormationLFTag_basic(t *testing.T) { - resourceName := "aws_lakeformation_lf_tag.test" - rKey := acctest.RandomWithPrefix("tf-acc-test") - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationLFTagConfig_basic(rKey), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "key", rKey), - resource.TestCheckResourceAttr(resourceName, "values.0", "value"), - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func testAccAWSLakeFormationLFTag_disappears(t *testing.T) { - resourceName := "aws_lakeformation_lf_tag.test" - rKey := acctest.RandomWithPrefix("tf-acc-test") - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationLFTagConfig_basic(rKey), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationLFTag(), resourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - -func testAccAWSLakeFormationLFTag_values(t *testing.T) { - resourceName := "aws_lakeformation_lf_tag.test" - rKey := acctest.RandomWithPrefix("tf-acc-test") - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationLFTagsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationLFTagConfig_values(rKey, []string{"value1", "value2"}), - Destroy: false, - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "key", rKey), - resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - // Test an update that adds, removes and retains a tag value - Config: testAccAWSLakeFormationLFTagConfig_values(rKey, []string{"value1", "value3"}), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationLFTagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "key", rKey), - resource.TestCheckResourceAttr(resourceName, "values.0", "value1"), - resource.TestCheckResourceAttr(resourceName, "values.1", "value3"), - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - ), - }, - }, - }) -} - -func testAccCheckAWSLakeFormationLFTagsDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_lakeformation_lf_tag" { - continue - } - - catalogID, tagKey, err := readLFTagID(rs.Primary.ID) - if err != nil { - return err - } - - input := &lakeformation.GetLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - if _, err := conn.GetLFTag(input); err != nil { - if isAWSErr(err, lakeformation.ErrCodeEntityNotFoundException, "") { - continue - } - // If the lake formation admin has been revoked, there will be access denied instead of entity not found - if isAWSErr(err, lakeformation.ErrCodeAccessDeniedException, "") { - continue - } - return err - } - return fmt.Errorf("Lake Formation LF-Tag (%s) still exists", rs.Primary.ID) - } - - return nil -} - -func testAccCheckAWSLakeFormationLFTagExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - catalogID, tagKey, err := readLFTagID(rs.Primary.ID) - if err != nil { - return err - } - - input := &lakeformation.GetLFTagInput{ - CatalogId: aws.String(catalogID), - TagKey: aws.String(tagKey), - } - - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn - _, err = conn.GetLFTag(input) - - if err != nil { - return err - } - - return nil - } -} - -func testAccAWSLakeFormationLFTagConfig_basic(rKey string) string { - return fmt.Sprintf(` -data "aws_caller_identity" "current" {} - -resource "aws_lakeformation_data_lake_settings" "test" { - admins = [data.aws_caller_identity.current.arn] -} - -resource "aws_lakeformation_lf_tag" "test" { - key = %[1]q - values = ["value"] - - # for consistency, ensure that admins are setup before testing - depends_on = [aws_lakeformation_data_lake_settings.test] -} -`, rKey) -} - -func testAccAWSLakeFormationLFTagConfig_values(rKey string, values []string) string { - quotedValues := make([]string, len(values)) - for i, v := range values { - quotedValues[i] = strconv.Quote(v) - } - - return fmt.Sprintf(` -data "aws_caller_identity" "current" {} - -resource "aws_lakeformation_data_lake_settings" "test" { - admins = [data.aws_caller_identity.current.arn] -} - -resource "aws_lakeformation_lf_tag" "test" { - key = %[1]q - values = [%s] - - # for consistency, ensure that admins are setup before testing - depends_on = [aws_lakeformation_data_lake_settings.test] -} -`, rKey, strings.Join(quotedValues, ",")) -} diff --git a/website/docs/r/lakeformation_lf_tag.html.markdow b/website/docs/r/lakeformation_lf_tag.html.markdow deleted file mode 100644 index b01be8397894..000000000000 --- a/website/docs/r/lakeformation_lf_tag.html.markdow +++ /dev/null @@ -1,43 +0,0 @@ ---- -subcategory: "Lake Formation" -layout: "aws" -page_title: "AWS: aws_lakeformation_lf_tag" -description: |- - Creates a tag with the specified name and values. ---- - -# Resource: aws_lakeformation_lf_tag - -Creates an LF-Tag with the specified name and values. Each key must have at least one value. The maximum number of values permitted is 15. - -## Example Usage - -```terraform -resource "aws_lakeformation_lf_tag" "example" { - key = "module" - values = ["Orders", "Sales", "Customers"] -} -``` - -## Argument Reference - -The following arguments are supported: - -* `catalog_id` - (Optional) ID of the Data Catalog to create the tag in. If omitted, this defaults to the AWS Account ID. -* `key` - (Required) The key-name for the tag. -* `values` - (Required) A list of possible values an attribute can take. - -## Attributes Reference - -In addition to all arguments above, the following attributes are exported: - -* `id` - Catalog ID and key-name of the tag - -## Import - -Lake Formation LF-Tags can be imported using the `catalog_id:key`. If you have not set a Catalog ID specify the AWS Account ID that the database is in, e.g. - -``` -$ terraform import aws_lakeformation_lf_tag.example 123456789012:some_key -``` - From 9623efd6f1652b5c28a046a15d57838f9df3bf87 Mon Sep 17 00:00:00 2001 From: Steven Ayers Date: Wed, 20 Apr 2022 05:54:28 +0100 Subject: [PATCH 04/10] Adding markdown files --- website/docs/d/lakeformation_permissions.html.markdown | 3 --- website/docs/r/lakeformation_permissions.html.markdown | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/website/docs/d/lakeformation_permissions.html.markdown b/website/docs/d/lakeformation_permissions.html.markdown index 4d66eb20de8b..37d77fa47c52 100644 --- a/website/docs/d/lakeformation_permissions.html.markdown +++ b/website/docs/d/lakeformation_permissions.html.markdown @@ -44,15 +44,12 @@ data "aws_lakeformation_permissions" "test" { ```terraform data "aws_lakeformation_permissions" "test" { principal = aws_iam_role.workflow_role.arn - lf_tag_policy { resource_type = "DATABASE" - expression { key = "Team" values = ["Sales"] } - expression { key = "Environment" values = ["Dev", "Production"] diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 4e41270e445d..73330b8a7617 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -130,15 +130,12 @@ resource "aws_lakeformation_permissions" "example" { resource "aws_lakeformation_permissions" "test" { role = aws_iam_role.sales_role.arn permissions = ["CREATE_TABLE", "ALTER", "DROP"] - lf_tag_policy { resource_type = "DATABASE" - expression { key = "Team" values = ["Sales"] } - expression { key = "Environment" values = ["Dev", "Production"] @@ -147,13 +144,11 @@ resource "aws_lakeformation_permissions" "test" { } ``` - ## Argument Reference The following arguments are required: -<<<<<<< HEAD -* `permissions` – (Required) List of permissions granted to the principal. Valid values may include `ALL`, `ALTER`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +* `permissions` – (Required) List of permissions granted to the principal. Valid values may include `ALL`, `ALTER`, `ASSOCIATE`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). * `principal` – (Required) Principal to be granted the permissions on the resource. Supported principals include `IAM_ALLOWED_PRINCIPALS` (see [Default Behavior and `IAMAllowedPrincipals`](#default-behavior-and-iamallowedprincipals) above), IAM roles, users, groups, SAML groups and users, QuickSight groups, OUs, and organizations as well as AWS account IDs for cross-account permissions. For more information, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). ======= * `permissions` – (Required) List of permissions granted to the principal. Valid values may include `ALL`, `ALTER`, `ASSOCIATE`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). From 25fe426c66a70a11058d15426c4f0e9eb5f6de74 Mon Sep 17 00:00:00 2001 From: Steven Ayers Date: Wed, 20 Apr 2022 08:18:00 +0100 Subject: [PATCH 05/10] Adding initial files --- .changelog/24315.txt | 4 ++ internal/service/lakeformation/filter.go | 40 +++++++++++++++++++ .../lakeformation/lakeformation_test.go | 4 ++ 3 files changed, 48 insertions(+) create mode 100644 .changelog/24315.txt diff --git a/.changelog/24315.txt b/.changelog/24315.txt new file mode 100644 index 000000000000..6c734dd5da32 --- /dev/null +++ b/.changelog/24315.txt @@ -0,0 +1,4 @@ +```release-note:note:enhancement +resource/aws_lakeformation_permissions: Add LF-tag support +datasource/aws_lakeformation_permissions: Add LF-tag support +``` \ No newline at end of file diff --git a/internal/service/lakeformation/filter.go b/internal/service/lakeformation/filter.go index d2c60486dc87..d37dcc0dced4 100644 --- a/internal/service/lakeformation/filter.go +++ b/internal/service/lakeformation/filter.go @@ -31,6 +31,14 @@ func FilterPermissions(input *lakeformation.ListPermissionsInput, tableType stri return FilterDatabasePermissions(input.Principal.DataLakePrincipalIdentifier, allPermissions) } + if input.Resource.LFTag != nil { + return FilterLFTagPermissions(input.Principal.DataLakePrincipalIdentifier, allPermissions) + } + + if input.Resource.LFTagPolicy != nil { + return FilterLFTagPolicyPermissions(input.Principal.DataLakePrincipalIdentifier, allPermissions) + } + if tableType == TableTypeTableWithColumns { return FilterTableWithColumnsPermissions(input.Principal.DataLakePrincipalIdentifier, input.Resource.Table, columnNames, excludedColumnNames, columnWildcard, allPermissions) } @@ -176,3 +184,35 @@ func FilterDatabasePermissions(principal *string, allPermissions []*lakeformatio return cleanPermissions } + +func FilterLFTagPermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + + if perm.Resource.LFTag != nil { + cleanPermissions = append(cleanPermissions, perm) + } + } + + return cleanPermissions +} + +func FilterLFTagPolicyPermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { + var cleanPermissions []*lakeformation.PrincipalResourcePermissions + + for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + + if perm.Resource.LFTagPolicy != nil { + cleanPermissions = append(cleanPermissions, perm) + } + } + + return cleanPermissions +} diff --git a/internal/service/lakeformation/lakeformation_test.go b/internal/service/lakeformation/lakeformation_test.go index 4f0c25fb9a44..0047ad803f66 100644 --- a/internal/service/lakeformation/lakeformation_test.go +++ b/internal/service/lakeformation/lakeformation_test.go @@ -19,11 +19,15 @@ func TestAccLakeFormation_serial(t *testing.T) { "databaseMultiple": testAccPermissions_databaseMultiple, "dataLocation": testAccPermissions_dataLocation, "disappears": testAccPermissions_disappears, + "lfTag": testAccAWSLakeFormationPermissions_lf_tag, + "lfTagPolicy": testAccAWSLakeFormationPermissions_lf_tag_policy, }, "PermissionsDataSource": { "basic": testAccPermissionsDataSource_basic, "database": testAccPermissionsDataSource_database, "dataLocation": testAccPermissionsDataSource_dataLocation, + "lfTag": testAccPermissionsDataSource_lf_tag, + "lfTagPolicy": testAccPermissionsDataSource_lf_tag_policy, "table": testAccPermissionsDataSource_table, "tableWithColumns": testAccPermissionsDataSource_tableWithColumns, }, From 310bcb16c60e926fcaf605e9a5c05b997f549946 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 21 Jun 2022 18:00:20 -0400 Subject: [PATCH 06/10] lf: Clean up --- .github/scripts/semgrep.sh | 6 +-- .github/workflows/semgrep-ci.yml | 12 ++--- GNUmakefile | 2 + .../lakeformation/lakeformation_test.go | 8 +-- internal/service/lakeformation/lf_tag.go | 14 ++--- internal/service/lakeformation/lf_tag_test.go | 26 ++++----- internal/service/lakeformation/permissions.go | 12 ++--- .../lakeformation/permissions_data_source.go | 4 +- .../permissions_data_source_test.go | 52 +++++++++++------- .../service/lakeformation/permissions_test.go | 54 ++++++++++--------- .../r/lakeformation_permissions.html.markdown | 4 -- 11 files changed, 105 insertions(+), 89 deletions(-) diff --git a/.github/scripts/semgrep.sh b/.github/scripts/semgrep.sh index ba8809f185d5..de4ecfd2369e 100755 --- a/.github/scripts/semgrep.sh +++ b/.github/scripts/semgrep.sh @@ -1,11 +1,11 @@ #!/bin/bash -idx=$1 +cfile=$1 -results=$( semgrep -c .semgrep-"${idx}".yml 2>&1 ) +results=$( semgrep -c "${cfile}" 2>&1 ) while [[ "${results}" == *Invalid_argument* ]] && [[ "${results}" == *" 0 findings"* ]]; do echo "${results}" - results=$( semgrep -c .semgrep-"${idx}".yml 2>&1 ) + results=$( semgrep -c "${cfile}" 2>&1 ) done if [[ ! "${results}" == *" 0 findings"* ]]; then echo "${results}" >&2 diff --git a/.github/workflows/semgrep-ci.yml b/.github/workflows/semgrep-ci.yml index 55b61dad38d2..6eaa9bcb2dc6 100644 --- a/.github/workflows/semgrep-ci.yml +++ b/.github/workflows/semgrep-ci.yml @@ -31,7 +31,7 @@ jobs: if: (github.action != 'dependabot[bot]') steps: - uses: actions/checkout@v3 - - run: .github/scripts/semgrep.sh caps-aws-ec2 + - run: .github/scripts/semgrep.sh .semgrep-caps-aws-ec2.yml naming_tests: name: Semgrep Test Configs Scan runs-on: ubuntu-latest @@ -40,7 +40,7 @@ jobs: if: (github.action != 'dependabot[bot]') steps: - uses: actions/checkout@v3 - - run: .github/scripts/semgrep.sh configs + - run: .github/scripts/semgrep.sh .semgrep-configs.yml naming_semgrep0: name: Semgrep Service Name Scan A-C runs-on: ubuntu-latest @@ -49,7 +49,7 @@ jobs: if: (github.action != 'dependabot[bot]') steps: - uses: actions/checkout@v3 - - run: .github/scripts/semgrep.sh service-name0 + - run: .github/scripts/semgrep.sh .semgrep-service-name0.yml naming_semgrep1: name: Semgrep Service Name Scan C-I runs-on: ubuntu-latest @@ -58,7 +58,7 @@ jobs: if: (github.action != 'dependabot[bot]') steps: - uses: actions/checkout@v3 - - run: .github/scripts/semgrep.sh service-name1 + - run: .github/scripts/semgrep.sh .semgrep-service-name1.yml naming_semgrep2: name: Semgrep Service Name Scan I-Q runs-on: ubuntu-latest @@ -67,7 +67,7 @@ jobs: if: (github.action != 'dependabot[bot]') steps: - uses: actions/checkout@v3 - - run: .github/scripts/semgrep.sh service-name2 + - run: .github/scripts/semgrep.sh .semgrep-service-name2.yml naming_semgrep3: name: Semgrep Service Name Scan Q-Z runs-on: ubuntu-latest @@ -76,4 +76,4 @@ jobs: if: (github.action != 'dependabot[bot]') steps: - uses: actions/checkout@v3 - - run: .github/scripts/semgrep.sh service-name3 + - run: .github/scripts/semgrep.sh .semgrep-service-name3.yml diff --git a/GNUmakefile b/GNUmakefile index d71ed81e92a2..f2c179920c81 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -199,6 +199,8 @@ semgrep: semall: @echo "==> Running Semgrep checks locally (must have semgrep installed)..." @semgrep -c .semgrep.yml + @semgrep -c .semgrep-caps-aws-ec2.yml + @semgrep -c .semgrep-configs.yml @semgrep -c .semgrep-service-name0.yml @semgrep -c .semgrep-service-name1.yml @semgrep -c .semgrep-service-name2.yml diff --git a/internal/service/lakeformation/lakeformation_test.go b/internal/service/lakeformation/lakeformation_test.go index 0047ad803f66..aa690a1fe201 100644 --- a/internal/service/lakeformation/lakeformation_test.go +++ b/internal/service/lakeformation/lakeformation_test.go @@ -19,15 +19,15 @@ func TestAccLakeFormation_serial(t *testing.T) { "databaseMultiple": testAccPermissions_databaseMultiple, "dataLocation": testAccPermissions_dataLocation, "disappears": testAccPermissions_disappears, - "lfTag": testAccAWSLakeFormationPermissions_lf_tag, - "lfTagPolicy": testAccAWSLakeFormationPermissions_lf_tag_policy, + "lfTag": testAccPermissions_lfTag, + "lfTagPolicy": testAccPermissions_lfTagPolicy, }, "PermissionsDataSource": { "basic": testAccPermissionsDataSource_basic, "database": testAccPermissionsDataSource_database, "dataLocation": testAccPermissionsDataSource_dataLocation, - "lfTag": testAccPermissionsDataSource_lf_tag, - "lfTagPolicy": testAccPermissionsDataSource_lf_tag_policy, + "lfTag": testAccPermissionsDataSource_lfTag, + "lfTagPolicy": testAccPermissionsDataSource_lfTagPolicy, "table": testAccPermissionsDataSource_table, "tableWithColumns": testAccPermissionsDataSource_tableWithColumns, }, diff --git a/internal/service/lakeformation/lf_tag.go b/internal/service/lakeformation/lf_tag.go index b5ed96028c30..69ec68a97ef3 100644 --- a/internal/service/lakeformation/lf_tag.go +++ b/internal/service/lakeformation/lf_tag.go @@ -74,7 +74,7 @@ func resourceLFTagCreate(d *schema.ResourceData, meta interface{}) error { _, err := conn.CreateLFTag(input) if err != nil { - return fmt.Errorf("Error creating Lake Formation LF-Tag: %w", err) + return fmt.Errorf("error creating Lake Formation LF-Tag: %w", err) } d.SetId(fmt.Sprintf("%s:%s", catalogID, tagKey)) @@ -96,14 +96,16 @@ func resourceLFTagRead(d *schema.ResourceData, meta interface{}) error { } output, err := conn.GetLFTag(input) - if err != nil { + if !d.IsNewResource() { if tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) { log.Printf("[WARN] Lake Formation LF-Tag (%s) not found, removing from state", d.Id()) d.SetId("") return nil } + } - return fmt.Errorf("Error reading Lake Formation LF-Tag: %s", err.Error()) + if err != nil { + return fmt.Errorf("error reading Lake Formation LF-Tag: %s", err.Error()) } d.Set("key", output.TagKey) @@ -142,7 +144,7 @@ func resourceLFTagUpdate(d *schema.ResourceData, meta interface{}) error { _, err = conn.UpdateLFTag(input) if err != nil { - return fmt.Errorf("Error updating Lake Formation LF-Tag (%s): %w", d.Id(), err) + return fmt.Errorf("error updating Lake Formation LF-Tag (%s): %w", d.Id(), err) } return resourceLFTagRead(d, meta) @@ -163,7 +165,7 @@ func resourceLFTagDelete(d *schema.ResourceData, meta interface{}) error { _, err = conn.DeleteLFTag(input) if err != nil { - return fmt.Errorf("Error deleting Lake Formation LF-Tag (%s): %w", d.Id(), err) + return fmt.Errorf("error deleting Lake Formation LF-Tag (%s): %w", d.Id(), err) } return nil @@ -172,7 +174,7 @@ func resourceLFTagDelete(d *schema.ResourceData, meta interface{}) error { func ReadLFTagID(id string) (catalogID string, tagKey string, err error) { idParts := strings.Split(id, ":") if len(idParts) != 2 { - return "", "", fmt.Errorf("Unexpected format of ID (%q), expected CATALOG-ID:TAG-KEY", id) + return "", "", fmt.Errorf("unexpected format of ID (%q), expected CATALOG-ID:TAG-KEY", id) } return idParts[0], idParts[1], nil } diff --git a/internal/service/lakeformation/lf_tag_test.go b/internal/service/lakeformation/lf_tag_test.go index 5d439dc26dc5..8d93a2dca959 100644 --- a/internal/service/lakeformation/lf_tag_test.go +++ b/internal/service/lakeformation/lf_tag_test.go @@ -22,10 +22,10 @@ func testAccLFTag_basic(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, - ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckLFTagsDestroy, + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckLFTagsDestroy, Steps: []resource.TestStep{ { Config: testAccLFTagConfig_basic(rName), @@ -50,10 +50,10 @@ func testAccLFTag_disappears(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, - ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckLFTagsDestroy, + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckLFTagsDestroy, Steps: []resource.TestStep{ { Config: testAccLFTagConfig_basic(rName), @@ -72,10 +72,10 @@ func testAccLFTag_values(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, - ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckLFTagsDestroy, + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckLFTagsDestroy, Steps: []resource.TestStep{ { Config: testAccLFTagConfig_values(rName, []string{"value1", "value2"}), @@ -149,7 +149,7 @@ func testAccCheckLFTagExists(name string) resource.TestCheckFunc { } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("no ID is set") } catalogID, tagKey, err := tflakeformation.ReadLFTagID(rs.Primary.ID) diff --git a/internal/service/lakeformation/permissions.go b/internal/service/lakeformation/permissions.go index 63247637c0b3..78f10f73557d 100644 --- a/internal/service/lakeformation/permissions.go +++ b/internal/service/lakeformation/permissions.go @@ -607,7 +607,7 @@ func resourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { } if cleanPermissions[0].Resource.LFTag != nil { - if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { + if err := d.Set("lf_tag", []interface{}{flattenLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { return fmt.Errorf("error setting database: %w", err) } } else { @@ -615,7 +615,7 @@ func resourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { } if cleanPermissions[0].Resource.LFTagPolicy != nil { - if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { + if err := d.Set("lf_tag_policy", []interface{}{flattenLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { return fmt.Errorf("error setting database: %w", err) } } else { @@ -908,7 +908,7 @@ func ExpandLFTagExpression(expression []interface{}) []*lakeformation.LFTag { return tagSlice } -func flattenLakeFormationLFTagPolicyResource(apiObject *lakeformation.LFTagPolicyResource) map[string]interface{} { +func flattenLFTagPolicyResource(apiObject *lakeformation.LFTagPolicyResource) map[string]interface{} { if apiObject == nil { return nil } @@ -920,7 +920,7 @@ func flattenLakeFormationLFTagPolicyResource(apiObject *lakeformation.LFTagPolic } if v := apiObject.Expression; v != nil { - tfMap["expression"] = flattenLakeFormationLFTagExpression(v) + tfMap["expression"] = flattenLFTagExpression(v) } if v := apiObject.ResourceType; v != nil { @@ -930,7 +930,7 @@ func flattenLakeFormationLFTagPolicyResource(apiObject *lakeformation.LFTagPolic return tfMap } -func flattenLakeFormationLFTagExpression(ts []*lakeformation.LFTag) []map[string]interface{} { +func flattenLFTagExpression(ts []*lakeformation.LFTag) []map[string]interface{} { tagSlice := make([]map[string]interface{}, len(ts)) if len(ts) > 0 { for i, t := range ts { @@ -973,7 +973,7 @@ func ExpandLFTagKeyResource(tfMap map[string]interface{}) *lakeformation.LFTagKe return apiObject } -func flattenLakeFormationLFTagKeyResource(apiObject *lakeformation.LFTagKeyResource) map[string]interface{} { +func flattenLFTagKeyResource(apiObject *lakeformation.LFTagKeyResource) map[string]interface{} { if apiObject == nil { return nil } diff --git a/internal/service/lakeformation/permissions_data_source.go b/internal/service/lakeformation/permissions_data_source.go index 7610ee22f345..efd8d49edb82 100644 --- a/internal/service/lakeformation/permissions_data_source.go +++ b/internal/service/lakeformation/permissions_data_source.go @@ -360,7 +360,7 @@ func dataSourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { } if cleanPermissions[0].Resource.LFTag != nil { - if err := d.Set("lf_tag", []interface{}{flattenLakeFormationLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { + if err := d.Set("lf_tag", []interface{}{flattenLFTagKeyResource(cleanPermissions[0].Resource.LFTag)}); err != nil { return fmt.Errorf("error setting LF-tag: %w", err) } } else { @@ -368,7 +368,7 @@ func dataSourcePermissionsRead(d *schema.ResourceData, meta interface{}) error { } if cleanPermissions[0].Resource.LFTagPolicy != nil { - if err := d.Set("lf_tag_policy", []interface{}{flattenLakeFormationLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { + if err := d.Set("lf_tag_policy", []interface{}{flattenLFTagPolicyResource(cleanPermissions[0].Resource.LFTagPolicy)}); err != nil { return fmt.Errorf("error setting LF-tag policy: %w", err) } } else { diff --git a/internal/service/lakeformation/permissions_data_source_test.go b/internal/service/lakeformation/permissions_data_source_test.go index c40a7abf0abe..7050467cdf15 100644 --- a/internal/service/lakeformation/permissions_data_source_test.go +++ b/internal/service/lakeformation/permissions_data_source_test.go @@ -88,19 +88,19 @@ func testAccPermissionsDataSource_database(t *testing.T) { }) } -func testAccPermissionsDataSource_lf_tag(t *testing.T) { +func testAccPermissionsDataSource_lfTag(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_lakeformation_permissions.test" dataSourceName := "data.aws_lakeformation_permissions.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, - ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckPermissionsDestroy, + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag(rName), + Config: testAccPermissionsDataSourceConfig_lfTag(rName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), resource.TestCheckResourceAttrPair(resourceName, "lf_tag.#", dataSourceName, "lf_tag.#"), @@ -118,19 +118,19 @@ func testAccPermissionsDataSource_lf_tag(t *testing.T) { }) } -func testAccPermissionsDataSource_lf_tag_policy(t *testing.T) { +func testAccPermissionsDataSource_lfTagPolicy(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_lakeformation_permissions.test" dataSourceName := "data.aws_lakeformation_permissions.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, - ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckPermissionsDestroy, + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(lakeformation.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag_policy(rName), + Config: testAccPermissionsDataSourceConfig_lfTagPolicy(rName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), resource.TestCheckResourceAttrPair(resourceName, "lf_tag_policy.#", dataSourceName, "lf_tag_policy.#"), @@ -378,12 +378,12 @@ data "aws_lakeformation_permissions" "test" { `, rName) } -func testAccAWSLakeFormationPermissionsDataSourceConfig_lf_tag(rName string) string { +func testAccPermissionsDataSourceConfig_lfTag(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" {} resource "aws_iam_role" "test" { - name = %[1]q - path = "/" + name = %[1]q + path = "/" assume_role_policy = <>>>>>> 5e8d0b964d (New resource: aws_lakeformation_permissions) ~> **NOTE:** We highly recommend that the `principal` _NOT_ be a Lake Formation administrator (granted using `aws_lakeformation_data_lake_settings`). The entity (e.g., IAM role) running Terraform will most likely need to be a Lake Formation administrator. As such, the entity will have implicit permissions and does not need permissions granted through this resource. From 5462217166b87bce985d1fd5e9c1266e05538691 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 21 Jun 2022 18:06:18 -0400 Subject: [PATCH 07/10] lf: Lint fixes --- .../lakeformation/permissions_data_source_test.go | 4 ++-- internal/service/lakeformation/permissions_test.go | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/internal/service/lakeformation/permissions_data_source_test.go b/internal/service/lakeformation/permissions_data_source_test.go index 7050467cdf15..79f8da4e375b 100644 --- a/internal/service/lakeformation/permissions_data_source_test.go +++ b/internal/service/lakeformation/permissions_data_source_test.go @@ -501,8 +501,8 @@ resource "aws_lakeformation_permissions" "test" { # for consistency, ensure that admins are setup before testing depends_on = [ - aws_lakeformation_data_lake_settings.test, - aws_lakeformation_lf_tag.test, + aws_lakeformation_data_lake_settings.test, + aws_lakeformation_lf_tag.test, ] } diff --git a/internal/service/lakeformation/permissions_test.go b/internal/service/lakeformation/permissions_test.go index 006e7857dcb8..a1c5b038c316 100644 --- a/internal/service/lakeformation/permissions_test.go +++ b/internal/service/lakeformation/permissions_test.go @@ -1304,9 +1304,10 @@ resource "aws_lakeformation_permissions" "test" { func testAccPermissionsConfig_lfTag(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" {} + resource "aws_iam_role" "test" { - name = %[1]q - path = "/" + name = %[1]q + path = "/" assume_role_policy = < Date: Tue, 21 Jun 2022 18:11:50 -0400 Subject: [PATCH 08/10] ci: Fix false positive issue --- .github/workflows/semgrep-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semgrep-ci.yml b/.github/workflows/semgrep-ci.yml index 6eaa9bcb2dc6..ccd5504ac6a1 100644 --- a/.github/workflows/semgrep-ci.yml +++ b/.github/workflows/semgrep-ci.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 - - run: semgrep --config .semgrep.yml + - run: .github/scripts/semgrep.sh .semgrep.yml naming_cac: name: Semgrep Naming Scan Caps/AWS/EC2 runs-on: ubuntu-latest From fa758dc1b71097abc6567fc632a4b356d8164fe3 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 21 Jun 2022 18:31:17 -0400 Subject: [PATCH 09/10] Add changelog --- .changelog/19523.txt | 3 +++ .changelog/{24315.txt => 19648.txt} | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changelog/19523.txt rename .changelog/{24315.txt => 19648.txt} (78%) diff --git a/.changelog/19523.txt b/.changelog/19523.txt new file mode 100644 index 000000000000..038344485a1f --- /dev/null +++ b/.changelog/19523.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_lakeformation_lf_tag +``` diff --git a/.changelog/24315.txt b/.changelog/19648.txt similarity index 78% rename from .changelog/24315.txt rename to .changelog/19648.txt index 6c734dd5da32..5061ae7f2bda 100644 --- a/.changelog/24315.txt +++ b/.changelog/19648.txt @@ -1,4 +1,7 @@ ```release-note:note:enhancement resource/aws_lakeformation_permissions: Add LF-tag support +``` + +```release-note:note:enhancement datasource/aws_lakeformation_permissions: Add LF-tag support -``` \ No newline at end of file +``` From e024c7e8e781ede92b741399b857728a3fe8984c Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 21 Jun 2022 18:32:58 -0400 Subject: [PATCH 10/10] changelog: Undo change --- .changelog/19523.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/19523.txt b/.changelog/19523.txt index 038344485a1f..92e459a2de23 100644 --- a/.changelog/19523.txt +++ b/.changelog/19523.txt @@ -1,3 +1,3 @@ ```release-note:new-resource aws_lakeformation_lf_tag -``` +``` \ No newline at end of file