commit
						e9edbab4e3
					
				@ -171,7 +171,7 @@
 | 
			
		||||
- [ ] update_webhook
 | 
			
		||||
 | 
			
		||||
## apigateway
 | 
			
		||||
24% implemented
 | 
			
		||||
25% implemented
 | 
			
		||||
- [ ] create_api_key
 | 
			
		||||
- [ ] create_authorizer
 | 
			
		||||
- [ ] create_base_path_mapping
 | 
			
		||||
@ -204,7 +204,7 @@
 | 
			
		||||
- [ ] delete_request_validator
 | 
			
		||||
- [X] delete_resource
 | 
			
		||||
- [X] delete_rest_api
 | 
			
		||||
- [ ] delete_stage
 | 
			
		||||
- [X] delete_stage
 | 
			
		||||
- [X] delete_usage_plan
 | 
			
		||||
- [X] delete_usage_plan_key
 | 
			
		||||
- [ ] delete_vpc_link
 | 
			
		||||
@ -687,12 +687,17 @@
 | 
			
		||||
## ce
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] get_cost_and_usage
 | 
			
		||||
- [ ] get_cost_and_usage_with_resources
 | 
			
		||||
- [ ] get_cost_forecast
 | 
			
		||||
- [ ] get_dimension_values
 | 
			
		||||
- [ ] get_reservation_coverage
 | 
			
		||||
- [ ] get_reservation_purchase_recommendation
 | 
			
		||||
- [ ] get_reservation_utilization
 | 
			
		||||
- [ ] get_rightsizing_recommendation
 | 
			
		||||
- [ ] get_savings_plans_coverage
 | 
			
		||||
- [ ] get_savings_plans_purchase_recommendation
 | 
			
		||||
- [ ] get_savings_plans_utilization
 | 
			
		||||
- [ ] get_savings_plans_utilization_details
 | 
			
		||||
- [ ] get_tags
 | 
			
		||||
- [ ] get_usage_forecast
 | 
			
		||||
 | 
			
		||||
@ -701,6 +706,7 @@
 | 
			
		||||
- [ ] associate_phone_number_with_user
 | 
			
		||||
- [ ] associate_phone_numbers_with_voice_connector
 | 
			
		||||
- [ ] associate_phone_numbers_with_voice_connector_group
 | 
			
		||||
- [ ] batch_create_room_membership
 | 
			
		||||
- [ ] batch_delete_phone_number
 | 
			
		||||
- [ ] batch_suspend_user
 | 
			
		||||
- [ ] batch_unsuspend_user
 | 
			
		||||
@ -709,11 +715,15 @@
 | 
			
		||||
- [ ] create_account
 | 
			
		||||
- [ ] create_bot
 | 
			
		||||
- [ ] create_phone_number_order
 | 
			
		||||
- [ ] create_room
 | 
			
		||||
- [ ] create_room_membership
 | 
			
		||||
- [ ] create_voice_connector
 | 
			
		||||
- [ ] create_voice_connector_group
 | 
			
		||||
- [ ] delete_account
 | 
			
		||||
- [ ] delete_events_configuration
 | 
			
		||||
- [ ] delete_phone_number
 | 
			
		||||
- [ ] delete_room
 | 
			
		||||
- [ ] delete_room_membership
 | 
			
		||||
- [ ] delete_voice_connector
 | 
			
		||||
- [ ] delete_voice_connector_group
 | 
			
		||||
- [ ] delete_voice_connector_origination
 | 
			
		||||
@ -731,6 +741,7 @@
 | 
			
		||||
- [ ] get_phone_number
 | 
			
		||||
- [ ] get_phone_number_order
 | 
			
		||||
- [ ] get_phone_number_settings
 | 
			
		||||
- [ ] get_room
 | 
			
		||||
- [ ] get_user
 | 
			
		||||
- [ ] get_user_settings
 | 
			
		||||
- [ ] get_voice_connector
 | 
			
		||||
@ -745,6 +756,8 @@
 | 
			
		||||
- [ ] list_bots
 | 
			
		||||
- [ ] list_phone_number_orders
 | 
			
		||||
- [ ] list_phone_numbers
 | 
			
		||||
- [ ] list_room_memberships
 | 
			
		||||
- [ ] list_rooms
 | 
			
		||||
- [ ] list_users
 | 
			
		||||
- [ ] list_voice_connector_groups
 | 
			
		||||
- [ ] list_voice_connector_termination_credentials
 | 
			
		||||
@ -766,6 +779,8 @@
 | 
			
		||||
- [ ] update_global_settings
 | 
			
		||||
- [ ] update_phone_number
 | 
			
		||||
- [ ] update_phone_number_settings
 | 
			
		||||
- [ ] update_room
 | 
			
		||||
- [ ] update_room_membership
 | 
			
		||||
- [ ] update_user
 | 
			
		||||
- [ ] update_user_settings
 | 
			
		||||
- [ ] update_voice_connector
 | 
			
		||||
@ -1003,6 +1018,7 @@
 | 
			
		||||
- [ ] delete_suggester
 | 
			
		||||
- [ ] describe_analysis_schemes
 | 
			
		||||
- [ ] describe_availability_options
 | 
			
		||||
- [ ] describe_domain_endpoint_options
 | 
			
		||||
- [ ] describe_domains
 | 
			
		||||
- [ ] describe_expressions
 | 
			
		||||
- [ ] describe_index_fields
 | 
			
		||||
@ -1012,6 +1028,7 @@
 | 
			
		||||
- [ ] index_documents
 | 
			
		||||
- [ ] list_domain_names
 | 
			
		||||
- [ ] update_availability_options
 | 
			
		||||
- [ ] update_domain_endpoint_options
 | 
			
		||||
- [ ] update_scaling_parameters
 | 
			
		||||
- [ ] update_service_access_policies
 | 
			
		||||
 | 
			
		||||
@ -1028,9 +1045,11 @@
 | 
			
		||||
- [ ] delete_trail
 | 
			
		||||
- [ ] describe_trails
 | 
			
		||||
- [ ] get_event_selectors
 | 
			
		||||
- [ ] get_trail
 | 
			
		||||
- [ ] get_trail_status
 | 
			
		||||
- [ ] list_public_keys
 | 
			
		||||
- [ ] list_tags
 | 
			
		||||
- [ ] list_trails
 | 
			
		||||
- [ ] lookup_events
 | 
			
		||||
- [ ] put_event_selectors
 | 
			
		||||
- [ ] remove_tags
 | 
			
		||||
@ -1252,6 +1271,22 @@
 | 
			
		||||
- [ ] update_team_member
 | 
			
		||||
- [ ] update_user_profile
 | 
			
		||||
 | 
			
		||||
## codestar-notifications
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] create_notification_rule
 | 
			
		||||
- [ ] delete_notification_rule
 | 
			
		||||
- [ ] delete_target
 | 
			
		||||
- [ ] describe_notification_rule
 | 
			
		||||
- [ ] list_event_types
 | 
			
		||||
- [ ] list_notification_rules
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] list_targets
 | 
			
		||||
- [ ] subscribe
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] unsubscribe
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
- [ ] update_notification_rule
 | 
			
		||||
 | 
			
		||||
## cognito-identity
 | 
			
		||||
28% implemented
 | 
			
		||||
- [X] create_identity_pool
 | 
			
		||||
@ -1545,10 +1580,13 @@
 | 
			
		||||
- [ ] list_queues
 | 
			
		||||
- [ ] list_routing_profiles
 | 
			
		||||
- [ ] list_security_profiles
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] list_user_hierarchy_groups
 | 
			
		||||
- [ ] list_users
 | 
			
		||||
- [ ] start_outbound_voice_contact
 | 
			
		||||
- [ ] stop_contact
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
- [ ] update_contact_attributes
 | 
			
		||||
- [ ] update_user_hierarchy
 | 
			
		||||
- [ ] update_user_identity_info
 | 
			
		||||
@ -1563,6 +1601,31 @@
 | 
			
		||||
- [ ] modify_report_definition
 | 
			
		||||
- [ ] put_report_definition
 | 
			
		||||
 | 
			
		||||
## dataexchange
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] cancel_job
 | 
			
		||||
- [ ] create_data_set
 | 
			
		||||
- [ ] create_job
 | 
			
		||||
- [ ] create_revision
 | 
			
		||||
- [ ] delete_asset
 | 
			
		||||
- [ ] delete_data_set
 | 
			
		||||
- [ ] delete_revision
 | 
			
		||||
- [ ] get_asset
 | 
			
		||||
- [ ] get_data_set
 | 
			
		||||
- [ ] get_job
 | 
			
		||||
- [ ] get_revision
 | 
			
		||||
- [ ] list_data_set_revisions
 | 
			
		||||
- [ ] list_data_sets
 | 
			
		||||
- [ ] list_jobs
 | 
			
		||||
- [ ] list_revision_assets
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] start_job
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
- [ ] update_asset
 | 
			
		||||
- [ ] update_data_set
 | 
			
		||||
- [ ] update_revision
 | 
			
		||||
 | 
			
		||||
## datapipeline
 | 
			
		||||
42% implemented
 | 
			
		||||
- [X] activate_pipeline
 | 
			
		||||
@ -1586,17 +1649,17 @@
 | 
			
		||||
- [ ] validate_pipeline_definition
 | 
			
		||||
 | 
			
		||||
## datasync
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] cancel_task_execution
 | 
			
		||||
22% implemented
 | 
			
		||||
- [X] cancel_task_execution
 | 
			
		||||
- [ ] create_agent
 | 
			
		||||
- [ ] create_location_efs
 | 
			
		||||
- [ ] create_location_nfs
 | 
			
		||||
- [ ] create_location_s3
 | 
			
		||||
- [ ] create_location_smb
 | 
			
		||||
- [ ] create_task
 | 
			
		||||
- [X] create_task
 | 
			
		||||
- [ ] delete_agent
 | 
			
		||||
- [ ] delete_location
 | 
			
		||||
- [ ] delete_task
 | 
			
		||||
- [X] delete_location
 | 
			
		||||
- [X] delete_task
 | 
			
		||||
- [ ] describe_agent
 | 
			
		||||
- [ ] describe_location_efs
 | 
			
		||||
- [ ] describe_location_nfs
 | 
			
		||||
@ -1609,11 +1672,11 @@
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] list_task_executions
 | 
			
		||||
- [ ] list_tasks
 | 
			
		||||
- [ ] start_task_execution
 | 
			
		||||
- [X] start_task_execution
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
- [ ] update_agent
 | 
			
		||||
- [ ] update_task
 | 
			
		||||
- [X] update_task
 | 
			
		||||
 | 
			
		||||
## dax
 | 
			
		||||
0% implemented
 | 
			
		||||
@ -1799,6 +1862,9 @@
 | 
			
		||||
- [ ] delete_lifecycle_policy
 | 
			
		||||
- [ ] get_lifecycle_policies
 | 
			
		||||
- [ ] get_lifecycle_policy
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
- [ ] update_lifecycle_policy
 | 
			
		||||
 | 
			
		||||
## dms
 | 
			
		||||
@ -2217,8 +2283,8 @@
 | 
			
		||||
- [X] describe_volumes
 | 
			
		||||
- [ ] describe_volumes_modifications
 | 
			
		||||
- [X] describe_vpc_attribute
 | 
			
		||||
- [ ] describe_vpc_classic_link
 | 
			
		||||
- [ ] describe_vpc_classic_link_dns_support
 | 
			
		||||
- [X] describe_vpc_classic_link
 | 
			
		||||
- [X] describe_vpc_classic_link_dns_support
 | 
			
		||||
- [ ] describe_vpc_endpoint_connection_notifications
 | 
			
		||||
- [ ] describe_vpc_endpoint_connections
 | 
			
		||||
- [ ] describe_vpc_endpoint_service_configurations
 | 
			
		||||
@ -2237,8 +2303,8 @@
 | 
			
		||||
- [ ] disable_ebs_encryption_by_default
 | 
			
		||||
- [ ] disable_transit_gateway_route_table_propagation
 | 
			
		||||
- [ ] disable_vgw_route_propagation
 | 
			
		||||
- [ ] disable_vpc_classic_link
 | 
			
		||||
- [ ] disable_vpc_classic_link_dns_support
 | 
			
		||||
- [X] disable_vpc_classic_link
 | 
			
		||||
- [X] disable_vpc_classic_link_dns_support
 | 
			
		||||
- [X] disassociate_address
 | 
			
		||||
- [ ] disassociate_client_vpn_target_network
 | 
			
		||||
- [ ] disassociate_iam_instance_profile
 | 
			
		||||
@ -2250,8 +2316,8 @@
 | 
			
		||||
- [ ] enable_transit_gateway_route_table_propagation
 | 
			
		||||
- [ ] enable_vgw_route_propagation
 | 
			
		||||
- [ ] enable_volume_io
 | 
			
		||||
- [ ] enable_vpc_classic_link
 | 
			
		||||
- [ ] enable_vpc_classic_link_dns_support
 | 
			
		||||
- [X] enable_vpc_classic_link
 | 
			
		||||
- [X] enable_vpc_classic_link_dns_support
 | 
			
		||||
- [ ] export_client_vpn_client_certificate_revocation_list
 | 
			
		||||
- [ ] export_client_vpn_client_configuration
 | 
			
		||||
- [ ] export_image
 | 
			
		||||
@ -2461,16 +2527,22 @@
 | 
			
		||||
## eks
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] create_cluster
 | 
			
		||||
- [ ] create_nodegroup
 | 
			
		||||
- [ ] delete_cluster
 | 
			
		||||
- [ ] delete_nodegroup
 | 
			
		||||
- [ ] describe_cluster
 | 
			
		||||
- [ ] describe_nodegroup
 | 
			
		||||
- [ ] describe_update
 | 
			
		||||
- [ ] list_clusters
 | 
			
		||||
- [ ] list_nodegroups
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] list_updates
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
- [ ] update_cluster_config
 | 
			
		||||
- [ ] update_cluster_version
 | 
			
		||||
- [ ] update_nodegroup_config
 | 
			
		||||
- [ ] update_nodegroup_version
 | 
			
		||||
 | 
			
		||||
## elasticache
 | 
			
		||||
0% implemented
 | 
			
		||||
@ -3217,6 +3289,7 @@
 | 
			
		||||
- [ ] create_filter
 | 
			
		||||
- [ ] create_ip_set
 | 
			
		||||
- [ ] create_members
 | 
			
		||||
- [ ] create_publishing_destination
 | 
			
		||||
- [ ] create_sample_findings
 | 
			
		||||
- [ ] create_threat_intel_set
 | 
			
		||||
- [ ] decline_invitations
 | 
			
		||||
@ -3225,7 +3298,9 @@
 | 
			
		||||
- [ ] delete_invitations
 | 
			
		||||
- [ ] delete_ip_set
 | 
			
		||||
- [ ] delete_members
 | 
			
		||||
- [ ] delete_publishing_destination
 | 
			
		||||
- [ ] delete_threat_intel_set
 | 
			
		||||
- [ ] describe_publishing_destination
 | 
			
		||||
- [ ] disassociate_from_master_account
 | 
			
		||||
- [ ] disassociate_members
 | 
			
		||||
- [ ] get_detector
 | 
			
		||||
@ -3244,6 +3319,7 @@
 | 
			
		||||
- [ ] list_invitations
 | 
			
		||||
- [ ] list_ip_sets
 | 
			
		||||
- [ ] list_members
 | 
			
		||||
- [ ] list_publishing_destinations
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] list_threat_intel_sets
 | 
			
		||||
- [ ] start_monitoring_members
 | 
			
		||||
@ -3255,6 +3331,7 @@
 | 
			
		||||
- [ ] update_filter
 | 
			
		||||
- [ ] update_findings_feedback
 | 
			
		||||
- [ ] update_ip_set
 | 
			
		||||
- [ ] update_publishing_destination
 | 
			
		||||
- [ ] update_threat_intel_set
 | 
			
		||||
 | 
			
		||||
## health
 | 
			
		||||
@ -3267,7 +3344,7 @@
 | 
			
		||||
- [ ] describe_events
 | 
			
		||||
 | 
			
		||||
## iam
 | 
			
		||||
62% implemented
 | 
			
		||||
65% implemented
 | 
			
		||||
- [ ] add_client_id_to_open_id_connect_provider
 | 
			
		||||
- [X] add_role_to_instance_profile
 | 
			
		||||
- [X] add_user_to_group
 | 
			
		||||
@ -3293,7 +3370,7 @@
 | 
			
		||||
- [X] delete_access_key
 | 
			
		||||
- [X] delete_account_alias
 | 
			
		||||
- [X] delete_account_password_policy
 | 
			
		||||
- [ ] delete_group
 | 
			
		||||
- [X] delete_group
 | 
			
		||||
- [ ] delete_group_policy
 | 
			
		||||
- [ ] delete_instance_profile
 | 
			
		||||
- [X] delete_login_profile
 | 
			
		||||
@ -3323,7 +3400,7 @@
 | 
			
		||||
- [X] get_access_key_last_used
 | 
			
		||||
- [X] get_account_authorization_details
 | 
			
		||||
- [X] get_account_password_policy
 | 
			
		||||
- [ ] get_account_summary
 | 
			
		||||
- [X] get_account_summary
 | 
			
		||||
- [ ] get_context_keys_for_custom_policy
 | 
			
		||||
- [ ] get_context_keys_for_principal_policy
 | 
			
		||||
- [X] get_credential_report
 | 
			
		||||
@ -3405,7 +3482,7 @@
 | 
			
		||||
- [X] update_signing_certificate
 | 
			
		||||
- [ ] update_ssh_public_key
 | 
			
		||||
- [X] update_user
 | 
			
		||||
- [ ] upload_server_certificate
 | 
			
		||||
- [X] upload_server_certificate
 | 
			
		||||
- [X] upload_signing_certificate
 | 
			
		||||
- [ ] upload_ssh_public_key
 | 
			
		||||
 | 
			
		||||
@ -3459,7 +3536,7 @@
 | 
			
		||||
- [ ] update_assessment_target
 | 
			
		||||
 | 
			
		||||
## iot
 | 
			
		||||
23% implemented
 | 
			
		||||
22% implemented
 | 
			
		||||
- [ ] accept_certificate_transfer
 | 
			
		||||
- [ ] add_thing_to_billing_group
 | 
			
		||||
- [X] add_thing_to_thing_group
 | 
			
		||||
@ -3544,11 +3621,13 @@
 | 
			
		||||
- [X] detach_thing_principal
 | 
			
		||||
- [ ] disable_topic_rule
 | 
			
		||||
- [ ] enable_topic_rule
 | 
			
		||||
- [ ] get_cardinality
 | 
			
		||||
- [ ] get_effective_policies
 | 
			
		||||
- [ ] get_indexing_configuration
 | 
			
		||||
- [ ] get_job_document
 | 
			
		||||
- [ ] get_logging_options
 | 
			
		||||
- [ ] get_ota_update
 | 
			
		||||
- [ ] get_percentiles
 | 
			
		||||
- [X] get_policy
 | 
			
		||||
- [ ] get_policy_version
 | 
			
		||||
- [ ] get_registration_code
 | 
			
		||||
@ -4295,6 +4374,15 @@
 | 
			
		||||
- [ ] reject_invitation
 | 
			
		||||
- [ ] vote_on_proposal
 | 
			
		||||
 | 
			
		||||
## marketplace-catalog
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] cancel_change_set
 | 
			
		||||
- [ ] describe_change_set
 | 
			
		||||
- [ ] describe_entity
 | 
			
		||||
- [ ] list_change_sets
 | 
			
		||||
- [ ] list_entities
 | 
			
		||||
- [ ] start_change_set
 | 
			
		||||
 | 
			
		||||
## marketplace-entitlement
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] get_entitlements
 | 
			
		||||
@ -4773,6 +4861,7 @@
 | 
			
		||||
 | 
			
		||||
## personalize
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] create_batch_inference_job
 | 
			
		||||
- [ ] create_campaign
 | 
			
		||||
- [ ] create_dataset
 | 
			
		||||
- [ ] create_dataset_group
 | 
			
		||||
@ -4788,6 +4877,7 @@
 | 
			
		||||
- [ ] delete_schema
 | 
			
		||||
- [ ] delete_solution
 | 
			
		||||
- [ ] describe_algorithm
 | 
			
		||||
- [ ] describe_batch_inference_job
 | 
			
		||||
- [ ] describe_campaign
 | 
			
		||||
- [ ] describe_dataset
 | 
			
		||||
- [ ] describe_dataset_group
 | 
			
		||||
@ -4799,6 +4889,7 @@
 | 
			
		||||
- [ ] describe_solution
 | 
			
		||||
- [ ] describe_solution_version
 | 
			
		||||
- [ ] get_solution_metrics
 | 
			
		||||
- [ ] list_batch_inference_jobs
 | 
			
		||||
- [ ] list_campaigns
 | 
			
		||||
- [ ] list_dataset_groups
 | 
			
		||||
- [ ] list_dataset_import_jobs
 | 
			
		||||
@ -4831,6 +4922,7 @@
 | 
			
		||||
- [ ] create_email_template
 | 
			
		||||
- [ ] create_export_job
 | 
			
		||||
- [ ] create_import_job
 | 
			
		||||
- [ ] create_journey
 | 
			
		||||
- [ ] create_push_template
 | 
			
		||||
- [ ] create_segment
 | 
			
		||||
- [ ] create_sms_template
 | 
			
		||||
@ -4847,6 +4939,7 @@
 | 
			
		||||
- [ ] delete_endpoint
 | 
			
		||||
- [ ] delete_event_stream
 | 
			
		||||
- [ ] delete_gcm_channel
 | 
			
		||||
- [ ] delete_journey
 | 
			
		||||
- [ ] delete_push_template
 | 
			
		||||
- [ ] delete_segment
 | 
			
		||||
- [ ] delete_sms_channel
 | 
			
		||||
@ -4879,6 +4972,10 @@
 | 
			
		||||
- [ ] get_gcm_channel
 | 
			
		||||
- [ ] get_import_job
 | 
			
		||||
- [ ] get_import_jobs
 | 
			
		||||
- [ ] get_journey
 | 
			
		||||
- [ ] get_journey_date_range_kpi
 | 
			
		||||
- [ ] get_journey_execution_activity_metrics
 | 
			
		||||
- [ ] get_journey_execution_metrics
 | 
			
		||||
- [ ] get_push_template
 | 
			
		||||
- [ ] get_segment
 | 
			
		||||
- [ ] get_segment_export_jobs
 | 
			
		||||
@ -4890,6 +4987,7 @@
 | 
			
		||||
- [ ] get_sms_template
 | 
			
		||||
- [ ] get_user_endpoints
 | 
			
		||||
- [ ] get_voice_channel
 | 
			
		||||
- [ ] list_journeys
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] list_templates
 | 
			
		||||
- [ ] phone_number_validate
 | 
			
		||||
@ -4913,6 +5011,8 @@
 | 
			
		||||
- [ ] update_endpoint
 | 
			
		||||
- [ ] update_endpoints_batch
 | 
			
		||||
- [ ] update_gcm_channel
 | 
			
		||||
- [ ] update_journey
 | 
			
		||||
- [ ] update_journey_state
 | 
			
		||||
- [ ] update_push_template
 | 
			
		||||
- [ ] update_segment
 | 
			
		||||
- [ ] update_sms_channel
 | 
			
		||||
@ -5661,6 +5761,17 @@
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] invoke_endpoint
 | 
			
		||||
 | 
			
		||||
## savingsplans
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] create_savings_plan
 | 
			
		||||
- [ ] describe_savings_plan_rates
 | 
			
		||||
- [ ] describe_savings_plans
 | 
			
		||||
- [ ] describe_savings_plans_offering_rates
 | 
			
		||||
- [ ] describe_savings_plans_offerings
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
 | 
			
		||||
## sdb
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] batch_delete_attributes
 | 
			
		||||
@ -5954,6 +6065,51 @@
 | 
			
		||||
- [X] verify_email_address
 | 
			
		||||
- [X] verify_email_identity
 | 
			
		||||
 | 
			
		||||
## sesv2
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] create_configuration_set
 | 
			
		||||
- [ ] create_configuration_set_event_destination
 | 
			
		||||
- [ ] create_dedicated_ip_pool
 | 
			
		||||
- [ ] create_deliverability_test_report
 | 
			
		||||
- [ ] create_email_identity
 | 
			
		||||
- [ ] delete_configuration_set
 | 
			
		||||
- [ ] delete_configuration_set_event_destination
 | 
			
		||||
- [ ] delete_dedicated_ip_pool
 | 
			
		||||
- [ ] delete_email_identity
 | 
			
		||||
- [ ] get_account
 | 
			
		||||
- [ ] get_blacklist_reports
 | 
			
		||||
- [ ] get_configuration_set
 | 
			
		||||
- [ ] get_configuration_set_event_destinations
 | 
			
		||||
- [ ] get_dedicated_ip
 | 
			
		||||
- [ ] get_dedicated_ips
 | 
			
		||||
- [ ] get_deliverability_dashboard_options
 | 
			
		||||
- [ ] get_deliverability_test_report
 | 
			
		||||
- [ ] get_domain_deliverability_campaign
 | 
			
		||||
- [ ] get_domain_statistics_report
 | 
			
		||||
- [ ] get_email_identity
 | 
			
		||||
- [ ] list_configuration_sets
 | 
			
		||||
- [ ] list_dedicated_ip_pools
 | 
			
		||||
- [ ] list_deliverability_test_reports
 | 
			
		||||
- [ ] list_domain_deliverability_campaigns
 | 
			
		||||
- [ ] list_email_identities
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] put_account_dedicated_ip_warmup_attributes
 | 
			
		||||
- [ ] put_account_sending_attributes
 | 
			
		||||
- [ ] put_configuration_set_delivery_options
 | 
			
		||||
- [ ] put_configuration_set_reputation_options
 | 
			
		||||
- [ ] put_configuration_set_sending_options
 | 
			
		||||
- [ ] put_configuration_set_tracking_options
 | 
			
		||||
- [ ] put_dedicated_ip_in_pool
 | 
			
		||||
- [ ] put_dedicated_ip_warmup_attributes
 | 
			
		||||
- [ ] put_deliverability_dashboard_option
 | 
			
		||||
- [ ] put_email_identity_dkim_attributes
 | 
			
		||||
- [ ] put_email_identity_feedback_attributes
 | 
			
		||||
- [ ] put_email_identity_mail_from_attributes
 | 
			
		||||
- [ ] send_email
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
- [ ] update_configuration_set_event_destination
 | 
			
		||||
 | 
			
		||||
## shield
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] associate_drt_log_bucket
 | 
			
		||||
@ -5984,8 +6140,11 @@
 | 
			
		||||
- [ ] list_signing_jobs
 | 
			
		||||
- [ ] list_signing_platforms
 | 
			
		||||
- [ ] list_signing_profiles
 | 
			
		||||
- [ ] list_tags_for_resource
 | 
			
		||||
- [ ] put_signing_profile
 | 
			
		||||
- [ ] start_signing_job
 | 
			
		||||
- [ ] tag_resource
 | 
			
		||||
- [ ] untag_resource
 | 
			
		||||
 | 
			
		||||
## sms
 | 
			
		||||
0% implemented
 | 
			
		||||
@ -6111,7 +6270,7 @@
 | 
			
		||||
- [X] untag_queue
 | 
			
		||||
 | 
			
		||||
## ssm
 | 
			
		||||
10% implemented
 | 
			
		||||
11% implemented
 | 
			
		||||
- [X] add_tags_to_resource
 | 
			
		||||
- [ ] cancel_command
 | 
			
		||||
- [ ] cancel_maintenance_window_execution
 | 
			
		||||
@ -6184,7 +6343,7 @@
 | 
			
		||||
- [ ] get_ops_item
 | 
			
		||||
- [ ] get_ops_summary
 | 
			
		||||
- [X] get_parameter
 | 
			
		||||
- [ ] get_parameter_history
 | 
			
		||||
- [X] get_parameter_history
 | 
			
		||||
- [X] get_parameters
 | 
			
		||||
- [X] get_parameters_by_path
 | 
			
		||||
- [ ] get_patch_baseline
 | 
			
		||||
@ -6233,6 +6392,19 @@
 | 
			
		||||
- [ ] update_patch_baseline
 | 
			
		||||
- [ ] update_service_setting
 | 
			
		||||
 | 
			
		||||
## sso
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] get_role_credentials
 | 
			
		||||
- [ ] list_account_roles
 | 
			
		||||
- [ ] list_accounts
 | 
			
		||||
- [ ] logout
 | 
			
		||||
 | 
			
		||||
## sso-oidc
 | 
			
		||||
0% implemented
 | 
			
		||||
- [ ] create_token
 | 
			
		||||
- [ ] register_client
 | 
			
		||||
- [ ] start_device_authorization
 | 
			
		||||
 | 
			
		||||
## stepfunctions
 | 
			
		||||
36% implemented
 | 
			
		||||
- [ ] create_activity
 | 
			
		||||
@ -6742,6 +6914,7 @@
 | 
			
		||||
- [ ] delete_ip_group
 | 
			
		||||
- [ ] delete_tags
 | 
			
		||||
- [ ] delete_workspace_image
 | 
			
		||||
- [ ] deregister_workspace_directory
 | 
			
		||||
- [ ] describe_account
 | 
			
		||||
- [ ] describe_account_modifications
 | 
			
		||||
- [ ] describe_client_properties
 | 
			
		||||
@ -6758,10 +6931,14 @@
 | 
			
		||||
- [ ] list_available_management_cidr_ranges
 | 
			
		||||
- [ ] modify_account
 | 
			
		||||
- [ ] modify_client_properties
 | 
			
		||||
- [ ] modify_selfservice_permissions
 | 
			
		||||
- [ ] modify_workspace_access_properties
 | 
			
		||||
- [ ] modify_workspace_creation_properties
 | 
			
		||||
- [ ] modify_workspace_properties
 | 
			
		||||
- [ ] modify_workspace_state
 | 
			
		||||
- [ ] reboot_workspaces
 | 
			
		||||
- [ ] rebuild_workspaces
 | 
			
		||||
- [ ] register_workspace_directory
 | 
			
		||||
- [ ] restore_workspace
 | 
			
		||||
- [ ] revoke_ip_rules
 | 
			
		||||
- [ ] start_workspaces
 | 
			
		||||
 | 
			
		||||
@ -46,8 +46,9 @@ try:
 | 
			
		||||
except ImportError:
 | 
			
		||||
    from backports.tempfile import TemporaryDirectory
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_stderr_regex = re.compile(r"START|END|REPORT RequestId: .*")
 | 
			
		||||
# The lambci container is returning a special escape character for the "RequestID" fields. Unicode 033:
 | 
			
		||||
# _stderr_regex = re.compile(r"START|END|REPORT RequestId: .*")
 | 
			
		||||
_stderr_regex = re.compile(r"\033\[\d+.*")
 | 
			
		||||
_orig_adapter_send = requests.adapters.HTTPAdapter.send
 | 
			
		||||
docker_3 = docker.__version__[0] >= "3"
 | 
			
		||||
 | 
			
		||||
@ -444,7 +445,7 @@ class LambdaFunction(BaseModel):
 | 
			
		||||
            if exit_code != 0:
 | 
			
		||||
                raise Exception("lambda invoke failed output: {}".format(output))
 | 
			
		||||
 | 
			
		||||
            # strip out RequestId lines
 | 
			
		||||
            # strip out RequestId lines (TODO: This will return an additional '\n' in the response)
 | 
			
		||||
            output = os.linesep.join(
 | 
			
		||||
                [
 | 
			
		||||
                    line
 | 
			
		||||
 | 
			
		||||
@ -624,7 +624,7 @@ class BatchBackend(BaseBackend):
 | 
			
		||||
 | 
			
		||||
    def get_job_definition(self, identifier):
 | 
			
		||||
        """
 | 
			
		||||
        Get job defintiion by name or ARN
 | 
			
		||||
        Get job definitions by name or ARN
 | 
			
		||||
        :param identifier: Name or ARN
 | 
			
		||||
        :type identifier: str
 | 
			
		||||
 | 
			
		||||
@ -643,7 +643,7 @@ class BatchBackend(BaseBackend):
 | 
			
		||||
 | 
			
		||||
    def get_job_definitions(self, identifier):
 | 
			
		||||
        """
 | 
			
		||||
        Get job defintiion by name or ARN
 | 
			
		||||
        Get job definitions by name or ARN
 | 
			
		||||
        :param identifier: Name or ARN
 | 
			
		||||
        :type identifier: str
 | 
			
		||||
 | 
			
		||||
@ -934,7 +934,7 @@ class BatchBackend(BaseBackend):
 | 
			
		||||
            self.ecs_backend.delete_cluster(compute_env.ecs_name)
 | 
			
		||||
 | 
			
		||||
            if compute_env.env_type == "MANAGED":
 | 
			
		||||
                # Delete compute envrionment
 | 
			
		||||
                # Delete compute environment
 | 
			
		||||
                instance_ids = [instance.id for instance in compute_env.instances]
 | 
			
		||||
                self.ec2_backend.terminate_instances(instance_ids)
 | 
			
		||||
 | 
			
		||||
@ -1195,7 +1195,7 @@ class BatchBackend(BaseBackend):
 | 
			
		||||
        depends_on=None,
 | 
			
		||||
        container_overrides=None,
 | 
			
		||||
    ):
 | 
			
		||||
        # TODO parameters, retries (which is a dict raw from request), job dependancies and container overrides are ignored for now
 | 
			
		||||
        # TODO parameters, retries (which is a dict raw from request), job dependencies and container overrides are ignored for now
 | 
			
		||||
 | 
			
		||||
        # Look for job definition
 | 
			
		||||
        job_def = self.get_job_definition(job_def_id)
 | 
			
		||||
 | 
			
		||||
@ -2444,6 +2444,7 @@ class VPC(TaggedEC2Resource):
 | 
			
		||||
        self.instance_tenancy = instance_tenancy
 | 
			
		||||
        self.is_default = "true" if is_default else "false"
 | 
			
		||||
        self.enable_dns_support = "true"
 | 
			
		||||
        self.classic_link_enabled = "false"
 | 
			
		||||
        # This attribute is set to 'true' only for default VPCs
 | 
			
		||||
        # or VPCs created using the wizard of the VPC console
 | 
			
		||||
        self.enable_dns_hostnames = "true" if is_default else "false"
 | 
			
		||||
@ -2540,6 +2541,32 @@ class VPC(TaggedEC2Resource):
 | 
			
		||||
        self.cidr_block_association_set[association_id] = association_set
 | 
			
		||||
        return association_set
 | 
			
		||||
 | 
			
		||||
    def enable_vpc_classic_link(self):
 | 
			
		||||
        # Check if current cidr block doesn't fall within the 10.0.0.0/8 block, excluding 10.0.0.0/16 and 10.1.0.0/16.
 | 
			
		||||
        # Doesn't check any route tables, maybe something for in the future?
 | 
			
		||||
        # See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-classiclink.html#classiclink-limitations
 | 
			
		||||
        network_address = ipaddress.ip_network(self.cidr_block).network_address
 | 
			
		||||
        if (
 | 
			
		||||
            network_address not in ipaddress.ip_network("10.0.0.0/8")
 | 
			
		||||
            or network_address in ipaddress.ip_network("10.0.0.0/16")
 | 
			
		||||
            or network_address in ipaddress.ip_network("10.1.0.0/16")
 | 
			
		||||
        ):
 | 
			
		||||
            self.classic_link_enabled = "true"
 | 
			
		||||
 | 
			
		||||
        return self.classic_link_enabled
 | 
			
		||||
 | 
			
		||||
    def disable_vpc_classic_link(self):
 | 
			
		||||
        self.classic_link_enabled = "false"
 | 
			
		||||
        return self.classic_link_enabled
 | 
			
		||||
 | 
			
		||||
    def enable_vpc_classic_link_dns_support(self):
 | 
			
		||||
        self.classic_link_dns_supported = "true"
 | 
			
		||||
        return self.classic_link_dns_supported
 | 
			
		||||
 | 
			
		||||
    def disable_vpc_classic_link_dns_support(self):
 | 
			
		||||
        self.classic_link_dns_supported = "false"
 | 
			
		||||
        return self.classic_link_dns_supported
 | 
			
		||||
 | 
			
		||||
    def disassociate_vpc_cidr_block(self, association_id):
 | 
			
		||||
        if self.cidr_block == self.cidr_block_association_set.get(
 | 
			
		||||
            association_id, {}
 | 
			
		||||
@ -2670,6 +2697,22 @@ class VPCBackend(object):
 | 
			
		||||
        else:
 | 
			
		||||
            raise InvalidParameterValueError(attr_name)
 | 
			
		||||
 | 
			
		||||
    def enable_vpc_classic_link(self, vpc_id):
 | 
			
		||||
        vpc = self.get_vpc(vpc_id)
 | 
			
		||||
        return vpc.enable_vpc_classic_link()
 | 
			
		||||
 | 
			
		||||
    def disable_vpc_classic_link(self, vpc_id):
 | 
			
		||||
        vpc = self.get_vpc(vpc_id)
 | 
			
		||||
        return vpc.disable_vpc_classic_link()
 | 
			
		||||
 | 
			
		||||
    def enable_vpc_classic_link_dns_support(self, vpc_id):
 | 
			
		||||
        vpc = self.get_vpc(vpc_id)
 | 
			
		||||
        return vpc.enable_vpc_classic_link_dns_support()
 | 
			
		||||
 | 
			
		||||
    def disable_vpc_classic_link_dns_support(self, vpc_id):
 | 
			
		||||
        vpc = self.get_vpc(vpc_id)
 | 
			
		||||
        return vpc.disable_vpc_classic_link_dns_support()
 | 
			
		||||
 | 
			
		||||
    def modify_vpc_attribute(self, vpc_id, attr_name, attr_value):
 | 
			
		||||
        vpc = self.get_vpc(vpc_id)
 | 
			
		||||
        if attr_name in ("enable_dns_support", "enable_dns_hostnames"):
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,13 @@ from moto.ec2.utils import filters_from_querystring
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VPCs(BaseResponse):
 | 
			
		||||
    def _get_doc_date(self):
 | 
			
		||||
        return (
 | 
			
		||||
            "2013-10-15"
 | 
			
		||||
            if "Boto/" in self.headers.get("user-agent", "")
 | 
			
		||||
            else "2016-11-15"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def create_vpc(self):
 | 
			
		||||
        cidr_block = self._get_param("CidrBlock")
 | 
			
		||||
        instance_tenancy = self._get_param("InstanceTenancy", if_none="default")
 | 
			
		||||
@ -16,11 +23,7 @@ class VPCs(BaseResponse):
 | 
			
		||||
            instance_tenancy,
 | 
			
		||||
            amazon_provided_ipv6_cidr_block=amazon_provided_ipv6_cidr_blocks,
 | 
			
		||||
        )
 | 
			
		||||
        doc_date = (
 | 
			
		||||
            "2013-10-15"
 | 
			
		||||
            if "Boto/" in self.headers.get("user-agent", "")
 | 
			
		||||
            else "2016-11-15"
 | 
			
		||||
        )
 | 
			
		||||
        doc_date = self._get_doc_date()
 | 
			
		||||
        template = self.response_template(CREATE_VPC_RESPONSE)
 | 
			
		||||
        return template.render(vpc=vpc, doc_date=doc_date)
 | 
			
		||||
 | 
			
		||||
@ -50,6 +53,64 @@ class VPCs(BaseResponse):
 | 
			
		||||
        template = self.response_template(DESCRIBE_VPC_ATTRIBUTE_RESPONSE)
 | 
			
		||||
        return template.render(vpc_id=vpc_id, attribute=attribute, value=value)
 | 
			
		||||
 | 
			
		||||
    def describe_vpc_classic_link_dns_support(self):
 | 
			
		||||
        vpc_ids = self._get_multi_param("VpcIds")
 | 
			
		||||
        filters = filters_from_querystring(self.querystring)
 | 
			
		||||
        vpcs = self.ec2_backend.get_all_vpcs(vpc_ids=vpc_ids, filters=filters)
 | 
			
		||||
        doc_date = self._get_doc_date()
 | 
			
		||||
        template = self.response_template(
 | 
			
		||||
            DESCRIBE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE
 | 
			
		||||
        )
 | 
			
		||||
        return template.render(vpcs=vpcs, doc_date=doc_date)
 | 
			
		||||
 | 
			
		||||
    def enable_vpc_classic_link_dns_support(self):
 | 
			
		||||
        vpc_id = self._get_param("VpcId")
 | 
			
		||||
        classic_link_dns_supported = self.ec2_backend.enable_vpc_classic_link_dns_support(
 | 
			
		||||
            vpc_id=vpc_id
 | 
			
		||||
        )
 | 
			
		||||
        doc_date = self._get_doc_date()
 | 
			
		||||
        template = self.response_template(ENABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE)
 | 
			
		||||
        return template.render(
 | 
			
		||||
            classic_link_dns_supported=classic_link_dns_supported, doc_date=doc_date
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def disable_vpc_classic_link_dns_support(self):
 | 
			
		||||
        vpc_id = self._get_param("VpcId")
 | 
			
		||||
        classic_link_dns_supported = self.ec2_backend.disable_vpc_classic_link_dns_support(
 | 
			
		||||
            vpc_id=vpc_id
 | 
			
		||||
        )
 | 
			
		||||
        doc_date = self._get_doc_date()
 | 
			
		||||
        template = self.response_template(DISABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE)
 | 
			
		||||
        return template.render(
 | 
			
		||||
            classic_link_dns_supported=classic_link_dns_supported, doc_date=doc_date
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def describe_vpc_classic_link(self):
 | 
			
		||||
        vpc_ids = self._get_multi_param("VpcId")
 | 
			
		||||
        filters = filters_from_querystring(self.querystring)
 | 
			
		||||
        vpcs = self.ec2_backend.get_all_vpcs(vpc_ids=vpc_ids, filters=filters)
 | 
			
		||||
        doc_date = self._get_doc_date()
 | 
			
		||||
        template = self.response_template(DESCRIBE_VPC_CLASSIC_LINK_RESPONSE)
 | 
			
		||||
        return template.render(vpcs=vpcs, doc_date=doc_date)
 | 
			
		||||
 | 
			
		||||
    def enable_vpc_classic_link(self):
 | 
			
		||||
        vpc_id = self._get_param("VpcId")
 | 
			
		||||
        classic_link_enabled = self.ec2_backend.enable_vpc_classic_link(vpc_id=vpc_id)
 | 
			
		||||
        doc_date = self._get_doc_date()
 | 
			
		||||
        template = self.response_template(ENABLE_VPC_CLASSIC_LINK_RESPONSE)
 | 
			
		||||
        return template.render(
 | 
			
		||||
            classic_link_enabled=classic_link_enabled, doc_date=doc_date
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def disable_vpc_classic_link(self):
 | 
			
		||||
        vpc_id = self._get_param("VpcId")
 | 
			
		||||
        classic_link_enabled = self.ec2_backend.disable_vpc_classic_link(vpc_id=vpc_id)
 | 
			
		||||
        doc_date = self._get_doc_date()
 | 
			
		||||
        template = self.response_template(DISABLE_VPC_CLASSIC_LINK_RESPONSE)
 | 
			
		||||
        return template.render(
 | 
			
		||||
            classic_link_enabled=classic_link_enabled, doc_date=doc_date
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def modify_vpc_attribute(self):
 | 
			
		||||
        vpc_id = self._get_param("VpcId")
 | 
			
		||||
 | 
			
		||||
@ -149,6 +210,56 @@ CREATE_VPC_RESPONSE = """
 | 
			
		||||
   </vpc>
 | 
			
		||||
</CreateVpcResponse>"""
 | 
			
		||||
 | 
			
		||||
DESCRIBE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE = """
 | 
			
		||||
<DescribeVpcClassicLinkDnsSupportResponse xmlns="http://ec2.amazonaws.com/doc/{{doc_date}}/">
 | 
			
		||||
  <requestId>7a62c442-3484-4f42-9342-6942EXAMPLE</requestId>
 | 
			
		||||
  <vpcs>
 | 
			
		||||
    {% for vpc in vpcs %}
 | 
			
		||||
      <item>
 | 
			
		||||
        <vpcId>{{ vpc.id }}</vpcId>
 | 
			
		||||
        <classicLinkDnsSupported>{{ vpc.classic_link_dns_supported }}</classicLinkDnsSupported>
 | 
			
		||||
      </item>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
  </vpcs>
 | 
			
		||||
</DescribeVpcClassicLinkDnsSupportResponse>"""
 | 
			
		||||
 | 
			
		||||
ENABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE = """
 | 
			
		||||
<EnableVpcClassicLinkDnsSupportResponse xmlns="http://ec2.amazonaws.com/doc/{{doc_date}}/">
 | 
			
		||||
  <requestId>7a62c442-3484-4f42-9342-6942EXAMPLE</requestId>
 | 
			
		||||
  <return>{{ classic_link_dns_supported }}</return>
 | 
			
		||||
</EnableVpcClassicLinkDnsSupportResponse>"""
 | 
			
		||||
 | 
			
		||||
DISABLE_VPC_CLASSIC_LINK_DNS_SUPPORT_RESPONSE = """
 | 
			
		||||
<DisableVpcClassicLinkDnsSupportResponse xmlns="http://ec2.amazonaws.com/doc/{{doc_date}}/">
 | 
			
		||||
  <requestId>7a62c442-3484-4f42-9342-6942EXAMPLE</requestId>
 | 
			
		||||
  <return>{{ classic_link_dns_supported }}</return>
 | 
			
		||||
</DisableVpcClassicLinkDnsSupportResponse>"""
 | 
			
		||||
 | 
			
		||||
DESCRIBE_VPC_CLASSIC_LINK_RESPONSE = """
 | 
			
		||||
<DescribeVpcClassicLinkResponse xmlns="http://ec2.amazonaws.com/doc/{{doc_date}}/">
 | 
			
		||||
  <requestId>7a62c442-3484-4f42-9342-6942EXAMPLE</requestId>
 | 
			
		||||
  <vpcSet>
 | 
			
		||||
    {% for vpc in vpcs %}
 | 
			
		||||
      <item>
 | 
			
		||||
        <vpcId>{{ vpc.id }}</vpcId>
 | 
			
		||||
        <classicLinkEnabled>{{ vpc.classic_link_enabled }}</classicLinkEnabled>
 | 
			
		||||
      </item>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
  </vpcSet>
 | 
			
		||||
</DescribeVpcClassicLinkResponse>"""
 | 
			
		||||
 | 
			
		||||
ENABLE_VPC_CLASSIC_LINK_RESPONSE = """
 | 
			
		||||
<EnableVpcClassicLinkResponse xmlns="http://ec2.amazonaws.com/doc/{{doc_date}}/">
 | 
			
		||||
  <requestId>7a62c442-3484-4f42-9342-6942EXAMPLE</requestId>
 | 
			
		||||
  <return>{{ classic_link_enabled }}</return>
 | 
			
		||||
</EnableVpcClassicLinkResponse>"""
 | 
			
		||||
 | 
			
		||||
DISABLE_VPC_CLASSIC_LINK_RESPONSE = """
 | 
			
		||||
<DisableVpcClassicLinkResponse xmlns="http://ec2.amazonaws.com/doc/{{doc_date}}/">
 | 
			
		||||
  <requestId>7a62c442-3484-4f42-9342-6942EXAMPLE</requestId>
 | 
			
		||||
  <return>{{ classic_link_enabled }}</return>
 | 
			
		||||
</DisableVpcClassicLinkResponse>"""
 | 
			
		||||
 | 
			
		||||
DESCRIBE_VPCS_RESPONSE = """
 | 
			
		||||
<DescribeVpcsResponse xmlns="http://ec2.amazonaws.com/doc/{{doc_date}}/">
 | 
			
		||||
  <requestId>7a62c442-3484-4f42-9342-6942EXAMPLE</requestId>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
import base64
 | 
			
		||||
import hashlib
 | 
			
		||||
import os
 | 
			
		||||
import random
 | 
			
		||||
import string
 | 
			
		||||
@ -475,6 +476,20 @@ class AccessKey(BaseModel):
 | 
			
		||||
        raise UnformattedGetAttTemplateException()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SshPublicKey(BaseModel):
 | 
			
		||||
    def __init__(self, user_name, ssh_public_key_body):
 | 
			
		||||
        self.user_name = user_name
 | 
			
		||||
        self.ssh_public_key_body = ssh_public_key_body
 | 
			
		||||
        self.ssh_public_key_id = "APKA" + random_access_key()
 | 
			
		||||
        self.fingerprint = hashlib.md5(ssh_public_key_body.encode()).hexdigest()
 | 
			
		||||
        self.status = "Active"
 | 
			
		||||
        self.upload_date = datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def uploaded_iso_8601(self):
 | 
			
		||||
        return iso_8601_datetime_without_milliseconds(self.upload_date)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Group(BaseModel):
 | 
			
		||||
    def __init__(self, name, path="/"):
 | 
			
		||||
        self.name = name
 | 
			
		||||
@ -536,6 +551,7 @@ class User(BaseModel):
 | 
			
		||||
        self.policies = {}
 | 
			
		||||
        self.managed_policies = {}
 | 
			
		||||
        self.access_keys = []
 | 
			
		||||
        self.ssh_public_keys = []
 | 
			
		||||
        self.password = None
 | 
			
		||||
        self.password_reset_required = False
 | 
			
		||||
        self.signing_certificates = {}
 | 
			
		||||
@ -605,6 +621,33 @@ class User(BaseModel):
 | 
			
		||||
                "The Access Key with id {0} cannot be found".format(access_key_id)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def upload_ssh_public_key(self, ssh_public_key_body):
 | 
			
		||||
        pubkey = SshPublicKey(self.name, ssh_public_key_body)
 | 
			
		||||
        self.ssh_public_keys.append(pubkey)
 | 
			
		||||
        return pubkey
 | 
			
		||||
 | 
			
		||||
    def get_ssh_public_key(self, ssh_public_key_id):
 | 
			
		||||
        for key in self.ssh_public_keys:
 | 
			
		||||
            if key.ssh_public_key_id == ssh_public_key_id:
 | 
			
		||||
                return key
 | 
			
		||||
        else:
 | 
			
		||||
            raise IAMNotFoundException(
 | 
			
		||||
                "The SSH Public Key with id {0} cannot be found".format(
 | 
			
		||||
                    ssh_public_key_id
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def get_all_ssh_public_keys(self):
 | 
			
		||||
        return self.ssh_public_keys
 | 
			
		||||
 | 
			
		||||
    def update_ssh_public_key(self, ssh_public_key_id, status):
 | 
			
		||||
        key = self.get_ssh_public_key(ssh_public_key_id)
 | 
			
		||||
        key.status = status
 | 
			
		||||
 | 
			
		||||
    def delete_ssh_public_key(self, ssh_public_key_id):
 | 
			
		||||
        key = self.get_ssh_public_key(ssh_public_key_id)
 | 
			
		||||
        self.ssh_public_keys.remove(key)
 | 
			
		||||
 | 
			
		||||
    def get_cfn_attribute(self, attribute_name):
 | 
			
		||||
        from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
 | 
			
		||||
 | 
			
		||||
@ -736,6 +779,134 @@ class AccountPasswordPolicy(BaseModel):
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AccountSummary(BaseModel):
 | 
			
		||||
    def __init__(self, iam_backend):
 | 
			
		||||
        self._iam_backend = iam_backend
 | 
			
		||||
 | 
			
		||||
        self._group_policy_size_quota = 5120
 | 
			
		||||
        self._instance_profiles_quota = 1000
 | 
			
		||||
        self._groups_per_user_quota = 10
 | 
			
		||||
        self._attached_policies_per_user_quota = 10
 | 
			
		||||
        self._policies_quota = 1500
 | 
			
		||||
        self._account_mfa_enabled = 0  # Haven't found any information being able to activate MFA for the root account programmatically
 | 
			
		||||
        self._access_keys_per_user_quota = 2
 | 
			
		||||
        self._assume_role_policy_size_quota = 2048
 | 
			
		||||
        self._policy_versions_in_use_quota = 10000
 | 
			
		||||
        self._global_endpoint_token_version = (
 | 
			
		||||
            1  # ToDo: Implement set_security_token_service_preferences()
 | 
			
		||||
        )
 | 
			
		||||
        self._versions_per_policy_quota = 5
 | 
			
		||||
        self._attached_policies_per_group_quota = 10
 | 
			
		||||
        self._policy_size_quota = 6144
 | 
			
		||||
        self._account_signing_certificates_present = 0  # valid values: 0 | 1
 | 
			
		||||
        self._users_quota = 5000
 | 
			
		||||
        self._server_certificates_quota = 20
 | 
			
		||||
        self._user_policy_size_quota = 2048
 | 
			
		||||
        self._roles_quota = 1000
 | 
			
		||||
        self._signing_certificates_per_user_quota = 2
 | 
			
		||||
        self._role_policy_size_quota = 10240
 | 
			
		||||
        self._attached_policies_per_role_quota = 10
 | 
			
		||||
        self._account_access_keys_present = 0  # valid values: 0 | 1
 | 
			
		||||
        self._groups_quota = 300
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def summary_map(self):
 | 
			
		||||
        return {
 | 
			
		||||
            "GroupPolicySizeQuota": self._group_policy_size_quota,
 | 
			
		||||
            "InstanceProfilesQuota": self._instance_profiles_quota,
 | 
			
		||||
            "Policies": self._policies,
 | 
			
		||||
            "GroupsPerUserQuota": self._groups_per_user_quota,
 | 
			
		||||
            "InstanceProfiles": self._instance_profiles,
 | 
			
		||||
            "AttachedPoliciesPerUserQuota": self._attached_policies_per_user_quota,
 | 
			
		||||
            "Users": self._users,
 | 
			
		||||
            "PoliciesQuota": self._policies_quota,
 | 
			
		||||
            "Providers": self._providers,
 | 
			
		||||
            "AccountMFAEnabled": self._account_mfa_enabled,
 | 
			
		||||
            "AccessKeysPerUserQuota": self._access_keys_per_user_quota,
 | 
			
		||||
            "AssumeRolePolicySizeQuota": self._assume_role_policy_size_quota,
 | 
			
		||||
            "PolicyVersionsInUseQuota": self._policy_versions_in_use_quota,
 | 
			
		||||
            "GlobalEndpointTokenVersion": self._global_endpoint_token_version,
 | 
			
		||||
            "VersionsPerPolicyQuota": self._versions_per_policy_quota,
 | 
			
		||||
            "AttachedPoliciesPerGroupQuota": self._attached_policies_per_group_quota,
 | 
			
		||||
            "PolicySizeQuota": self._policy_size_quota,
 | 
			
		||||
            "Groups": self._groups,
 | 
			
		||||
            "AccountSigningCertificatesPresent": self._account_signing_certificates_present,
 | 
			
		||||
            "UsersQuota": self._users_quota,
 | 
			
		||||
            "ServerCertificatesQuota": self._server_certificates_quota,
 | 
			
		||||
            "MFADevices": self._mfa_devices,
 | 
			
		||||
            "UserPolicySizeQuota": self._user_policy_size_quota,
 | 
			
		||||
            "PolicyVersionsInUse": self._policy_versions_in_use,
 | 
			
		||||
            "ServerCertificates": self._server_certificates,
 | 
			
		||||
            "Roles": self._roles,
 | 
			
		||||
            "RolesQuota": self._roles_quota,
 | 
			
		||||
            "SigningCertificatesPerUserQuota": self._signing_certificates_per_user_quota,
 | 
			
		||||
            "MFADevicesInUse": self._mfa_devices_in_use,
 | 
			
		||||
            "RolePolicySizeQuota": self._role_policy_size_quota,
 | 
			
		||||
            "AttachedPoliciesPerRoleQuota": self._attached_policies_per_role_quota,
 | 
			
		||||
            "AccountAccessKeysPresent": self._account_access_keys_present,
 | 
			
		||||
            "GroupsQuota": self._groups_quota,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _groups(self):
 | 
			
		||||
        return len(self._iam_backend.groups)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _instance_profiles(self):
 | 
			
		||||
        return len(self._iam_backend.instance_profiles)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _mfa_devices(self):
 | 
			
		||||
        # Don't know, if hardware devices are also counted here
 | 
			
		||||
        return len(self._iam_backend.virtual_mfa_devices)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _mfa_devices_in_use(self):
 | 
			
		||||
        devices = 0
 | 
			
		||||
 | 
			
		||||
        for user in self._iam_backend.users.values():
 | 
			
		||||
            devices += len(user.mfa_devices)
 | 
			
		||||
 | 
			
		||||
        return devices
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _policies(self):
 | 
			
		||||
        customer_policies = [
 | 
			
		||||
            policy
 | 
			
		||||
            for policy in self._iam_backend.managed_policies
 | 
			
		||||
            if not policy.startswith("arn:aws:iam::aws:policy")
 | 
			
		||||
        ]
 | 
			
		||||
        return len(customer_policies)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _policy_versions_in_use(self):
 | 
			
		||||
        attachments = 0
 | 
			
		||||
 | 
			
		||||
        for policy in self._iam_backend.managed_policies.values():
 | 
			
		||||
            attachments += policy.attachment_count
 | 
			
		||||
 | 
			
		||||
        return attachments
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _providers(self):
 | 
			
		||||
        providers = len(self._iam_backend.saml_providers) + len(
 | 
			
		||||
            self._iam_backend.open_id_providers
 | 
			
		||||
        )
 | 
			
		||||
        return providers
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _roles(self):
 | 
			
		||||
        return len(self._iam_backend.roles)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _server_certificates(self):
 | 
			
		||||
        return len(self._iam_backend.certificates)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def _users(self):
 | 
			
		||||
        return len(self._iam_backend.users)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IAMBackend(BaseBackend):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.instance_profiles = {}
 | 
			
		||||
@ -751,6 +922,7 @@ class IAMBackend(BaseBackend):
 | 
			
		||||
        self.policy_arn_regex = re.compile(r"^arn:aws:iam::[0-9]*:policy/.*$")
 | 
			
		||||
        self.virtual_mfa_devices = {}
 | 
			
		||||
        self.account_password_policy = None
 | 
			
		||||
        self.account_summary = AccountSummary(self)
 | 
			
		||||
        super(IAMBackend, self).__init__()
 | 
			
		||||
 | 
			
		||||
    def _init_managed_policies(self):
 | 
			
		||||
@ -820,7 +992,7 @@ class IAMBackend(BaseBackend):
 | 
			
		||||
        )
 | 
			
		||||
        if policy.arn in self.managed_policies:
 | 
			
		||||
            raise EntityAlreadyExists(
 | 
			
		||||
                "A policy called {} already exists. Duplicate names are not allowed.".format(
 | 
			
		||||
                "A policy called {0} already exists. Duplicate names are not allowed.".format(
 | 
			
		||||
                    policy_name
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
@ -898,6 +1070,10 @@ class IAMBackend(BaseBackend):
 | 
			
		||||
                    permissions_boundary
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        if [role for role in self.get_roles() if role.name == role_name]:
 | 
			
		||||
            raise EntityAlreadyExists(
 | 
			
		||||
                "Role with name {0} already exists.".format(role_name)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        clean_tags = self._tag_verification(tags)
 | 
			
		||||
        role = Role(
 | 
			
		||||
@ -1158,7 +1334,7 @@ class IAMBackend(BaseBackend):
 | 
			
		||||
    def get_all_server_certs(self, marker=None):
 | 
			
		||||
        return self.certificates.values()
 | 
			
		||||
 | 
			
		||||
    def upload_server_cert(
 | 
			
		||||
    def upload_server_certificate(
 | 
			
		||||
        self, cert_name, cert_body, private_key, cert_chain=None, path=None
 | 
			
		||||
    ):
 | 
			
		||||
        certificate_id = random_resource_id()
 | 
			
		||||
@ -1451,6 +1627,26 @@ class IAMBackend(BaseBackend):
 | 
			
		||||
        user = self.get_user(user_name)
 | 
			
		||||
        user.delete_access_key(access_key_id)
 | 
			
		||||
 | 
			
		||||
    def upload_ssh_public_key(self, user_name, ssh_public_key_body):
 | 
			
		||||
        user = self.get_user(user_name)
 | 
			
		||||
        return user.upload_ssh_public_key(ssh_public_key_body)
 | 
			
		||||
 | 
			
		||||
    def get_ssh_public_key(self, user_name, ssh_public_key_id):
 | 
			
		||||
        user = self.get_user(user_name)
 | 
			
		||||
        return user.get_ssh_public_key(ssh_public_key_id)
 | 
			
		||||
 | 
			
		||||
    def get_all_ssh_public_keys(self, user_name):
 | 
			
		||||
        user = self.get_user(user_name)
 | 
			
		||||
        return user.get_all_ssh_public_keys()
 | 
			
		||||
 | 
			
		||||
    def update_ssh_public_key(self, user_name, ssh_public_key_id, status):
 | 
			
		||||
        user = self.get_user(user_name)
 | 
			
		||||
        return user.update_ssh_public_key(ssh_public_key_id, status)
 | 
			
		||||
 | 
			
		||||
    def delete_ssh_public_key(self, user_name, ssh_public_key_id):
 | 
			
		||||
        user = self.get_user(user_name)
 | 
			
		||||
        return user.delete_ssh_public_key(ssh_public_key_id)
 | 
			
		||||
 | 
			
		||||
    def enable_mfa_device(
 | 
			
		||||
        self, user_name, serial_number, authentication_code_1, authentication_code_2
 | 
			
		||||
    ):
 | 
			
		||||
@ -1737,5 +1933,8 @@ class IAMBackend(BaseBackend):
 | 
			
		||||
 | 
			
		||||
        self.account_password_policy = None
 | 
			
		||||
 | 
			
		||||
    def get_account_summary(self):
 | 
			
		||||
        return self.account_summary
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
iam_backend = IAMBackend()
 | 
			
		||||
 | 
			
		||||
@ -351,7 +351,7 @@ class IamResponse(BaseResponse):
 | 
			
		||||
        private_key = self._get_param("PrivateKey")
 | 
			
		||||
        cert_chain = self._get_param("CertificateName")
 | 
			
		||||
 | 
			
		||||
        cert = iam_backend.upload_server_cert(
 | 
			
		||||
        cert = iam_backend.upload_server_certificate(
 | 
			
		||||
            cert_name, cert_body, private_key, cert_chain=cert_chain, path=path
 | 
			
		||||
        )
 | 
			
		||||
        template = self.response_template(UPLOAD_CERT_TEMPLATE)
 | 
			
		||||
@ -590,6 +590,46 @@ class IamResponse(BaseResponse):
 | 
			
		||||
        template = self.response_template(GENERIC_EMPTY_TEMPLATE)
 | 
			
		||||
        return template.render(name="DeleteAccessKey")
 | 
			
		||||
 | 
			
		||||
    def upload_ssh_public_key(self):
 | 
			
		||||
        user_name = self._get_param("UserName")
 | 
			
		||||
        ssh_public_key_body = self._get_param("SSHPublicKeyBody")
 | 
			
		||||
 | 
			
		||||
        key = iam_backend.upload_ssh_public_key(user_name, ssh_public_key_body)
 | 
			
		||||
        template = self.response_template(UPLOAD_SSH_PUBLIC_KEY_TEMPLATE)
 | 
			
		||||
        return template.render(key=key)
 | 
			
		||||
 | 
			
		||||
    def get_ssh_public_key(self):
 | 
			
		||||
        user_name = self._get_param("UserName")
 | 
			
		||||
        ssh_public_key_id = self._get_param("SSHPublicKeyId")
 | 
			
		||||
 | 
			
		||||
        key = iam_backend.get_ssh_public_key(user_name, ssh_public_key_id)
 | 
			
		||||
        template = self.response_template(GET_SSH_PUBLIC_KEY_TEMPLATE)
 | 
			
		||||
        return template.render(key=key)
 | 
			
		||||
 | 
			
		||||
    def list_ssh_public_keys(self):
 | 
			
		||||
        user_name = self._get_param("UserName")
 | 
			
		||||
 | 
			
		||||
        keys = iam_backend.get_all_ssh_public_keys(user_name)
 | 
			
		||||
        template = self.response_template(LIST_SSH_PUBLIC_KEYS_TEMPLATE)
 | 
			
		||||
        return template.render(keys=keys)
 | 
			
		||||
 | 
			
		||||
    def update_ssh_public_key(self):
 | 
			
		||||
        user_name = self._get_param("UserName")
 | 
			
		||||
        ssh_public_key_id = self._get_param("SSHPublicKeyId")
 | 
			
		||||
        status = self._get_param("Status")
 | 
			
		||||
 | 
			
		||||
        iam_backend.update_ssh_public_key(user_name, ssh_public_key_id, status)
 | 
			
		||||
        template = self.response_template(UPDATE_SSH_PUBLIC_KEY_TEMPLATE)
 | 
			
		||||
        return template.render()
 | 
			
		||||
 | 
			
		||||
    def delete_ssh_public_key(self):
 | 
			
		||||
        user_name = self._get_param("UserName")
 | 
			
		||||
        ssh_public_key_id = self._get_param("SSHPublicKeyId")
 | 
			
		||||
 | 
			
		||||
        iam_backend.delete_ssh_public_key(user_name, ssh_public_key_id)
 | 
			
		||||
        template = self.response_template(DELETE_SSH_PUBLIC_KEY_TEMPLATE)
 | 
			
		||||
        return template.render()
 | 
			
		||||
 | 
			
		||||
    def deactivate_mfa_device(self):
 | 
			
		||||
        user_name = self._get_param("UserName")
 | 
			
		||||
        serial_number = self._get_param("SerialNumber")
 | 
			
		||||
@ -888,6 +928,12 @@ class IamResponse(BaseResponse):
 | 
			
		||||
        template = self.response_template(DELETE_ACCOUNT_PASSWORD_POLICY_TEMPLATE)
 | 
			
		||||
        return template.render()
 | 
			
		||||
 | 
			
		||||
    def get_account_summary(self):
 | 
			
		||||
        account_summary = iam_backend.get_account_summary()
 | 
			
		||||
 | 
			
		||||
        template = self.response_template(GET_ACCOUNT_SUMMARY_TEMPLATE)
 | 
			
		||||
        return template.render(summary_map=account_summary.summary_map)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LIST_ENTITIES_FOR_POLICY_TEMPLATE = """<ListEntitiesForPolicyResponse>
 | 
			
		||||
 <ListEntitiesForPolicyResult>
 | 
			
		||||
@ -1690,6 +1736,73 @@ GET_ACCESS_KEY_LAST_USED_TEMPLATE = """
 | 
			
		||||
</GetAccessKeyLastUsedResponse>
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
UPLOAD_SSH_PUBLIC_KEY_TEMPLATE = """<UploadSSHPublicKeyResponse>
 | 
			
		||||
   <UploadSSHPublicKeyResult>
 | 
			
		||||
     <SSHPublicKey>
 | 
			
		||||
         <UserName>{{ key.user_name }}</UserName>
 | 
			
		||||
         <SSHPublicKeyBody>{{ key.ssh_public_key_body }}</SSHPublicKeyBody>
 | 
			
		||||
         <SSHPublicKeyId>{{ key.ssh_public_key_id }}</SSHPublicKeyId>
 | 
			
		||||
         <Fingerprint>{{ key.fingerprint }}</Fingerprint>
 | 
			
		||||
         <Status>{{ key.status }}</Status>
 | 
			
		||||
         <UploadDate>{{ key.uploaded_iso_8601 }}</UploadDate>
 | 
			
		||||
      </SSHPublicKey>
 | 
			
		||||
   </UploadSSHPublicKeyResult>
 | 
			
		||||
   <ResponseMetadata>
 | 
			
		||||
      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
			
		||||
   </ResponseMetadata>
 | 
			
		||||
</UploadSSHPublicKeyResponse>"""
 | 
			
		||||
 | 
			
		||||
GET_SSH_PUBLIC_KEY_TEMPLATE = """<GetSSHPublicKeyResponse>
 | 
			
		||||
   <GetSSHPublicKeyResult>
 | 
			
		||||
     <SSHPublicKey>
 | 
			
		||||
         <UserName>{{ key.user_name }}</UserName>
 | 
			
		||||
         <SSHPublicKeyBody>{{ key.ssh_public_key_body }}</SSHPublicKeyBody>
 | 
			
		||||
         <SSHPublicKeyId>{{ key.ssh_public_key_id }}</SSHPublicKeyId>
 | 
			
		||||
         <Fingerprint>{{ key.fingerprint }}</Fingerprint>
 | 
			
		||||
         <Status>{{ key.status }}</Status>
 | 
			
		||||
         <UploadDate>{{ key.uploaded_iso_8601 }}</UploadDate>
 | 
			
		||||
      </SSHPublicKey>
 | 
			
		||||
   </GetSSHPublicKeyResult>
 | 
			
		||||
   <ResponseMetadata>
 | 
			
		||||
      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
			
		||||
   </ResponseMetadata>
 | 
			
		||||
</GetSSHPublicKeyResponse>"""
 | 
			
		||||
 | 
			
		||||
LIST_SSH_PUBLIC_KEYS_TEMPLATE = """<ListSSHPublicKeysResponse>
 | 
			
		||||
   <ListSSHPublicKeysResult>
 | 
			
		||||
      <SSHPublicKeys>
 | 
			
		||||
        {% for key in keys %}
 | 
			
		||||
            <member>
 | 
			
		||||
                <UserName>{{ key.user_name }}</UserName>
 | 
			
		||||
                <SSHPublicKeyId>{{ key.ssh_public_key_id }}</SSHPublicKeyId>
 | 
			
		||||
                <Status>{{ key.status }}</Status>
 | 
			
		||||
                <UploadDate>{{ key.uploaded_iso_8601 }}</UploadDate>
 | 
			
		||||
            </member>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
      </SSHPublicKeys>
 | 
			
		||||
      <IsTruncated>false</IsTruncated>
 | 
			
		||||
   </ListSSHPublicKeysResult>
 | 
			
		||||
   <ResponseMetadata>
 | 
			
		||||
      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
			
		||||
   </ResponseMetadata>
 | 
			
		||||
</ListSSHPublicKeysResponse>"""
 | 
			
		||||
 | 
			
		||||
UPDATE_SSH_PUBLIC_KEY_TEMPLATE = """<UpdateSSHPublicKeyResponse>
 | 
			
		||||
   <UpdateSSHPublicKeyResult>
 | 
			
		||||
   </UpdateSSHPublicKeyResult>
 | 
			
		||||
   <ResponseMetadata>
 | 
			
		||||
      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
			
		||||
   </ResponseMetadata>
 | 
			
		||||
</UpdateSSHPublicKeyResponse>"""
 | 
			
		||||
 | 
			
		||||
DELETE_SSH_PUBLIC_KEY_TEMPLATE = """<DeleteSSHPublicKeyResponse>
 | 
			
		||||
   <DeleteSSHPublicKeyResult>
 | 
			
		||||
   </DeleteSSHPublicKeyResult>
 | 
			
		||||
   <ResponseMetadata>
 | 
			
		||||
      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
			
		||||
   </ResponseMetadata>
 | 
			
		||||
</DeleteSSHPublicKeyResponse>"""
 | 
			
		||||
 | 
			
		||||
CREDENTIAL_REPORT_GENERATING = """
 | 
			
		||||
<GenerateCredentialReportResponse>
 | 
			
		||||
    <GenerateCredentialReportResult>
 | 
			
		||||
@ -2261,3 +2374,20 @@ DELETE_ACCOUNT_PASSWORD_POLICY_TEMPLATE = """<DeleteAccountPasswordPolicyRespons
 | 
			
		||||
    <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
			
		||||
  </ResponseMetadata>
 | 
			
		||||
</DeleteAccountPasswordPolicyResponse>"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GET_ACCOUNT_SUMMARY_TEMPLATE = """<GetAccountSummaryResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
 | 
			
		||||
  <GetAccountSummaryResult>
 | 
			
		||||
    <SummaryMap>
 | 
			
		||||
      {% for key, value in summary_map.items() %}
 | 
			
		||||
      <entry>
 | 
			
		||||
        <key>{{ key }}</key>
 | 
			
		||||
        <value>{{ value }}</value>
 | 
			
		||||
      </entry>
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
    </SummaryMap>
 | 
			
		||||
  </GetAccountSummaryResult>
 | 
			
		||||
  <ResponseMetadata>
 | 
			
		||||
    <RequestId>85cb9b90-ac28-11e4-a88d-97964EXAMPLE</RequestId>
 | 
			
		||||
  </ResponseMetadata>
 | 
			
		||||
</GetAccountSummaryResponse>"""
 | 
			
		||||
 | 
			
		||||
@ -125,7 +125,7 @@ class HTTPrettyRequest(BaseHTTPRequestHandler, BaseClass):
 | 
			
		||||
    internal `parse_request` method.
 | 
			
		||||
 | 
			
		||||
    It also replaces the `rfile` and `wfile` attributes with StringIO
 | 
			
		||||
    instances so that we garantee that it won't make any I/O, neighter
 | 
			
		||||
    instances so that we guarantee that it won't make any I/O, neighter
 | 
			
		||||
    for writing nor reading.
 | 
			
		||||
 | 
			
		||||
    It has some convenience attributes:
 | 
			
		||||
 | 
			
		||||
@ -190,7 +190,7 @@ def create_backend_app(service):
 | 
			
		||||
        index = 2
 | 
			
		||||
        while endpoint in backend_app.view_functions:
 | 
			
		||||
            # HACK: Sometimes we map the same view to multiple url_paths. Flask
 | 
			
		||||
            # requries us to have different names.
 | 
			
		||||
            # requires us to have different names.
 | 
			
		||||
            endpoint = original_endpoint + str(index)
 | 
			
		||||
            index += 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -147,7 +147,7 @@ class SESBackend(BaseBackend):
 | 
			
		||||
 | 
			
		||||
    def __type_of_message__(self, destinations):
 | 
			
		||||
        """Checks the destination for any special address that could indicate delivery,
 | 
			
		||||
        complaint or bounce like in SES simualtor"""
 | 
			
		||||
        complaint or bounce like in SES simulator"""
 | 
			
		||||
        alladdress = (
 | 
			
		||||
            destinations.get("ToAddresses", [])
 | 
			
		||||
            + destinations.get("CcAddresses", [])
 | 
			
		||||
 | 
			
		||||
@ -227,7 +227,7 @@ class Subscription(BaseModel):
 | 
			
		||||
                        return False
 | 
			
		||||
 | 
			
		||||
                    for attribute_values in attribute_values:
 | 
			
		||||
                        # Even the offical documentation states a 5 digits of accuracy after the decimal point for numerics, in reality it is 6
 | 
			
		||||
                        # Even the official documentation states a 5 digits of accuracy after the decimal point for numerics, in reality it is 6
 | 
			
		||||
                        # https://docs.aws.amazon.com/sns/latest/dg/sns-subscription-filter-policies.html#subscription-filter-policy-constraints
 | 
			
		||||
                        if int(attribute_values * 1000000) == int(rule * 1000000):
 | 
			
		||||
                            return True
 | 
			
		||||
@ -573,7 +573,7 @@ class SNSBackend(BaseBackend):
 | 
			
		||||
        combinations = 1
 | 
			
		||||
        for rules in six.itervalues(value):
 | 
			
		||||
            combinations *= len(rules)
 | 
			
		||||
        # Even the offical documentation states the total combination of values must not exceed 100, in reality it is 150
 | 
			
		||||
        # Even the official documentation states the total combination of values must not exceed 100, in reality it is 150
 | 
			
		||||
        # https://docs.aws.amazon.com/sns/latest/dg/sns-subscription-filter-policies.html#subscription-filter-policy-constraints
 | 
			
		||||
        if combinations > 150:
 | 
			
		||||
            raise SNSInvalidParameter(
 | 
			
		||||
 | 
			
		||||
@ -77,7 +77,7 @@ class SNSResponse(BaseResponse):
 | 
			
		||||
                    transform_value = value["StringValue"]
 | 
			
		||||
            elif "BinaryValue" in value:
 | 
			
		||||
                transform_value = value["BinaryValue"]
 | 
			
		||||
            if not transform_value:
 | 
			
		||||
            if transform_value == "":
 | 
			
		||||
                raise InvalidParameterValue(
 | 
			
		||||
                    "The message attribute '{0}' must contain non-empty "
 | 
			
		||||
                    "message attribute value for message attribute "
 | 
			
		||||
 | 
			
		||||
@ -761,7 +761,7 @@ class SQSBackend(BaseBackend):
 | 
			
		||||
 | 
			
		||||
        new_messages = []
 | 
			
		||||
        for message in queue._messages:
 | 
			
		||||
            # Only delete message if it is not visible and the reciept_handle
 | 
			
		||||
            # Only delete message if it is not visible and the receipt_handle
 | 
			
		||||
            # matches.
 | 
			
		||||
            if message.receipt_handle == receipt_handle:
 | 
			
		||||
                queue.pending_messages.remove(message)
 | 
			
		||||
 | 
			
		||||
@ -430,7 +430,7 @@ class WorkflowExecution(BaseModel):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def fail(self, event_id, details=None, reason=None):
 | 
			
		||||
        # TODO: implement lenght constraints on details/reason
 | 
			
		||||
        # TODO: implement length constraints on details/reason
 | 
			
		||||
        self.execution_status = "CLOSED"
 | 
			
		||||
        self.close_status = "FAILED"
 | 
			
		||||
        self.close_timestamp = unix_time()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							@ -39,7 +39,7 @@ install_requires = [
 | 
			
		||||
    "werkzeug",
 | 
			
		||||
    "PyYAML>=5.1",
 | 
			
		||||
    "pytz",
 | 
			
		||||
    "python-dateutil<3.0.0,>=2.1",
 | 
			
		||||
    "python-dateutil<2.8.1,>=2.1",
 | 
			
		||||
    "python-jose<4.0.0",
 | 
			
		||||
    "mock",
 | 
			
		||||
    "docker>=2.5.1",
 | 
			
		||||
 | 
			
		||||
@ -162,7 +162,7 @@ if settings.TEST_SERVER_MODE:
 | 
			
		||||
        conn = boto3.client("lambda", "us-west-2")
 | 
			
		||||
        conn.create_function(
 | 
			
		||||
            FunctionName="testFunction",
 | 
			
		||||
            Runtime="python2.7",
 | 
			
		||||
            Runtime="python3.7",
 | 
			
		||||
            Role="test-iam-role",
 | 
			
		||||
            Handler="lambda_function.lambda_handler",
 | 
			
		||||
            Code={"ZipFile": get_test_zip_file2()},
 | 
			
		||||
@ -184,18 +184,20 @@ if settings.TEST_SERVER_MODE:
 | 
			
		||||
            vol.id,
 | 
			
		||||
            vol.state,
 | 
			
		||||
            vol.size,
 | 
			
		||||
            json.dumps(in_data),
 | 
			
		||||
            json.dumps(in_data).replace(
 | 
			
		||||
                " ", ""
 | 
			
		||||
            ),  # Makes the tests pass as the result is missing the whitespace
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        log_result = base64.b64decode(result["LogResult"]).decode("utf-8")
 | 
			
		||||
 | 
			
		||||
        # fix for running under travis (TODO: investigate why it has an extra newline)
 | 
			
		||||
        # The Docker lambda invocation will return an additional '\n', so need to replace it:
 | 
			
		||||
        log_result = log_result.replace("\n\n", "\n")
 | 
			
		||||
        log_result.should.equal(msg)
 | 
			
		||||
 | 
			
		||||
        payload = result["Payload"].read().decode("utf-8")
 | 
			
		||||
 | 
			
		||||
        # fix for running under travis (TODO: investigate why it has an extra newline)
 | 
			
		||||
        # The Docker lambda invocation will return an additional '\n', so need to replace it:
 | 
			
		||||
        payload = payload.replace("\n\n", "\n")
 | 
			
		||||
        payload.should.equal(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ from nose.tools import assert_raises
 | 
			
		||||
from moto import mock_iam, mock_ec2, mock_s3, mock_sts, mock_elbv2, mock_rds2
 | 
			
		||||
from moto.core import set_initial_no_auth_action_count
 | 
			
		||||
from moto.iam.models import ACCOUNT_ID
 | 
			
		||||
from uuid import uuid4
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
@ -71,8 +72,10 @@ def create_user_with_access_key_and_multiple_policies(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_group_with_attached_policy_and_add_user(
 | 
			
		||||
    user_name, policy_document, group_name="test-group", policy_name="policy1"
 | 
			
		||||
    user_name, policy_document, group_name="test-group", policy_name=None
 | 
			
		||||
):
 | 
			
		||||
    if not policy_name:
 | 
			
		||||
        policy_name = str(uuid4())
 | 
			
		||||
    client = boto3.client("iam", region_name="us-east-1")
 | 
			
		||||
    client.create_group(GroupName=group_name)
 | 
			
		||||
    policy_arn = client.create_policy(
 | 
			
		||||
@ -101,8 +104,10 @@ def create_group_with_multiple_policies_and_add_user(
 | 
			
		||||
    attached_policy_document,
 | 
			
		||||
    group_name="test-group",
 | 
			
		||||
    inline_policy_name="policy1",
 | 
			
		||||
    attached_policy_name="policy1",
 | 
			
		||||
    attached_policy_name=None,
 | 
			
		||||
):
 | 
			
		||||
    if not attached_policy_name:
 | 
			
		||||
        attached_policy_name = str(uuid4())
 | 
			
		||||
    client = boto3.client("iam", region_name="us-east-1")
 | 
			
		||||
    client.create_group(GroupName=group_name)
 | 
			
		||||
    client.put_group_policy(
 | 
			
		||||
 | 
			
		||||
@ -678,3 +678,150 @@ def test_create_vpc_with_invalid_cidr_range():
 | 
			
		||||
        "An error occurred (InvalidVpc.Range) when calling the CreateVpc "
 | 
			
		||||
        "operation: The CIDR '{}' is invalid.".format(vpc_cidr_block)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_enable_vpc_classic_link():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.1.0.0/16")
 | 
			
		||||
 | 
			
		||||
    response = ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
 | 
			
		||||
    assert response.get("Return").should.be.true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_enable_vpc_classic_link_failure():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.90.0.0/16")
 | 
			
		||||
 | 
			
		||||
    response = ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
 | 
			
		||||
    assert response.get("Return").should.be.false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_disable_vpc_classic_link():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
 | 
			
		||||
 | 
			
		||||
    ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
 | 
			
		||||
    response = ec2.meta.client.disable_vpc_classic_link(VpcId=vpc.id)
 | 
			
		||||
    assert response.get("Return").should.be.false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_describe_classic_link_enabled():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
 | 
			
		||||
 | 
			
		||||
    ec2.meta.client.enable_vpc_classic_link(VpcId=vpc.id)
 | 
			
		||||
    response = ec2.meta.client.describe_vpc_classic_link(VpcIds=[vpc.id])
 | 
			
		||||
    assert response.get("Vpcs")[0].get("ClassicLinkEnabled").should.be.true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_describe_classic_link_disabled():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.90.0.0/16")
 | 
			
		||||
 | 
			
		||||
    response = ec2.meta.client.describe_vpc_classic_link(VpcIds=[vpc.id])
 | 
			
		||||
    assert response.get("Vpcs")[0].get("ClassicLinkEnabled").should.be.false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_describe_classic_link_multiple():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc1 = ec2.create_vpc(CidrBlock="10.90.0.0/16")
 | 
			
		||||
    vpc2 = ec2.create_vpc(CidrBlock="10.0.0.0/16")
 | 
			
		||||
 | 
			
		||||
    ec2.meta.client.enable_vpc_classic_link(VpcId=vpc2.id)
 | 
			
		||||
    response = ec2.meta.client.describe_vpc_classic_link(VpcIds=[vpc1.id, vpc2.id])
 | 
			
		||||
    expected = [
 | 
			
		||||
        {"VpcId": vpc1.id, "ClassicLinkDnsSupported": False},
 | 
			
		||||
        {"VpcId": vpc2.id, "ClassicLinkDnsSupported": True},
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    # Ensure response is sorted, because they can come in random order
 | 
			
		||||
    assert response.get("Vpcs").sort(key=lambda x: x["VpcId"]) == expected.sort(
 | 
			
		||||
        key=lambda x: x["VpcId"]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_enable_vpc_classic_link_dns_support():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.1.0.0/16")
 | 
			
		||||
 | 
			
		||||
    response = ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc.id)
 | 
			
		||||
    assert response.get("Return").should.be.true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_disable_vpc_classic_link_dns_support():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
 | 
			
		||||
 | 
			
		||||
    ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc.id)
 | 
			
		||||
    response = ec2.meta.client.disable_vpc_classic_link_dns_support(VpcId=vpc.id)
 | 
			
		||||
    assert response.get("Return").should.be.false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_describe_classic_link_dns_support_enabled():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
 | 
			
		||||
 | 
			
		||||
    ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc.id)
 | 
			
		||||
    response = ec2.meta.client.describe_vpc_classic_link_dns_support(VpcIds=[vpc.id])
 | 
			
		||||
    assert response.get("Vpcs")[0].get("ClassicLinkDnsSupported").should.be.true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_describe_classic_link_dns_support_disabled():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc = ec2.create_vpc(CidrBlock="10.90.0.0/16")
 | 
			
		||||
 | 
			
		||||
    response = ec2.meta.client.describe_vpc_classic_link_dns_support(VpcIds=[vpc.id])
 | 
			
		||||
    assert response.get("Vpcs")[0].get("ClassicLinkDnsSupported").should.be.false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_ec2
 | 
			
		||||
def test_describe_classic_link_dns_support_multiple():
 | 
			
		||||
    ec2 = boto3.resource("ec2", region_name="us-west-1")
 | 
			
		||||
 | 
			
		||||
    # Create VPC
 | 
			
		||||
    vpc1 = ec2.create_vpc(CidrBlock="10.90.0.0/16")
 | 
			
		||||
    vpc2 = ec2.create_vpc(CidrBlock="10.0.0.0/16")
 | 
			
		||||
 | 
			
		||||
    ec2.meta.client.enable_vpc_classic_link_dns_support(VpcId=vpc2.id)
 | 
			
		||||
    response = ec2.meta.client.describe_vpc_classic_link_dns_support(
 | 
			
		||||
        VpcIds=[vpc1.id, vpc2.id]
 | 
			
		||||
    )
 | 
			
		||||
    expected = [
 | 
			
		||||
        {"VpcId": vpc1.id, "ClassicLinkDnsSupported": False},
 | 
			
		||||
        {"VpcId": vpc2.id, "ClassicLinkDnsSupported": True},
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    # Ensure response is sorted, because they can come in random order
 | 
			
		||||
    assert response.get("Vpcs").sort(key=lambda x: x["VpcId"]) == expected.sort(
 | 
			
		||||
        key=lambda x: x["VpcId"]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ from nose.tools import raises
 | 
			
		||||
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from tests.helpers import requires_boto_gte
 | 
			
		||||
from uuid import uuid4
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MOCK_CERT = """-----BEGIN CERTIFICATE-----
 | 
			
		||||
@ -1315,6 +1316,122 @@ def test_get_access_key_last_used():
 | 
			
		||||
    resp["UserName"].should.equal(create_key_response["UserName"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_upload_ssh_public_key():
 | 
			
		||||
    iam = boto3.resource("iam", region_name="us-east-1")
 | 
			
		||||
    client = iam.meta.client
 | 
			
		||||
    username = "test-user"
 | 
			
		||||
    iam.create_user(UserName=username)
 | 
			
		||||
    public_key = MOCK_CERT
 | 
			
		||||
 | 
			
		||||
    resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
 | 
			
		||||
    pubkey = resp["SSHPublicKey"]
 | 
			
		||||
    pubkey["SSHPublicKeyBody"].should.equal(public_key)
 | 
			
		||||
    pubkey["UserName"].should.equal(username)
 | 
			
		||||
    pubkey["SSHPublicKeyId"].should.have.length_of(20)
 | 
			
		||||
    assert pubkey["SSHPublicKeyId"].startswith("APKA")
 | 
			
		||||
    pubkey.should.have.key("Fingerprint")
 | 
			
		||||
    pubkey["Status"].should.equal("Active")
 | 
			
		||||
    (
 | 
			
		||||
        datetime.utcnow() - pubkey["UploadDate"].replace(tzinfo=None)
 | 
			
		||||
    ).seconds.should.be.within(0, 10)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_get_ssh_public_key():
 | 
			
		||||
    iam = boto3.resource("iam", region_name="us-east-1")
 | 
			
		||||
    client = iam.meta.client
 | 
			
		||||
    username = "test-user"
 | 
			
		||||
    iam.create_user(UserName=username)
 | 
			
		||||
    public_key = MOCK_CERT
 | 
			
		||||
 | 
			
		||||
    with assert_raises(ClientError):
 | 
			
		||||
        client.get_ssh_public_key(
 | 
			
		||||
            UserName=username, SSHPublicKeyId="xxnon-existent-keyxx", Encoding="SSH"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
 | 
			
		||||
    ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
 | 
			
		||||
 | 
			
		||||
    resp = client.get_ssh_public_key(
 | 
			
		||||
        UserName=username, SSHPublicKeyId=ssh_public_key_id, Encoding="SSH"
 | 
			
		||||
    )
 | 
			
		||||
    resp["SSHPublicKey"]["SSHPublicKeyBody"].should.equal(public_key)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_list_ssh_public_keys():
 | 
			
		||||
    iam = boto3.resource("iam", region_name="us-east-1")
 | 
			
		||||
    client = iam.meta.client
 | 
			
		||||
    username = "test-user"
 | 
			
		||||
    iam.create_user(UserName=username)
 | 
			
		||||
    public_key = MOCK_CERT
 | 
			
		||||
 | 
			
		||||
    resp = client.list_ssh_public_keys(UserName=username)
 | 
			
		||||
    resp["SSHPublicKeys"].should.have.length_of(0)
 | 
			
		||||
 | 
			
		||||
    resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
 | 
			
		||||
    ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
 | 
			
		||||
 | 
			
		||||
    resp = client.list_ssh_public_keys(UserName=username)
 | 
			
		||||
    resp["SSHPublicKeys"].should.have.length_of(1)
 | 
			
		||||
    resp["SSHPublicKeys"][0]["SSHPublicKeyId"].should.equal(ssh_public_key_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_update_ssh_public_key():
 | 
			
		||||
    iam = boto3.resource("iam", region_name="us-east-1")
 | 
			
		||||
    client = iam.meta.client
 | 
			
		||||
    username = "test-user"
 | 
			
		||||
    iam.create_user(UserName=username)
 | 
			
		||||
    public_key = MOCK_CERT
 | 
			
		||||
 | 
			
		||||
    with assert_raises(ClientError):
 | 
			
		||||
        client.update_ssh_public_key(
 | 
			
		||||
            UserName=username, SSHPublicKeyId="xxnon-existent-keyxx", Status="Inactive"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
 | 
			
		||||
    ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
 | 
			
		||||
    resp["SSHPublicKey"]["Status"].should.equal("Active")
 | 
			
		||||
 | 
			
		||||
    resp = client.update_ssh_public_key(
 | 
			
		||||
        UserName=username, SSHPublicKeyId=ssh_public_key_id, Status="Inactive"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    resp = client.get_ssh_public_key(
 | 
			
		||||
        UserName=username, SSHPublicKeyId=ssh_public_key_id, Encoding="SSH"
 | 
			
		||||
    )
 | 
			
		||||
    resp["SSHPublicKey"]["Status"].should.equal("Inactive")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_delete_ssh_public_key():
 | 
			
		||||
    iam = boto3.resource("iam", region_name="us-east-1")
 | 
			
		||||
    client = iam.meta.client
 | 
			
		||||
    username = "test-user"
 | 
			
		||||
    iam.create_user(UserName=username)
 | 
			
		||||
    public_key = MOCK_CERT
 | 
			
		||||
 | 
			
		||||
    with assert_raises(ClientError):
 | 
			
		||||
        client.delete_ssh_public_key(
 | 
			
		||||
            UserName=username, SSHPublicKeyId="xxnon-existent-keyxx"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    resp = client.upload_ssh_public_key(UserName=username, SSHPublicKeyBody=public_key)
 | 
			
		||||
    ssh_public_key_id = resp["SSHPublicKey"]["SSHPublicKeyId"]
 | 
			
		||||
 | 
			
		||||
    resp = client.list_ssh_public_keys(UserName=username)
 | 
			
		||||
    resp["SSHPublicKeys"].should.have.length_of(1)
 | 
			
		||||
 | 
			
		||||
    resp = client.delete_ssh_public_key(
 | 
			
		||||
        UserName=username, SSHPublicKeyId=ssh_public_key_id
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    resp = client.list_ssh_public_keys(UserName=username)
 | 
			
		||||
    resp["SSHPublicKeys"].should.have.length_of(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_get_account_authorization_details():
 | 
			
		||||
    test_policy = json.dumps(
 | 
			
		||||
@ -2050,6 +2167,42 @@ def test_create_role_with_permissions_boundary():
 | 
			
		||||
    conn.list_roles().get("Roles")[0].get("PermissionsBoundary").should.equal(expected)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_create_role_with_same_name_should_fail():
 | 
			
		||||
    iam = boto3.client("iam", region_name="us-east-1")
 | 
			
		||||
    test_role_name = str(uuid4())
 | 
			
		||||
    iam.create_role(
 | 
			
		||||
        RoleName=test_role_name, AssumeRolePolicyDocument="policy", Description="test"
 | 
			
		||||
    )
 | 
			
		||||
    # Create the role again, and verify that it fails
 | 
			
		||||
    with assert_raises(ClientError) as err:
 | 
			
		||||
        iam.create_role(
 | 
			
		||||
            RoleName=test_role_name,
 | 
			
		||||
            AssumeRolePolicyDocument="policy",
 | 
			
		||||
            Description="test",
 | 
			
		||||
        )
 | 
			
		||||
    err.exception.response["Error"]["Code"].should.equal("EntityAlreadyExists")
 | 
			
		||||
    err.exception.response["Error"]["Message"].should.equal(
 | 
			
		||||
        "Role with name {0} already exists.".format(test_role_name)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_create_policy_with_same_name_should_fail():
 | 
			
		||||
    iam = boto3.client("iam", region_name="us-east-1")
 | 
			
		||||
    test_policy_name = str(uuid4())
 | 
			
		||||
    policy = iam.create_policy(PolicyName=test_policy_name, PolicyDocument=MOCK_POLICY)
 | 
			
		||||
    # Create the role again, and verify that it fails
 | 
			
		||||
    with assert_raises(ClientError) as err:
 | 
			
		||||
        iam.create_policy(PolicyName=test_policy_name, PolicyDocument=MOCK_POLICY)
 | 
			
		||||
    err.exception.response["Error"]["Code"].should.equal("EntityAlreadyExists")
 | 
			
		||||
    err.exception.response["Error"]["Message"].should.equal(
 | 
			
		||||
        "A policy called {0} already exists. Duplicate names are not allowed.".format(
 | 
			
		||||
            test_policy_name
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_create_open_id_connect_provider():
 | 
			
		||||
    client = boto3.client("iam", region_name="us-east-1")
 | 
			
		||||
@ -2325,3 +2478,123 @@ def test_delete_account_password_policy_errors():
 | 
			
		||||
    client.delete_account_password_policy.when.called_with().should.throw(
 | 
			
		||||
        ClientError, "The account policy with name PasswordPolicy cannot be found."
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_iam
 | 
			
		||||
def test_get_account_summary():
 | 
			
		||||
    client = boto3.client("iam", region_name="us-east-1")
 | 
			
		||||
    iam = boto3.resource("iam", region_name="us-east-1")
 | 
			
		||||
 | 
			
		||||
    account_summary = iam.AccountSummary()
 | 
			
		||||
 | 
			
		||||
    account_summary.summary_map.should.equal(
 | 
			
		||||
        {
 | 
			
		||||
            "GroupPolicySizeQuota": 5120,
 | 
			
		||||
            "InstanceProfilesQuota": 1000,
 | 
			
		||||
            "Policies": 0,
 | 
			
		||||
            "GroupsPerUserQuota": 10,
 | 
			
		||||
            "InstanceProfiles": 0,
 | 
			
		||||
            "AttachedPoliciesPerUserQuota": 10,
 | 
			
		||||
            "Users": 0,
 | 
			
		||||
            "PoliciesQuota": 1500,
 | 
			
		||||
            "Providers": 0,
 | 
			
		||||
            "AccountMFAEnabled": 0,
 | 
			
		||||
            "AccessKeysPerUserQuota": 2,
 | 
			
		||||
            "AssumeRolePolicySizeQuota": 2048,
 | 
			
		||||
            "PolicyVersionsInUseQuota": 10000,
 | 
			
		||||
            "GlobalEndpointTokenVersion": 1,
 | 
			
		||||
            "VersionsPerPolicyQuota": 5,
 | 
			
		||||
            "AttachedPoliciesPerGroupQuota": 10,
 | 
			
		||||
            "PolicySizeQuota": 6144,
 | 
			
		||||
            "Groups": 0,
 | 
			
		||||
            "AccountSigningCertificatesPresent": 0,
 | 
			
		||||
            "UsersQuota": 5000,
 | 
			
		||||
            "ServerCertificatesQuota": 20,
 | 
			
		||||
            "MFADevices": 0,
 | 
			
		||||
            "UserPolicySizeQuota": 2048,
 | 
			
		||||
            "PolicyVersionsInUse": 0,
 | 
			
		||||
            "ServerCertificates": 0,
 | 
			
		||||
            "Roles": 0,
 | 
			
		||||
            "RolesQuota": 1000,
 | 
			
		||||
            "SigningCertificatesPerUserQuota": 2,
 | 
			
		||||
            "MFADevicesInUse": 0,
 | 
			
		||||
            "RolePolicySizeQuota": 10240,
 | 
			
		||||
            "AttachedPoliciesPerRoleQuota": 10,
 | 
			
		||||
            "AccountAccessKeysPresent": 0,
 | 
			
		||||
            "GroupsQuota": 300,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    client.create_instance_profile(InstanceProfileName="test-profile")
 | 
			
		||||
    client.create_open_id_connect_provider(
 | 
			
		||||
        Url="https://example.com", ThumbprintList=[],
 | 
			
		||||
    )
 | 
			
		||||
    response_policy = client.create_policy(
 | 
			
		||||
        PolicyName="test-policy", PolicyDocument=MOCK_POLICY
 | 
			
		||||
    )
 | 
			
		||||
    client.create_role(RoleName="test-role", AssumeRolePolicyDocument="test policy")
 | 
			
		||||
    client.attach_role_policy(
 | 
			
		||||
        RoleName="test-role", PolicyArn=response_policy["Policy"]["Arn"]
 | 
			
		||||
    )
 | 
			
		||||
    client.create_saml_provider(
 | 
			
		||||
        Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024
 | 
			
		||||
    )
 | 
			
		||||
    client.create_group(GroupName="test-group")
 | 
			
		||||
    client.attach_group_policy(
 | 
			
		||||
        GroupName="test-group", PolicyArn=response_policy["Policy"]["Arn"]
 | 
			
		||||
    )
 | 
			
		||||
    client.create_user(UserName="test-user")
 | 
			
		||||
    client.attach_user_policy(
 | 
			
		||||
        UserName="test-user", PolicyArn=response_policy["Policy"]["Arn"]
 | 
			
		||||
    )
 | 
			
		||||
    client.enable_mfa_device(
 | 
			
		||||
        UserName="test-user",
 | 
			
		||||
        SerialNumber="123456789",
 | 
			
		||||
        AuthenticationCode1="234567",
 | 
			
		||||
        AuthenticationCode2="987654",
 | 
			
		||||
    )
 | 
			
		||||
    client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
 | 
			
		||||
    client.upload_server_certificate(
 | 
			
		||||
        ServerCertificateName="test-cert",
 | 
			
		||||
        CertificateBody="cert-body",
 | 
			
		||||
        PrivateKey="private-key",
 | 
			
		||||
    )
 | 
			
		||||
    account_summary.load()
 | 
			
		||||
 | 
			
		||||
    account_summary.summary_map.should.equal(
 | 
			
		||||
        {
 | 
			
		||||
            "GroupPolicySizeQuota": 5120,
 | 
			
		||||
            "InstanceProfilesQuota": 1000,
 | 
			
		||||
            "Policies": 1,
 | 
			
		||||
            "GroupsPerUserQuota": 10,
 | 
			
		||||
            "InstanceProfiles": 1,
 | 
			
		||||
            "AttachedPoliciesPerUserQuota": 10,
 | 
			
		||||
            "Users": 1,
 | 
			
		||||
            "PoliciesQuota": 1500,
 | 
			
		||||
            "Providers": 2,
 | 
			
		||||
            "AccountMFAEnabled": 0,
 | 
			
		||||
            "AccessKeysPerUserQuota": 2,
 | 
			
		||||
            "AssumeRolePolicySizeQuota": 2048,
 | 
			
		||||
            "PolicyVersionsInUseQuota": 10000,
 | 
			
		||||
            "GlobalEndpointTokenVersion": 1,
 | 
			
		||||
            "VersionsPerPolicyQuota": 5,
 | 
			
		||||
            "AttachedPoliciesPerGroupQuota": 10,
 | 
			
		||||
            "PolicySizeQuota": 6144,
 | 
			
		||||
            "Groups": 1,
 | 
			
		||||
            "AccountSigningCertificatesPresent": 0,
 | 
			
		||||
            "UsersQuota": 5000,
 | 
			
		||||
            "ServerCertificatesQuota": 20,
 | 
			
		||||
            "MFADevices": 1,
 | 
			
		||||
            "UserPolicySizeQuota": 2048,
 | 
			
		||||
            "PolicyVersionsInUse": 3,
 | 
			
		||||
            "ServerCertificates": 1,
 | 
			
		||||
            "Roles": 1,
 | 
			
		||||
            "RolesQuota": 1000,
 | 
			
		||||
            "SigningCertificatesPerUserQuota": 2,
 | 
			
		||||
            "MFADevicesInUse": 1,
 | 
			
		||||
            "RolePolicySizeQuota": 10240,
 | 
			
		||||
            "AttachedPoliciesPerRoleQuota": 10,
 | 
			
		||||
            "AccountAccessKeysPresent": 0,
 | 
			
		||||
            "GroupsQuota": 300,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@ -173,6 +173,27 @@ def test_publish_to_sqs_msg_attr_byte_value():
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_sqs
 | 
			
		||||
@mock_sns
 | 
			
		||||
def test_publish_to_sqs_msg_attr_number_type():
 | 
			
		||||
    sns = boto3.resource("sns", region_name="us-east-1")
 | 
			
		||||
    topic = sns.create_topic(Name="test-topic")
 | 
			
		||||
    sqs = boto3.resource("sqs", region_name="us-east-1")
 | 
			
		||||
    queue = sqs.create_queue(QueueName="test-queue")
 | 
			
		||||
    topic.subscribe(Protocol="sqs", Endpoint=queue.attributes["QueueArn"])
 | 
			
		||||
 | 
			
		||||
    topic.publish(
 | 
			
		||||
        Message="test message",
 | 
			
		||||
        MessageAttributes={"retries": {"DataType": "Number", "StringValue": "0"}},
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    message = json.loads(queue.receive_messages()[0].body)
 | 
			
		||||
    message["Message"].should.equal("test message")
 | 
			
		||||
    message["MessageAttributes"].should.equal(
 | 
			
		||||
        {"retries": {"Type": "Number", "Value": 0}}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_sns
 | 
			
		||||
def test_publish_sms():
 | 
			
		||||
    client = boto3.client("sns", region_name="us-east-1")
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user