Skip to content

MFA

aws-annoying mfa configure

Configure AWS profile for MFA.

This command retrieves temporary MFA credentials using the provided source profile (--mfa-source-profile) and MFA token code then updates the specified AWS profile with these credentials.

You can configure it interactively, by omitting the options, or provide them directly via command-line options.

aws-annoying mfa configure

If you want to use MFA as primary authentication method for an AWS profile, you can configure it to save the credentials to the default profile.

aws configure --profile mfa
aws-annoying mfa configure \
    --mfa-profile default \
    --mfa-source-profile mfa
Source code in aws_annoying/cli/mfa/configure.py
@mfa_app.command()
def configure(  # noqa: PLR0913
    ctx: typer.Context,
    *,
    mfa_profile: Optional[str] = typer.Option(
        None,
        help="The MFA profile to configure.",
    ),
    mfa_source_profile: Optional[str] = typer.Option(
        None,
        help="The AWS profile to use to retrieve MFA credentials.",
    ),
    mfa_serial_number: Optional[str] = typer.Option(
        None,
        help="The MFA device serial number. It is required if not persisted in configuration.",
        show_default=False,
    ),
    mfa_token_code: Optional[str] = typer.Option(
        None,
        help="The MFA token code.",
        show_default=False,
    ),
    aws_credentials: Path = typer.Option(  # noqa: B008
        "~/.aws/credentials",
        help="The path to the AWS credentials file.",
    ),
    aws_config: Path = typer.Option(  # noqa: B008
        "~/.aws/config",
        help="The path to the AWS config file. Used to persist the MFA configuration.",
    ),
    aws_config_section: str = typer.Option(
        "aws-annoying:mfa",
        help="The section in the AWS config file to persist the MFA configuration.",
    ),
    persist: bool = typer.Option(
        True,  # noqa: FBT003
        help="Persist the MFA configuration.",
    ),
) -> None:
    r"""Configure AWS profile for MFA.

    This command retrieves temporary MFA credentials using the provided source profile (`--mfa-source-profile`)
    and MFA token code then updates the specified AWS profile with these credentials.

    You can configure it interactively, by omitting the options, or provide them directly via command-line options.

    ```shell
    aws-annoying mfa configure
    ```

    If you want to use MFA as primary authentication method for an AWS profile, you can configure
    it to save the credentials to the default profile.

    ```shell
    aws configure --profile mfa
    aws-annoying mfa configure \
        --mfa-profile default \
        --mfa-source-profile mfa
    ```
    """
    dry_run = ctx.meta["dry_run"]

    # Expand user home directory
    aws_credentials = aws_credentials.expanduser()
    aws_config = aws_config.expanduser()

    # Load configuration
    mfa_config, exists = MfaConfig.from_ini_file(aws_config, aws_config_section)
    if exists:
        logger.info("Loaded MFA configuration from AWS config (%s).", aws_config)

    mfa_profile = (
        mfa_profile
        or mfa_config.mfa_profile
        # _
        or Prompt.ask("👤 Enter name of MFA profile to configure", default="mfa")
    )
    mfa_source_profile = (
        mfa_source_profile
        or mfa_config.mfa_source_profile
        or Prompt.ask("👤 Enter AWS profile to use to retrieve MFA credentials", default="default")
    )
    mfa_serial_number = (
        mfa_serial_number
        or mfa_config.mfa_serial_number
        # _
        or Prompt.ask("🔒 Enter MFA serial number")
    )
    mfa_token_code = (
        mfa_token_code
        # _
        or Prompt.ask("🔑 Enter MFA token code")
    )

    # Get credentials
    logger.info("Retrieving MFA credentials using profile [bold]%s[/bold]", mfa_source_profile)
    session = boto3.session.Session(profile_name=mfa_source_profile)
    sts = session.client("sts")
    response = sts.get_session_token(
        SerialNumber=mfa_serial_number,
        TokenCode=mfa_token_code,
    )
    credentials = response["Credentials"]

    # Update MFA profile in AWS credentials
    logger.warning(
        "Updating MFA profile ([bold]%s[/bold]) to AWS credentials ([bold]%s[/bold])",
        mfa_profile,
        aws_credentials,
    )
    if not dry_run:
        update_credentials(
            aws_credentials,
            mfa_profile,  # type: ignore[arg-type]
            access_key=credentials["AccessKeyId"],
            secret_key=credentials["SecretAccessKey"],
            session_token=credentials["SessionToken"],
        )

    # Persist MFA configuration
    if persist:
        logger.info(
            "Persisting MFA configuration in AWS config (%s), in [bold]%s[/bold] section.",
            aws_config,
            aws_config_section,
        )
        mfa_config.mfa_profile = mfa_profile
        mfa_config.mfa_source_profile = mfa_source_profile
        mfa_config.mfa_serial_number = mfa_serial_number
        if not dry_run:
            mfa_config.save_ini_file(aws_config, aws_config_section)
    else:
        logger.warning("MFA configuration not persisted.")