def CopyEBSSnapshotToS3Process(
    aws_account: account.AWSAccount,
    s3_destination: str,
    snapshot_id: str,
    instance_profile_arn: str,
    subnet_id: Optional[str] = None,
    security_group_id: Optional[str] = None,
    ) -> Dict[str, Any]:
  """Copy an EBS snapshot into S3.

  Unfortunately, this action is not natively supported in AWS, so it requires
  creating a volume and attaching it to an instance. This instance, using a
  userdata script then performs a `dd` operation to send the disk image to S3.

  Args:
    aws_account (account.AWSAccount): An AWS account to use for the operation.
    s3_destination (str): S3 directory in the form of s3://bucket/path/folder
    snapshot_id (str): EBS snapshot ID.
    instance_profile_arn (str): The name of an existing instance profile to
      attach to the instance, or to create if it does not yet exist.
    subnet_id (str): Optional. The subnet to launch the instance in.
    security_group_id (str): Optional. Security group ID to attach.

  Raises:
    ResourceCreationError: If any dependent resource could not be created.
    ResourceNotFoundError: If the snapshot ID cannot be found.
  """
  # Correct destination if necessary
  if not s3_destination.startswith('s3://'):
    s3_destination = 's3://' + s3_destination
  path_components = SplitStoragePath(s3_destination)
  bucket = path_components[0]
  object_path = path_components[1]

  # read in the instance userdata script, sub in the snap id and S3 dest
  startup_script = utils.ReadStartupScript(
    utils.EBS_SNAPSHOT_COPY_SCRIPT_AWS).format(snapshot_id, s3_destination)

  # Find the AMI - ALinux 2, latest version
  logger.info('Finding AMI')
  qfilter = [
    {'Name': 'name', 'Values': [ALINUX2_BASE_FILTER]},
    {'Name':'owner-alias', 'Values':['amazon']}
  ]
  results = aws_account.ec2.ListImages(qfilter)

  # Find the most recent
  ami_id = None
  date = ''
  for result in results:
    if result['CreationDate'] > date:
      ami_id = result['ImageId']
      date = result['CreationDate']
  if not ami_id:
    raise errors.ResourceCreationError(
      'Could not fnd suitable AMI for instance creation', __name__)

  # start the VM
  logger.info('Starting copy instance')
  aws_account.ec2.GetOrCreateVm(
    'ebsCopy-{0:d}'.format(random.randint(10**(9),(10**10)-1)),
    10,
    ami_id,
    4,
    subnet_id=subnet_id,
    security_group_id=security_group_id,
    userdata=startup_script,
    instance_profile=instance_profile_arn,
    terminate_on_shutdown=True,
    wait_for_health_checks=False
  )

  logger.info('Pausing 60 seconds while copy instance launches')
  sleep(60)

  # Calculate the times we should check for completion based on volume size
  # and transfer rates (documented in cloud-forensics-utils/issues/354)
  snapshot_size = aws_account.ec2.GetSnapshotInfo(snapshot_id)['VolumeSize']
  percentiles = [0.25, 0.5, 0.85, 1.15, 1.5, 2.0]
  transfer_speed = 60 # seconds per GB
  curr_wait = 0
  success = False
  prefix = '{0:s}/{1:s}/'.format(object_path, snapshot_id)
  files = ['image.bin', 'log.txt', 'hlog.txt', 'mlog.txt',
           'instance_copy_stdout.txt', 'instance_copy_stderr.txt']

  logger.info('Transfer expected to take {0:d} seconds'.
    format(snapshot_size * transfer_speed))

  for percentile in percentiles:
    curr_step = int(percentile * snapshot_size * transfer_speed)
    logger.info('Waiting {0:d} seconds ({1:d} seconds total wait time) '
      'to check for outputs'.format(curr_step - curr_wait, curr_step))
    sleep(curr_step - curr_wait)
    curr_wait = curr_step

    checks = [aws_account.s3.CheckForObject(bucket, prefix + file)
      for file in files]
    if all(checks):
      success = True
      logger.info('Output files found')
      break

  if success:
    logger.info('Image and hash copied to {0:s}/{1:s}/'.format(
      s3_destination, snapshot_id))
  else:
    logger.info(
      'Image copy timeout. The process may be ongoing, or might have failed.')

  path_base = 's3://{0:s}{1:s}/{2:s}'.format(bucket,
      '/' + object_path if object_path else '', snapshot_id)

  return {
      'image': path_base + '/image.bin',
      'hashes': [
        path_base + '/log.txt',
        path_base + '/hlog.txt',
        path_base + '/mlog.txt'
      ]
    }
Copy an EBS snapshot into S3.

Unfortunately, this action is not natively supported in AWS, so it requires
creating a volume and attaching it to an instance. This instance, using a
userdata script then performs a `dd` operation to send the disk image to S3.

Args:
aws_account (account.AWSAccount): An AWS account to use for the operation.
s3_destination (str): S3 directory in the form of s3://bucket/path/folder
snapshot_id (str): EBS snapshot ID.
instance_profile_arn (str): The name of an existing instance profile to
attach to the instance, or to create if it does not yet exist.
subnet_id (str): Optional. The subnet to launch the instance in.
security_group_id (str): Optional. Security group ID to attach.

Raises:
ResourceCreationError: If any dependent resource could not be created.
ResourceNotFoundError: If the snapshot ID cannot be found.
libcloudforensics/providers/aws/forensics.py