diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py
index ccf268ecf..88ca20b98 100644
--- a/monkey/infection_monkey/monkey.py
+++ b/monkey/infection_monkey/monkey.py
@@ -75,8 +75,7 @@ class InfectionMonkey(object):
     def initialize(self):
         logger.info("Monkey is initializing...")
 
-        if not self._singleton.try_lock():
-            raise Exception("Another instance of the monkey is already running")
+        self._check_for_running_monkey()
 
         arg_parser = argparse.ArgumentParser()
         arg_parser.add_argument("-p", "--parent")
@@ -85,7 +84,7 @@ class InfectionMonkey(object):
         arg_parser.add_argument("-d", "--depth", type=int)
         arg_parser.add_argument("-vp", "--vulnerable-port")
         self._opts, self._args = arg_parser.parse_known_args(self._args)
-        self.log_arguments()
+        self._log_arguments()
 
         self._parent = self._opts.parent
         self._default_tunnel = self._opts.tunnel
@@ -109,18 +108,25 @@ class InfectionMonkey(object):
                     "Default server: %s is already in command servers list" % self._default_server
                 )
 
+    def _check_for_running_monkey(self):
+        if not self._singleton.try_lock():
+            raise Exception("Another instance of the monkey is already running")
+
+    def _log_arguments(self):
+        arg_string = " ".join([f"{key}: {value}" for key, value in vars(self._opts).items()])
+        logger.info(f"Monkey started with arguments: {arg_string}")
+
     def start(self):
         try:
             logger.info("Monkey is starting...")
-
             # Sets the monkey up
-            self.setup()
+            self._setup()
 
             # Start post breach phase
-            self.start_post_breach()
+            self._start_post_breach_async()
 
             # Start propagation phase
-            self.start_propagation()
+            self._start_propagation()
 
         except PlannedShutdownException:
             logger.info(
@@ -130,28 +136,28 @@ class InfectionMonkey(object):
             logger.exception("Planned shutdown, reason:")
 
         finally:
-            self.teardown()
+            self._teardown()
 
-    def setup(self):
+    def _setup(self):
         logger.debug("Starting the setup phase.")
 
-        self.shutdown_by_not_alive_config()
+        InfectionMonkey._shutdown_by_not_alive_config()
 
         # Sets island's IP and port for monkey to communicate to
-        self.set_default_server()
-        self.set_default_port()
+        self._set_default_server()
+        self._set_default_port()
 
         # Create a dir for monkey files if there isn't one
         create_monkey_dir()
 
-        self.upgrade_to_64_if_needed()
-
         ControlClient.wakeup(parent=self._parent)
         ControlClient.load_control_config()
 
         if ControlClient.check_for_stop():
             raise PlannedShutdownException("Monkey has been marked for shutdown.")
 
+        self._upgrade_to_64_if_needed()
+
         if not ControlClient.should_monkey_run(self._opts.vulnerable_port):
             raise PlannedShutdownException(
                 "Monkey shouldn't run on current machine "
@@ -175,61 +181,53 @@ class InfectionMonkey(object):
         StateTelem(is_done=False, version=get_version()).send()
         TunnelTelem().send()
 
-        self.master.start()
-
         register_signal_handlers(self.master)
 
-    def start_propagation(self):
-        if not InfectionMonkey.max_propagation_depth_reached():
-            logger.info("Starting the propagation phase.")
-            logger.debug("Running with depth: %d" % WormConfiguration.depth)
-            self.propagate()
-        else:
-            logger.info("Maximum propagation depth has been reached; monkey will not propagate.")
-            TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send()
+        self.master.start()
 
-        if self._keep_running and WormConfiguration.alive:
-            InfectionMonkey.run_ransomware()
+    @staticmethod
+    def _shutdown_by_not_alive_config():
+        if not WormConfiguration.alive:
+            raise PlannedShutdownException("Marked 'not alive' from configuration.")
 
-        # if host was exploited, before continue to closing the tunnel ensure the exploited
-        # host had its chance to
-        # connect to the tunnel
-        if len(self._exploited_machines) > 0:
-            time_to_sleep = WormConfiguration.keep_tunnel_open_time
-            logger.info(
-                "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep
+    def _set_default_server(self):
+        """
+        Sets the default server for the Monkey to communicate back to.
+        :raises PlannedShutdownException if couldn't find the server.
+        """
+        if not ControlClient.find_server(default_tunnel=self._default_tunnel):
+            raise PlannedShutdownException(
+                "Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel)
             )
-            time.sleep(time_to_sleep)
+        self._default_server = WormConfiguration.current_server
+        logger.debug("default server set to: %s" % self._default_server)
 
-    def start_post_breach(self):
+    def _set_default_port(self):
+        try:
+            self._default_server_port = self._default_server.split(":")[1]
+        except KeyError:
+            self._default_server_port = ""
+
+    def _upgrade_to_64_if_needed(self):
+        if WindowsUpgrader.should_upgrade():
+            self._upgrading_to_64 = True
+            self._singleton.unlock()
+            logger.info("32bit monkey running on 64bit Windows. Upgrading.")
+            WindowsUpgrader.upgrade(self._opts)
+            raise PlannedShutdownException("Finished upgrading from 32bit to 64bit.")
+
+    def _start_post_breach_async(self):
         logger.debug("Starting the post-breach phase asynchronously.")
-        self._post_breach_phase = Thread(target=self.start_post_breach_phase)
+        self._post_breach_phase = Thread(target=InfectionMonkey._start_post_breach_phase)
         self._post_breach_phase.start()
 
-    def teardown(self):
-        if self._monkey_tunnel:
-            self._monkey_tunnel.stop()
-            self._monkey_tunnel.join()
-
-        if self._post_breach_phase:
-            self._post_breach_phase.join()
-
-        if firewall.is_enabled():
-            firewall.remove_firewall_rule()
-            firewall.close()
-
-        self.master.terminate()
-        self.master.cleanup()
-
-    def start_post_breach_phase(self):
-        self.collect_system_info_if_configured()
+    @staticmethod
+    def _start_post_breach_phase():
+        InfectionMonkey._collect_system_info_if_configured()
         PostBreach().execute_all_configured()
 
     @staticmethod
-    def max_propagation_depth_reached():
-        return 0 == WormConfiguration.depth
-
-    def collect_system_info_if_configured(self):
+    def _collect_system_info_if_configured():
         logger.debug("Calling for system info collection")
         try:
             system_info_collector = SystemInfoCollector()
@@ -238,11 +236,32 @@ class InfectionMonkey(object):
         except Exception as e:
             logger.exception(f"Exception encountered during system info collection: {str(e)}")
 
-    def shutdown_by_not_alive_config(self):
-        if not WormConfiguration.alive:
-            raise PlannedShutdownException("Marked 'not alive' from configuration.")
+    def _start_propagation(self):
+        if not InfectionMonkey._max_propagation_depth_reached():
+            logger.info("Starting the propagation phase.")
+            logger.debug("Running with depth: %d" % WormConfiguration.depth)
+            self._propagate()
+        else:
+            logger.info("Maximum propagation depth has been reached; monkey will not propagate.")
+            TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send()
 
-    def propagate(self):
+        if self._keep_running and WormConfiguration.alive:
+            InfectionMonkey._run_ransomware()
+
+        # if host was exploited, before continue to closing the tunnel ensure the exploited
+        # host had its chance to connect to the tunnel
+        if len(self._exploited_machines) > 0:
+            time_to_sleep = WormConfiguration.keep_tunnel_open_time
+            logger.info(
+                "Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep
+            )
+            time.sleep(time_to_sleep)
+
+    @staticmethod
+    def _max_propagation_depth_reached():
+        return 0 == WormConfiguration.depth
+
+    def _propagate(self):
         ControlClient.keepalive()
         ControlClient.load_control_config()
 
@@ -304,7 +323,7 @@ class InfectionMonkey(object):
             )
             host_exploited = False
             for exploiter in [exploiter(machine) for exploiter in self._exploiters]:
-                if self.try_exploiting(machine, exploiter):
+                if self._try_exploiting(machine, exploiter):
                     host_exploited = True
                     VictimHostTelem("T1210", ScanStatus.USED, machine=machine).send()
                     if exploiter.RUNS_AGENT_ON_SUCCESS:
@@ -319,35 +338,125 @@ class InfectionMonkey(object):
         if not WormConfiguration.alive:
             logger.info("Marked not alive from configuration")
 
-    def upgrade_to_64_if_needed(self):
-        if WindowsUpgrader.should_upgrade():
-            self._upgrading_to_64 = True
-            self._singleton.unlock()
-            logger.info("32bit monkey running on 64bit Windows. Upgrading.")
-            WindowsUpgrader.upgrade(self._opts)
-            raise PlannedShutdownException("Finished upgrading from 32bit to 64bit.")
+    def _try_exploiting(self, machine, exploiter):
+        """
+        Workflow of exploiting one machine with one exploiter
+        :param machine: Machine monkey tries to exploit
+        :param exploiter: Exploiter to use on that machine
+        :return: True if successfully exploited, False otherwise
+        """
+        if not exploiter.is_os_supported():
+            logger.info(
+                "Skipping exploiter %s host:%r, os %s is not supported",
+                exploiter.__class__.__name__,
+                machine,
+                machine.os,
+            )
+            return False
+
+        logger.info(
+            "Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__
+        )
+
+        result = False
+        try:
+            result = exploiter.exploit_host()
+            if result:
+                self._successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS)
+                return True
+            else:
+                logger.info(
+                    "Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__
+                )
+        except ExploitingVulnerableMachineError as exc:
+            logger.error(
+                "Exception while attacking %s using %s: %s",
+                machine,
+                exploiter.__class__.__name__,
+                exc,
+            )
+            self._successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS)
+            return True
+        except FailedExploitationError as e:
+            logger.info(
+                "Failed exploiting %r with exploiter %s, %s",
+                machine,
+                exploiter.__class__.__name__,
+                e,
+            )
+        except Exception as exc:
+            logger.exception(
+                "Exception while attacking %s using %s: %s",
+                machine,
+                exploiter.__class__.__name__,
+                exc,
+            )
+        finally:
+            exploiter.send_exploit_telemetry(exploiter.__class__.__name__, result)
+        return False
+
+    def _successfully_exploited(self, machine, exploiter, RUNS_AGENT_ON_SUCCESS=True):
+        """
+        Workflow of registering successfully exploited machine
+        :param machine: machine that was exploited
+        :param exploiter: exploiter that succeeded
+        """
+        if RUNS_AGENT_ON_SUCCESS:
+            self._exploited_machines.add(machine)
+
+        logger.info("Successfully propagated to %s using %s", machine, exploiter.__class__.__name__)
+
+        # check if max-exploitation limit is reached
+        if WormConfiguration.victims_max_exploit <= len(self._exploited_machines):
+            self._keep_running = False
+
+            logger.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit)
+
+    @staticmethod
+    def _run_ransomware():
+        try:
+            ransomware_payload = build_ransomware_payload(WormConfiguration.ransomware)
+            ransomware_payload.run_payload()
+        except Exception as ex:
+            logger.error(f"An unexpected error occurred while running the ransomware payload: {ex}")
+
+    def _teardown(self):
+        logger.info("Monkey teardown started")
+        if self._monkey_tunnel:
+            self._monkey_tunnel.stop()
+            self._monkey_tunnel.join()
+
+        if self._post_breach_phase:
+            self._post_breach_phase.join()
+
+        if firewall.is_enabled():
+            firewall.remove_firewall_rule()
+            firewall.close()
+
+        self.master.terminate()
+        self.master.cleanup()
 
     def cleanup(self):
         logger.info("Monkey cleanup started")
         self._keep_running = False
 
         if self._upgrading_to_64:
-            InfectionMonkey.close_tunnel()
+            InfectionMonkey._close_tunnel()
             firewall.close()
         else:
             StateTelem(
                 is_done=True, version=get_version()
             ).send()  # Signal the server (before closing the tunnel)
-            InfectionMonkey.close_tunnel()
+            InfectionMonkey._close_tunnel()
             firewall.close()
-            self.send_log()
+            InfectionMonkey._send_log()
             self._singleton.unlock()
 
-        InfectionMonkey.self_delete()
+        InfectionMonkey._self_delete()
         logger.info("Monkey is shutting down")
 
     @staticmethod
-    def close_tunnel():
+    def _close_tunnel():
         tunnel_address = (
             ControlClient.proxies.get("https", "").replace("https://", "").split(":")[0]
         )
@@ -356,7 +465,18 @@ class InfectionMonkey(object):
             tunnel.quit_tunnel(tunnel_address)
 
     @staticmethod
-    def self_delete():
+    def _send_log():
+        monkey_log_path = get_monkey_log_path()
+        if os.path.exists(monkey_log_path):
+            with open(monkey_log_path, "r") as f:
+                log = f.read()
+        else:
+            log = ""
+
+        ControlClient.send_log(log)
+
+    @staticmethod
+    def _self_delete():
         status = ScanStatus.USED if remove_monkey_dir() else ScanStatus.SCANNED
         T1107Telem(status, get_monkey_dir_path()).send()
 
@@ -385,117 +505,3 @@ class InfectionMonkey(object):
                 status = ScanStatus.SCANNED
             if status:
                 T1107Telem(status, sys.executable).send()
-
-    def send_log(self):
-        monkey_log_path = get_monkey_log_path()
-        if os.path.exists(monkey_log_path):
-            with open(monkey_log_path, "r") as f:
-                log = f.read()
-        else:
-            log = ""
-
-        ControlClient.send_log(log)
-
-    def try_exploiting(self, machine, exploiter):
-        """
-        Workflow of exploiting one machine with one exploiter
-        :param machine: Machine monkey tries to exploit
-        :param exploiter: Exploiter to use on that machine
-        :return: True if successfully exploited, False otherwise
-        """
-        if not exploiter.is_os_supported():
-            logger.info(
-                "Skipping exploiter %s host:%r, os %s is not supported",
-                exploiter.__class__.__name__,
-                machine,
-                machine.os,
-            )
-            return False
-
-        logger.info(
-            "Trying to exploit %r with exploiter %s...", machine, exploiter.__class__.__name__
-        )
-
-        result = False
-        try:
-            result = exploiter.exploit_host()
-            if result:
-                self.successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS)
-                return True
-            else:
-                logger.info(
-                    "Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__
-                )
-        except ExploitingVulnerableMachineError as exc:
-            logger.error(
-                "Exception while attacking %s using %s: %s",
-                machine,
-                exploiter.__class__.__name__,
-                exc,
-            )
-            self.successfully_exploited(machine, exploiter, exploiter.RUNS_AGENT_ON_SUCCESS)
-            return True
-        except FailedExploitationError as e:
-            logger.info(
-                "Failed exploiting %r with exploiter %s, %s",
-                machine,
-                exploiter.__class__.__name__,
-                e,
-            )
-        except Exception as exc:
-            logger.exception(
-                "Exception while attacking %s using %s: %s",
-                machine,
-                exploiter.__class__.__name__,
-                exc,
-            )
-        finally:
-            exploiter.send_exploit_telemetry(result)
-        return False
-
-    def successfully_exploited(self, machine, exploiter, RUNS_AGENT_ON_SUCCESS=True):
-        """
-        Workflow of registering successfully exploited machine
-        :param machine: machine that was exploited
-        :param exploiter: exploiter that succeeded
-        """
-        if RUNS_AGENT_ON_SUCCESS:
-            self._exploited_machines.add(machine)
-
-        logger.info("Successfully propagated to %s using %s", machine, exploiter.__class__.__name__)
-
-        # check if max-exploitation limit is reached
-        if WormConfiguration.victims_max_exploit <= len(self._exploited_machines):
-            self._keep_running = False
-
-            logger.info("Max exploited victims reached (%d)", WormConfiguration.victims_max_exploit)
-
-    def set_default_port(self):
-        try:
-            self._default_server_port = self._default_server.split(":")[1]
-        except KeyError:
-            self._default_server_port = ""
-
-    def set_default_server(self):
-        """
-        Sets the default server for the Monkey to communicate back to.
-        :raises PlannedShutdownException if couldn't find the server.
-        """
-        if not ControlClient.find_server(default_tunnel=self._default_tunnel):
-            raise PlannedShutdownException(
-                "Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel)
-            )
-        self._default_server = WormConfiguration.current_server
-        logger.debug("default server set to: %s" % self._default_server)
-
-    def log_arguments(self):
-        arg_string = " ".join([f"{key}: {value}" for key, value in vars(self._opts).items()])
-        logger.info(f"Monkey started with arguments: {arg_string}")
-
-    @staticmethod
-    def run_ransomware():
-        try:
-            ransomware_payload = build_ransomware_payload(WormConfiguration.ransomware)
-            ransomware_payload.run_payload()
-        except Exception as ex:
-            logger.error(f"An unexpected error occurred while running the ransomware payload: {ex}")