def set_lambda_var(env_var: str, value, lambda_name: str, quiet: bool = False) -> None: """Set a single variable in environment of the specified lambda function""" environment = get_deployed_lambda_environment(lambda_name, quiet=False) environment[env_var] = value set_deployed_lambda_environment(lambda_name, environment) polite_print( quiet, f"Success! Set variable {env_var} in deployed lambda function {lambda_name}" )
def get_deployed_lambda_environment(lambda_name: str, quiet: bool = True) -> dict: """Get environment variables in a deployed lambda function""" try: lambda_client.get_function(FunctionName=lambda_name) c = lambda_client.get_function_configuration(FunctionName=lambda_name) except lambda_client.exceptions.ResourceNotFoundException: polite_print(quiet, f"{lambda_name} is not a deployed lambda function") return {} else: # get_function_configuration() above returns a dict, no need to convert return c["Environment"]["Variables"]
def lambda_set(argv: typing.List[str], args: argparse.Namespace): """ Set a variable in the SSM store under $DSS_DEPLOYMENT_STAGE/environment, then set the variable in each deployed lambda. """ name = args.name # Use stdin for value if not select.select([sys.stdin], [], [], 0.0)[0]: raise RuntimeError( "Error: stdin was empty! A variable value must be provided via stdin" ) val = sys.stdin.read() if args.dry_run: polite_print( args.quiet, f"Dry-run setting variable {name} in lambda environment in SSM store under " "$DSS_DEPLOYMENT_STAGE/environment") polite_print(args.quiet, f" Name: {name}") polite_print(args.quiet, f" Value: {val}") for lambda_name in get_deployed_lambdas(): polite_print( args.quiet, f"Dry-run setting variable {name} in lambda {lambda_name}") else: # Set the variable in the SSM store first, then in each deployed lambda set_ssm_parameter(name, val, quiet=args.quiet) for lambda_name in get_deployed_lambdas(): set_lambda_var(name, val, lambda_name, quiet=args.quiet)
def set_ssm_parameter(env_var: str, value, quiet: bool = False) -> None: """ Set a variable in the lambda environment stored in the SSM store under $DSS_DEPLOYMENT_STAGE/environment :param env_var: name of environment variable being set :param value: value of environment variable being set :param bool quiet: suppress all output if true """ environment = get_ssm_environment() environment[env_var] = value set_ssm_environment(environment) polite_print( quiet, f"Success! Set variable in SSM parameter store environment:\n" f" Name: {env_var}\n" f" Value: {value}\n")
def unset_lambda_var(env_var: str, lambda_name: str, quiet: bool = False) -> None: """Unset a single variable in environment of the specified lambda function""" environment = get_deployed_lambda_environment(lambda_name, quiet=False) try: del environment[env_var] set_deployed_lambda_environment(lambda_name, environment) polite_print( quiet, f"Success! Unset variable {env_var} in deployed lambda function {lambda_name}" ) except KeyError: polite_print( quiet, f"Nothing to unset for variable {env_var} in deployed lambda function {lambda_name}" )
def del_secret(argv: typing.List[str], args: argparse.Namespace): """ Delete the value of the secret variable specified by the --secret-name flag from the secrets manager """ secret_name = fix_secret_variable_prefix(args.secret_name) try: # Start by trying to get the secret variable sm_client.get_secret_value(SecretId=secret_name) except ClientError: # No secret var found polite_print( args.quiet, f"Secret variable {secret_name} not found in secrets manager!") except sm_client.exceptions.InvalidRequestException: # Already deleted secret var polite_print( args.quiet, f"Secret variable {secret_name} already marked for deletion in secrets manager!" ) else: # Get operation was successful, secret variable exists if not args.force and not args.dry_run: # Make sure the user really wants to do this confirm = f""" *** WARNING!!! **** You are about to delete secret {secret_name} from the secrets manager. Are you sure you want to delete the secret? (Type 'y' or 'yes' to confirm): """ response = input(confirm) if response.lower() not in ["y", "yes"]: raise RuntimeError( "You safely aborted the delete secret operation!") if args.dry_run: # Delete it for fakes polite_print( args.quiet, f"Secret variable {secret_name} found in secrets manager, dry-run deleting it" ) else: # Delete it for real polite_print( args.quiet, f"Secret variable {secret_name} found in secrets manager, deleting it" ) sm_client.delete_secret(SecretId=secret_name)
def get_local_lambda_environment(quiet: bool = True) -> dict: """ For each environment variable being set in deployed lambda functions, get each environment variable and its value from the local environment, put them in a dict, and return it. :param bool quiet: if true, don't print warning messages :returns: dict containing local environment's value of each variable exported to deployed lambda functions """ env = dict() for name in os.environ["EXPORT_ENV_VARS_TO_LAMBDA"].split(): try: env[name] = os.environ[name] except KeyError: polite_print( quiet, f"Warning: environment variable {name} is in the list of environment variables " "to export to lambda functions, EXPORT_ENV_VARS_TO_LAMBDA, but variable is not " "defined in the local environment, so there is no value to set." ) return env
def lambda_unset(argv: typing.List[str], args: argparse.Namespace): """ Unset a variable in the SSM store under $DSS_DEPLOYMENT_STAGE/environment, then unset the variable in each deployed lambda. """ name = args.name # Unset the variable from the SSM store first, then from each deployed lambda if args.dry_run: polite_print(args.quiet, f"Dry-run deleting variable {name} from SSM store") for lambda_name in get_deployed_lambdas(): polite_print( args.quiet, f'Dry-run deleting variable {name} from lambda function "{lambda_name}"' ) else: unset_ssm_parameter(name, quiet=args.quiet) for lambda_name in get_deployed_lambdas(): unset_lambda_var(name, lambda_name, quiet=args.quiet)
def unset_ssm_parameter(env_var: str, quiet: bool = False) -> None: """ Unset a variable in the lambda environment stored in the SSM store undre $DSS_DEPLOYMENT_STAGE/environment :param env_var: name of environment variable being set :param value: value of environment variable being set :param bool quiet: suppress all output if true """ environment = get_ssm_environment() try: prev_value = environment[env_var] del environment[env_var] set_ssm_environment(environment) polite_print( quiet, f"Success! Unset variable in SSM store under $DSS_DEPLOYMENT_STAGE/environment:\n" f" Name: {env_var}\n" f" Previous value: {prev_value}\n") except KeyError: polite_print( quiet, f"Nothing to unset for variable {env_var} in SSM store under $DSS_DEPLOYMENT_STAGE/environment" )
def get_deployed_lambdas(quiet: bool = True): """ Generator returning names of lambda functions :param bool quiet: if true, don't print warnings about lambdas that can't be found """ stage = os.environ["DSS_DEPLOYMENT_STAGE"] dirs = [] path = os.path.join(os.environ["DSS_HOME"], "daemons") for item in os.scandir(path): if not item.name.startswith('.') and item.is_dir(): dirs.append(item.name) functions = [f"{name}-{stage}" for name in dirs] functions.append(f"dss-{stage}") for name in functions: try: lambda_client.get_function(FunctionName=name) yield name except lambda_client.exceptions.ResourceNotFoundException: polite_print( quiet, f"{name} not deployed, or does not deploy a lambda function")
def set_secret(argv: typing.List[str], args: argparse.Namespace): """Set the value of the secret variable.""" secret_name = fix_secret_variable_prefix(args.secret_name) # Decide what to use for infile secret_val = None if args.infile is not None: if os.path.isfile(args.infile): with open(args.infile, 'r') as f: secret_val = f.read() else: raise RuntimeError( f"Error: specified input file {args.infile} does not exist!") else: # Use stdin (input piped to this script) as secret value. # stdin provides secret value, flag --secret-name provides secret name. if not select.select([sys.stdin], [], [], 0.0)[0]: raise RuntimeError( "Error: stdin was empty! A secret value must be provided via stdin" ) secret_val = sys.stdin.read() # Create or update try: # Start by trying to get the secret variable sm_client.get_secret_value(SecretId=secret_name) except ClientError: # A secret variable with that name does not exist, so create it if args.dry_run: polite_print( args.quiet, f"Secret variable {secret_name} not found in secrets manager, dry-run creating it" ) else: if args.infile: polite_print( args.quiet, f"Secret variable {secret_name} not found in secrets manager, creating from input file" ) else: polite_print( args.quiet, f"Secret variable {secret_name} not found in secrets manager, creating from stdin" ) sm_client.create_secret(Name=secret_name, SecretString=secret_val) else: # Get operation was successful, secret variable exists # Prompt the user before overwriting, unless --force flag present if not args.force and not args.dry_run: # Prompt the user to make sure they really want to do this confirm = f""" *** WARNING!!! *** The secret you are setting currently has a value. Calling the set secret function will overwrite the current value of the secret! Note: - To do a dry run of this operation first, use the --dry-run flag. - To ignore this warning, use the --force flag. Are you really sure you want to update the secret? (Type 'y' or 'yes' to confirm): """ response = input(confirm) if response.lower() not in ["y", "yes"]: print("You safely aborted the set secret operation!") sys.exit(0) if args.dry_run: polite_print( args.quiet, f"Secret variable {secret_name} found in secrets manager, dry-run updating it" ) else: if args.infile: polite_print( args.quiet, f"Secret variable {secret_name} found in secrets manager, updating from input file" ) else: polite_print( args.quiet, f"Secret variable {secret_name} found in secrets manager, updating from stdin" ) sm_client.update_secret(SecretId=secret_name, SecretString=secret_val)
def lambda_update(argv: typing.List[str], args: argparse.Namespace): """ Update the lambda environment stored in the SSM store under $DSS_DEPLOYMENT_STAGE/environment by taking values from the current (local) environment. If --update-deployed flag is provided, also update the environment of deployed lambda functions. """ if not args.force and not args.dry_run: # Prompt the user to make sure they really want to do this confirm = f""" *** WARNING!!! *** Calling the lambda update function will overwrite the current values of the lambda function environment stored in the SSM store at $DSS_DEPLOY_STAGE/environment with local values from environment variables on your machine. Note: - To do a dry run of this operation first, use the --dry-run flag. - To ignore this warning, use the --force flag. - To see the current environment stored in the SSM store, run: ./dss-ops.py lambda environment Are you really sure you want to update the SSM store environment? (Type 'y' or 'yes' to confirm): """ response = input(confirm) if response.lower() not in ["y", "yes"]: raise RuntimeError( "You safely aborted the lambda update operation!") # Only elasticsearch endpoint and admin emails are updated dynamically, # everything else comes from the local environment. local_env = get_local_lambda_environment() local_env["DSS_ES_ENDPOINT"] = get_elasticsearch_endpoint() local_env["ADMIN_USER_EMAILS"] = get_admin_emails() if args.dry_run: polite_print( args.quiet, f"Dry-run redeploying local environment to lambda function environment " "stored in SSM store under $DSS_DEPLOYMENT_STAGE/environment") else: set_ssm_environment(local_env) polite_print( args.quiet, f"Finished redeploying local environment to lambda function environment " "stored in SSM store under $DSS_DEPLOY_STAGE/environment") # Optionally, update environment of each deployed lambda if args.update_deployed: for lambda_name in get_deployed_lambdas(): # Add the new variable to each lambda's environment lambda_env = get_deployed_lambda_environment(lambda_name) lambda_env.update(local_env) if args.dry_run: polite_print( args.quiet, f"Dry-run redeploying lambda function environment from SSM store for {lambda_name}" ) else: set_deployed_lambda_environment(lambda_name, lambda_env) polite_print( args.quiet, f"Finished redeploying lambda function environment from SSM store for {lambda_name}" )