Repository: awslabs/seed-farmer (language: python)

Issue:
Refactor all boto3 session references to use SessionManager
All references to boto3 sessions that refer to `services/_service_utils.py` should use the new SessionManager capability...

See  #67 



Discussion / hints:
This is backwards....SessionManager should use `services/_service_utils.py`

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a7bb4dd..5b4e399b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
 - create SessionManager class for supporting multi-account, multi-region
 - bootstrap command support to generate CFN templates for Toolchain and Target accounts
 - added deployment for toolchain and target accounts via CFN
+- support for cross-account and cross-region deployments
 
 
 ### Changes
@@ -21,7 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
 - refactor cli commands/groups to reduce line count in `__main__.py`
 - moved projectpolicy.yaml into resources/.
 - added profile and region support for session in `_session_utils.py`
-- convertd `sessionmanager.py` to only use `_session_utils.py`
+- convertd `session_manager.py` to only use `_session_utils.py`
 
 ### Fixes
 - fix import failure of seedfarmer top-level module if seedfarmer.yaml doesn't exist
@@ -45,7 +46,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
 ### New
 - support logging output and url gereration from codebuild output / codeseeder
 - added use of CodeSeederRuntimeError
-- added ModuleDeploymentResponse object 
+- added ModuleDeploymentResponse object
 - added export of moduledata (metadata) in UNIX format from CLI (--export-local-env)
 
 ### Changes
@@ -66,7 +67,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
 ### Changes
 
 ### Fixes
-- use HTTPS instead off SSH for git cloning 
+- use HTTPS instead off SSH for git cloning
 
 
 ---
@@ -81,7 +82,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
 - lock versions in requirements-dev.in
 - updating CLI output to discern changes to manifest, intended deployment
 - lazy-load seedkit (version 0.3.2 of codeseeder)
-- improve logging messages from codeseeder 
+- improve logging messages from codeseeder
 - align message output / tables listing modules requested, modified, deleted for deployment
 
 ### Fixes
diff --git a/seedfarmer/__init__.py b/seedfarmer/__init__.py
index 497b3b7e..34508529 100644
--- a/seedfarmer/__init__.py
+++ b/seedfarmer/__init__.py
@@ -66,11 +66,9 @@ def _load_config_data(self) -> None:
         count = 0
         while not os.path.exists(os.path.join(self.OPS_ROOT, self.CONFIG_FILE)):
             if count >= 4:
-                _logger.error(
-                    "The seedfarmer.yaml was not found at the root of your project.  Please set it and rerun."
-                )
+                _logger.error("The seedfarmer.yaml was not found at the root of your project. Please set it and rerun.")
                 raise FileNotFoundError(
-                    "The seedfarmer.yaml was not found at the root of your project.  Please set it and rerun."
+                    "The seedfarmer.yaml was not found at the root of your project. Please set it and rerun."
                 )
             else:
                 self.OPS_ROOT = pathlib.Path(self.OPS_ROOT).parent  # type: ignore
diff --git a/seedfarmer/__main__.py b/seedfarmer/__main__.py
index 386e02e3..71950fe9 100644
--- a/seedfarmer/__main__.py
+++ b/seedfarmer/__main__.py
@@ -14,17 +14,28 @@
 
 
 import logging
+from typing import Optional
 
 import click
 
 import seedfarmer
-from seedfarmer import DEBUG_LOGGING_FORMAT, commands, enable_debug
+from seedfarmer import DEBUG_LOGGING_FORMAT, commands, config, enable_debug
 from seedfarmer.cli_groups import bootstrap, init, list, remove, store
 from seedfarmer.output_utils import print_bolded
+from seedfarmer.services.session_manager import SessionManager
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
 
+def _load_project() -> str:
+    try:
+        print_bolded("No --project provided, attempting load from seedfarmer.yaml", "white")
+        return config.PROJECT
+    except FileNotFoundError:
+        print_bolded("Unable to determine project to bootstrap, one of --project or a seedfarmer.yaml is required")
+        raise click.ClickException("Failed to determine project identifier")
+
+
 @click.group()
 def cli() -> None:
     """SeedFarmer CLI interface"""
@@ -89,6 +100,25 @@ def apply(spec: str, debug: bool, dry_run: bool, show_manifest: bool) -> None:
     help="Write out the generated deployment manifest",
     show_default=True,
 )
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--profile",
+    default=None,
+    help="The AWS profile to use for boto3.Sessions",
+    required=False,
+)
+@click.option(
+    "--region",
+    default=None,
+    help="The AWS region to use for boto3.Sessions",
+    required=False,
+)
 @click.option(
     "--debug/--no-debug",
     default=False,
@@ -99,14 +129,23 @@ def destroy(
     deployment: str,
     dry_run: bool,
     show_manifest: bool,
+    project: Optional[str],
+    profile: Optional[str],
+    region: Optional[str],
     debug: bool,
 ) -> None:
     """Destroy a SeedFarmer managed deployment"""
     if debug:
         enable_debug(format=DEBUG_LOGGING_FORMAT)
-    _logger.info("Destroy for %s", deployment)
+    if project is None:
+        project = _load_project()
+    _logger.debug("Listing all deployments for Project %s", project)
+
+    _logger.info("Destroy for Project %s, Deployment %s", project, deployment)
     if dry_run:
         print_bolded(" ***   This is a dry-run...NO ACTIONS WILL BE TAKEN  *** ", "white")
+
+    (SessionManager().get_or_create(project_name=project, profile=profile, region_name=region).toolchain_session)
     commands.destroy(deployment_name=deployment, dryrun=dry_run, show_manifest=show_manifest)
 
 
diff --git a/seedfarmer/cli_groups/_bootstrap_group.py b/seedfarmer/cli_groups/_bootstrap_group.py
index 357d2fca..4d6eee3a 100644
--- a/seedfarmer/cli_groups/_bootstrap_group.py
+++ b/seedfarmer/cli_groups/_bootstrap_group.py
@@ -18,7 +18,7 @@
 
 import click
 
-from seedfarmer import DEBUG_LOGGING_FORMAT, enable_debug
+from seedfarmer import DEBUG_LOGGING_FORMAT, config, enable_debug
 from seedfarmer.commands import bootstrap_target_account, bootstrap_toolchain_account
 from seedfarmer.output_utils import print_bolded
 
@@ -26,10 +26,8 @@
 
 
 def _load_project() -> str:
-    print_bolded("No --project provided, attempting load from seedfarmer.yaml", "white")
     try:
-        from seedfarmer import config
-
+        print_bolded("No --project provided, attempting load from seedfarmer.yaml", "white")
         return config.PROJECT
     except FileNotFoundError:
         print_bolded("Unable to determine project to bootstrap, one of --project or a seedfarmer.yaml is required")
diff --git a/seedfarmer/cli_groups/_list_group.py b/seedfarmer/cli_groups/_list_group.py
index 02fa561b..b4cd7c11 100644
--- a/seedfarmer/cli_groups/_list_group.py
+++ b/seedfarmer/cli_groups/_list_group.py
@@ -15,17 +15,29 @@
 import json
 import logging
 import sys
+from typing import Optional
 
 import click
+from boto3 import Session
 
 import seedfarmer.mgmt.deploy_utils as du
 import seedfarmer.mgmt.module_info as mi
-from seedfarmer import DEBUG_LOGGING_FORMAT, commands, enable_debug
+from seedfarmer import DEBUG_LOGGING_FORMAT, commands, config, enable_debug
 from seedfarmer.output_utils import print_bolded, print_deployment_inventory, print_json, print_manifest_inventory
+from seedfarmer.services.session_manager import SessionManager
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
 
+def _load_project() -> str:
+    try:
+        print_bolded("No --project provided, attempting load from seedfarmer.yaml", "white")
+        return config.PROJECT
+    except FileNotFoundError:
+        print_bolded("Unable to determine project to bootstrap, one of --project or a seedfarmer.yaml is required")
+        raise click.ClickException("Failed to determine project identifier")
+
+
 @click.group(name="list", help="List the relative data (module or deployment)")
 def list() -> None:
     """List module data"""
@@ -54,6 +66,25 @@ def list() -> None:
     help="The Module Name",
     required=True,
 )
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--target-account-id",
+    default=None,
+    help="Account Id to remove module data from, if specifed --target-region is required",
+    show_default=True,
+)
+@click.option(
+    "--target-region",
+    default=None,
+    help="Region to remove module data from, if specifed --target-account-id is required",
+    show_default=True,
+)
 @click.option(
     "--debug/--no-debug",
     default=False,
@@ -64,13 +95,29 @@ def list_deployspec(
     deployment: str,
     group: str,
     module: str,
+    project: Optional[str],
+    target_account_id: Optional[str],
+    target_region: Optional[str],
     debug: bool,
 ) -> None:
     if debug:
         enable_debug(format=DEBUG_LOGGING_FORMAT)
     _logger.debug("We are getting deployspec for  %s in %s of deployment %s", module, group, deployment)
 
-    val = mi.get_deployspec(deployment=deployment, group=group, module=module)
+    if project is None:
+        project = _load_project()
+
+    session: Optional[Session] = None
+    if (target_account_id is not None) != (target_region is not None):
+        raise ValueError("Must either specify both --target-account-id and --target-region, or neither")
+    elif target_account_id is not None and target_region is not None:
+        session = (
+            SessionManager()
+            .get_or_create(project_name=project)
+            .get_deployment_session(account_id=target_account_id, region_name=target_region)
+        )
+
+    val = mi.get_deployspec(deployment=deployment, group=group, module=module, session=session)
     print_json(val)
 
 
@@ -96,6 +143,25 @@ def list_deployspec(
     help="The Module Name",
     required=True,
 )
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--target-account-id",
+    default=None,
+    help="Account Id to remove module data from, if specifed --target-region is required",
+    show_default=True,
+)
+@click.option(
+    "--target-region",
+    default=None,
+    help="Region to remove module data from, if specifed --target-account-id is required",
+    show_default=True,
+)
 @click.option(
     "--export-local-env/--no-export-local-env",
     default=False,
@@ -112,13 +178,30 @@ def list_module_metadata(
     deployment: str,
     group: str,
     module: str,
+    project: Optional[str],
+    target_account_id: Optional[str],
+    target_region: Optional[str],
     export_local_env: str,
     debug: bool,
 ) -> None:
     if debug:
         enable_debug(format=DEBUG_LOGGING_FORMAT)
     _logger.debug("We are getting module data for  %s in %s of %s", module, group, deployment)
-    metadata_json = mi.get_module_metadata(deployment, group, module)
+
+    if project is None:
+        project = _load_project()
+
+    session: Optional[Session] = None
+    if (target_account_id is not None) != (target_region is not None):
+        raise ValueError("Must either specify both --target-account-id and --target-region, or neither")
+    elif target_account_id is not None and target_region is not None:
+        session = (
+            SessionManager()
+            .get_or_create(project_name=project)
+            .get_deployment_session(account_id=target_account_id, region_name=target_region)
+        )
+
+    metadata_json = mi.get_module_metadata(deployment, group, module, session=session)
     if not export_local_env:
         sys.stdout.write(json.dumps(metadata_json))
     else:
@@ -133,7 +216,7 @@ def list_module_metadata(
             print_bolded(f"To see all deployed modules in {deployment}, run seedfarmer list modules -d {deployment}")
 
 
-@list.command(name="modules", help="List the modules in a group")
+@list.command(name="modules", help="List the modules in a deployment")
 @click.option(
     "--deployment",
     "-d",
@@ -155,15 +238,34 @@ def list_modules(
         enable_debug(format=DEBUG_LOGGING_FORMAT)
     _logger.debug("We are getting modules for %s", deployment)
 
-    cache = du.generate_deployment_cache(deployment_name=deployment)
-    dep_manifest = du.generate_deployed_manifest(
-        deployment_name=deployment, deployment_params_cache=cache, skip_deploy_spec=True
-    )
+    project = _load_project()
+    SessionManager().get_or_create(project_name=project)
+
+    dep_manifest = du.generate_deployed_manifest(deployment_name=deployment, skip_deploy_spec=True)
     if dep_manifest:
         print_manifest_inventory("Deployed Modules", dep_manifest, False, "green")
 
 
 @list.command(name="deployments", help="List the deployments in this account")
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--profile",
+    default=None,
+    help="The AWS profile to use for boto3.Sessions",
+    required=False,
+)
+@click.option(
+    "--region",
+    default=None,
+    help="The AWS region to use for boto3.Sessions",
+    required=False,
+)
 @click.option(
     "--debug/--no-debug",
     default=False,
@@ -171,11 +273,20 @@ def list_modules(
     show_default=True,
 )
 def list_deployments(
+    project: Optional[str],
+    profile: Optional[str],
+    region: Optional[str],
     debug: bool,
 ) -> None:
     if debug:
         enable_debug(format=DEBUG_LOGGING_FORMAT)
-    _logger.debug("We are getting all deployments")
+    if project is None:
+        project = _load_project()
+    _logger.debug("Listing all deployments for Project %s", project)
 
-    deps = mi.get_all_deployments()
+    deps = mi.get_all_deployments(
+        session=SessionManager()
+        .get_or_create(project_name=project, profile=profile, region_name=region)
+        .toolchain_session
+    )
     print_deployment_inventory(description="Deployment Names", dep=deps)
diff --git a/seedfarmer/cli_groups/_remove_group.py b/seedfarmer/cli_groups/_remove_group.py
index 807e9154..857e4e0c 100644
--- a/seedfarmer/cli_groups/_remove_group.py
+++ b/seedfarmer/cli_groups/_remove_group.py
@@ -13,15 +13,28 @@
 #    limitations under the License.
 
 import logging
+from typing import Optional
 
 import click
+from boto3 import Session
 
 import seedfarmer.mgmt.module_info as mi
-from seedfarmer import DEBUG_LOGGING_FORMAT, enable_debug
+from seedfarmer import DEBUG_LOGGING_FORMAT, config, enable_debug
+from seedfarmer.output_utils import print_bolded
+from seedfarmer.services.session_manager import SessionManager
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
 
+def _load_project() -> str:
+    try:
+        print_bolded("No --project provided, attempting load from seedfarmer.yaml", "white")
+        return config.PROJECT
+    except FileNotFoundError:
+        print_bolded("Unable to determine project to bootstrap, one of --project or a seedfarmer.yaml is required")
+        raise click.ClickException("Failed to determine project identifier")
+
+
 @click.group(name="remove", help="Top Level command to support removing module metadata")
 def remove() -> None:
     """Remove module data"""
@@ -50,6 +63,25 @@ def remove() -> None:
     help="The Module Name",
     required=True,
 )
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--target-account-id",
+    default=None,
+    help="Account Id to remove module data from, if specifed --target-region is required",
+    show_default=True,
+)
+@click.option(
+    "--target-region",
+    default=None,
+    help="Region to remove module data from, if specifed --target-account-id is required",
+    show_default=True,
+)
 @click.option(
     "--debug/--no-debug",
     default=False,
@@ -60,10 +92,26 @@ def remove_module_data(
     deployment: str,
     group: str,
     module: str,
+    project: Optional[str],
+    target_account_id: Optional[str],
+    target_region: Optional[str],
     debug: bool,
 ) -> None:
     if debug:
         enable_debug(format=DEBUG_LOGGING_FORMAT)
-    _logger.debug(" We are removing module data for %s of group %s in %s", module, group, deployment)
+    _logger.debug("We are removing module data for %s of group %s in %s", module, group, deployment)
+
+    if project is None:
+        project = _load_project()
+
+    session: Optional[Session] = None
+    if (target_account_id is not None) != (target_region is not None):
+        raise ValueError("Must either specify both --target-account-id and --target-region, or neither")
+    elif target_account_id is not None and target_region is not None:
+        session = (
+            SessionManager()
+            .get_or_create(project_name=project)
+            .get_deployment_session(account_id=target_account_id, region_name=target_region)
+        )
 
-    mi.remove_module_info(deployment, group, module)
+    mi.remove_module_info(deployment, group, module, session=session)
diff --git a/seedfarmer/cli_groups/_store_group.py b/seedfarmer/cli_groups/_store_group.py
index f6f0a563..9f543511 100644
--- a/seedfarmer/cli_groups/_store_group.py
+++ b/seedfarmer/cli_groups/_store_group.py
@@ -14,17 +14,30 @@
 
 import logging
 import sys
+from typing import Optional
 
 import click
 import yaml
+from boto3 import Session
 
 import seedfarmer.mgmt.deploy_utils as du
 import seedfarmer.mgmt.module_info as mi
-from seedfarmer import DEBUG_LOGGING_FORMAT, enable_debug, utils
+from seedfarmer import DEBUG_LOGGING_FORMAT, config, enable_debug, utils
+from seedfarmer.output_utils import print_bolded
+from seedfarmer.services.session_manager import SessionManager
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
 
+def _load_project() -> str:
+    try:
+        print_bolded("No --project provided, attempting load from seedfarmer.yaml", "white")
+        return config.PROJECT
+    except FileNotFoundError:
+        print_bolded("Unable to determine project to bootstrap, one of --project or a seedfarmer.yaml is required")
+        raise click.ClickException("Failed to determine project identifier")
+
+
 @click.group(name="store", help="Top Level command to support storing module metadata")
 def store() -> None:
     "Store module data"
@@ -60,6 +73,25 @@ def store() -> None:
     help="The relative module path (ex. modules/optionals/networking) -- *** DO NOT PASS IN filename `deployspec.yaml`",
     required=True,
 )
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--target-account-id",
+    default=None,
+    help="Account Id to remove module data from, if specifed --target-region is required",
+    show_default=True,
+)
+@click.option(
+    "--target-region",
+    default=None,
+    help="Region to remove module data from, if specifed --target-account-id is required",
+    show_default=True,
+)
 @click.option(
     "--debug/--no-debug",
     default=False,
@@ -71,6 +103,9 @@ def store_deployspec(
     group: str,
     module: str,
     path: str,
+    project: Optional[str],
+    target_account_id: Optional[str],
+    target_region: Optional[str],
     debug: bool,
 ) -> None:
     if debug:
@@ -80,7 +115,21 @@ def store_deployspec(
         f"""Storing deployspec for module {module} of group {group}
         in deployment {deployment} located at {path}/deployspec.yaml"""
     )
-    du.update_deployspec(deployment, group, module, path)
+
+    if project is None:
+        project = _load_project()
+
+    session: Optional[Session] = None
+    if (target_account_id is not None) != (target_region is not None):
+        raise ValueError("Must either specify both --target-account-id and --target-region, or neither")
+    elif target_account_id is not None and target_region is not None:
+        session = (
+            SessionManager()
+            .get_or_create(project_name=project)
+            .get_deployment_session(account_id=target_account_id, region_name=target_region)
+        )
+
+    du.update_deployspec(deployment, group, module, path, session=session)
 
 
 @store.command(name="moduledata", help="CAT or pipe in a json or yaml object")
@@ -105,6 +154,25 @@ def store_deployspec(
     help="The Module Name",
     required=True,
 )
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--target-account-id",
+    default=None,
+    help="Account Id to remove module data from, if specifed --target-region is required",
+    show_default=True,
+)
+@click.option(
+    "--target-region",
+    default=None,
+    help="Region to remove module data from, if specifed --target-account-id is required",
+    show_default=True,
+)
 @click.option(
     "--debug/--no-debug",
     default=False,
@@ -115,14 +183,31 @@ def store_module_metadata(
     deployment: str,
     group: str,
     module: str,
+    project: Optional[str],
+    target_account_id: Optional[str],
+    target_region: Optional[str],
     debug: bool,
 ) -> None:
     if debug:
         enable_debug(format=DEBUG_LOGGING_FORMAT)
     _logger.debug("Writing metadata for module %s in deployment %s", module, deployment)
+
+    if project is None:
+        project = _load_project()
+
+    session: Optional[Session] = None
+    if (target_account_id is not None) != (target_region is not None):
+        raise ValueError("Must either specify both --target-account-id and --target-region, or neither")
+    elif target_account_id is not None and target_region is not None:
+        session = (
+            SessionManager()
+            .get_or_create(project_name=project)
+            .get_deployment_session(account_id=target_account_id, region_name=target_region)
+        )
+
     d = yaml.load(sys.stdin.read(), Loader=utils.CfnSafeYamlLoader)
     if d:
-        mi.write_metadata(deployment=deployment, group=group, module=module, data=d)
+        mi.write_metadata(deployment=deployment, group=group, module=module, data=d, session=session)
     else:
         _logger.info("No Data avaiable...skipping")
 
@@ -156,6 +241,25 @@ def store_module_metadata(
     help="The kind of MD5: bundle or spec",
     required=True,
 )
+@click.option(
+    "--project",
+    "-p",
+    help="Project identifier",
+    required=False,
+    default=None,
+)
+@click.option(
+    "--target-account-id",
+    default=None,
+    help="Account Id to remove module data from, if specifed --target-region is required",
+    show_default=True,
+)
+@click.option(
+    "--target-region",
+    default=None,
+    help="Region to remove module data from, if specifed --target-account-id is required",
+    show_default=True,
+)
 @click.option(
     "--debug/--no-debug",
     default=False,
@@ -167,17 +271,34 @@ def store_module_md5(
     group: str,
     module: str,
     type: str,
+    project: Optional[str],
+    target_account_id: Optional[str],
+    target_region: Optional[str],
     debug: bool,
 ) -> None:
     if debug:
         enable_debug(format=DEBUG_LOGGING_FORMAT)
     _logger.debug("Writing md5 of  %s for module %s in deployment %s", type, module, deployment)
+
+    if project is None:
+        project = _load_project()
+
+    session: Optional[Session] = None
+    if (target_account_id is not None) != (target_region is not None):
+        raise ValueError("Must either specify both --target-account-id and --target-region, or neither")
+    elif target_account_id is not None and target_region is not None:
+        session = (
+            SessionManager()
+            .get_or_create(project_name=project)
+            .get_deployment_session(account_id=target_account_id, region_name=target_region)
+        )
+
     d = sys.stdin.readline().strip()
     if d:
         if type.casefold() == "bundle":
             _type = mi.ModuleConst.BUNDLE
         elif type.casefold() == "spec":
             _type = mi.ModuleConst.DEPLOYSPEC
-        mi.write_module_md5(deployment=deployment, group=group, module=module, hash=d, type=_type)
+        mi.write_module_md5(deployment=deployment, group=group, module=module, hash=d, type=_type, session=session)
     else:
         _logger.info("No Data avaiable...skipping")
diff --git a/seedfarmer/commands/_deployment_commands.py b/seedfarmer/commands/_deployment_commands.py
index 30becdfc..9970b044 100644
--- a/seedfarmer/commands/_deployment_commands.py
+++ b/seedfarmer/commands/_deployment_commands.py
@@ -17,7 +17,7 @@
 import json
 import logging
 import os
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, List, Optional, cast
 
 import checksumdir
 import yaml
@@ -28,14 +28,12 @@
 from seedfarmer.mgmt.module_info import (
     _get_deployspec_path,
     _get_modulestack_path,
-    get_deployed_modules,
     get_module_metadata,
     remove_deployed_deployment_manifest,
     remove_deployment_manifest,
-    remove_group_info,
     write_deployment_manifest,
 )
-from seedfarmer.models.deploy_responses import ModuleDeploymentResponse
+from seedfarmer.models.deploy_responses import ModuleDeploymentResponse, StatusType
 from seedfarmer.models.manifests import DeploymentManifest, DeploySpec, ModuleManifest, ModulesManifest
 from seedfarmer.output_utils import (
     _print_modules,
@@ -44,6 +42,7 @@
     print_manifest_inventory,
     print_manifest_json,
 )
+from seedfarmer.services.session_manager import SessionManager
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
@@ -52,24 +51,34 @@ def _execute_deploy(
     d_name: str,
     g_name: str,
     m: ModuleManifest,
+    deployment_manifest: DeploymentManifest,
     d_secret: Optional[str] = None,
     permission_boundary_arn: Optional[str] = None,
 ) -> ModuleDeploymentResponse:
-    parameters = load_parameter_values(deployment_name=d_name, parameters=m.parameters)
+    parameters = load_parameter_values(
+        deployment_name=d_name, parameters=m.parameters, deployment_manifest=deployment_manifest
+    )
 
+    target_account_id = cast(str, m.get_target_account_id())
+    target_region = cast(str, m.target_region)
     # Deploys the IAM role per module
     commands.deploy_module_stack(
         _get_modulestack_path(m.path),
         d_name,
         g_name,
         m.name,
+        target_account_id,
+        target_region,
         parameters,
         docker_credentials_secret=d_secret,
         permission_boundary_arn=permission_boundary_arn,
     )
 
     #   Get the current module's SSM if it was alreadly loaded...
-    module_metadata = json.dumps(get_module_metadata(d_name, g_name, m.name))
+    session = (
+        SessionManager().get_or_create().get_deployment_session(account_id=target_account_id, region_name=target_region)
+    )
+    module_metadata = json.dumps(get_module_metadata(d_name, g_name, m.name, session=session))
 
     if m.deploy_spec is None:
         raise ValueError(f"Invalid value for ModuleManifest.deploy_spec in group {g_name} and module : {m.name}")
@@ -80,6 +89,8 @@ def _execute_deploy(
         module_path=os.path.join(config.OPS_ROOT, m.path),
         module_deploy_spec=m.deploy_spec,
         module_manifest_name=m.name,
+        account_id=target_account_id,
+        region=target_region,
         parameters=parameters,
         module_metadata=module_metadata,
         module_bundle_md5=m.bundle_md5,
@@ -92,6 +103,7 @@ def _execute_destroy(
     d_name: str,
     g_name: str,
     m: ModuleManifest,
+    deployment_manifest: DeploymentManifest,
     d_secret: Optional[str] = None,
 ) -> Optional[ModuleDeploymentResponse]:
     if m.deploy_spec is None:
@@ -103,18 +115,27 @@ def _execute_destroy(
         module_path=m.path,
         module_deploy_spec=m.deploy_spec,
         module_manifest_name=m.name,
-        parameters=load_parameter_values(deployment_name=d_name, parameters=m.parameters),
+        account_id=cast(str, m.get_target_account_id()),
+        region=cast(str, m.target_region),
+        parameters=load_parameter_values(
+            deployment_name=d_name, parameters=m.parameters, deployment_manifest=deployment_manifest
+        ),
         module_metadata=None,
     )
-    commands.destroy_module_stack(
-        d_name,
-        g_name,
-        m.name,
-        docker_credentials_secret=d_secret,
-    )
 
-    if not get_deployed_modules(deployment=d_name, group=g_name):
-        remove_group_info(d_name, g_name)
+    if resp.status == StatusType.SUCCESS.value:
+        commands.destroy_module_stack(
+            d_name,
+            g_name,
+            m.name,
+            account_id=cast(str, m.get_target_account_id()),
+            region=cast(str, m.target_region),
+            docker_credentials_secret=d_secret,
+        )
+
+    # TODO: Confirm whether this is needed, commenting for now
+    # if not get_deployed_modules(deployment=d_name, group=g_name):
+    #     remove_group_info(d_name, g_name)
     return resp
 
 
@@ -123,8 +144,6 @@ def _deploy_deployment_is_not_dry_run(
     deployment_manifest_wip: DeploymentManifest,
     deployment_name: str,
     groups_to_deploy: List[ModulesManifest],
-    permission_boundary_arn: Optional[str],
-    docker_credentials_secret: Optional[str],
 ) -> None:
     if groups_to_deploy:
         deployment_manifest_wip.groups = groups_to_deploy
@@ -144,7 +163,12 @@ def _deploy_deployment_is_not_dry_run(
 
                     def _exec_deploy(args: Dict[str, Any]) -> ModuleDeploymentResponse:
                         return _execute_deploy(
-                            args["d_name"], args["g"], args["m"], args["d_secret"], args["permission_boundary_arn"]
+                            args["d_name"],
+                            args["g"],
+                            args["m"],
+                            deployment_manifest,
+                            args["d_secret"],
+                            args["permission_boundary_arn"],
                         )
 
                     params = [
@@ -152,8 +176,16 @@ def _exec_deploy(args: Dict[str, Any]) -> ModuleDeploymentResponse:
                             "d_name": deployment_name,
                             "g": _group.name,
                             "m": _module,
-                            "d_secret": docker_credentials_secret,
-                            "permission_boundary_arn": permission_boundary_arn,
+                            "d_secret": deployment_manifest_wip.get_parameter_value(
+                                "dockerCredentialsSecret",
+                                account_alias=_module.target_account,
+                                region=_module.target_region,
+                            ),
+                            "permission_boundary_arn": deployment_manifest_wip.get_parameter_value(
+                                "permissionBoundaryArn",
+                                account_alias=_module.target_account,
+                                region=_module.target_region,
+                            ),
                         }
                         for _module in _group.modules
                         if _module and _module.deploy_spec
@@ -182,6 +214,21 @@ def _deploy_deployment_is_dry_run(groups_to_deploy: List[ModulesManifest], deplo
     _print_modules(f"Modules scheduled to be deployed (created or updated): {deployment_name}", mods_would_deploy)
 
 
+def prime_target_accounts(deployment_manifest: DeploymentManifest) -> None:
+    # TODO: Include output on Target accounts and regions to be primed
+    # TODO: Investigate whether we need to validate the requested mappings against previously deployed mappings
+    # TODO: multi-thread this for reach Target account/region for better performance
+
+    for target_account_region in deployment_manifest.target_accounts_regions:
+        commands.deploy_seedkit(account_id=target_account_region["account_id"], region=target_account_region["region"])
+        commands.deploy_managed_policy_stack(
+            deployment_name=deployment_manifest.name,
+            deployment_manifest=deployment_manifest,
+            account_id=target_account_region["account_id"],
+            region=target_account_region["region"],
+        )
+
+
 def destroy_deployment(
     destroy_manifest: DeploymentManifest,
     remove_deploy_manifest: bool = False,
@@ -197,7 +244,7 @@ def destroy_deployment(
     deployment_manifest : DeploymentManifest
         The DeploymentManifest objec of all modules to destroy
     remove_deploy_manifest : bool, optional
-        This flag indicates whether the project resouurce policy should be deleted.
+        This flag indicates whether the project resource policy should be deleted.
         If there are ANY modules deployed, this should not be set to True.  This is only
         used when the entire deployment is destroyed
     dryrun : bool, optional
@@ -216,8 +263,6 @@ def destroy_deployment(
     print_manifest_inventory(f"Modules removed from manifest: {destroy_manifest.name}", destroy_manifest, False, "red")
 
     deployment_name = destroy_manifest.name
-    docker_credentials_secret = destroy_manifest.get_parameter_value("dockerCredentialsSecret")
-    _logger.debug("dockerCredentialsSecret: %s", docker_credentials_secret)
 
     print_manifest_inventory(
         f"Modules scheduled to be destroyed for: {destroy_manifest.name}", destroy_manifest, False, "red"
@@ -233,6 +278,7 @@ def _exec_destroy(args: Dict[str, Any]) -> Optional[ModuleDeploymentResponse]:
                             args["d"],
                             args["g"],
                             args["m"],
+                            destroy_manifest,
                             args["d_secret"],
                         )
 
@@ -241,7 +287,11 @@ def _exec_destroy(args: Dict[str, Any]) -> Optional[ModuleDeploymentResponse]:
                             "d": deployment_name,
                             "g": _group.name,
                             "m": _module,
-                            "d_secret": docker_credentials_secret,
+                            "d_secret": destroy_manifest.get_parameter_value(
+                                "dockerCredentialsSecret",
+                                account_alias=_module.target_account,
+                                region=_module.target_region,
+                            ),
                         }
                         for _module in _group.modules
                         if _module and _module.deploy_spec
@@ -256,15 +306,16 @@ def _exec_destroy(args: Dict[str, Any]) -> Optional[ModuleDeploymentResponse]:
 
         print_manifest_inventory(f"Modules Destroyed: {deployment_name}", destroy_manifest, False, "red")
         if remove_deploy_manifest:
-            remove_deployment_manifest(deployment_name)
-            remove_deployed_deployment_manifest(deployment_name)
+            session = SessionManager().get_or_create().toolchain_session
+            remove_deployment_manifest(deployment_name, session=session)
+            remove_deployed_deployment_manifest(deployment_name, session=session)
     if show_manifest:
         print_manifest_json(destroy_manifest)
 
 
 def deploy_deployment(
     deployment_manifest: DeploymentManifest,
-    deployment_params_cache: Optional[Dict[str, Any]] = None,
+    module_info_index: du.ModuleInfoIndex,
     dryrun: bool = False,
     show_manifest: bool = False,
 ) -> None:
@@ -278,8 +329,8 @@ def deploy_deployment(
     ----------
     deployment_manifest : DeploymentManifest
         The DeploymentManifest objec of all modules to deploy
-    deployment_params_cache: Dict[str,Any]
-        A dictionary representation of what is in the store (SSM for DDB) of the modules deployed
+    module_info_index:ModuleInfoIndex
+        An index of all Module Info stored in SSM across all target accounts and regions
     dryrun : bool, optional
         This flag indicates that the DeploymentManifest object should be consumed but DOES NOT
         enact any deployment changes.
@@ -291,36 +342,33 @@ def deploy_deployment(
     """
     deployment_manifest_wip = deployment_manifest.copy()
     deployment_name = deployment_manifest_wip.name
-    docker_credentials_secret = deployment_manifest.get_parameter_value("dockerCredentialsSecret", default=None)
-    permission_boundary_arn = deployment_manifest.get_parameter_value("permissionBoundaryArn", default=None)
     _logger.debug("Setting up deployment for %s", deployment_name)
 
     print_manifest_inventory(
         f"Modules added to manifest: {deployment_manifest_wip.name}", deployment_manifest_wip, True
     )
-    commands.deploy_seedkit()
-    commands.deploy_managed_policy_stack(deployment_name=deployment_name, deployment_manifest=deployment_manifest_wip)
 
     groups_to_deploy = []
     unchanged_modules = []
     for group in deployment_manifest_wip.groups:
-        working_group = group.copy()
-        # TODO - Write the group manifest here...without the module deployspec
+        # working_group = group.copy()
         group_name = group.name
-        du.write_group_manifest(deployment_name=deployment_name, group_manifest=working_group)
+        # TODO: Investigate whether we need the group manifest, for now commenting
+        # du.write_group_manifest(deployment_name=deployment_name, group_manifest=working_group)
         modules_to_deploy = []
         _logger.info(" Verifying all modules in %s for deploy ", group.name)
         for module in group.modules:
-            _logger.debug("Working on --  %s", module)
+            _logger.debug("Working on -- %s", module)
             if not module.path:
-                raise Exception("Unable to parse module manifest, `path` not specified")
+                raise ValueError("Unable to parse module manifest, `path` not specified")
 
             # This checks if the modulestack file exists, else fail fast
-            _ = _get_modulestack_path(module.path)
+            # TODO: Uncomment this if it's needed, but this isn't actually doing anything
+            # _ = _get_modulestack_path(module.path)
 
             deployspec_path = _get_deployspec_path(module.path)
             with open(deployspec_path) as module_spec_file:
-                module_deploy_spec = DeploySpec(**yaml.load(module_spec_file, Loader=yaml.SafeLoader))
+                module.deploy_spec = DeploySpec(**yaml.safe_load(module_spec_file))
 
             # This MD5 is generated from the module manifest content before setting the generated values
             # of `bundle_md5` and `deploy_spec` below
@@ -333,17 +381,20 @@ def deploy_deployment(
                 deployment_name=deployment_name,
                 group_name=group_name,
                 module_manifest=module,
-                module_deployspec=module_deploy_spec,
+                module_deployspec=module.deploy_spec,
                 module_deployspec_md5=module_deployspec_md5,
                 module_manifest_md5=module_manifest_md5,
                 dryrun=dryrun,
-                deployment_params_cache=deployment_params_cache,
+                deployment_params_cache=module_info_index.get_module_info(
+                    group=group_name,
+                    account_id=cast(str, module.get_target_account_id()),
+                    region=cast(str, module.target_region),
+                    module_name=module.name,
+                ),
             )
             if not _build_module:
-                module.deploy_spec = module_deploy_spec
                 unchanged_modules.append([deployment_name, group_name, module.name])
             else:
-                module.deploy_spec = module_deploy_spec
                 modules_to_deploy.append(module)
 
         if modules_to_deploy:
@@ -362,8 +413,6 @@ def deploy_deployment(
             deployment_manifest_wip=deployment_manifest_wip,
             deployment_name=deployment_name,
             groups_to_deploy=groups_to_deploy,
-            permission_boundary_arn=permission_boundary_arn,
-            docker_credentials_secret=docker_credentials_secret,
         )
     else:
         _deploy_deployment_is_dry_run(groups_to_deploy=groups_to_deploy, deployment_name=deployment_name)
@@ -374,7 +423,7 @@ def deploy_deployment(
         print_manifest_json(deployment_manifest)
 
 
-def apply(deployment_spec: str, dryrun: bool = False, show_manifest: bool = False) -> None:
+def apply(deployment_manifest_path: str, dryrun: bool = False, show_manifest: bool = False) -> None:
     """
     apply
         This function takes the relative path of a deployment manifest and
@@ -384,7 +433,7 @@ def apply(deployment_spec: str, dryrun: bool = False, show_manifest: bool = Fals
 
     Parameters
     ----------
-    deployment_spec : str
+    deployment_manifest_path : str
         Relative path to the deployment manifest
     dryrun : bool, optional
         This flag indicates that the deployment manifest should be consumed and a
@@ -405,14 +454,19 @@ def apply(deployment_spec: str, dryrun: bool = False, show_manifest: bool = Fals
         If the relative `path' value is a list
     """
 
-    spec_path = os.path.join(config.OPS_ROOT, deployment_spec)
-    with open(spec_path) as manifest_file:
+    manifest_path = os.path.join(config.OPS_ROOT, deployment_manifest_path)
+    with open(manifest_path) as manifest_file:
         deployment_manifest = DeploymentManifest(**yaml.safe_load(manifest_file))
     _logger.debug(deployment_manifest.dict())
+
+    # Initialize the SessionManager for the entire project
+    session_manager = SessionManager().get_or_create(project_name=config.PROJECT)
     if not dryrun:
-        write_deployment_manifest(deployment_manifest.name, deployment_manifest.dict())
+        write_deployment_manifest(
+            deployment_manifest.name, deployment_manifest.dict(), session=session_manager.toolchain_session
+        )
 
-    for module_group in deployment_manifest.groups if deployment_manifest.groups else []:
+    for module_group in deployment_manifest.groups:
         if module_group.path and module_group.modules:
             _logger.debug("module_group: %s", module_group)
             raise Exception("Only one of the `path` or `modules` attributes can be defined on a Group")
@@ -422,16 +476,22 @@ def apply(deployment_spec: str, dryrun: bool = False, show_manifest: bool = Fals
         if module_group.path:
             with open(os.path.join(config.OPS_ROOT, module_group.path)) as manifest_file:
                 module_group.modules = [ModuleManifest(**m) for m in yaml.safe_load_all(manifest_file)]
-    deployment_manifest.set_module_defaults()
+    deployment_manifest.validate_and_set_module_defaults()
+
+    prime_target_accounts(deployment_manifest=deployment_manifest)
+
+    module_info_index = du.populate_module_info_index(deployment_manifest=deployment_manifest)
+    destroy_manifest = du.filter_deploy_destroy(deployment_manifest, module_info_index)
 
-    deployment_params_cache = du.generate_deployment_cache(deployment_name=deployment_manifest.name)
-    destroy_manifest = du.filter_deploy_destroy(deployment_manifest, deployment_params_cache)
     destroy_deployment(
-        destroy_manifest=destroy_manifest, remove_deploy_manifest=False, dryrun=dryrun, show_manifest=show_manifest
+        destroy_manifest=destroy_manifest,
+        remove_deploy_manifest=False,
+        dryrun=dryrun,
+        show_manifest=show_manifest,
     )
     deploy_deployment(
         deployment_manifest=deployment_manifest,
-        deployment_params_cache=deployment_params_cache,
+        module_info_index=module_info_index,
         dryrun=dryrun,
         show_manifest=show_manifest,
     )
@@ -445,7 +505,7 @@ def destroy(deployment_name: str, dryrun: bool = False, show_manifest: bool = Fa
     Parameters
     ----------
     deployment_name : str
-       The name of the deployment to destroy
+        The name of the deployment to destroy
     dryrun : bool, optional
         This flag indicates that the deployment WILL NOT
         enact any deployment changes.
@@ -458,11 +518,14 @@ def destroy(deployment_name: str, dryrun: bool = False, show_manifest: bool = Fa
 
     """
     _logger.debug("Preparing to destroy %s", deployment_name)
-    deployment_params_cache = du.generate_deployment_cache(deployment_name=deployment_name)
-    destroy_manifest = du.generate_deployed_manifest(
-        deployment_name=deployment_name, deployment_params_cache=deployment_params_cache, skip_deploy_spec=False
-    )
+    destroy_manifest = du.generate_deployed_manifest(deployment_name=deployment_name, skip_deploy_spec=False)
     if destroy_manifest:
-        destroy_deployment(destroy_manifest, remove_deploy_manifest=True, dryrun=dryrun, show_manifest=show_manifest)
+        destroy_manifest.validate_and_set_module_defaults()
+        destroy_deployment(
+            destroy_manifest,
+            remove_deploy_manifest=True,
+            dryrun=dryrun,
+            show_manifest=show_manifest,
+        )
     else:
         _logger.info("Deployment %s was not found, ignoring... ", deployment_name)
diff --git a/seedfarmer/commands/_module_commands.py b/seedfarmer/commands/_module_commands.py
index 67389fb9..4598082e 100644
--- a/seedfarmer/commands/_module_commands.py
+++ b/seedfarmer/commands/_module_commands.py
@@ -22,10 +22,12 @@
 import botocore.exceptions
 from aws_codeseeder import codeseeder
 from aws_codeseeder.errors import CodeSeederRuntimeError
+from boto3 import Session
 
 from seedfarmer import config
 from seedfarmer.models.deploy_responses import CodeSeederMetadata, ModuleDeploymentResponse, StatusType
 from seedfarmer.models.manifests import DeploySpec, ModuleParameter
+from seedfarmer.services.session_manager import SessionManager
 from seedfarmer.utils import generate_hash
 
 _logger: logging.Logger = logging.getLogger(__name__)
@@ -69,6 +71,8 @@ def deploy_module(
     module_path: str,
     module_deploy_spec: DeploySpec,
     module_manifest_name: str,
+    account_id: str,
+    region: str,
     parameters: Optional[List[ModuleParameter]] = None,
     module_metadata: Optional[str] = None,
     module_bundle_md5: Optional[str] = None,
@@ -110,6 +114,8 @@ def deploy_module(
             deployment_name=deployment_name,
             group_name=group_name,
             module_manifest_name=module_manifest_name,
+            account_id=account_id,
+            region=region,
             extra_dirs={"module": module_path},
             extra_install_commands=["cd module/"] + _phases.install.commands,
             extra_pre_build_commands=["cd module/"] + _phases.pre_build.commands,
@@ -147,6 +153,8 @@ def destroy_module(
     module_path: str,
     module_deploy_spec: DeploySpec,
     module_manifest_name: str,
+    account_id: str,
+    region: str,
     parameters: Optional[List[ModuleParameter]] = None,
     module_metadata: Optional[str] = None,
 ) -> ModuleDeploymentResponse:
@@ -178,6 +186,8 @@ def destroy_module(
             deployment_name=deployment_name,
             group_name=group_name,
             module_manifest_name=module_manifest_name,
+            account_id=account_id,
+            region=region,
             extra_dirs={"module": module_path},
             extra_install_commands=["cd module/"] + _phases.install.commands,
             extra_pre_build_commands=["cd module/"] + _phases.pre_build.commands + export_info,
@@ -210,6 +220,8 @@ def _execute_module_commands(
     deployment_name: str,
     group_name: str,
     module_manifest_name: str,
+    account_id: str,
+    region: str,
     extra_dirs: Optional[Dict[str, Any]] = None,
     extra_install_commands: Optional[List[str]] = None,
     extra_pre_build_commands: Optional[List[str]] = None,
@@ -218,6 +230,11 @@ def _execute_module_commands(
     extra_env_vars: Optional[Dict[str, Any]] = None,
     codebuild_compute_type: Optional[str] = None,
 ) -> Tuple[str, Optional[Dict[str, str]]]:
+    session: Optional[Session] = None
+
+    if not codeseeder.EXECUTING_REMOTELY:
+        session = SessionManager().get_or_create().get_deployment_session(account_id=account_id, region_name=region)
+
     @codeseeder.remote_function(
         config.PROJECT.lower(),
         extra_dirs=extra_dirs,
@@ -233,11 +250,14 @@ def _execute_module_commands(
         bundle_id=module_manifest_name,
         codebuild_compute_type=codebuild_compute_type,
         extra_files={config.CONFIG_FILE: os.path.join(config.OPS_ROOT, config.CONFIG_FILE)},
+        boto3_session=session,
     )
     def _execute_module_commands(
         deployment_name: str,
         group_name: str,
         module_manifest_name: str,
+        account_id: str,
+        region: str,
         extra_dirs: Optional[Dict[str, Any]] = None,
         extra_install_commands: Optional[List[str]] = None,
         extra_pre_build_commands: Optional[List[str]] = None,
@@ -264,6 +284,8 @@ def _execute_module_commands(
                     deployment_name=deployment_name,
                     group_name=group_name,
                     module_manifest_name=module_manifest_name,
+                    account_id=account_id,
+                    region=region,
                     extra_dirs=extra_dirs,
                     extra_install_commands=extra_install_commands,
                     extra_pre_build_commands=extra_pre_build_commands,
diff --git a/seedfarmer/commands/_parameter_commands.py b/seedfarmer/commands/_parameter_commands.py
index 7eec83ca..5e52038e 100644
--- a/seedfarmer/commands/_parameter_commands.py
+++ b/seedfarmer/commands/_parameter_commands.py
@@ -18,7 +18,8 @@
 
 from seedfarmer import config
 from seedfarmer.mgmt.module_info import get_module_metadata
-from seedfarmer.models.manifests import ModuleParameter
+from seedfarmer.models.manifests import DeploymentManifest, ModuleParameter
+from seedfarmer.services.session_manager import SessionManager
 from seedfarmer.utils import upper_snake_case
 
 _logger: logging.Logger = logging.getLogger(__name__)
@@ -33,7 +34,9 @@ def generate_export_env_params(metadata: Optional[Dict[str, Any]]) -> Optional[L
     return envs
 
 
-def load_parameter_values(deployment_name: str, parameters: List[ModuleParameter]) -> List[ModuleParameter]:
+def load_parameter_values(
+    deployment_name: str, parameters: List[ModuleParameter], deployment_manifest: DeploymentManifest
+) -> List[ModuleParameter]:
     parameter_values = []
     parameter_values_cache: Dict[Tuple[str, str, str], Any] = {}
     for parameter in parameters:
@@ -44,25 +47,43 @@ def load_parameter_values(deployment_name: str, parameters: List[ModuleParameter
             parameter_values.append(parameter)
         # Load parameter from Module Metadata
         elif parameter.value_from and parameter.value_from.module_metadata:
-            module_metatdata = _module_metatdata(deployment_name, parameter, parameter_values_cache)
+            module_metatdata = _module_metatdata(
+                deployment_name, parameter, parameter_values_cache, deployment_manifest
+            )
             parameter_values.append(module_metatdata) if module_metatdata else None
     return parameter_values
 
 
 def _get_param_value_cache(
-    d_name: str, g_name: str, m_name: str, parameter_values_cache: Dict[Tuple[str, str, str], Any]
+    d_name: str,
+    g_name: str,
+    m_name: str,
+    parameter_values_cache: Dict[Tuple[str, str, str], Any],
+    deployment_manifest: DeploymentManifest,
 ) -> Dict[Any, Any]:
     if (d_name, g_name, m_name) not in parameter_values_cache:
-        parameter_values_cache[(d_name, g_name, m_name)] = get_module_metadata(
-            d_name,
-            g_name,
-            m_name,
-        )
+        module = deployment_manifest.get_module(group=g_name, module=m_name)
+        if module is not None:
+            module_session = (
+                SessionManager()
+                .get_or_create()
+                .get_deployment_session(
+                    account_id=cast(str, module.get_target_account_id()), region_name=cast(str, module.target_region)
+                )
+            )
+            parameter_values_cache[(d_name, g_name, m_name)] = get_module_metadata(
+                d_name, g_name, m_name, session=module_session
+            )
+        else:
+            return {}
     return cast(Dict[Any, Any], parameter_values_cache[(d_name, g_name, m_name)])
 
 
 def _module_metatdata(
-    deployment_name: str, parameter: ModuleParameter, parameter_values_cache: Dict[Tuple[str, str, str], Any]
+    deployment_name: str,
+    parameter: ModuleParameter,
+    parameter_values_cache: Dict[Tuple[str, str, str], Any],
+    deployment_manifest: DeploymentManifest,
 ) -> Optional[ModuleParameter]:
     if parameter.value_from and parameter.value_from.module_metadata:
         group = parameter.value_from.module_metadata.group
@@ -70,7 +91,9 @@ def _module_metatdata(
         _logger.debug("Loading metadata for dependency group, module: %s, %s" % (group, module_name))
 
         # Ensure we only retrieve the SSM Parameter value once per module
-        parameter_value = _get_param_value_cache(deployment_name, group, module_name, parameter_values_cache)
+        parameter_value = _get_param_value_cache(
+            deployment_name, group, module_name, parameter_values_cache, deployment_manifest
+        )
         _logger.debug("loaded parameter value: %s", parameter_value)
 
         parameter_value = (
diff --git a/seedfarmer/commands/_stack_commands.py b/seedfarmer/commands/_stack_commands.py
index 197c1490..28c75e6a 100644
--- a/seedfarmer/commands/_stack_commands.py
+++ b/seedfarmer/commands/_stack_commands.py
@@ -26,6 +26,7 @@
 from seedfarmer.mgmt.module_info import _get_module_stack_names
 from seedfarmer.models.manifests import DeploymentManifest, ModuleParameter
 from seedfarmer.services._service_utils import get_account_id, get_region
+from seedfarmer.services.session_manager import SessionManager
 from seedfarmer.utils import upper_snake_case
 
 _logger: logging.Logger = logging.getLogger(__name__)
@@ -59,7 +60,9 @@ def PROJECT_MANAGED_POLICY_CFN_NAME(self) -> str:
 info = StackInfo()
 
 
-def deploy_managed_policy_stack(deployment_name: str, deployment_manifest: DeploymentManifest) -> None:
+def deploy_managed_policy_stack(
+    deployment_name: str, deployment_manifest: DeploymentManifest, account_id: str, region: str
+) -> None:
     """
     deploy_managed_policy_stack
         This function deploys the deployment-specific policy to allow CodeSeeder to deploy.
@@ -70,21 +73,28 @@ def deploy_managed_policy_stack(deployment_name: str, deployment_manifest: Deplo
         The name of the deployment
     deployment_manifest : DeploymentManifest
         The DeploymentManifest object of the deploy
-
+    account_id: str
+        The Account Id where the module is deployed
+    region: str
+        The region wher
     """
     # Determine if managed policy stack already deployed
+    session = SessionManager().get_or_create().get_deployment_session(account_id=account_id, region_name=region)
     project_managed_policy_stack_exists, _ = services.cfn.does_stack_exist(
-        stack_name=info.PROJECT_MANAGED_POLICY_CFN_NAME
+        stack_name=info.PROJECT_MANAGED_POLICY_CFN_NAME, session=session
     )
     if not project_managed_policy_stack_exists:
         project_managed_policy_template = cast(
-            str, deployment_manifest.get_parameter_value("projectPolicy", default=info.PROJECT_POLICY_PATH)
+            str,
+            deployment_manifest.get_parameter_value(
+                "projectPolicy", account_id=account_id, region=region, default=info.PROJECT_POLICY_PATH
+            ),
         )
         project_managed_policy_template = os.path.join(config.OPS_ROOT, project_managed_policy_template)
         if not os.path.exists(project_managed_policy_template):
             raise Exception(f"Unable to find the Project Managed Policy Template: {project_managed_policy_template}")
         _logger.debug(
-            f"Validated the existence of Project Managed Policy Template at:{project_managed_policy_template}"
+            f"Validated the existence of Project Managed Policy Template at: {project_managed_policy_template}"
         )
         _logger.info("Deploying %s", info.PROJECT_MANAGED_POLICY_CFN_NAME)
         services.cfn.deploy_template(
@@ -92,6 +102,7 @@ def deploy_managed_policy_stack(deployment_name: str, deployment_manifest: Deplo
             filename=project_managed_policy_template,
             seedkit_tag=deployment_name,
             parameters={"ProjectName": config.PROJECT.lower(), "DeploymentName": deployment_name},
+            session=session,
         )
 
 
@@ -99,6 +110,8 @@ def destroy_module_stack(
     deployment_name: str,
     group_name: str,
     module_name: str,
+    account_id: str,
+    region: str,
     docker_credentials_secret: Optional[str] = None,
 ) -> None:
     """
@@ -111,12 +124,19 @@ def destroy_module_stack(
         The name of the deployment
     group : str
         The name of the group
-    module : str
+    module_name : str
         The name of the module
+    account_id: str
+        The Account Id where the module is deployed
+    region: str
+        The region where the module is deployed
     """
+    session = SessionManager().get_or_create().get_deployment_session(account_id=account_id, region_name=region)
     module_stack_name, module_role_name = _get_module_stack_names(deployment_name, group_name, module_name)
     # Detach the Project Policy
-    seedkit_stack_exists, seedkit_stack_name, stack_outputs = commands.seedkit_deployed(seedkit_name=config.PROJECT)
+    seedkit_stack_exists, seedkit_stack_name, stack_outputs = commands.seedkit_deployed(
+        seedkit_name=config.PROJECT, session=session
+    )
 
     policies_arn = []
     if seedkit_stack_exists:
@@ -126,7 +146,7 @@ def destroy_module_stack(
 
     # Extract Project Managed policy name
     project_managed_policy_stack_exists, stack_outputs = services.cfn.does_stack_exist(
-        stack_name=info.PROJECT_MANAGED_POLICY_CFN_NAME
+        stack_name=info.PROJECT_MANAGED_POLICY_CFN_NAME, session=session
     )
     if project_managed_policy_stack_exists:
         project_managed_policy_arn = stack_outputs.get("ProjectPolicyARN")
@@ -138,14 +158,16 @@ def destroy_module_stack(
     _logger.debug("module_role_name %s", module_role_name)
 
     for policy_arn in policies_arn:
-        iam.detach_policy_from_role(role_name=module_role_name, policy_arn=policy_arn)
+        iam.detach_policy_from_role(role_name=module_role_name, policy_arn=policy_arn, session=session)
 
     if not codeseeder.EXECUTING_REMOTELY:
-        services.cfn.destroy_stack(module_stack_name)
+        services.cfn.destroy_stack(module_stack_name, session=session)
 
     if docker_credentials_secret:
-        iam.detach_inline_policy_from_role(role_name=module_role_name, policy_name=docker_credentials_secret)
-    iam.delete_role(role_name=module_role_name)
+        iam.detach_inline_policy_from_role(
+            role_name=module_role_name, policy_name=docker_credentials_secret, session=session
+        )
+    iam.delete_role(role_name=module_role_name, session=session)
 
 
 def deploy_module_stack(
@@ -153,6 +175,8 @@ def deploy_module_stack(
     deployment_name: str,
     group_name: str,
     module_name: str,
+    account_id: str,
+    region: str,
     parameters: List[ModuleParameter],
     docker_credentials_secret: Optional[str] = None,
     permission_boundary_arn: Optional[str] = None,
@@ -174,6 +198,10 @@ def deploy_module_stack(
         The name of the module
     parameters : List[ModuleParameter]
         A ModuleParameter object with any necessary parameters from other deployed modules
+    account_id: str
+        The Account Id where the module is deployed
+    region: str
+        The region where the module is deployed
     docker_credentials_secret: str
         OPTIONAL parameter with name of SecrestManager of docker credentials
     permission_boundary_arn: str
@@ -193,7 +221,8 @@ def deploy_module_stack(
         ],
     }
 
-    iam.create_check_iam_role(trust_policy, module_role_name, permission_boundary_arn)
+    session = SessionManager().get_or_create().get_deployment_session(account_id=account_id, region_name=region)
+    iam.create_check_iam_role(trust_policy, module_role_name, permission_boundary_arn, session=session)
 
     group_module_name = f"{group_name}-{module_name}"
 
@@ -229,18 +258,21 @@ def deploy_module_stack(
             filename=module_stack_path,
             seedkit_tag=module_stack_name,
             parameters=stack_parameters,
+            session=session,
         )
 
     # Attaching managed IAM Policies
     _logger.debug("Extracting the Codeseeder Managed policy")
-    seedkit_stack_exists, seedkit_stack_name, stack_outputs = commands.seedkit_deployed(seedkit_name=config.PROJECT)
+    seedkit_stack_exists, seedkit_stack_name, stack_outputs = commands.seedkit_deployed(
+        seedkit_name=config.PROJECT, session=session
+    )
     if seedkit_stack_exists:
         _logger.debug("Seedkit stack exists - %s", seedkit_stack_name)
         seedkit_managed_policy_arn = stack_outputs.get("SeedkitResourcesPolicyArn")
 
     # Extract Project Managed policy name
     project_managed_policy_stack_exists, stack_outputs = services.cfn.does_stack_exist(
-        stack_name=info.PROJECT_MANAGED_POLICY_CFN_NAME
+        stack_name=info.PROJECT_MANAGED_POLICY_CFN_NAME, session=session
     )
 
     _logger.debug("project_managed_policy_output is : %s", stack_outputs)
@@ -251,7 +283,7 @@ def deploy_module_stack(
         raise ValueError("Project Managed Stack is missing the export `ProjectPolicyARN`")
 
     policies = [seedkit_managed_policy_arn, project_managed_policy_arn]
-    policies_attached = iam.attach_policy_to_role(module_role_name, policies)
+    policies_attached = iam.attach_policy_to_role(module_role_name, policies, session=session)
     if policies.sort() == policies_attached.sort():
         _logger.info("Delaying module %s deployment to allow IAM Roles and Policies to take effect", group_module_name)
         time.sleep(10)  # on first deployment roles and policy attachments need time to take effect
@@ -279,17 +311,22 @@ def deploy_module_stack(
 
     if docker_credentials_secret:
         iam.attach_inline_policy(
-            role_name=module_role_name,
-            policy_body=policy_body,
-            policy_name=docker_credentials_secret,
+            role_name=module_role_name, policy_body=policy_body, policy_name=docker_credentials_secret, session=session
         )
 
 
-def deploy_seedkit() -> None:
+def deploy_seedkit(account_id: str, region: str) -> None:
     """
     deploy_seedkit
         Accessor method to CodeSeeder to deploy the SeedKit if not deployed
-    """
-    stack_exists, _, _ = commands.seedkit_deployed(seedkit_name=config.PROJECT)
+
+    Parameters
+    ----------
+    account_id: str
+        The Account Id where the module is deployed
+    region: str
+        The region wher"""
+    session = SessionManager().get_or_create().get_deployment_session(account_id=account_id, region_name=region)
+    stack_exists, _, _ = commands.seedkit_deployed(seedkit_name=config.PROJECT, session=session)
     if not stack_exists:
-        commands.deploy_seedkit(seedkit_name=config.PROJECT)
+        commands.deploy_seedkit(seedkit_name=config.PROJECT, session=session)
diff --git a/seedfarmer/mgmt/deploy_utils.py b/seedfarmer/mgmt/deploy_utils.py
index a4b98950..fb40cdbe 100644
--- a/seedfarmer/mgmt/deploy_utils.py
+++ b/seedfarmer/mgmt/deploy_utils.py
@@ -13,33 +13,102 @@
 #    limitations under the License.
 
 import logging
-from typing import Any, Dict, List, Optional
+from threading import Lock
+from typing import Any, Dict, List, Optional, Set, Tuple, cast
 
 import yaml
+from boto3 import Session
 
 import seedfarmer.mgmt.module_info as mi
 from seedfarmer.models.manifests import DeploymentManifest, DeploySpec, ModuleManifest, ModulesManifest
+from seedfarmer.services.session_manager import SessionManager
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
 
-def generate_deployment_cache(deployment_name: str) -> Optional[Dict[str, Any]]:
+class ModuleInfoIndex(object):
+    def __init__(self) -> None:
+        super().__init__()
+        self._index: Dict[Tuple[str, str, str, str], Dict[str, Any]] = dict()
+        self._groups: Set[str] = set()
+        self._groups_idx: Dict[str, Set[Tuple[str, str, str, str]]] = dict()
+        self._module_names_idx: Dict[Tuple[str, str], Tuple[str, str, str, str]] = dict()
+        self._lock = Lock()
+
+    @property
+    def groups(self) -> Set[str]:
+        return self._groups
+
+    def get_module_info(
+        self, *, group: str, account_id: str, region: str, module_name: str
+    ) -> Optional[Dict[str, Any]]:
+        return self._index.get((group, account_id, region, module_name))
+
+    def index_module_info(
+        self, *, group: str, account_id: str, region: str, module_name: str, module_info: Dict[str, Any]
+    ) -> None:
+        with self._lock:
+            module_info_key = (group, account_id, region, module_name)
+            current_module_info = self._index.get(module_info_key, {})
+            self._index[module_info_key] = {**current_module_info, **module_info}
+            self._groups.add(group)
+            current_group_keys = self._groups_idx.get(group, set())
+            current_group_keys.add(module_info_key)
+            self._groups_idx[group] = current_group_keys
+            self._module_names_idx[(group, module_name)] = module_info_key
+
+    def get_keys_for_group(self, group: str) -> List[Dict[str, str]]:
+        return [
+            {"group": m[0], "account_id": m[1], "region": m[2], "module_name": m[3]}
+            for m in self._groups_idx.get(group, [])
+        ]
+
+    def get_key_for_module_name(self, group: str, module_name: str) -> Dict[str, str]:
+        m = self._module_names_idx.get((group, module_name), None)
+        if m is not None:
+            return {"group": m[0], "account_id": m[1], "region": m[2], "module_name": m[3]}
+        else:
+            _logger.debug("module_names_idx: %s", self._module_names_idx)
+            return {"group": "", "account_id": "", "region": "", "module_name": ""}
+
+
+def populate_module_info_index(deployment_manifest: DeploymentManifest) -> ModuleInfoIndex:
     """
-    generate_deployment_cache
-        Fetch all parameters for the deployemnt currently stored
+    populate_module_info_index
+        Fetch all info for the deployemnt currently stored, across all Target accounts and regions
 
     Parameters
     ----------
-    deployment_name: str
-        The name of the deployment
+    deployment_manifest: DeploymentManifest
+        The DeploymentManifest, including TargetAccount and Region mappings
 
     Returns
     -------
-    Dict[str,Any]
-        A dictionary representation of what is in the store (SSM for DDB) of the modules deployed
+    ModuleInfoIndex
+        An index of Module info for all Target accounts and regions
     """
-    c = mi.get_parameter_data_cache(deployment=deployment_name)
-    return c if len(c) > 0 else None
+    module_info_index = ModuleInfoIndex()
+    # TODO: Enable multi-threaded query of account/region pairs
+    for account_region in deployment_manifest.target_accounts_regions:
+        account_id = account_region["account_id"]
+        region = account_region["region"]
+        session = SessionManager().get_or_create().get_deployment_session(account_id=account_id, region_name=region)
+        module_info = mi.get_parameter_data_cache(deployment=deployment_manifest.name, session=session)
+        for key, value in module_info.items():
+            key_parts = key.split("/")[1:]
+            _logger.debug("key: %s key_parts: %s", key, key_parts)
+            if len(key_parts) < 4:
+                continue
+            group = key_parts[2]
+            module_name = key_parts[3]
+            module_info_index.index_module_info(
+                group=group,
+                account_id=account_id,
+                region=region,
+                module_name=module_name,
+                module_info={key: value},
+            )
+    return module_info_index
 
 
 def write_deployed_deployment_manifest(deployment_manifest: DeploymentManifest) -> None:
@@ -55,12 +124,12 @@ def write_deployed_deployment_manifest(deployment_manifest: DeploymentManifest)
     deployment_name = deployment_manifest.name
     for group in deployment_manifest.groups:
         delattr(group, "modules")
-    mi.write_deployed_deployment_manifest(deployment=deployment_name, data=deployment_manifest.dict())
+    session = SessionManager().get_or_create().toolchain_session
+    mi.write_deployed_deployment_manifest(deployment=deployment_name, data=deployment_manifest.dict(), session=session)
 
 
 def generate_deployed_manifest(
     deployment_name: str,
-    deployment_params_cache: Optional[Dict[str, Any]] = None,
     skip_deploy_spec: bool = False,
 ) -> Optional[DeploymentManifest]:
     """
@@ -78,22 +147,27 @@ def generate_deployed_manifest(
     Optional[DeploymentManifest]
         The hydrated DeploymentManifest object of deployed modules
     """
-    dep_manifest_dict = mi.get_deployed_deployment_manifest(deployment_name, deployment_params_cache)
+    session_manager = SessionManager().get_or_create()
+    dep_manifest_dict = mi.get_deployed_deployment_manifest(deployment_name, session=session_manager.toolchain_session)
     if dep_manifest_dict is None:
         # No successful deployments, just use what was last requested
-        dep_manifest_dict = mi.get_deployment_manifest(deployment_name, deployment_params_cache)
-    destroy_manifest = None
+        dep_manifest_dict = mi.get_deployment_manifest(deployment_name, session=session_manager.toolchain_session)
+    deployed_manifest = None
     if dep_manifest_dict:
+        deployed_manifest = DeploymentManifest(**dep_manifest_dict)
+        module_info_index = populate_module_info_index(deployment_manifest=deployed_manifest)
         for module_group in dep_manifest_dict["groups"] if dep_manifest_dict["groups"] else []:
+            # TODO: Investigate whether this group manifest in SSM is needed, for now load from index
+            # from_ssm = mi.get_group_manifest(deployment_name, group_name, module_info_index.get_keys_for_group())
             group_name = module_group["name"]
-            from_ssm = mi.get_group_manifest(deployment_name, group_name, deployment_params_cache)
-            if from_ssm:
-                module_group["modules"] = from_ssm["modules"]
-                if not skip_deploy_spec:
-                    for module in module_group["modules"] if module_group["modules"] else []:
-                        module["deploy_spec"] = mi.get_deployspec(deployment_name, group_name, module["name"])
-        destroy_manifest = DeploymentManifest(**dep_manifest_dict)
-    return destroy_manifest
+            module_group["modules"] = _populate_group_modules_from_index(
+                deployment_name=deployment_name,
+                group_name=group_name,
+                module_info_index=module_info_index,
+                skip_deploy_spec=skip_deploy_spec,
+            )
+        deployed_manifest.groups = [ModulesManifest(**group) for group in dep_manifest_dict["groups"]]
+    return deployed_manifest
 
 
 def need_to_build(
@@ -137,23 +211,43 @@ def need_to_build(
     d = deployment_name
     g = group_name
     m = module_manifest.name if module_manifest.name else ""
-    module_bundle_md5 = module_manifest.bundle_md5 if module_manifest.bundle_md5 else []
-
+    module_bundle_md5 = module_manifest.bundle_md5 if module_manifest.bundle_md5 else ""
+    session = (
+        SessionManager()
+        .get_or_create()
+        .get_deployment_session(
+            account_id=cast(str, module_manifest.get_target_account_id()),
+            region_name=cast(str, module_manifest.target_region),
+        )
+    )
     if (
-        mi.does_md5_match(d, g, m, str(module_bundle_md5), mi.ModuleConst.BUNDLE, deployment_params_cache)
-        and mi.does_md5_match(d, g, m, module_deployspec_md5, mi.ModuleConst.DEPLOYSPEC, deployment_params_cache)
-        and mi.does_md5_match(d, g, m, module_manifest_md5, mi.ModuleConst.MANIFEST, deployment_params_cache)
+        mi.does_md5_match(
+            d, g, m, str(module_bundle_md5), mi.ModuleConst.BUNDLE, deployment_params_cache, session=session
+        )
+        and mi.does_md5_match(
+            d, g, m, module_deployspec_md5, mi.ModuleConst.DEPLOYSPEC, deployment_params_cache, session=session
+        )
+        and mi.does_md5_match(
+            d, g, m, module_manifest_md5, mi.ModuleConst.MANIFEST, deployment_params_cache, session=session
+        )
     ):
         return False
     else:
         if not dryrun:
-            mi.write_deployspec(deployment=d, group=g, module=m, data=module_deployspec.dict())
-            mi.write_module_manifest(deployment=d, group=g, module=m, data=module_manifest.dict())
+            mi.write_deployspec(deployment=d, group=g, module=m, data=module_deployspec.dict(), session=session)
+            mi.write_module_manifest(deployment=d, group=g, module=m, data=module_manifest.dict(), session=session)
             mi.write_module_md5(
-                deployment=d, group=g, module=m, hash=module_deployspec_md5, type=mi.ModuleConst.DEPLOYSPEC
+                deployment=d,
+                group=g,
+                module=m,
+                hash=module_deployspec_md5,
+                type=mi.ModuleConst.DEPLOYSPEC,
+                session=session,
             )
-            mi.write_module_md5(deployment=d, group=g, module=m, hash=module_manifest_md5, type=mi.ModuleConst.MANIFEST)
-            mi.remove_module_md5(deployment=d, group=g, module=m, type=mi.ModuleConst.BUNDLE)
+            mi.write_module_md5(
+                deployment=d, group=g, module=m, hash=module_manifest_md5, type=mi.ModuleConst.MANIFEST, session=session
+            )
+            mi.remove_module_md5(deployment=d, group=g, module=m, type=mi.ModuleConst.BUNDLE, session=session)
         return True
 
 
@@ -173,9 +267,7 @@ def write_group_manifest(deployment_name: str, group_manifest: ModulesManifest)
     mi.write_group_manifest(deployment=deployment_name, group=g, data=group_manifest.dict())
 
 
-def filter_deploy_destroy(
-    apply_manifest: DeploymentManifest, deployment_params_cache: Optional[Dict[str, Any]] = None
-) -> DeploymentManifest:
+def filter_deploy_destroy(apply_manifest: DeploymentManifest, module_info_index: ModuleInfoIndex) -> DeploymentManifest:
     """
     This method takes a populated DeploymentManifest object based off the requested deployment.
     It compares this requested deployment with what is currently deployed.  If there are groups or
@@ -186,74 +278,97 @@ def filter_deploy_destroy(
     ----------
     apply_manifest : DeploymentManifest
         The DeploymentManifest object based off a deployment manifest of a requested deployment.
+    module_info_index: ModuleInfoIndex
+        The index of existing module info for all Target accounts and regions
 
     Returns
     -------
     DeploymentManifest
         A populated DeploymentManifest object with the modules needed to be destroyed
     """
-    dep_name = apply_manifest.name
+    deployment_name = apply_manifest.name
 
     destroy_manifest = apply_manifest.copy()
     delattr(destroy_manifest, "groups")
-    destroy_group_list = _populate_groups_to_remove(dep_name, apply_manifest.groups)
+    destroy_group_list = _populate_groups_to_remove(deployment_name, apply_manifest.groups, module_info_index)
     destroy_manifest.groups = destroy_group_list
     for group in apply_manifest.groups:
-        destroy_module_list = _populate_modules_to_remove(dep_name, group.name, group.modules, deployment_params_cache)
+        destroy_module_list = _populate_modules_to_remove(deployment_name, group.name, group.modules, module_info_index)
         if destroy_module_list:
             to_destroy = ModulesManifest(name=group.name, path=group.path, modules=destroy_module_list)
             destroy_manifest.groups.append(to_destroy)
+    destroy_manifest.validate_and_set_module_defaults()
     return destroy_manifest
 
 
+def _populate_group_modules_from_index(
+    deployment_name: str, group_name: str, module_info_index: ModuleInfoIndex, skip_deploy_spec: bool
+) -> List[Dict[str, Any]]:
+    modules = []
+    for group_key in module_info_index.get_keys_for_group(group_name):
+        deployment_params_cache = module_info_index.get_module_info(**group_key)
+        module_manifest = mi.get_module_manifest(
+            deployment_name, group_name, group_key["module_name"], deployment_params_cache
+        )
+        if module_manifest is not None:
+            if not skip_deploy_spec:
+                module_manifest["deploy_spec"] = mi.get_deployspec(
+                    deployment_name, group_name, group_key["module_name"], deployment_params_cache
+                )
+            modules.append(module_manifest)
+    return modules
+
+
 def _populate_groups_to_remove(
-    dep_name: str, apply_groups: List[ModulesManifest], deployment_params_cache: Optional[Dict[str, Any]] = None
+    deployment_name: str, apply_groups: List[ModulesManifest], module_info_index: ModuleInfoIndex
 ) -> List[ModulesManifest]:
     set_requested_groups = set([g.name for g in apply_groups])
-    set_deployed_groups = set(mi.get_all_groups(dep_name))
+    set_deployed_groups = module_info_index.groups
     destroy_groups = list(sorted(set_deployed_groups - set_requested_groups))
     _logger.debug("Groups already deployed that will be DESTROYED : %s", destroy_groups)
     destroy_group_list = []
     for destroy_group in destroy_groups:
-        group_manifests = mi.get_group_manifest(dep_name, destroy_group, deployment_params_cache)
-        if group_manifests:
-            valid_modules = []
-            for module in group_manifests["modules"]:
-                mod_manifest = mi.get_module_manifest(dep_name, destroy_group, module["name"], deployment_params_cache)
-                if mod_manifest:
-                    module["deploy_spec"] = mi.get_deployspec(
-                        dep_name, destroy_group, module["name"], deployment_params_cache
-                    )
-                    module["parameters"] = mod_manifest["parameters"]
-                    valid_modules.append(module)
-            if valid_modules:
-                group_manifests["modules"] = valid_modules
-            destroy_group_list.append(ModulesManifest(**group_manifests))
+        # TODO: Investigate whether it's necessary to read the existing group manifest from SSM
+        # group_manifest = mi.get_group_manifest(deployment_name, destroy_group, deployment_params_cache)
+        group_manifest = {
+            "name": destroy_group,
+            "modules": _populate_group_modules_from_index(
+                deployment_name=deployment_name,
+                group_name=destroy_group,
+                module_info_index=module_info_index,
+                skip_deploy_spec=False,
+            ),
+        }
+        destroy_group_list.append(ModulesManifest(**group_manifest))
     return destroy_group_list
 
 
 def _populate_modules_to_remove(
-    dep_name: str,
-    group: str,
-    apply_modules: List[ModuleManifest],
-    deployment_params_cache: Optional[Dict[str, Any]] = None,
+    deployment_name: str, group: str, apply_modules: List[ModuleManifest], module_info_index: ModuleInfoIndex
 ) -> List[ModuleManifest]:
     set_requests_modules = set([m.name for m in apply_modules])
-    set_deployed_modules = set(mi.get_deployed_modules(dep_name, group, deployment_params_cache))
+    set_deployed_modules = set([m["module_name"] for m in module_info_index.get_keys_for_group(group)])
     destroy_modules = list(sorted(set_deployed_modules - set_requests_modules))
     _logger.debug("Modules of Group %s already deployed that will be DESTROYED: %s", group, destroy_modules)
 
     destroy_module_list = []
     for destroy_module in destroy_modules:
-        mod_manifest = mi.get_module_manifest(dep_name, group, destroy_module, deployment_params_cache)
-        if mod_manifest:
-            mod_manifest["deploy_spec"] = mi.get_deployspec(dep_name, group, destroy_module, deployment_params_cache)
-            destroy_module_list.append(ModuleManifest(**mod_manifest))
+        deployment_params_cache = module_info_index.get_module_info(
+            **module_info_index.get_key_for_module_name(group=group, module_name=destroy_module)
+        )
+        module_manifest = mi.get_module_manifest(deployment_name, group, destroy_module, deployment_params_cache)
+        if module_manifest is not None:
+            module_manifest["deploy_spec"] = mi.get_deployspec(
+                deployment_name, group, destroy_module, deployment_params_cache
+            )
+            destroy_module_list.append(ModuleManifest(**module_manifest))
     return destroy_module_list
 
 
-def update_deployspec(deployment: str, group: str, module: str, module_path: str) -> None:
+def update_deployspec(
+    deployment: str, group: str, module: str, module_path: str, session: Optional[Session] = None
+) -> None:
     d_path = mi._get_deployspec_path(module_path=module_path)
     with open(d_path) as deploymentspec:
         new_spec = DeploySpec(**yaml.safe_load(deploymentspec))
-    mi.write_deployspec(deployment, group, module, new_spec.dict())
+    mi.write_deployspec(deployment, group, module, new_spec.dict(), session=session)
diff --git a/seedfarmer/mgmt/module_info.py b/seedfarmer/mgmt/module_info.py
index 82aeb800..b544fe8c 100644
--- a/seedfarmer/mgmt/module_info.py
+++ b/seedfarmer/mgmt/module_info.py
@@ -17,6 +17,8 @@
 from enum import Enum
 from typing import Any, Dict, List, Optional, Tuple
 
+from boto3 import Session
+
 from seedfarmer import config
 from seedfarmer.services import _secrets_manager as secrets
 from seedfarmer.services import _ssm as store
@@ -34,7 +36,7 @@ class ModuleConst(Enum):
     DEPLOYED = "deployed"
 
 
-def get_parameter_data_cache(deployment: str) -> Dict[str, Any]:
+def get_parameter_data_cache(deployment: str, session: Session) -> Dict[str, Any]:
     """
     get_parameter_data_cache
         Fetch the deployment parameters stored
@@ -42,20 +44,26 @@ def get_parameter_data_cache(deployment: str) -> Dict[str, Any]:
     Parameters
     ----------
     deployment : str
-       Name of the deployment
+        Name of the deployment
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Dict[str,Any]
         A dictionary representation of what is in the store (SSM for DDB) of the modules deployed
     """
-    return store.get_all_parameter_data_by_path(_deployment_key(deployment))
+    return store.get_all_parameter_data_by_path(prefix=_deployment_key(deployment), session=session)
 
 
-def get_all_deployments() -> List[str]:
+def get_all_deployments(session: Optional[Session] = None) -> List[str]:
     """
     get_all_deployments
         Get all names of curently deployments
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
@@ -65,7 +73,7 @@ def get_all_deployments() -> List[str]:
     prefix = f"/{config.PROJECT}"
     _filter = f"{ModuleConst.MANIFEST.value}"
     ret = set()
-    params = store.list_parameters_with_filter(prefix, _filter)
+    params = store.list_parameters_with_filter(prefix=prefix, contains_string=_filter, session=session)
     for param in params:
         _logger.debug(param)
         p = param.split("/")[3]
@@ -74,7 +82,9 @@ def get_all_deployments() -> List[str]:
     return list(ret)
 
 
-def get_all_groups(deployment: str, params_cache: Optional[Dict[str, Any]] = None) -> List[str]:
+def get_all_groups(
+    deployment: str, params_cache: Optional[Dict[str, Any]] = None, session: Optional[Session] = None
+) -> List[str]:
     """
     get_all_groups
         Get all groups in a deployment
@@ -82,9 +92,11 @@ def get_all_groups(deployment: str, params_cache: Optional[Dict[str, Any]] = Non
     Parameters
     ----------
     deployment : str
-       The name of the deployment
+        The name of the deployment
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
@@ -94,7 +106,11 @@ def get_all_groups(deployment: str, params_cache: Optional[Dict[str, Any]] = Non
     prefix = _deployment_key(deployment)
     _filter = f"{ModuleConst.MANIFEST.value}"
     ret = set()
-    params = params_cache.keys() if params_cache else store.list_parameters_with_filter(prefix, _filter)
+    params = (
+        params_cache.keys()
+        if params_cache
+        else store.list_parameters_with_filter(prefix=prefix, contains_string=_filter, session=session)
+    )
     for param in params:
         p = param.split("/")[3]
         if ModuleConst.MANIFEST.value not in p:
@@ -102,7 +118,9 @@ def get_all_groups(deployment: str, params_cache: Optional[Dict[str, Any]] = Non
     return list(ret)
 
 
-def get_deployed_modules(deployment: str, group: str, params_cache: Optional[Dict[str, Any]] = None) -> List[str]:
+def get_deployed_modules(
+    deployment: str, group: str, params_cache: Optional[Dict[str, Any]] = None, session: Optional[Session] = None
+) -> List[str]:
     """
     get_deployed_modules
         Get all modules deployed in a group of a deployment
@@ -114,7 +132,9 @@ def get_deployed_modules(deployment: str, group: str, params_cache: Optional[Dic
     group : str
         The name of the group
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
@@ -123,14 +143,18 @@ def get_deployed_modules(deployment: str, group: str, params_cache: Optional[Dic
     """
     prefix = f"/{config.PROJECT}/{deployment}/{group}"
     _filter = f"{ModuleConst.MD5.value}/{ModuleConst.BUNDLE.value}"
-    params = params_cache.keys() if params_cache else store.list_parameters_with_filter(prefix, _filter)
+    params = (
+        params_cache.keys() if params_cache else store.list_parameters_with_filter(prefix, _filter, session=session)
+    )
     ret: List[str] = []
     for param in params:
         ret.append(param.split("/")[4]) if _filter in param else None
     return ret
 
 
-def get_module_md5(deployment: str, group: str, module: str, type: ModuleConst) -> Optional[str]:
+def get_module_md5(
+    deployment: str, group: str, module: str, type: ModuleConst, session: Optional[Session] = None
+) -> Optional[str]:
     """
     get_module_md5
         Get the md5 of a currently deployed module.
@@ -149,6 +173,8 @@ def get_module_md5(deployment: str, group: str, module: str, type: ModuleConst)
         The name of the module
     type : ModuleConst
         An emumeration value of the md5 of the module you want
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
@@ -156,12 +182,16 @@ def get_module_md5(deployment: str, group: str, module: str, type: ModuleConst)
         The md5 hash as a string
     """
     name = _md5_module_key(deployment, group, module, type)
-    p = store.get_parameter_if_exists(name=name)
+    p = store.get_parameter_if_exists(name=name, session=session)
     return p["hash"] if p else None
 
 
 def get_module_metadata(
-    deployment: str, group: str, module: str, params_cache: Optional[Dict[str, Any]] = None
+    deployment: str,
+    group: str,
+    module: str,
+    params_cache: Optional[Dict[str, Any]] = None,
+    session: Optional[Session] = None,
 ) -> Optional[Dict[str, Any]]:
     """
     get_module_metadata
@@ -176,18 +206,24 @@ def get_module_metadata(
     module : str
         The name of the module
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Optional[Dict[str, Any]]
         A dict containg the metadata of the module requested
     """
-    return _fetch_helper(_metadata_key(deployment, group, module), params_cache)
+    return _fetch_helper(_metadata_key(deployment, group, module), params_cache, session=session)
 
 
 def get_module_manifest(
-    deployment: str, group: str, module: str, params_cache: Optional[Dict[str, Any]] = None
+    deployment: str,
+    group: str,
+    module: str,
+    params_cache: Optional[Dict[str, Any]] = None,
+    session: Optional[Session] = None,
 ) -> Optional[Dict[str, Any]]:
     """
     get_module_manifest
@@ -202,18 +238,24 @@ def get_module_manifest(
     module : str
         The name of the module
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Optional[Dict[str, Any]]
         A dict containg the manifest of the module requested
     """
-    return _fetch_helper(_manifest_key(deployment, group, module), params_cache)
+    return _fetch_helper(_manifest_key(deployment, group, module), params_cache, session=session)
 
 
 def get_deployspec(
-    deployment: str, group: str, module: str, params_cache: Optional[Dict[str, Any]] = None
+    deployment: str,
+    group: str,
+    module: str,
+    params_cache: Optional[Dict[str, Any]] = None,
+    session: Optional[Session] = None,
 ) -> Optional[Dict[str, Any]]:
     """
     get_deployspec
@@ -228,17 +270,21 @@ def get_deployspec(
     module : str
         The name of the module
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Optional[Dict[str, Any]]
         A dict containg the deployspec of the module requested
     """
-    return _fetch_helper(_deployspec_key(deployment, group, module), params_cache)
+    return _fetch_helper(_deployspec_key(deployment, group, module), params_cache, session=session)
 
 
-def get_deployment_manifest(deployment: str, params_cache: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
+def get_deployment_manifest(
+    deployment: str, params_cache: Optional[Dict[str, Any]] = None, session: Optional[Session] = None
+) -> Optional[Dict[str, Any]]:
     """
     get_deployment_manifest
         Get the deployment manifest stored for a deployment
@@ -248,18 +294,20 @@ def get_deployment_manifest(deployment: str, params_cache: Optional[Dict[str, An
     deployment : str
         The name of the deployment
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Optional[Dict[str, Any]]
         A dict of the deployment
     """
-    return _fetch_helper(_deployment_manifest_key(deployment), params_cache)
+    return _fetch_helper(_deployment_manifest_key(deployment), params_cache, session=session)
 
 
 def get_deployed_deployment_manifest(
-    deployment: str, params_cache: Optional[Dict[str, Any]] = None
+    deployment: str, params_cache: Optional[Dict[str, Any]] = None, session: Optional[Session] = None
 ) -> Optional[Dict[str, Any]]:
     """
     get_deployed_deployment_manifest
@@ -270,17 +318,19 @@ def get_deployed_deployment_manifest(
     deployment : str
         The name of the deployment
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Optional[Dict[str, Any]]
         A dict of the deployment
     """
-    return _fetch_helper(_deployed_deployment_manifest_key(deployment), params_cache)
+    return _fetch_helper(_deployed_deployment_manifest_key(deployment), params_cache, session=session)
 
 
-def get_secret_secrets_manager(name: str) -> Dict[str, Any]:
+def get_secret_secrets_manager(name: str, session: Optional[Session] = None) -> Dict[str, Any]:
     """
     get_secret_secrets_manager Fetches the data in the Secrets Manager with the key (name) given
 
@@ -288,17 +338,19 @@ def get_secret_secrets_manager(name: str) -> Dict[str, Any]:
     ----------
     name : str
         The name of the Secret in the Secrets manager
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Dict[str, Any]
         The object in the Secrets Manager
     """
-    return secrets.get_secret_secrets_manager(name=name)
+    return secrets.get_secret_secrets_manager(name=name, session=session)
 
 
 def get_group_manifest(
-    deployment: str, group: str, params_cache: Optional[Dict[str, Any]] = None
+    deployment: str, group: str, params_cache: Optional[Dict[str, Any]] = None, session: Optional[Session] = None
 ) -> Optional[Dict[str, Any]]:
     """
     get_group_manifest
@@ -311,14 +363,16 @@ def get_group_manifest(
     group : str
         The name of the group
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
     Optional[Dict[str, Any]]
         A dict of the group in a deployment
     """
-    return _fetch_helper(_group_key(deployment, group), params_cache)
+    return _fetch_helper(_group_key(deployment, group), params_cache, session=session)
 
 
 def does_md5_match(
@@ -328,6 +382,7 @@ def does_md5_match(
     hash: str,
     type: ModuleConst,
     deployment_params_cache: Optional[Dict[str, Any]] = None,
+    session: Optional[Session] = None,
 ) -> bool:
     """
     does_md5_match
@@ -346,7 +401,9 @@ def does_md5_match(
     type : ModuleConst
         An emumeration value of the md5 of the module you want
     params_cache : Dict[str,Any], optional
-       A populated dict with the key  of the parameter stored and its value
+        A populated dict with the key  of the parameter stored and its value
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
 
     Returns
     -------
@@ -355,7 +412,7 @@ def does_md5_match(
     """
     name = _md5_module_key(deployment, group, module, type)
     if not deployment_params_cache:
-        p = store.get_parameter_if_exists(name=name)
+        p = store.get_parameter_if_exists(name=name, session=session)
     else:
         p = deployment_params_cache[name] if name in deployment_params_cache.keys() else None
     if not p:
@@ -366,7 +423,7 @@ def does_md5_match(
         return True
 
 
-def does_module_exist(deployment: str, group: str, module: str) -> bool:
+def does_module_exist(deployment: str, group: str, module: str, session: Optional[Session] = None) -> bool:
     """
     does_module_exist
         Checks if a module of a group is deployed
@@ -379,15 +436,22 @@ def does_module_exist(deployment: str, group: str, module: str) -> bool:
         The name of the group
     module : str
         The name of the module
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
+
     Returns
     -------
     bool
         Whether the module is deployed
     """
-    return store.does_parameter_exist(name=_md5_module_key(deployment, group, module, ModuleConst.BUNDLE))
+    return store.does_parameter_exist(
+        name=_md5_module_key(deployment, group, module, ModuleConst.BUNDLE), session=session
+    )
 
 
-def write_metadata(deployment: str, group: str, module: str, data: Dict[str, Any]) -> None:
+def write_metadata(
+    deployment: str, group: str, module: str, data: Dict[str, Any], session: Optional[Session] = None
+) -> None:
     """
     write_metadata
         Persists the medadata of a deployed module
@@ -402,14 +466,16 @@ def write_metadata(deployment: str, group: str, module: str, data: Dict[str, Any
         The name of the module
     data : Dict[str, Any]
         The metadata of the module
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.put_parameter(name=_metadata_key(deployment, group, module), obj=data)
+    store.put_parameter(name=_metadata_key(deployment, group, module), obj=data, session=session)
 
 
-def write_group_manifest(deployment: str, group: str, data: Dict[str, Any]) -> None:
+def write_group_manifest(deployment: str, group: str, data: Dict[str, Any], session: Optional[Session] = None) -> None:
     """
     write_group_manifest
-         Persists the manifest of a deployed group
+        Persists the manifest of a deployed group
 
     Parameters
     ----------
@@ -419,11 +485,15 @@ def write_group_manifest(deployment: str, group: str, data: Dict[str, Any]) -> N
         The name of the group
     data : Dict[str, Any]
         The metadat of the module
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.put_parameter(name=_group_key(deployment, group), obj=data)
+    store.put_parameter(name=_group_key(deployment, group), obj=data, session=session)
 
 
-def write_module_manifest(deployment: str, group: str, module: str, data: Dict[str, Any]) -> None:
+def write_module_manifest(
+    deployment: str, group: str, module: str, data: Dict[str, Any], session: Optional[Session] = None
+) -> None:
     """
     write_module_manifest
         Persists the manifest of a deployed module
@@ -438,11 +508,15 @@ def write_module_manifest(deployment: str, group: str, module: str, data: Dict[s
         The name of the module
     data : Dict[str, Any]
         A dict of the data to be persisted
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.put_parameter(name=_manifest_key(deployment, group, module), obj=data)
+    store.put_parameter(name=_manifest_key(deployment, group, module), obj=data, session=session)
 
 
-def write_deployspec(deployment: str, group: str, module: str, data: Dict[str, Any]) -> None:
+def write_deployspec(
+    deployment: str, group: str, module: str, data: Dict[str, Any], session: Optional[Session] = None
+) -> None:
     """
     write_deployspec
         Persists the deployspec of a deployed module
@@ -457,11 +531,15 @@ def write_deployspec(deployment: str, group: str, module: str, data: Dict[str, A
         The name of the module
     data : Dict[str, Any]
         A dict of the data to be persisted
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.put_parameter(name=_deployspec_key(deployment, group, module), obj=data)
+    store.put_parameter(name=_deployspec_key(deployment, group, module), obj=data, session=session)
 
 
-def write_module_md5(deployment: str, group: str, module: str, hash: str, type: ModuleConst) -> None:
+def write_module_md5(
+    deployment: str, group: str, module: str, hash: str, type: ModuleConst, session: Optional[Session] = None
+) -> None:
     """
     write_module_md5
         Persists the md5 of a module.
@@ -482,11 +560,13 @@ def write_module_md5(deployment: str, group: str, module: str, hash: str, type:
         The md5 has of the data
     type : ModuleConst
         An emumeration value of the md5 of the module this is
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.put_parameter(name=_md5_module_key(deployment, group, module, type), obj={"hash": hash})
+    store.put_parameter(name=_md5_module_key(deployment, group, module, type), obj={"hash": hash}, session=session)
 
 
-def write_deployment_manifest(deployment: str, data: Dict[str, Any]) -> None:
+def write_deployment_manifest(deployment: str, data: Dict[str, Any], session: Optional[Session] = None) -> None:
     """
     write_deployment
         Persists the deployment manifest
@@ -497,13 +577,17 @@ def write_deployment_manifest(deployment: str, data: Dict[str, Any]) -> None:
         The name of the deployment
     data : Dict[str, Any]
         A dict of the deployment manifest
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
     if _logger.isEnabledFor(logging.DEBUG):
         _logger.debug("Writing to %s values %s", _deployment_manifest_key(deployment), data)
-    store.put_parameter(name=_deployment_manifest_key(deployment), obj=data)
+    store.put_parameter(name=_deployment_manifest_key(deployment), obj=data, session=session)
 
 
-def write_deployed_deployment_manifest(deployment: str, data: Dict[str, Any]) -> None:
+def write_deployed_deployment_manifest(
+    deployment: str, data: Dict[str, Any], session: Optional[Session] = None
+) -> None:
     """
     write_deployed_deployment_manifest
         Persists the deployment manifest once all modules have been deployed
@@ -514,14 +598,16 @@ def write_deployed_deployment_manifest(deployment: str, data: Dict[str, Any]) ->
         The name of the deployment
     data : Dict[str, Any]
         A dict of the deployment manifest
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
     key = _deployed_deployment_manifest_key(deployment)
     _logger.debug("Writing to %s value %s", key, data)
 
-    store.put_parameter(name=key, obj=data)
+    store.put_parameter(name=key, obj=data, session=session)
 
 
-def remove_module_info(deployment: str, group: str, module: str) -> None:
+def remove_module_info(deployment: str, group: str, module: str, session: Optional[Session] = None) -> None:
     """
     remove_module_info
         Delete all persisted data of a module in a group of a deployment
@@ -534,11 +620,13 @@ def remove_module_info(deployment: str, group: str, module: str) -> None:
         The name of the group
     module : str
         The name of the module
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.delete_parameters(parameters=_all_module_keys(deployment, group, module))
+    store.delete_parameters(parameters=_all_module_keys(deployment, group, module), session=session)
 
 
-def remove_group_info(deployment: str, group: str) -> None:
+def remove_group_info(deployment: str, group: str, session: Optional[Session] = None) -> None:
     """
     remove_group_info
         Delete all persisted data of a group of a deployment
@@ -549,11 +637,15 @@ def remove_group_info(deployment: str, group: str) -> None:
         The name of the deployment
     group : str
         The name of the group
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.delete_parameters(parameters=(_all_group_keys(deployment, group)))
+    store.delete_parameters(parameters=(_all_group_keys(deployment, group)), session=session)
 
 
-def remove_module_md5(deployment: str, group: str, module: str, type: ModuleConst) -> None:
+def remove_module_md5(
+    deployment: str, group: str, module: str, type: ModuleConst, session: Optional[Session] = None
+) -> None:
     """
     remove_module_md5
         Delete the md5 hash persisted of a module.
@@ -571,12 +663,14 @@ def remove_module_md5(deployment: str, group: str, module: str, type: ModuleCons
     module : str
         The name of the module
     type : ModuleConst
-         An emumeration value of the md5 you want to delete
+        An emumeration value of the md5 you want to delete
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.delete_parameters(parameters=[_md5_module_key(deployment, group, module, type)])
+    store.delete_parameters(parameters=[_md5_module_key(deployment, group, module, type)], session=session)
 
 
-def remove_deployment_manifest(deployment: str) -> None:
+def remove_deployment_manifest(deployment: str, session: Optional[Session] = None) -> None:
     """
     remove_deployment_manifest
         Delete the deployment manifest persisted of a deployment
@@ -585,11 +679,13 @@ def remove_deployment_manifest(deployment: str) -> None:
     ----------
     deployment : str
         The name of the deployment
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.delete_parameters(parameters=[_deployment_manifest_key(deployment)])
+    store.delete_parameters(parameters=[_deployment_manifest_key(deployment)], session=session)
 
 
-def remove_deployed_deployment_manifest(deployment: str) -> None:
+def remove_deployed_deployment_manifest(deployment: str, session: Optional[Session] = None) -> None:
     """
     remove_deployment_manifest
         Delete the deployment manifest persisted of a deployment
@@ -598,8 +694,10 @@ def remove_deployed_deployment_manifest(deployment: str) -> None:
     ----------
     deployment : str
         The name of the deployment
+    session: Session, optional
+        The boto3.Session to use to for SSM Parameter queries, default None
     """
-    store.delete_parameters(parameters=[_deployed_deployment_manifest_key(deployment)])
+    store.delete_parameters(parameters=[_deployed_deployment_manifest_key(deployment)], session=session)
 
 
 def _metadata_key(deployment: str, group: str, module: str) -> str:
@@ -653,11 +751,13 @@ def _deployment_key(deployment: str) -> str:
     return f"/{config.PROJECT}/{deployment}/"
 
 
-def _fetch_helper(name: str, params_cache: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
+def _fetch_helper(
+    name: str, params_cache: Optional[Dict[str, Any]] = None, session: Optional[Session] = None
+) -> Optional[Dict[str, Any]]:
     if params_cache:
-        return params_cache[name] if name in params_cache.keys() else None
+        return params_cache.get(name, None)
     else:
-        return store.get_parameter_if_exists(name=name)
+        return store.get_parameter_if_exists(name=name, session=session)
 
 
 def _get_module_stack_names(deployment_name: str, group_name: str, module_name: str) -> Tuple[str, str]:
diff --git a/seedfarmer/models/manifests.py b/seedfarmer/models/manifests.py
index e74a634f..ff233dfd 100644
--- a/seedfarmer/models/manifests.py
+++ b/seedfarmer/models/manifests.py
@@ -13,7 +13,7 @@
 #    limitations under the License.
 
 from enum import Enum
-from typing import Any, Dict, List, Optional, cast
+from typing import Any, Dict, List, Optional, Tuple, cast
 
 from pydantic import PrivateAttr
 
@@ -120,9 +120,16 @@ class ModuleManifest(CamelModel):
     path: str
     bundle_md5: Optional[str]
     parameters: List[ModuleParameter] = []
-    deploy_spec: Optional[DeploySpec]
+    deploy_spec: Optional[DeploySpec] = None
     target_account: Optional[str] = None
     target_region: Optional[str] = None
+    _target_account_id: Optional[str] = PrivateAttr(default=None)
+
+    def set_target_account_id(self, account_id: str) -> None:
+        self._target_account_id = account_id
+
+    def get_target_account_id(self) -> Optional[str]:
+        return self._target_account_id
 
 
 class ModulesManifest(CamelModel):
@@ -194,6 +201,8 @@ class DeploymentManifest(CamelModel):
     _default_account: Optional[TargetAccountMapping] = PrivateAttr(default=None)
     _account_alias_index: Dict[str, TargetAccountMapping] = PrivateAttr(default_factory=dict)
     _account_id_index: Dict[str, TargetAccountMapping] = PrivateAttr(default_factory=dict)
+    _accounts_regions: Optional[List[Dict[str, str]]] = PrivateAttr(default=None)
+    _module_index: Dict[Tuple[str, str], ModuleManifest] = PrivateAttr(default_factory=dict)
 
     def __init__(self, **kwargs: Any) -> None:
         super().__init__(**kwargs)
@@ -221,6 +230,21 @@ def get_target_account_mapping(
     def default_target_account_mapping(self) -> Optional[TargetAccountMapping]:
         return self._default_account
 
+    @property
+    def target_accounts_regions(self) -> List[Dict[str, str]]:
+        if self._accounts_regions is None:
+            self._accounts_regions = []
+            for target_account in self.target_account_mappings:
+                for region in target_account.region_mappings:
+                    self._accounts_regions.append(
+                        {
+                            "alias": target_account.alias,
+                            "account_id": target_account.account_id,
+                            "region": region.region,
+                        }
+                    )
+        return self._accounts_regions
+
     def get_parameter_value(
         self,
         parameter: str,
@@ -255,9 +279,10 @@ def get_parameter_value(
         else:
             return default
 
-    def set_module_defaults(self) -> None:
+    def validate_and_set_module_defaults(self) -> None:
         for group in self.groups:
             for module in group.modules:
+                self._module_index[(group.name, module.name)] = module
                 module.target_account = (
                     self.default_target_account_mapping.alias
                     if self.default_target_account_mapping is not None and module.target_account is None
@@ -270,6 +295,7 @@ def set_module_defaults(self) -> None:
                         f"Invalid target_account ({module.target_account}) for "
                         f"Module {module.name} in Group {group.name}"
                     )
+                module.set_target_account_id(target_account.account_id)
 
                 module.target_region = (
                     target_account.default_region_mapping.region
@@ -282,3 +308,6 @@ def set_module_defaults(self) -> None:
                         f"Invalid target_region ({module.target_region}) in target_account ({target_account.alias}) "
                         f"for Module {module.name} in Group {group.name}"
                     )
+
+    def get_module(self, group: str, module: str) -> Optional[ModuleManifest]:
+        return self._module_index.get((group, module), None)
diff --git a/seedfarmer/resources/deployment_role.template b/seedfarmer/resources/deployment_role.template
index 56d1589f..47ba849d 100644
--- a/seedfarmer/resources/deployment_role.template
+++ b/seedfarmer/resources/deployment_role.template
@@ -38,18 +38,8 @@ Resources:
               - kms:Create*
               Effect: Allow
               Resource:
-                - Fn::Join: [
-                    "",[
-                    "arn:aws:kms:*:",
-                    "Ref": AWS::AccountId,
-                    ":key/*"]
-                  ]
-                - Fn::Join: [
-                    "",[
-                    "arn:aws:kms:*:",
-                    "Ref": AWS::AccountId,
-                    ":alias/*"]
-                  ]  
+                - Fn::Sub: "arn:aws:kms:*:${AWS::AccountId}:key/*"
+                - Fn::Sub: "arn:aws:kms:*:${AWS::AccountId}:alias/*"
               Sid: DeploymentKMS
             - Action:
               - iam:Delete*
@@ -62,34 +52,21 @@ Resources:
               - iam:PutRolePolicy
               - iam:Pass*
               - iam:DetachRolePolicy
+              - iam:List*
               Effect: Allow
               Resource:
-                - Fn::Join: [
-                    "",[
-                    "arn:aws:iam::",
-                    "Ref": AWS::AccountId,
-                    ":role/*"]
-                  ]
-                - Fn::Join: [
-                    "",[
-                    "arn:aws:iam::",
-                    "Ref": AWS::AccountId,
-                    ":policy/*"]
-                  ]            
+                - Fn::Sub: "arn:aws:iam::${AWS::AccountId}:role/*"
+                - Fn::Sub: "arn:aws:iam::${AWS::AccountId}:policy/*"
               Sid: DeploymentIAM
             - Action:
               - codebuild:Update*
               - codebuild:Batch*
               - codebuild:Create*
               - codebuild:Delete*
+              - codebuild:StartBuild
               Effect: Allow
               Resource:
-                Fn::Join: [
-                  "",[
-                  "arn:aws:codebuild:*:",
-                  "Ref": AWS::AccountId,
-                  ":project/*"]
-                ]
+                Fn::Sub: "arn:aws:codebuild:*:${AWS::AccountId}:project/codeseeder-{{ project_name }}*"
               Sid: DeploymentCodeBuild
             - Action:
               - iam:ListPolicies
@@ -98,9 +75,16 @@ Resources:
               - kms:UpdateCustomKeyStore
               - kms:ListAliases
               - codebuild:ListProjects
+              - cloudformation:Describe*
+              - cloudformation:GetTemplate
               Effect: Allow
               Resource: '*'
               Sid: DeploymentListStuff
+            - Action:
+              - cloudformation:*
+              Effect: Allow
+              Resource:
+                Fn::Sub: "arn:aws:cloudformation:*:${AWS::AccountId}:stack/exampleproj*"
             - Action:
               - s3:Delete*
               - s3:Put*
@@ -110,7 +94,7 @@ Resources:
               - s3:List*
               Effect: Allow
               Resource:
-              - arn:aws:s3:::{{ project_name }}*/*
+              - arn:aws:s3:::codeseeder-{{ project_name }}*/*
               Sid: DeploymentS3
             - Action:
               - sts:AssumeRole
@@ -127,13 +111,19 @@ Resources:
               - ssm:Add*
               - ssm:Describe*
               - ssm:Get*
-              Effect: Allow              
-              Resource: 
-                Fn::Join: [
-                  "",[
-                  "arn:aws:ssm:*:",
-                  "Ref": AWS::AccountId,
-                  ":parameter/{{ project_name }}/*"]
-                ]
+              Effect: Allow
+              Resource:
+                Fn::Sub: "arn:aws:ssm:*:${AWS::AccountId}:parameter/{{ project_name }}/*"
+            - Effect: Allow
+              Action:
+              - logs:CreateLogStream
+              - logs:PutLogEvents
+              - logs:GetLogEvents
+              - logs:GetLogRecord
+              - logs:GetLogGroupFields
+              - logs:GetQueryResults
+              - logs:DescribeLogStreams
+              Resource:
+                Fn::Sub: "arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/codebuild/codeseeder-{{ project_name }}*"
       RoleName: seedfarmer-{{ project_name }}-deployment-role
     Type: AWS::IAM::Role
diff --git a/seedfarmer/resources/toolchain_role.template b/seedfarmer/resources/toolchain_role.template
index 9bf2c6eb..04a65936 100644
--- a/seedfarmer/resources/toolchain_role.template
+++ b/seedfarmer/resources/toolchain_role.template
@@ -35,13 +35,8 @@ Resources:
               - ssm:Describe*
               - ssm:Get*
               Effect: Allow
-              Resource: 
-                Fn::Join: [
-                  "",[
-                  "arn:aws:ssm:*:",
-                  "Ref": AWS::AccountId,
-                  ":parameter/{{ project_name }}/*"]
-                ]
+              Resource:
+                Fn::Sub: "arn:aws:ssm:*:${AWS::AccountId}:parameter/{{ project_name }}/*"
               Sid: ToolChainSSM
       RoleName: seedfarmer-{{ project_name }}-toolchain-role
     Type: AWS::IAM::Role
diff --git a/seedfarmer/services/_iam.py b/seedfarmer/services/_iam.py
index 79fdab6e..3ea88b86 100644
--- a/seedfarmer/services/_iam.py
+++ b/seedfarmer/services/_iam.py
@@ -16,24 +16,31 @@
 import logging
 from typing import Any, Dict, List, Optional, cast
 
+from boto3 import Session
+
 from seedfarmer.services._service_utils import boto3_client, boto3_resource, get_region
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
 
-def get_role(role_name: str) -> Optional[Dict[str, Any]]:
+def get_role(role_name: str, session: Optional[Session] = None) -> Optional[Dict[str, Any]]:
     _logger.debug("Getting Role: %s", role_name)
 
-    iam_client = boto3_client("iam")
+    iam_client = boto3_client("iam", session=session)
     try:
         return cast(Dict[str, Any], iam_client.get_role(RoleName=role_name))
     except iam_client.exceptions.NoSuchEntityException:
         return None
 
 
-def create_check_iam_role(trust_policy: Dict[str, Any], role_name: str, permission_boundary_arn: Optional[str]) -> None:
+def create_check_iam_role(
+    trust_policy: Dict[str, Any],
+    role_name: str,
+    permission_boundary_arn: Optional[str],
+    session: Optional[Session] = None,
+) -> None:
     _logger.debug("Creating IAM Role with name: %s ", role_name)
-    iam_client = boto3_client("iam")
+    iam_client = boto3_client("iam", session=session)
     try:
         iam_client.get_role(RoleName=role_name)
     except iam_client.exceptions.NoSuchEntityException:
@@ -41,15 +48,15 @@ def create_check_iam_role(trust_policy: Dict[str, Any], role_name: str, permissi
             "RoleName": role_name,
             "AssumeRolePolicyDocument": json.dumps(trust_policy),
             "Description": f"deployment-role for {role_name}",
-            "Tags": [{"Key": "Region", "Value": get_region()}],
+            "Tags": [{"Key": "Region", "Value": get_region(session=session)}],
         }
         if permission_boundary_arn:
             args["PermissionsBoundary"] = permission_boundary_arn
         iam_client.create_role(**args)
 
 
-def attach_policy_to_role(role_name: str, policies: List[str]) -> List[str]:
-    iam_client = boto3_client("iam")
+def attach_policy_to_role(role_name: str, policies: List[str], session: Optional[Session] = None) -> List[str]:
+    iam_client = boto3_client("iam", session=session)
     try:
         response = iam_client.list_attached_role_policies(RoleName=role_name)
         attached_policies = [i["PolicyArn"] for i in response["AttachedPolicies"] if response["AttachedPolicies"]]
@@ -70,8 +77,8 @@ def attach_policy_to_role(role_name: str, policies: List[str]) -> List[str]:
         raise e
 
 
-def attach_inline_policy(role_name: str, policy_body: str, policy_name: str) -> None:
-    iam_client = boto3_client("iam")
+def attach_inline_policy(role_name: str, policy_body: str, policy_name: str, session: Optional[Session] = None) -> None:
+    iam_client = boto3_client("iam", session=session)
     _logger.debug("Attaching the Inline policy %s to the IAM Role: %s", policy_name, role_name)
     try:
         iam_client.put_role_policy(RoleName=role_name, PolicyName=policy_name, PolicyDocument=policy_body)
@@ -80,9 +87,9 @@ def attach_inline_policy(role_name: str, policy_body: str, policy_name: str) ->
         raise e
 
 
-def detach_policy_from_role(role_name: str, policy_arn: str) -> None:
+def detach_policy_from_role(role_name: str, policy_arn: str, session: Optional[Session] = None) -> None:
     _logger.debug("Detatching policy: %s  from the IAM Role: %s ", policy_arn, role_name)
-    iam_client = boto3_client("iam")
+    iam_client = boto3_client("iam", session=session)
     try:
         iam_client.detach_role_policy(RoleName=role_name, PolicyArn=policy_arn)
     except Exception as e:
@@ -90,8 +97,8 @@ def detach_policy_from_role(role_name: str, policy_arn: str) -> None:
         _logger.info("Caught exception: %s ", e)
 
 
-def delete_role(role_name: str) -> None:
-    iam_client = boto3_client("iam")
+def delete_role(role_name: str, session: Optional[Session] = None) -> None:
+    iam_client = boto3_client("iam", session=session)
     try:
         iam_client.delete_role(RoleName=role_name)
     except iam_client.exceptions.NoSuchEntityException as e:
@@ -99,8 +106,8 @@ def delete_role(role_name: str) -> None:
         _logger.info("Caught exception: %s ", e)
 
 
-def detach_inline_policy_from_role(role_name: str, policy_name: str) -> None:
-    iam_resource = boto3_resource("iam")
+def detach_inline_policy_from_role(role_name: str, policy_name: str, session: Optional[Session] = None) -> None:
+    iam_resource = boto3_resource("iam", session=session)
     try:
         iam_resource.RolePolicy(role_name, policy_name).delete()
     except Exception as e:
diff --git a/seedfarmer/services/_secrets_manager.py b/seedfarmer/services/_secrets_manager.py
index 96393d7a..02d73af6 100644
--- a/seedfarmer/services/_secrets_manager.py
+++ b/seedfarmer/services/_secrets_manager.py
@@ -13,13 +13,15 @@
 #    limitations under the License.
 
 import json
-from typing import Any, Dict, cast
+from typing import Any, Dict, Optional, cast
+
+from boto3 import Session
 
 from seedfarmer.services._service_utils import boto3_client, get_account_id, get_region
 
 
-def get_secret_secrets_manager(name: str) -> Dict[str, Any]:
-    client = boto3_client(service_name="secretsmanager")
-    secret_arn = f"arn:aws:secretsmanager:{get_region()}:{get_account_id()}:secret:{name}"
+def get_secret_secrets_manager(name: str, session: Optional[Session] = None) -> Dict[str, Any]:
+    client = boto3_client(service_name="secretsmanager", session=session)
+    secret_arn = f"arn:aws:secretsmanager:{get_region(session=session)}:{get_account_id(session=session)}:secret:{name}"
     json_str: str = client.get_secret_value(SecretId=secret_arn).get("SecretString")
     return cast(Dict[str, Any], json.loads(json_str))
diff --git a/seedfarmer/services/_ssm.py b/seedfarmer/services/_ssm.py
index 85d93d42..f30dda7a 100644
--- a/seedfarmer/services/_ssm.py
+++ b/seedfarmer/services/_ssm.py
@@ -17,13 +17,15 @@
 import time
 from typing import Any, Dict, List, Optional, cast
 
+from boto3 import Session
+
 from seedfarmer.services._service_utils import boto3_client
 
 _logger: logging.Logger = logging.getLogger(__name__)
 
 
-def put_parameter(name: str, obj: Dict[str, Any]) -> None:
-    client = boto3_client(service_name="ssm")
+def put_parameter(name: str, obj: Dict[str, Any], session: Optional[Session] = None) -> None:
+    client = boto3_client(service_name="ssm", session=session)
     retries = 3
     while retries > 0:
         try:
@@ -45,14 +47,14 @@ def put_parameter(name: str, obj: Dict[str, Any]) -> None:
                 raise Exception(err)
 
 
-def get_parameter(name: str) -> Dict[str, Any]:
-    client = boto3_client(service_name="ssm")
+def get_parameter(name: str, session: Optional[Session] = None) -> Dict[str, Any]:
+    client = boto3_client(service_name="ssm", session=session)
     json_str: str = client.get_parameter(Name=name)["Parameter"]["Value"]
     return cast(Dict[str, Any], json.loads(json_str))
 
 
-def get_parameter_if_exists(name: str) -> Optional[Dict[str, Any]]:
-    client = boto3_client(service_name="ssm")
+def get_parameter_if_exists(name: str, session: Optional[Session] = None) -> Optional[Dict[str, Any]]:
+    client = boto3_client(service_name="ssm", session=session)
     try:
         json_str: str = client.get_parameter(Name=name)["Parameter"]["Value"]
     except client.exceptions.ParameterNotFound:
@@ -60,8 +62,8 @@ def get_parameter_if_exists(name: str) -> Optional[Dict[str, Any]]:
     return cast(Dict[str, Any], json.loads(json_str))
 
 
-def does_parameter_exist(name: str) -> bool:
-    client = boto3_client(service_name="ssm")
+def does_parameter_exist(name: str, session: Optional[Session] = None) -> bool:
+    client = boto3_client(service_name="ssm", session=session)
     try:
         client.get_parameter(Name=name)
         return True
@@ -69,8 +71,8 @@ def does_parameter_exist(name: str) -> bool:
         return False
 
 
-def list_parameters(prefix: str) -> List[str]:
-    client = boto3_client(service_name="ssm")
+def list_parameters(prefix: str, session: Optional[Session] = None) -> List[str]:
+    client = boto3_client(service_name="ssm", session=session)
     paginator = client.get_paginator("describe_parameters")
     response_iterator = paginator.paginate(
         ParameterFilters=[
@@ -91,8 +93,8 @@ def list_parameters(prefix: str) -> List[str]:
     return ret
 
 
-def list_parameters_with_filter(prefix: str, contains_string: str) -> List[str]:
-    client = boto3_client(service_name="ssm")
+def list_parameters_with_filter(prefix: str, contains_string: str, session: Optional[Session] = None) -> List[str]:
+    client = boto3_client(service_name="ssm", session=session)
     paginator = client.get_paginator("describe_parameters")
 
     response_iterator = paginator.paginate(
@@ -115,8 +117,8 @@ def list_parameters_with_filter(prefix: str, contains_string: str) -> List[str]:
     return ret
 
 
-def get_all_parameter_data_by_path(prefix: str) -> Dict[str, Any]:
-    client = boto3_client(service_name="ssm")
+def get_all_parameter_data_by_path(prefix: str, session: Optional[Session] = None) -> Dict[str, Any]:
+    client = boto3_client(service_name="ssm", session=session)
     paginator = client.get_paginator("get_parameters_by_path")
     response_iterator = paginator.paginate(
         Path=prefix,
@@ -138,11 +140,12 @@ def get_all_parameter_data_by_path(prefix: str) -> Dict[str, Any]:
     return ret
 
 
-def delete_parameters(parameters: List[str]) -> None:
+def delete_parameters(parameters: List[str], session: Optional[Session] = None) -> None:
     if parameters:
         if len(parameters) < 10:
-            client = boto3_client(service_name="ssm")
+            _logger.debug("deleting parameters: %s", parameters)
+            client = boto3_client(service_name="ssm", session=session)
             client.delete_parameters(Names=parameters)
         else:
-            delete_parameters(parameters[0:9])
-            delete_parameters(parameters[9 : len(parameters)])
+            delete_parameters(parameters[0:9], session=session)
+            delete_parameters(parameters[9:], session=session)
diff --git a/seedfarmer/services/sessionmanager.py b/seedfarmer/services/session_manager.py
similarity index 66%
rename from seedfarmer/services/sessionmanager.py
rename to seedfarmer/services/session_manager.py
index 06dd824e..29f8aa69 100644
--- a/seedfarmer/services/sessionmanager.py
+++ b/seedfarmer/services/session_manager.py
@@ -1,6 +1,6 @@
 import logging
 import threading
-from abc import abstractmethod
+from abc import abstractmethod, abstractproperty
 from threading import Thread
 from time import sleep
 from typing import Any, Dict, List, Optional, Tuple
@@ -33,17 +33,21 @@ def __call__(cls, *args: List[Any], **kwargs: Dict[Any, Any]) -> Any:
         return cls._instances[cls]
 
 
-class ISessionManager:
+class ISessionManager(object):
     @abstractmethod
-    def configure(self, project_name: str, **kwargs: Optional[Any]) -> Any:
-        ...
-
-    @abstractmethod
-    def create(self, region_name: Optional[str] = None, profile: Optional[str] = None) -> Any:
+    def get_or_create(
+        self,
+        *,
+        project_name: Optional[str] = None,
+        region_name: Optional[str] = None,
+        profile: Optional[str] = None,
+        enable_reaper: bool = False,
+        **kwargs: Optional[Any],
+    ) -> Any:
         ...
 
-    @abstractmethod
-    def get_toolchain_session(self) -> Session:
+    @abstractproperty
+    def toolchain_session(self) -> Session:
         ...
 
     @abstractmethod
@@ -63,49 +67,57 @@ class SessionManager(ISessionManager, metaclass=SingletonMeta):
     sessions: Dict[Any, Any] = {}
 
     config: Dict[Any, Any] = {}
-    toolchain_create_params: Dict[Any, Any] = {}
     created: bool = False
     reaper: Thread = None  # type: ignore
     reaperInterval: int = 3000  # every 50 minutes
 
-    def configure(self, project_name: str, **kwargs: Optional[Any]) -> ISessionManager:
-        self.config["project_name"] = project_name
-        self.config = {**self.config, **kwargs}
-        return self
+    def __init__(self) -> None:
+        super().__init__()
 
-    def create(self, region_name: Optional[str] = None, profile: Optional[str] = None) -> ISessionManager:
-        p_name = self.config["project_name"] if self.config.get("project_name") else "sf"
-        self.toolchain_create_params = {"region_name": region_name, "profile": profile}
-        self.toolchain_role_name = f"seedfarmer-{p_name}-toolchain-role"
-        if not self.reaper or not self.reaper.is_alive():
-            self._setup_reaper()
-        if self.TOOLCHAIN_KEY not in self.sessions.keys():
-            session, role = self._get_toolchain(region_name, profile)
-            self.sessions = {self.TOOLCHAIN_KEY: {self.SESSION: session, self.ROLE: role}}
+    def get_or_create(
+        self,
+        *,
+        project_name: Optional[str] = None,
+        region_name: Optional[str] = None,
+        profile: Optional[str] = None,
+        enable_reaper: bool = False,
+        **kwargs: Optional[Any],
+    ) -> ISessionManager:
+        if not self.created:
+            if project_name is None:
+                raise ValueError("A 'project_name' is required for first time initialization of the SessionManager")
+            self.config["project_name"] = project_name
+            self.config["region_name"] = region_name
+            self.config["profile"] = profile
+            self.config = {**self.config, **kwargs}
+            self.toolchain_role_name = f"seedfarmer-{project_name}-toolchain-role"
+
+            if enable_reaper and (not self.reaper or not self.reaper.is_alive()):
+                self._setup_reaper()
+
+            if self.TOOLCHAIN_KEY not in self.sessions.keys():
+                session, role = self._get_toolchain()
+                self.sessions = {self.TOOLCHAIN_KEY: {self.SESSION: session, self.ROLE: role}}
             self.created = True
         else:
             _logger.info("Toolchain Already Created")
         return self
 
-    def get_toolchain_session(self) -> Session:
-        if not self.sessions or not self.sessions[self.TOOLCHAIN_KEY][self.SESSION]:
-            self.create(
-                region_name=self.toolchain_create_params["region_name"]
-                if self.toolchain_create_params["region_name"]
-                else None,
-                profile=self.toolchain_create_params["profile"] if self.toolchain_create_params["profile"] else None,
-            )
+    @property
+    def toolchain_session(self) -> Session:
+        if not self.created:
+            raise RuntimeError("The SessionManager object was never properly created...call .create()")
         return self.sessions[self.TOOLCHAIN_KEY][self.SESSION]
 
     def get_deployment_session(self, account_id: str, region_name: str) -> Session:
         session_key = f"{account_id}-{region_name}"
-        p_name = self.config["project_name"] if self.config.get("project_name") else "NA"
+        project_name = self.config["project_name"]
         if not self.created:
             raise RuntimeError("The SessionManager object was never properly created...call .create()")
         if session_key not in self.sessions.keys():
             _logger.info(f"Creating Session for {session_key}")
             toolchain_session = self.sessions[self.TOOLCHAIN_KEY][self.SESSION]
-            deployment_role_arn = f"arn:aws:iam::{account_id}:role/seedfarmer-{p_name}-deployment-role"
+            deployment_role_arn = f"arn:aws:iam::{account_id}:role/seedfarmer-{project_name}-deployment-role"
             sts_toolchain_client = boto3_client(service_name="sts", session=toolchain_session)
             deployment_role = sts_toolchain_client.assume_role(
                 RoleArn=deployment_role_arn,
@@ -125,12 +137,9 @@ def get_deployment_session(self, account_id: str, region_name: str) -> Session:
 
     # These methods below should not be called outside of this class
 
-    def _get_toolchain(
-        self,
-        region_name: Optional[str],
-        profile_name: Optional[str],
-    ) -> Tuple[Session, Dict[Any, Any]]:
-
+    def _get_toolchain(self) -> Tuple[Session, Dict[Any, Any]]:
+        region_name = self.config.get("region_name")
+        profile_name = self.config.get("profile")
         _logger.debug("Getting toolchain role")
         user_session = create_new_session(region_name=region_name, profile=profile_name)
         user_client = boto3_client(service_name="sts", session=user_session)
@@ -145,7 +154,7 @@ def _get_toolchain(
             aws_access_key_id=toolchain_role["Credentials"]["AccessKeyId"],
             aws_secret_access_key=toolchain_role["Credentials"]["SecretAccessKey"],
             aws_session_token=toolchain_role["Credentials"]["SessionToken"],
-            region_name=region_name if region_name else None,
+            region_name=region_name,
         )
 
         return toolchain_session, toolchain_role
@@ -162,12 +171,3 @@ def _reap_sessions(self, interval: int) -> None:
             sleep(interval)
             _logger.debug(f"Reaping Sessions - sleeping for {interval} seconds")
             self.sessions = {}
-
-    def _fetch_session_obj(self) -> Dict[Any, Any]:
-        return self.sessions
-
-    def _fetch_toolchain_session(self) -> Session:
-        if self.TOOLCHAIN_KEY not in self.sessions.keys():
-            _logger.error("The toolchain session is not establshed....go create one")
-        else:
-            return self.sessions[self.TOOLCHAIN_KEY][self.SESSION]
diff --git a/setup.py b/setup.py
index 6fcd5b1f..e386472b 100644
--- a/setup.py
+++ b/setup.py
@@ -45,7 +45,7 @@
     keywords=["aws", "cdk"],
     python_requires=">=3.7",
     install_requires=[
-        "aws-codeseeder~=0.4.0",
+        "aws-codeseeder~=0.5.0",
         "cookiecutter~=2.1.0",
         "pyhumps~=3.5.0",
         "pydantic~=1.9.0",


Test patch:
diff --git a/test/unit-test/test_models.py b/test/unit-test/test_models.py
index d37e1ad4..387e73f2 100644
--- a/test/unit-test/test_models.py
+++ b/test/unit-test/test_models.py
@@ -123,7 +123,7 @@ def test_module_manifest_with_defaults():
 
     module = ModuleManifest(**module_yaml)
     manifest.groups[0].modules = [module]
-    manifest.set_module_defaults()
+    manifest.validate_and_set_module_defaults()
 
     assert module.target_account == "primary"
     assert module.target_region == "us-west-2"
@@ -137,7 +137,7 @@ def test_module_manifest_with_defaults():
 
     module = ModuleManifest(**module_yaml)
     manifest.groups[0].modules = [module]
-    manifest.set_module_defaults()
+    manifest.validate_and_set_module_defaults()
 
     assert module.target_account == "primary"
     assert module.target_region == "us-west-2"
@@ -154,7 +154,7 @@ def test_module_manifest_with_defaults():
     module = ModuleManifest(**module_yaml)
     manifest.groups[0].modules = [module]
     with pytest.raises(ValueError):
-        manifest.set_module_defaults()
+        manifest.validate_and_set_module_defaults()
 
     module_yaml = yaml.safe_load(
         """
@@ -168,7 +168,7 @@ def test_module_manifest_with_defaults():
     module = ModuleManifest(**module_yaml)
     manifest.groups[0].modules = [module]
     with pytest.raises(ValueError):
-        manifest.set_module_defaults()
+        manifest.validate_and_set_module_defaults()
 
 
 @pytest.mark.models
@@ -191,7 +191,7 @@ def test_module_manifest_without_defaults():
 
     module = ModuleManifest(**module_yaml)
     manifest.groups[0].modules = [module]
-    manifest.set_module_defaults()
+    manifest.validate_and_set_module_defaults()
 
     assert module.target_account == "primary"
     assert module.target_region == "us-west-2"
@@ -208,7 +208,7 @@ def test_module_manifest_without_defaults():
     manifest.groups[0].modules = [module]
 
     with pytest.raises(ValueError):
-        manifest.set_module_defaults()
+        manifest.validate_and_set_module_defaults()
 
     module_yaml = yaml.safe_load(
         """
@@ -222,4 +222,4 @@ def test_module_manifest_without_defaults():
     manifest.groups[0].modules = [module]
 
     with pytest.raises(ValueError):
-        manifest.set_module_defaults()
+        manifest.validate_and_set_module_defaults()