codecov-commenter: # [Codecov](https://codecov.io/gh/google/cloud-forensics-utils/pull/187?src=pr&el=h1) Report
> Merging [#187](https://codecov.io/gh/google/cloud-forensics-utils/pull/187?src=pr&el=desc) into [master](https://codecov.io/gh/google/cloud-forensics-utils/commit/e4448c60002eee03c344ec2803276ff5d91936be&el=desc) will **decrease** coverage by `0.52%`.
> The diff coverage is `66.18%`.

[![Impacted file tree graph](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/graphs/tree.svg?width=650&height=150&src=pr&token=zZkPno3ah0)](https://codecov.io/gh/google/cloud-forensics-utils/pull/187?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #187      +/-   ##
==========================================
- Coverage   65.39%   64.87%   -0.53%     
==========================================
  Files          14       16       +2     
  Lines         968     1207     +239     
==========================================
+ Hits          633      783     +150     
- Misses        335      424      +89     
```

| Flag | Coverage Δ | |
|---|---|---|
| #nosetests | `64.87% <66.18%> (-0.53%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/google/cloud-forensics-utils/pull/187?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [libcloudforensics/providers/gcp/forensics.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2djcC9mb3JlbnNpY3MucHk=) | `52.45% <40.62%> (-12.55%)` | :arrow_down: |
| [libcloudforensics/providers/aws/forensics.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2F3cy9mb3JlbnNpY3MucHk=) | `60.00% <43.75%> (-9.77%)` | :arrow_down: |
| [...ibcloudforensics/providers/gcp/internal/compute.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2djcC9pbnRlcm5hbC9jb21wdXRlLnB5) | `54.88% <46.07%> (-7.48%)` | :arrow_down: |
| [...cs/providers/gcp/internal/compute\_base\_resource.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2djcC9pbnRlcm5hbC9jb21wdXRlX2Jhc2VfcmVzb3VyY2UucHk=) | `37.33% <52.94%> (+0.82%)` | :arrow_up: |
| [libcloudforensics/providers/aws/internal/log.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2F3cy9pbnRlcm5hbC9sb2cucHk=) | `73.07% <55.55%> (-4.20%)` | :arrow_down: |
| [...ibcloudforensics/providers/gcp/internal/project.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2djcC9pbnRlcm5hbC9wcm9qZWN0LnB5) | `63.63% <71.42%> (-2.08%)` | :arrow_down: |
| [libcloudforensics/providers/gcp/internal/common.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2djcC9pbnRlcm5hbC9jb21tb24ucHk=) | `59.22% <75.00%> (+2.90%)` | :arrow_up: |
| [libcloudforensics/providers/aws/internal/ec2.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2F3cy9pbnRlcm5hbC9lYzIucHk=) | `61.76% <77.77%> (+0.47%)` | :arrow_up: |
| [...ibcloudforensics/providers/gcp/internal/storage.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2djcC9pbnRlcm5hbC9zdG9yYWdlLnB5) | `80.00% <80.00%> (ø)` | |
| [libcloudforensics/providers/gcp/internal/build.py](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree#diff-bGliY2xvdWRmb3JlbnNpY3MvcHJvdmlkZXJzL2djcC9pbnRlcm5hbC9idWlsZC5weQ==) | `80.85% <83.33%> (+50.85%)` | :arrow_up: |
| ... and [9 more](https://codecov.io/gh/google/cloud-forensics-utils/pull/187/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/cloud-forensics-utils/pull/187?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/google/cloud-forensics-utils/pull/187?src=pr&el=footer). Last update [5039697...523b624](https://codecov.io/gh/google/cloud-forensics-utils/pull/187?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

hacktobeer: Ran tests and some live e2e runs and works like a charm.

LGTM 

diff --git a/examples/cli.py b/examples/cli.py
index 3bf318b..d1d8392 100644
--- a/examples/cli.py
+++ b/examples/cli.py
@@ -155,11 +155,13 @@ def Main() -> None:
   AddParser('gcp', gcp_subparsers, 'copydisk', 'Create a GCP disk copy.',
             args=[
                 ('dst_project', 'Destination GCP project.', ''),
-                ('instance_name', 'Name of the instance to copy disk from.',
-                 ''),
                 ('zone', 'Zone to create the disk in.', ''),
-                ('--disk_name', 'Name of the disk to copy. If None, the boot '
-                                'disk of the instance will be copied.', None)
+                ('--instance_name', 'Name of the instance to copy disk from.',
+                 ''),
+                ('--disk_name', 'Name of the disk to copy. If none specified, '
+                                'then --instance_name must be specified and '
+                                'the boot disk of the instance will be copied.',
+                 None)
             ])
   AddParser('gcp', gcp_subparsers, 'startvm', 'Start a forensic analysis VM.',
             args=[
diff --git a/examples/gcp_cli.py b/examples/gcp_cli.py
index db9f2b7..6cd2378 100644
--- a/examples/gcp_cli.py
+++ b/examples/gcp_cli.py
@@ -68,8 +68,8 @@ def CreateDiskCopy(args: 'argparse.Namespace') -> None:
 
   disk = forensics.CreateDiskCopy(args.project,
                                   args.dst_project,
-                                  args.instance_name,
                                   args.zone,
+                                  instance_name=args.instance_name,
                                   disk_name=args.disk_name)
 
   print('Disk copy completed.')
diff --git a/libcloudforensics/providers/aws/internal/account.py b/libcloudforensics/providers/aws/internal/account.py
index 3cacbdf..924d47c 100644
--- a/libcloudforensics/providers/aws/internal/account.py
+++ b/libcloudforensics/providers/aws/internal/account.py
@@ -81,9 +81,11 @@ class AWSAccount:
 
   def ResourceApi(self,
                   service: str,
+                  # The return type doesn't exist until Runtime, therefore we
+                  # need to ignore the type hint
                   # pylint: disable=line-too-long
                   region: Optional[str] = None) -> 'boto3.resources.factory.ec2.ServiceResource':  # type: ignore
-    # pylint: enable=line-too-long
+                  # pylint: enable=line-too-long
     """Create an AWS resource object.
 
     Args:
@@ -605,7 +607,8 @@ class AWSAccount:
           str(exception)))
 
     # The response contains the key ID
-    return kms_key['KeyMetadata']['KeyId']  # type: ignore
+    key_id = kms_key['KeyMetadata']['KeyId']  # type: str
+    return key_id
 
   def ShareKMSKeyWithAWSAccount(self,
                                 kms_key_id: str,
@@ -703,11 +706,11 @@ class AWSAccount:
       truncate_at -= len(volume_name_prefix)
       volume_name = '{0:s}{1:s}-{2:s}-copy'.format(
           volume_name_prefix,
-          snapshot.name[:truncate_at],  # type: ignore
+          snapshot.name[:truncate_at],
           volume_id_crc32)
     else:
       volume_name = '{0:s}-{1:s}-copy'.format(
-          snapshot.name[:truncate_at], volume_id_crc32)  # type: ignore
+          snapshot.name[:truncate_at], volume_id_crc32)
 
     return volume_name
 
diff --git a/libcloudforensics/providers/aws/internal/ebs.py b/libcloudforensics/providers/aws/internal/ebs.py
index 7937ab6..4549638 100644
--- a/libcloudforensics/providers/aws/internal/ebs.py
+++ b/libcloudforensics/providers/aws/internal/ebs.py
@@ -42,7 +42,7 @@ class AWSElasticBlockStore:
                region: str,
                availability_zone: str,
                encrypted: bool,
-               name: Optional[str] = None) -> None:
+               name: str = '') -> None:
     """Initialize the AWS EBS resource.
 
     Args:
@@ -80,7 +80,7 @@ class AWSVolume(AWSElasticBlockStore):
                region: str,
                availability_zone: str,
                encrypted: bool,
-               name: Optional[str] = None,
+               name: str = '',
                device_name: Optional[str] = None) -> None:
     """Initialize an AWS EBS volume.
 
@@ -183,7 +183,7 @@ class AWSSnapshot(AWSElasticBlockStore):
                region: str,
                availability_zone: str,
                volume: AWSVolume,
-               name: Optional[str] = None) -> None:
+               name: str = '') -> None:
     """Initialize an AWS EBS snapshot.
 
     Args:
diff --git a/libcloudforensics/providers/gcp/forensics.py b/libcloudforensics/providers/gcp/forensics.py
index 492d367..c93f55a 100644
--- a/libcloudforensics/providers/gcp/forensics.py
+++ b/libcloudforensics/providers/gcp/forensics.py
@@ -30,8 +30,8 @@ if TYPE_CHECKING:
 def CreateDiskCopy(
     src_proj: str,
     dst_proj: str,
-    instance_name: str,
     zone: str,
+    instance_name: Optional[str] = None,
     disk_name: Optional[str] = None,
     disk_type: str = 'pd-standard') -> 'compute.GoogleComputeDisk':
   """Creates a copy of a Google Compute Disk.
@@ -39,10 +39,10 @@ def CreateDiskCopy(
   Args:
     src_proj (str): Name of project that holds the disk to be copied.
     dst_proj (str): Name of project to put the copied disk in.
-    instance_name (str): Instance using the disk to be copied.
     zone (str): Zone where the new disk is to be created.
-    disk_name (str): Optional. Name of the disk to copy. If None, boot disk
-        will be copied.
+    instance_name (str): Optional. Instance using the disk to be copied.
+    disk_name (str): Optional. Name of the disk to copy. If None,
+        instance_name must be specified and the boot disk will be copied.
     disk_type (str): Optional. URL of the disk type resource describing
         which disk type to use to create the disk. Default is pd-standard. Use
         pd-ssd to have a SSD disk.
@@ -51,19 +51,23 @@ def CreateDiskCopy(
     GoogleComputeDisk: A Google Compute Disk object.
 
   Raises:
-    RuntimeError: If there are errors copying the disk
+    RuntimeError: If there are errors copying the disk.
+    ValueError: If both instance_name and disk_name are missing.
   """
 
+  if not instance_name and not disk_name:
+    raise ValueError(
+        'You must specify at least one of [instance_name, disk_name].')
+
   src_project = gcp_project.GoogleCloudProject(src_proj)
   dst_project = gcp_project.GoogleCloudProject(dst_proj, default_zone=zone)
-  instance = src_project.compute.GetInstance(
-      instance_name) if instance_name else None
 
   try:
     if disk_name:
       disk_to_copy = src_project.compute.GetDisk(disk_name)
-    else:
-      disk_to_copy = instance.GetBootDisk()  # type: ignore
+    elif instance_name:
+      instance = src_project.compute.GetInstance(instance_name)
+      disk_to_copy = instance.GetBootDisk()
 
     common.LOGGER.info('Disk copy of {0:s} started...'.format(
         disk_to_copy.name))
diff --git a/libcloudforensics/providers/gcp/internal/compute.py b/libcloudforensics/providers/gcp/internal/compute.py
index 37be05d..39eb99c 100644
--- a/libcloudforensics/providers/gcp/internal/compute.py
+++ b/libcloudforensics/providers/gcp/internal/compute.py
@@ -44,13 +44,13 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
     Args:
       project_id (str): Google Cloud project ID.
       default_zone (str): Optional. Default zone to create new resources in.
-          None means GlobalZone.
+          Default is us-central1-f.
     """
 
     self.project_id = project_id  # type: str
-    self.default_zone = default_zone
-    self._instances = None
-    self._disks = None
+    self.default_zone = default_zone or 'us-central1-f'
+    self._instances = {}  # type: Dict[str, GoogleComputeInstance]
+    self._disks = {}  # type: Dict[str, GoogleComputeDisk]
     super(GoogleCloudCompute, self).__init__(self.project_id)
 
   def Instances(self,
@@ -67,8 +67,8 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
     """
     if not refresh and self._instances:
       return self._instances
-    self._instances = self.ListInstances()  # type: ignore
-    return self._instances  # type: ignore
+    self._instances = self.ListInstances()
+    return self._instances
 
   def Disks(self,
             refresh: bool = True
@@ -84,8 +84,8 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
     """
     if not refresh and self._disks:
       return self._disks
-    self._disks = self.ListDisks()  # type: ignore
-    return self._disks  # type: ignore
+    self._disks = self.ListDisks()
+    return self._disks
 
   def ListInstances(self) -> Dict[str, 'GoogleComputeInstance']:
     """List instances in project.
@@ -232,7 +232,7 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
     self.BlockOperation(response, zone=self.default_zone)
     return GoogleComputeDisk(
         project_id=self.project_id,
-        zone=self.default_zone,  # type: ignore
+        zone=self.default_zone,
         name=disk_name)
 
   def GetOrCreateAnalysisVm(self,
@@ -284,7 +284,7 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
       pass
 
     machine_type = \
-        'zones/{0}/machineTypes/n1-standard-{1:d}'.format(  # type: ignore
+        'zones/{0:s}/machineTypes/n1-standard-{1:d}'.format(
             self.default_zone, cpu_cores)
     ubuntu_image = self.GceApi().images().getFromFamily(
         project=image_project, family=image_family).execute()
@@ -367,8 +367,8 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
     """
 
     instance_service_object = self.GceApi().instances()
-    return self._ListByLabel(  # type: ignore
-        labels_filter, instance_service_object, filter_union)  # type: ignore
+    return self._ListByLabel(
+        labels_filter, instance_service_object, filter_union)
 
   def ListDiskByLabels(self,
                        labels_filter: Dict[str, str],
@@ -393,13 +393,13 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
     """
 
     disk_service_object = self.GceApi().disks()
-    return self._ListByLabel(  # type: ignore
-        labels_filter, disk_service_object, filter_union)  # type: ignore
+    return self._ListByLabel(
+        labels_filter, disk_service_object, filter_union)
 
   def _ListByLabel(self,
                    labels_filter: Dict[str, str],
                    service_object: 'googleapiclient.discovery.Resource',
-                   filter_union: bool) -> Dict[str, Union['GoogleComputeInstance', 'GoogleComputeDisk']]:  # pylint: disable=line-too-long
+                   filter_union: bool) -> Dict[str, Any]:
     """List Disks/VMs in a project with one/all of the provided labels.
 
     Private method used to select different compute resources by labels.
@@ -503,7 +503,7 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
         project=self.project_id, body=image_body, forceCreate=True)
     response = request.execute()
     self.BlockOperation(response)
-    return GoogleComputeImage(self.project_id, None, name)  # type: ignore
+    return GoogleComputeImage(self.project_id, '', name)
 
   def CreateDiskFromImage(self,
                           src_image: 'GoogleComputeImage',
@@ -642,8 +642,7 @@ class GoogleCloudCompute(common.GoogleCloudComputeClient):
     common.LOGGER.info(
         'Image {0:s} imported as GCE image {1:s}.'.format(
             storage_image_path, image_name))
-    return GoogleComputeImage(
-        self.project_id, None, image_name)  # type: ignore
+    return GoogleComputeImage(self.project_id, '', image_name)
 
 
 class GoogleComputeInstance(compute_base_resource.GoogleComputeBaseResource):
@@ -664,18 +663,22 @@ class GoogleComputeInstance(compute_base_resource.GoogleComputeBaseResource):
     response = request.execute()  # type: Dict[str, Any]
     return response
 
-  def GetBootDisk(self) -> Union['GoogleComputeDisk', None]:
+  def GetBootDisk(self) -> 'GoogleComputeDisk':
     """Get the virtual machine boot disk.
 
     Returns:
-      GoogleComputeDisk: Disk object or None if no disk can be found.
+      GoogleComputeDisk: Disk object.
+
+    Raises:
+      RuntimeError: If no boot disk could be found.
     """
 
     for disk in self.GetValue('disks'):
-      if disk['boot']:  # type: ignore
-        disk_name = disk['source'].split('/')[-1]  # type: ignore
+      if disk['boot']:
+        disk_name = disk['source'].split('/')[-1]
         return GoogleCloudCompute(self.project_id).GetDisk(disk_name=disk_name)
-    return None
+    raise RuntimeError(
+        'Boot disk not found for instance: {0:s}'.format(self.name))
 
   def GetDisk(self, disk_name: str) -> 'GoogleComputeDisk':
     """Gets a disk attached to this virtual machine disk by name.
@@ -692,7 +695,7 @@ class GoogleComputeInstance(compute_base_resource.GoogleComputeBaseResource):
     """
 
     for disk in self.GetValue('disks'):
-      if disk['source'].split('/')[-1] == disk_name:  # type: ignore
+      if disk['source'].split('/')[-1] == disk_name:
         return GoogleCloudCompute(self.project_id).GetDisk(disk_name=disk_name)
     error_msg = 'Disk name "{0:s}" not attached to instance'.format(disk_name)
     raise RuntimeError(error_msg)
@@ -707,7 +710,7 @@ class GoogleComputeInstance(compute_base_resource.GoogleComputeBaseResource):
 
     disks = {}
     disk_names = [
-        disk['source'].split('/')[-1]  # type: ignore
+        disk['source'].split('/')[-1]
         for disk in self.GetValue('disks')
     ]
     for name in disk_names:
diff --git a/libcloudforensics/providers/gcp/internal/compute_base_resource.py b/libcloudforensics/providers/gcp/internal/compute_base_resource.py
index 144647e..d60f2c1 100644
--- a/libcloudforensics/providers/gcp/internal/compute_base_resource.py
+++ b/libcloudforensics/providers/gcp/internal/compute_base_resource.py
@@ -49,7 +49,7 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
     self.zone = zone
     self.name = name
     self.labels = labels
-    self._data = None
+    self._data = {}  # type: Dict[str, Any]
     self.project_id = project_id  # type: str
     super(GoogleComputeBaseResource, self).__init__(self.project_id)
 
@@ -73,18 +73,18 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
     """
     raise NotImplementedError("GetOperation is not implemented by this class.")
 
-  def GetValue(self, key: str) -> str:
+  def GetValue(self, key: str) -> Any:
     """Get specific value from the resource key value store.
 
     Args:
       key (str): A key of type String to get key's corresponding value.
 
     Returns:
-      str: Value of key or None if key is missing.
+      str|Dict: Value of key/dictionary or None if key is missing.
     """
 
-    self._data = self.GetOperation()  # type: ignore
-    return self._data.get(key)  # type: ignore
+    self._data = self.GetOperation()
+    return self._data.get(key)
 
   def GetSourceString(self) -> str:
     """API URL to the resource.
@@ -94,8 +94,10 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
     """
 
     if self._data:
-      return self._data['selfLink']
-    return self.GetValue('selfLink')
+      url = self._data['selfLink']  # type: str
+      return url
+    url = self.GetValue('selfLink')
+    return url
 
   def GetResourceType(self) -> str:
     """Get the resource type from the resource key-value store.
@@ -108,8 +110,10 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
     """
 
     if self._data:
-      return self._data['kind']
-    return self.GetValue('kind')
+      resource_type = self._data['kind']  # type: str
+      return resource_type
+    resource_type = self.GetValue('kind')
+    return resource_type
 
   def FormOperation(
       self,
@@ -158,8 +162,11 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
       Dict: A dictionary of all labels.
     """
 
-    operation = self.GetOperation()  # type: ignore
-    return operation.get('labels')  # type: ignore
+    operation = self.GetOperation()  # type: Dict[str, Dict[str, Any]]
+    labels = operation.get('labels')
+    if labels:
+      return labels
+    return {}
 
   def AddLabels(self,
                 new_labels_dict: Dict[str, Any],
@@ -184,7 +191,7 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
     get_operation = self.GetOperation()
     label_fingerprint = get_operation['labelFingerprint']
 
-    existing_labels_dict = {}
+    existing_labels_dict = {}  # type: Dict[str, Any]
     if self.GetLabels() is not None:
       existing_labels_dict = self.GetLabels()
     existing_labels_dict.update(new_labels_dict)
@@ -194,7 +201,7 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
     }
 
     resource_type = self.GetResourceType()
-    response = None
+    response = {}
     if resource_type not in ['compute#instance',
                              'compute#Snapshot',
                              'compute#disk']:
@@ -220,6 +227,6 @@ class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
           resource=self.name, project=self.project_id,
           body=request_body).execute()
     if blocking_call:
-      self.BlockOperation(response, zone=self.zone)  # type: ignore
+      self.BlockOperation(response, zone=self.zone)
 
     return response
diff --git a/libcloudforensics/providers/gcp/internal/project.py b/libcloudforensics/providers/gcp/internal/project.py
index 4d95f70..57a9fd0 100644
--- a/libcloudforensics/providers/gcp/internal/project.py
+++ b/libcloudforensics/providers/gcp/internal/project.py
@@ -51,12 +51,14 @@ class GoogleCloudProject:
 
     self.project_id = project_id
     self.default_zone = default_zone
-    self._compute = None
-    self._function = None
-    self._build = None
-    self._log = None
-    self._storage = None
-    self._monitoring = None
+    self._compute = None  # type: Optional[compute_module.GoogleCloudCompute]
+    self._function = None  # type: Optional[function_module.GoogleCloudFunction]
+    self._build = None  # type: Optional[build_module.GoogleCloudBuild]
+    self._log = None  # type: Optional[log_module.GoogleCloudLog]
+    self._storage = None  # type: Optional[storage_module.GoogleCloudStorage]
+    # pylint: disable=line-too-long
+    self._monitoring = None  # type: Optional[monitoring_module.GoogleCloudMonitoring]
+    # pylint: enable=line-too-long
 
   @property
   def compute(self) -> compute_module.GoogleCloudCompute:
@@ -68,9 +70,9 @@ class GoogleCloudProject:
 
     if self._compute:
       return self._compute
-    self._compute = compute_module.GoogleCloudCompute(  # type: ignore
+    self._compute = compute_module.GoogleCloudCompute(
         self.project_id, self.default_zone)
-    return self._compute  # type: ignore
+    return self._compute
 
   @property
   def function(self) -> function_module.GoogleCloudFunction:
@@ -82,9 +84,9 @@ class GoogleCloudProject:
 
     if self._function:
       return self._function
-    self._function = function_module.GoogleCloudFunction(  # type: ignore
+    self._function = function_module.GoogleCloudFunction(
         self.project_id)
-    return self._function  # type: ignore
+    return self._function
 
   @property
   def build(self) -> build_module.GoogleCloudBuild:
@@ -96,9 +98,9 @@ class GoogleCloudProject:
 
     if self._build:
       return self._build
-    self._build = build_module.GoogleCloudBuild(  # type: ignore
+    self._build = build_module.GoogleCloudBuild(
         self.project_id)
-    return self._build  # type: ignore
+    return self._build
 
   @property
   def log(self) -> log_module.GoogleCloudLog:
@@ -110,9 +112,9 @@ class GoogleCloudProject:
 
     if self._log:
       return self._log
-    self._log = log_module.GoogleCloudLog(  # type: ignore
+    self._log = log_module.GoogleCloudLog(
         self.project_id)
-    return self._log  # type: ignore
+    return self._log
 
   @property
   def storage(self) -> storage_module.GoogleCloudStorage:
@@ -124,9 +126,9 @@ class GoogleCloudProject:
 
     if self._storage:
       return self._storage
-    self._storage = storage_module.GoogleCloudStorage(  # type: ignore
+    self._storage = storage_module.GoogleCloudStorage(
         self.project_id)
-    return self._storage  # type: ignore
+    return self._storage
 
   @property
   def monitoring(self) -> monitoring_module.GoogleCloudMonitoring:
@@ -138,6 +140,6 @@ class GoogleCloudProject:
 
     if self._monitoring:
       return self._monitoring
-    self._monitoring = monitoring_module.GoogleCloudMonitoring(  # type: ignore
+    self._monitoring = monitoring_module.GoogleCloudMonitoring(
         self.project_id)
-    return self._monitoring  # type: ignore
+    return self._monitoring

Limit the number of type hinting ignore statemetns
The codebase is riddled with `# type: ignore` statements, which defeats the purpose of using type hinting. Let's see if we can limit the usage of this.
diff --git a/tests/providers/gcp/gcp_e2e.py b/tests/providers/gcp/gcp_e2e.py
index 62e79b2..0fc3af8 100644
--- a/tests/providers/gcp/gcp_e2e.py
+++ b/tests/providers/gcp/gcp_e2e.py
@@ -100,8 +100,8 @@ class EndToEndTest(unittest.TestCase):
     self.boot_disk_copy = forensics.CreateDiskCopy(
         src_proj=self.project_id,
         dst_proj=self.project_id,
-        instance_name=self.instance_to_analyse,
         zone=self.zone,
+        instance_name=self.instance_to_analyse,
         # disk_name=None by default, boot disk will be copied
     )
 
@@ -161,8 +161,10 @@ class EndToEndTest(unittest.TestCase):
 
     # Make a copy of another disk of the instance to analyse
     self.disk_to_forensic_copy = forensics.CreateDiskCopy(
-        src_proj=self.project_id, dst_proj=self.project_id,
-        instance_name=self.instance_to_analyse, zone=self.zone,
+        src_proj=self.project_id,
+        dst_proj=self.project_id,
+        zone=self.zone,
+        instance_name=self.instance_to_analyse,
         disk_name=self.disk_to_forensic)
 
     gcp_client_api = common.GoogleCloudComputeClient(self.project_id).GceApi()
diff --git a/tests/providers/gcp/gcp_test.py b/tests/providers/gcp/gcp_test.py
index 80ba00e..6d72036 100644
--- a/tests/providers/gcp/gcp_test.py
+++ b/tests/providers/gcp/gcp_test.py
@@ -764,13 +764,16 @@ class GCPTest(unittest.TestCase):
     mock_get_boot_disk.return_value = FAKE_BOOT_DISK
     mock_block_operation.return_value = None
 
-    # create_disk_copy(src_proj, dst_proj, instance_name='fake-instance',
-    #     zone='fake-zone', disk_name=None) Should grab the boot disk
+    # create_disk_copy(
+    #     src_proj,
+    #     dst_proj,
+    #     zone='fake-zone',
+    #     instance_name='fake-instance',
+    #     disk_name=None) Should grab the boot disk
     new_disk = forensics.CreateDiskCopy(FAKE_SOURCE_PROJECT.project_id,
                                         FAKE_ANALYSIS_PROJECT.project_id,
-                                        instance_name=FAKE_INSTANCE.name,
                                         zone=FAKE_INSTANCE.zone,
-                                        disk_name=None)
+                                        instance_name=FAKE_INSTANCE.name)
     mock_get_instance.assert_called_with(FAKE_INSTANCE.name)
     mock_get_disk.assert_not_called()
     self.assertIsInstance(new_disk, compute.GoogleComputeDisk)
@@ -798,12 +801,11 @@ class GCPTest(unittest.TestCase):
     # create_disk_copy(
     #     src_proj,
     #     dst_proj,
-    #     instance_name=None,
     #     zone='fake-zone',
+    #     instance_name=None,
     #     disk_name='fake-disk') Should grab 'fake-disk'
     new_disk = forensics.CreateDiskCopy(FAKE_SOURCE_PROJECT.project_id,
                                         FAKE_ANALYSIS_PROJECT.project_id,
-                                        instance_name=None,
                                         zone=FAKE_INSTANCE.zone,
                                         disk_name=FAKE_DISK.name)
     mock_get_disk.assert_called_with(FAKE_DISK.name)
@@ -825,14 +827,13 @@ class GCPTest(unittest.TestCase):
     # create_disk_copy(
     #     src_proj,
     #     dst_proj,
-    #     instance_name=None,
     #     zone='fake-zone',
+    #     instance_name=None,
     #     disk_name='non-existent-disk') Should raise an exception
     self.assertRaises(RuntimeError,
                       forensics.CreateDiskCopy,
                       FAKE_SOURCE_PROJECT.project_id,
                       FAKE_ANALYSIS_PROJECT.project_id,
-                      instance_name=None,
                       zone=FAKE_INSTANCE.zone,
                       disk_name='non-existent-disk')
 
@@ -928,5 +929,6 @@ class GoogleCloudMonitoringTest(unittest.TestCase):
     self.assertIn('logging.googleapis.com', active_services)
     self.assertEqual(active_services['logging.googleapis.com'], MOCK_LOGGING_METRIC)
 
+
 if __name__ == '__main__':
   unittest.main()

pip install -e .[dev]
parse_log_pytest
requirements.txt
nose mock pylint coverage yapf pytest
apt-get update apt-get install -y python3-pip
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==2.15.8
boto3==1.33.13
botocore==1.33.13
cachetools==5.5.2
certifi @ file:///croot/certifi_1671487769961/work/certifi
charset-normalizer==3.4.1
coverage==7.2.7
dill==0.3.7
exceptiongroup==1.2.2
google-api-core==2.24.2
google-api-python-client==2.166.0
google-auth==2.38.0
google-auth-httplib2==0.2.0
googleapis-common-protos==1.69.2
httplib2==0.22.0
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
isort==5.11.5
jmespath==1.0.1
lazy-object-proxy==1.9.0
-e git+https://github.com/google/cloud-forensics-utils.git@6202cdecc9a9afdb8c1b1f4c6d5e69c19b6a0d92#egg=libcloudforensics
mccabe==0.7.0
mock==5.2.0
nose==1.3.7
packaging==24.0
platformdirs==4.0.0
pluggy==1.2.0
proto-plus==1.26.1
protobuf==4.24.4
pyasn1==0.5.1
pyasn1-modules==0.3.0
pylint==2.17.7
pyparsing==3.1.4
pytest==7.4.4
python-dateutil==2.9.0.post0
requests==2.31.0
rsa==4.9
s3transfer==0.8.2
six==1.17.0
tomli==2.0.1
tomlkit==0.12.5
typed-ast==1.5.5
typing_extensions==4.7.1
uritemplate==4.1.1
urllib3==1.26.20
wrapt==1.16.0
yapf==0.43.0
zipp==3.15.0

name: cloud-forensics-utils
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astroid==2.15.8
      - boto3==1.33.13
      - botocore==1.33.13
      - cachetools==5.5.2
      - charset-normalizer==3.4.1
      - coverage==7.2.7
      - dill==0.3.7
      - exceptiongroup==1.2.2
      - google-api-core==2.24.2
      - google-api-python-client==2.166.0
      - google-auth==2.38.0
      - google-auth-httplib2==0.2.0
      - googleapis-common-protos==1.69.2
      - httplib2==0.22.0
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - isort==5.11.5
      - jmespath==1.0.1
      - lazy-object-proxy==1.9.0
      - mccabe==0.7.0
      - mock==5.2.0
      - nose==1.3.7
      - packaging==24.0
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - proto-plus==1.26.1
      - protobuf==4.24.4
      - pyasn1==0.5.1
      - pyasn1-modules==0.3.0
      - pylint==2.17.7
      - pyparsing==3.1.4
      - pytest==7.4.4
      - python-dateutil==2.9.0.post0
      - requests==2.31.0
      - rsa==4.9
      - s3transfer==0.8.2
      - six==1.17.0
      - tomli==2.0.1
      - tomlkit==0.12.5
      - typed-ast==1.5.5
      - typing-extensions==4.7.1
      - uritemplate==4.1.1
      - urllib3==1.26.20
      - wrapt==1.16.0
      - yapf==0.43.0
      - zipp==3.15.0
prefix: /opt/conda/envs/cloud-forensics-utils

tests/providers/gcp/gcp_test.py::GCPTest::testCreateDiskCopy2 tests/providers/gcp/gcp_test.py::GCPTest::testCreateDiskCopy3
tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testCreateDiskFromSnapshot tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testFormatLogMessage tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testGetDisk tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testGetInstance tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testGetOrCreateAnalysisVm tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testListDisks tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testListDisksByLabels tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testListInstanceByLabels tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testListInstances tests/providers/gcp/gcp_test.py::GoogleCloudProjectTest::testReadStartupScript tests/providers/gcp/gcp_test.py::GoogleComputeBaseResourceTest::testGetValue tests/providers/gcp/gcp_test.py::GoogleComputeInstanceTest::testGetBootDisk tests/providers/gcp/gcp_test.py::GoogleComputeInstanceTest::testGetDisk tests/providers/gcp/gcp_test.py::GoogleComputeInstanceTest::testListDisks tests/providers/gcp/gcp_test.py::GoogleComputeDiskTest::testSnapshot tests/providers/gcp/gcp_test.py::GoogleCloudLogTest::testExecuteQuery tests/providers/gcp/gcp_test.py::GoogleCloudLogTest::testListLogs tests/providers/gcp/gcp_test.py::GoogleCloudStorageTest::testGetObjectMetadata tests/providers/gcp/gcp_test.py::GoogleCloudStorageTest::testSplitGcsPath tests/providers/gcp/gcp_test.py::GoogleCloudBuildeTest::testBlockOperation tests/providers/gcp/gcp_test.py::GoogleCloudBuildeTest::testCreateBuild tests/providers/gcp/gcp_test.py::GCPTest::testCreateDiskCopy1 tests/providers/gcp/gcp_test.py::GCPTest::testGenerateDiskName tests/providers/gcp/gcp_test.py::GoogleCloudMonitoringTest::testActiveServices
libcloudforensics/providers/aws/internal/ebs.py libcloudforensics/providers/aws/internal/account.py examples/cli.py libcloudforensics/providers/gcp/internal/compute.py libcloudforensics/providers/gcp/internal/compute_base_resource.py libcloudforensics/providers/gcp/internal/project.py libcloudforensics/providers/gcp/forensics.py examples/gcp_cli.py
libcloudforensics/providers/aws/internal/ebs.py libcloudforensics/providers/aws/internal/account.py examples/cli.py libcloudforensics/providers/gcp/internal/compute.py libcloudforensics/providers/gcp/internal/compute_base_resource.py libcloudforensics/providers/gcp/internal/project.py libcloudforensics/providers/gcp/forensics.py examples/gcp_cli.py