Repository: PrefectHQ/prefect

Issue:
Add new AWS Secrets Manager Task
To add to our non-Prefect Cloud [Secret Task library](https://docs.prefect.io/api/latest/tasks/secrets.html), we should add a new AWS Secrets Manager Task.  This should only involve exposing the following three kwargs at initialization:
- `secret_id`
- `version_id`
- `version_stage`

as described here: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.get_secret_value


Discussion:
👋 Hi there! Just for some clarification questions on this:

* After reading through the code, it looks like we're going to need AWS Secrets to make the request to Secrets Manager. Are the AWS Lambda tasks good examples for doing this?
* There are multiple places where a secret value can be stored (and due to my lack of familiarity with it, maybe both?). Is there a preference towards creating a parameter indicating which one to grab vs returning a dictionary with both?
* Looking at the EnvVarSecret as a starting point, it looks like there's a flag of whether an error should be thrown or not depending on whether the secret can be retrieved. Since retrieving the secret from AWS includes 2 steps, one based on the authentication via access keys and one actually getting the secret, should an error occurring at either step be raised if a `raise_if_missing` flag is set to True? Or should it only be specifically if authentication was successful and there is not secret?

@alexisprince1994 In a recent release there was a new AWS utility added to handle authentication for places where credentials are used for the boto3 client https://github.com/PrefectHQ/prefect/blob/master/src/prefect/utilities/aws.py and I think it might address some of your questions as the tasks now make use of this utility as well as prefect Secret tasks.

Resolving patch:
diff --git a/src/prefect/tasks/aws/__init__.py b/src/prefect/tasks/aws/__init__.py
--- a/src/prefect/tasks/aws/__init__.py
+++ b/src/prefect/tasks/aws/__init__.py
@@ -12,6 +12,7 @@
         LambdaList,
     )
     from prefect.tasks.aws.step_function import StepActivate
+    from prefect.tasks.aws.secrets_manager import AWSSecretsManager
 except ImportError:
     raise ImportError(
         'Using `prefect.tasks.aws` requires Prefect to be installed with the "aws" extra.'
diff --git a/src/prefect/tasks/aws/secrets_manager.py b/src/prefect/tasks/aws/secrets_manager.py
new file mode 100644
--- /dev/null
+++ b/src/prefect/tasks/aws/secrets_manager.py
@@ -0,0 +1,55 @@
+import json
+
+from prefect.tasks.secrets.base import SecretBase
+from prefect.utilities.aws import get_boto_client
+from prefect.utilities.tasks import defaults_from_attrs
+
+
+class AWSSecretsManager(SecretBase):
+    """
+    Task for retrieving secrets from an AWS Secrets Manager and returning it as a dictionary.
+    Note that all initialization arguments can optionally be provided or overwritten at runtime.
+
+    For authentication, there are two options: you can set the `AWS_CREDENTIALS` Prefect Secret
+    containing your AWS access keys which will be passed directly to the `boto3` client, or you
+    can [configure your flow's runtime
+    environment](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#guide-configuration)
+    for `boto3`.
+
+    Args:
+        - secret (str, optional): the name of the secret to retrieve
+        - **kwargs (dict, optional): additional keyword arguments to pass to the
+            Task constructor
+    """
+
+    def __init__(self, secret: str = None, **kwargs):
+        self.secret = secret
+        super().__init__(**kwargs)
+
+    @defaults_from_attrs("secret")
+    def run(self, secret: str = None, credentials: str = None) -> dict:
+        """
+        Task run method.
+
+        Args:
+            - secret (str): the name of the secret to retrieve
+            - credentials (dict, optional): your AWS credentials passed from an upstream
+                Secret task; this Secret must be a JSON string
+                with two keys: `ACCESS_KEY` and `SECRET_ACCESS_KEY` which will be
+                passed directly to `boto3`.  If not provided here or in context, `boto3`
+                will fall back on standard AWS rules for authentication.
+
+        Returns:
+            - dict: the contents of this secret, as a dictionary
+        """
+
+        if secret is None:
+            raise ValueError("A secret name must be provided.")
+
+        secrets_client = get_boto_client("secretsmanager", credentials=credentials)
+
+        secret_string = secrets_client.get_secret_value(SecretId=secret)["SecretString"]
+
+        secret_dict = json.loads(secret_string)
+
+        return secret_dict


Test patch:
diff --git a/tests/tasks/aws/test_secrets_manager.py b/tests/tasks/aws/test_secrets_manager.py
new file mode 100644
--- /dev/null
+++ b/tests/tasks/aws/test_secrets_manager.py
@@ -0,0 +1,23 @@
+from unittest.mock import MagicMock
+
+import pytest
+
+import prefect
+from prefect.tasks.aws import AWSSecretsManager
+from prefect.utilities.configuration import set_temporary_config
+
+
+class TestAWSSecretsManager:
+    def test_initialization(self):
+        task = AWSSecretsManager("test")
+
+    def test_initialization_passes_to_task_constructor(self):
+        task = AWSSecretsManager(name="test", tags=["AWS"])
+        assert task.name == "test"
+        assert task.tags == {"AWS"}
+
+    def test_raises_if_secret_not_eventually_provided(self):
+        task = AWSSecretsManager()
+
+        with pytest.raises(ValueError, match="secret"):
+            task.run()