Skip to content

Commit 5c81cf6

Browse files
committed
connection/aws_ssm - use port forwarding session combined with nc for
file transfer
1 parent d9899d0 commit 5c81cf6

File tree

13 files changed

+499
-44
lines changed

13 files changed

+499
-44
lines changed

plugins/connection/aws_ssm.py

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,29 @@
8686
vars:
8787
- name: ansible_aws_ssm_bucket_endpoint_url
8888
version_added: 5.3.0
89+
host_port_number:
90+
description:
91+
- The Port number of the server on the instance when using Port Forwarding Using AWS System Manager Session Manager
92+
to transfer files from/to local host to/from remote host.
93+
- The port V(80) is used if not provided.
94+
- The C(nc) command should be installed in the remote host to use this option.
95+
- This is not supported for Windows hosts for now.
96+
type: integer
97+
default: 80
98+
vars:
99+
- name: ansible_aws_ssm_host_port_number
100+
version_added: 9.3.0
101+
local_port_number:
102+
description:
103+
- Port number on local machine to forward traffic to when using Port Forwarding Using AWS System Manager Session Manager
104+
to transfer files from/to local host to/from remote host.
105+
- An open port is chosen at run-time if not provided.
106+
- The C(nc) command should be installed in the remote host to use this option.
107+
- This is not supported for Windows hosts for now.
108+
type: integer
109+
vars:
110+
- name: ansible_aws_ssm_local_port_number
111+
version_added: 9.3.0
89112
plugin:
90113
description:
91114
- This defines the location of the session-manager-plugin binary.
@@ -360,6 +383,7 @@
360383
from ansible.utils.display import Display
361384

362385
from ansible_collections.amazon.aws.plugins.module_utils.botocore import HAS_BOTO3
386+
from ansible_collections.community.aws.plugins.plugin_utils.ssm_file_transfer import PortForwardingFileTransferManager
363387

364388
from ansible_collections.community.aws.plugins.plugin_utils.s3clientmanager import S3ClientManager
365389

@@ -472,6 +496,7 @@ class Connection(ConnectionBase):
472496
_stdout = None
473497
_session_id = ""
474498
_timeout = False
499+
_filetransfer_mgr = None
475500
MARK_LENGTH = 26
476501

477502
def __init__(self, *args: Any, **kwargs: Any) -> None:
@@ -515,19 +540,39 @@ def _init_clients(self) -> None:
515540
profile_name = self.get_option("profile") or ""
516541
region_name = self.get_option("region")
517542

518-
# Initialize S3ClientManager
519-
self.s3_manager = S3ClientManager(self)
520-
521-
# Initialize S3 client
522-
s3_endpoint_url, s3_region_name = self.s3_manager.get_bucket_endpoint()
523-
self._vvvv(f"SETUP BOTO3 CLIENTS: S3 {s3_endpoint_url}")
524-
self.s3_manager.initialize_client(
525-
region_name=s3_region_name, endpoint_url=s3_endpoint_url, profile_name=profile_name
526-
)
527-
self._s3_client = self.s3_manager._s3_client
528-
529543
# Initialize SSM client
530544
self._initialize_ssm_client(region_name, profile_name)
545+
if self._use_bucket():
546+
# Initialize S3ClientManager
547+
self.s3_manager = S3ClientManager(self)
548+
549+
# Initialize S3 client
550+
s3_endpoint_url, s3_region_name = self.s3_manager.get_bucket_endpoint()
551+
self._vvvv(f"SETUP BOTO3 CLIENTS: S3 {s3_endpoint_url}")
552+
self.s3_manager.initialize_client(
553+
region_name=s3_region_name, endpoint_url=s3_endpoint_url, profile_name=profile_name
554+
)
555+
self._s3_client = self.s3_manager._s3_client
556+
else:
557+
self._initialize_file_transfer_manager()
558+
559+
def _initialize_file_transfer_manager(self) -> None:
560+
ssm_timeout = self.get_option("ssm_timeout")
561+
region_name = self.get_option("region")
562+
profile_name = self.get_option("profile") or ""
563+
host_port = self.get_option("host_port_number")
564+
local_port = self.get_option("local_port_number")
565+
self._filetransfer_mgr = PortForwardingFileTransferManager(
566+
self.host,
567+
ssm_client=self._client,
568+
instance_id=self.instance_id,
569+
executable=self.get_executable(),
570+
ssm_timeout=ssm_timeout,
571+
region_name=region_name,
572+
profile_name=profile_name,
573+
host_port=host_port,
574+
local_port=local_port,
575+
)
531576

532577
def _initialize_ssm_client(self, region_name: Optional[str], profile_name: str) -> None:
533578
"""
@@ -572,6 +617,10 @@ def reset(self) -> Any:
572617
self.close()
573618
return self.start_session()
574619

620+
def _use_bucket(self) -> bool:
621+
"""return true if the file transfer is performed using s3 bucket"""
622+
return self.is_windows or self.get_option("bucket_name")
623+
575624
@property
576625
def instance_id(self) -> str:
577626
if not self._instance_id:
@@ -1088,15 +1137,21 @@ def put_file(self, in_path: str, out_path: str) -> Tuple[int, str, str]:
10881137
if not os.path.exists(to_bytes(in_path, errors="surrogate_or_strict")):
10891138
raise AnsibleFileNotFound(f"file or module does not exist: {in_path}")
10901139

1091-
return self._file_transport_command(in_path, out_path, "put")
1140+
if self._use_bucket():
1141+
return self._file_transport_command(in_path, out_path, "put")
1142+
else:
1143+
return self._filetransfer_mgr.put_file(in_path, out_path)
10921144

10931145
def fetch_file(self, in_path: str, out_path: str) -> Tuple[int, str, str]:
10941146
"""fetch a file from remote to local"""
10951147

10961148
super().fetch_file(in_path, out_path)
10971149

10981150
self._vvv(f"FETCH {in_path} TO {out_path}")
1099-
return self._file_transport_command(in_path, out_path, "get")
1151+
if self._use_bucket():
1152+
return self._file_transport_command(in_path, out_path, "get")
1153+
else:
1154+
return self._filetransfer_mgr.fetch_file(in_path, out_path)
11001155

11011156
def close(self) -> None:
11021157
"""terminate the connection"""

0 commit comments

Comments
 (0)