class Promote(ConfigCommand): def __init__(self, source_ssm: SsmDao, config_completer_init: WordCompleter, colors_enabled: bool, config_context: ConfigContext, session_mgr: SessionManager): super().__init__(promote, colors_enabled, config_context) self.config_context = config_context self._source_ssm = source_ssm self._session_mgr = session_mgr self._config_completer = config_completer_init self._utils = Utils(colors_enabled) self._out = Output(colors_enabled) def _promote(self): repeat = True parameters: List[Dict] = [] while repeat: namespace = Input.input("Please input a namespace prefix to promote:" f" (i.e. {self.context.defaults.service_ns}/foo/): ", completer=self._config_completer) if not self._utils.is_valid_input(namespace, "namespace", notify=False): continue try: parameters: List[Dict] = self._source_ssm.get_all_parameters([namespace]) if not parameters and self._source_ssm.get_parameter(namespace): parameters, latest_version = self._source_ssm.get_parameter_details(namespace) parameters = list(parameters) if parameters: repeat = False else: self._out.warn("\nNo parameters found. Try again.\n") except ClientError as e: print(f"{self.c.fg_rd}ERROR: >> {e}{self.c.rs}") continue self._out.notify(f'\nFound [[{len(parameters)}]] parameter{"s" if len(parameters) > 1 else ""} to migrate.\n') assumable_roles = self.context.defaults.assumable_roles matching_roles = list(set([x for x in assumable_roles if x.role == self.config_context.role])) valid_envs = set([x.run_env.env for x in matching_roles]) valid_envs.remove(self.run_env.env) # Remove current env, we can't promote from dev -> dev next_env = Input.select(f'Please select the destination environment.', valid_options=list(valid_envs)) matching_role = [role for role in matching_roles if role.run_env == RunEnv(env=next_env)][0] env: GlobalEnvironment = GlobalEnvironment(role=matching_role, region=self.config_context.defaults.region) dest_ssm = SsmDao(self._session_mgr.get_session(env, prompt=False).client('ssm')) for param in parameters: if 'KeyId' in param: self._out.print(f"Skipping param: [[{param['Name']}]]. It is encrypted and cannot be migrated.") else: promote_it = Input.y_n_input(f"Would you like to promote: {param['Name']}?", default_yes=True) if promote_it: val = self._source_ssm.get_parameter(param['Name']) description = param.get('Description', "") dest_ssm.set_parameter(param['Name'], val, description, SSM_STRING) self._out.success(f"Successfully promoted [[{param['Name']}]] to [[{next_env}]].\r\n") @VersionTracker.notify_user @AnonymousUsageTracker.track_command_usage def execute(self): self._promote()
class Edit(ConfigCommand): def __init__(self, ssm_init: SsmDao, colors_enabled: bool, config_context: ConfigContext, config_view: RBACLimitedConfigView, config_completer: WordCompleter): super().__init__(edit, colors_enabled, config_context) self._ssm = ssm_init self._config_view = config_view self._utils = Utils(colors_enabled) self._config_completer = config_completer def edit(self) -> None: """ Allows a user to define a PS name and add or edit a parameter at that location. Uses NPYscreen editor. """ key = Input.input('Please input a PS Name: ', completer=self._config_completer) try: value, desc = self._ssm.get_parameter_with_description(key) edit_app = EditApp(key, value, desc) edit_app.run() value, desc = edit_app.value_box.value, edit_app.description_box.value log.info(f"Edited value: {value} - description: {desc}") is_secret = Input.is_secret() parameter_type, kms_id = SSM_SECURE_STRING if is_secret else SSM_STRING, None if is_secret: valid_keys = self._config_view.get_authorized_kms_keys() if len(valid_keys) > 1: key_name = Input.select_kms_key(valid_keys) else: key_name = valid_keys[0] kms_id = self._config_view.get_authorized_key_id( key_name, self.run_env) if not self._utils.is_valid_input(key, f"Parameter name", True) \ or not self._utils.is_valid_input(value, key, True): self._utils.error_exit( "Invalid input detected, please resolve the issue and retry." ) self._ssm.set_parameter(key, value, desc, parameter_type, key_id=kms_id) print(f"{self.c.fg_gr}{key} saved successfully.{self.c.rs}") except ClientError as e: if "AccessDeniedException" == e.response['Error']['Code']: denied = "AccessDeniedException" == e.response['Error']['Code'] if denied and "AWSKMS; Status Code: 400;" in e.response[ 'Error']['Message']: print( f"\n{self.c.fg_rd}You do not have access to decrypt the value of: {key}{self.c.rs}" ) elif denied: print( f"\n{self.c.fg_rd}You do not have access to Parameter: {key}{self.c.rs}" ) else: raise else: self._utils.error_exit( f"{self.c.fg_rd}Exception caught attempting to add config: {e}{self.c.rs}" ) @VersionTracker.notify_user @AnonymousUsageTracker.track_command_usage def execute(self): self.edit()