Création d’une fonction externe sur AWS

Ce document montre une façon de créer une fonction externe sur Amazon AWS, notamment :

  • Création d’un service distant (fonction Lambda sur AWS)

  • Création d’un service proxy sur AWS API Gateway.

  • Création d’une intégration API dans Snowflake.

  • Création d’une fonction externe dans Snowflake.

  • Appel de la fonction externe.

Ces instructions supposent que vous connaissez déjà l’administration d’Amazon AWS. Ces instructions spécifient les étapes générales que vous devez exécuter, mais ne vous guident pas dans les détails de la console d’administration AWS car les détails pourraient changer.

Dans ce chapitre :

Conditions préalables

Vous avez besoin des éléments suivants :

  • Un compte avec AWS, y compris des privilèges pour :

    • Créer des rôles AWS via IAM (gestion des identités et des accès).

    • Créer des fonctions Lambda.

    • Créer un point de terminaison API Gateway.

  • Un compte Snowflake dans lequel vous disposez des privilèges ACCOUNTADMIN ou un rôle avec le privilège CREATE INTEGRATION.

Ce document suppose que vous êtes un administrateur AWS expérimenté.

Astuce

Lors de la création de la fonction externe, vous devez copier les informations liées à l’authentification de Snowflake vers votre plate-forme Cloud et de votre fenêtre d’administration de plate-forme Cloud vers Snowflake.

Lorsque vous suivez les instructions ci-dessous, Snowflake vous recommande d’enregistrer les informations dans certaines étapes pour les utiliser dans les étapes suivantes :

Cloud Platform (IAM) Account Id: _____________________________________________
Lambda Function Name...........: _____________________________________________
New IAM Role Name..............: _____________________________________________
Cloud Platform (IAM) Role ARN..: _____________________________________________
Proxy Service Resource Name....: _____________________________________________
Resource Invocation URL........: _____________________________________________
Method Request ARN.............: _____________________________________________
API_AWS_IAM_USER_ARN...........: _____________________________________________
API_AWS_EXTERNAL_ID............: _____________________________________________

Etape 1 : Créer le service distant (fonction Lambda sur AWS)

Il existe plusieurs façons de créer un service distant. Cette section montre comment créer un service distant implémenté en tant que fonction Python s’exécutant dans AWS Lambda.

Cet exemple de fonction en langage Python renvoie simplement ses entrées.

Cette fonction accepte les données au même format que celui envoyé par Snowflake et renvoie les données au même format que celui lu par Snowflake.

Cette fonction Python reçoit deux paramètres, event et context. Le paramètre event comprend de nombreux sous-champs, dont l’un est body. Le corps est un dictionnaire qui comprend une clé nommée data ; la valeur correspondante pour data est une chaîne contenant les données envoyées par Snowflake au format JSON. Etant donné que AWS Lambda traite commodément la requête HTTP POST envoyée par Snowflake, extrait le corps et transmet le corps à l’intérieur du paramètre d’événement, cet exemple de fonction n’a pas besoin d’analyser l’intégralité de la requête HTTP POST.

Après que cette fonction Python a extrait le corps sous forme de chaîne, la fonction appelle une fonction de bibliothèque JSON pour convertir la chaîne en une structure de données Python. La fonction extrait ensuite des lignes individuelles de cette structure de données, les traite et renvoie une valeur pour chacune d’entre elles.

Le JSON pour une valeur de retour typique d’une fonction AWS Lambda ressemble à ceci :

{
"statusCode": <http_status_code>,
"body":
        {
            "data":
                  [
                      [ 0, <value> ],
                      [ 1, <value> ]
                      ...
                  ]
        }
}

Les données de la valeur de retour correspondent au format décrit précédemment pour les données d’entrée. Sur AWS, la convention pour un service compatible HTTP est de renvoyer le corps à l’intérieur d’un objet JSON qui inclut également le code de statut HTTP.

Pour créer cette fonction Lambda AWS, procédez comme suit :

  1. Connectez-vous à la console de gestion AWS, si ce n’est pas déjà fait.

  2. Enregistrez votre ID de compte de plate-forme Cloud dans le modèle ci-dessus.

  3. Sélectionnez « Lambda ».

  4. Sélectionnez « Créer une fonction ».

  5. Saisissez un nom de fonction.

    Enregistrez ce nom dans le modèle ci-dessus sur la ligne intitulée « Nom de la fonction Lambda ».

  6. Sélectionnez le langage à utiliser. Pour cet exemple, choisissez Python 3.7.

  7. Choisissez ou créez un rôle d’exécution pour cette fonction.

    Sélectionnez la ou les options appropriées, généralement « Créer un nouveau rôle avec des autorisations Lambda de base ».

    (Ce rôle est distinct de votre rôle de compte Cloud et distinct de vos rôles Snowflake.)

  8. Ouvrez la fenêtre dans laquelle vous pouvez coller le code source de votre fonction.

  9. Entrez le code de la fonction. Si vous n’avez pas encore écrit votre propre fonction, vous pouvez remplacer le code de fonction par défaut par le code ci-dessous, qui fera écho à son entrée. Vous pouvez remplacer ou mettre à jour ce code ultérieurement lorsque vous serez prêt à créer une fonction personnalisée.

    import json
    
    def lambda_handler(event, context):
    
        # 200 is the HTTP status code for "ok".
        status_code = 200
    
        # The return value will contain an array of arrays (one inner array per input row).
        array_of_rows_to_return = [ ]
    
        try:
            # From the input parameter named "event", get the body, which contains
            # the input rows.
            event_body = event["body"]
    
            # Convert the input from a JSON string into a JSON object.
            payload = json.loads(event_body)
            # This is basically an array of arrays. The inner array contains the
            # row number, and a value for each parameter passed to the function.
            rows = payload["data"]
    
            # For each input row in the JSON object...
            for row in rows:
                # Read the input row number (the output row number will be the same).
                row_number = row[0]
    
                # Read the first input parameter's value. For example, this can be a
                # numeric value or a string, or it can be a compound value such as
                # a JSON structure.
                input_value_1 = row[1]
    
                # Read the second input parameter's value.
                input_value_2 = row[2]
    
                # Compose the output based on the input. This simple example
                # merely echoes the input by collecting the values into an array that
                # will be treated as a single VARIANT value.
                output_value = [input_value_1, input_value_2]
    
                # Put the returned row number and the returned value into an array.
                row_to_return = [row_number, output_value]
    
                # ... and add that array to the main array.
                array_of_rows_to_return.append(row_to_return)
    
            json_compatible_string_to_return = json.dumps({"data" : array_of_rows_to_return})
    
        except Exception as err:
            # 400 implies some type of error.
            status_code = 400
            # Tell caller what this function could not handle.
            json_compatible_string_to_return = event_body
    
        # Return the return value and HTTP status code.
        return {
            'statusCode': status_code,
            'body': json_compatible_string_to_return
        }
    
  10. Facultatif, mais fortement recommandé : testez votre fonction.

    Pour l’exemple de fonction Python fournie par Snowflake, utilisez les données de test suivantes (remplacez toutes les données par défaut par les données ci-dessous) :

    {
      "body":
        "{ \"data\": [ [ 0, 43, \"page\" ], [ 1, 42, \"life, the universe, and everything\" ] ] }"
    }
    

    Les résultats d’exécution doivent être similaires à ceci :

    Response:
    {
      "statusCode": 200,
      "body": "{\"data\": [[0, [43, \"page\"]], [1, [42, \"life, the universe, and everything\"]]]}"
    }
    ...
    

Si le précédent a fonctionné correctement, vous disposez maintenant d’une fonction AWS Lambda que vous pouvez utiliser comme service distant pour votre fonction externe.

Etape 2 : Configurer le service proxy (API Gateway sur AWS) et créer l’intégration API (dans Snowflake)

La configuration de AWS API Gateway en tant que service proxy de plate-forme Cloud nécessite plusieurs étapes, notamment :

  • Création d’un nouveau rôle IAM dans votre compte de plate-forme Cloud.

  • Création d’une API Gateway (le service proxy) et configuration de celle-ci.

  • Sécurisation de votre point de terminaison de service proxy.

  • Création d’un objet d’intégration API dans Snowflake.

  • Configuration d’une relation de confiance entre Snowflake et le nouveau rôle IAM.

Les étapes pour les créer sont entrelacées, car l’intégration API a besoin d’informations de la plate-forme Cloud (ARN du rôle de la plate-forme Cloud), et l” API Gateway a besoin des API_AWS_EXTERNAL_ID et API_AWS_IAM_USER_ARN de l’intégration API.

Créez un nouveau rôle IAM dans votre compte Cloud Platform

Pour que Snowflake s’authentifie auprès de votre compte AWS, un utilisateur IAM appartenant à Snowflake doit être autorisé à assumer un rôle IAM dans votre compte AWS. Pour ce faire, une relation de confiance doit être établie. Pour établir la relation de confiance, vous devez créer un rôle IAM sur votre compte AWS et le configurer avec l” ARN de l’utilisateur IAM appartenant à Snowflake, et vous devez créer un objet d’intégration API dans Snowflake et configurer l’objet d’intégration API avec les informations sur le rôle IAM à assumer.

  1. Créez un nouveau rôle Cloud Platform via IAM (gestion des identités et des accès).

  2. Lorsque vous devez sélectionner le type d’entité de confiance, choisissez « Un autre compte AWS ».

  3. Lorsque vous êtes invité à « spécifier les comptes pouvant utiliser ce rôle », collez l’ID de compte Cloud Platform précédemment enregistré.

  4. Cliquez sur « Suivant : Autorisations ».

  5. Définissez les autorisations (« Attacher les stratégies d’autorisations ») si nécessaire.

  6. Entrez un nom de rôle.

    Enregistrez le nom du rôle en tant que « Nouveau nom de rôle IAM ».

  7. Après avoir créé le rôle :

    • Enregistrez l”« ARN du rôle » comme « ARN de rôle Cloud Platform (IAM) ».

Créer et configurer une API dans API Gateway (service proxy)

Assurez-vous que vous utilisez un point de terminaison régional. L’aperçu de cette fonctionnalité prend en charge uniquement les points de terminaison régionaux pour AWS API Gateway. (Pour une description des différents types de points de terminaison, voir https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-basic-concept.html)

Bien que le seul type de service proxy actuellement pris en charge par Snowflake soit AWS API Gateway, vous pouvez créer une fonction externe sur des instances de Snowflake non hébergées par AWS. Si votre entrepôt virtuel est sur Azure ou GCP, vous pouvez créer une fonction externe qui accède à un service distant via AWS API Gateway.

Les étapes pour créer une API Gateway sont les suivantes :

  1. Sélectionnez « API Gateway ».

  2. Sélectionnez « Créer API ».

  3. Trouvez « REST API » et cliquez sur son bouton « Build ».

  4. Choisissez le protocole REST.

  5. Sélectionnez l’option « Nouvelle API ».

  6. Terminez la création de la nouvelle API.

  7. Créez une ressource.

    Enregistrez le nom de la ressource en tant que « Nom de ressource du serveur proxy » ; vous en aurez besoin plus tard.

  8. Sélectionnez « Créer une méthode » pour cette ressource et spécifiez l’option POST.

    Le type d’intégration doit être la fonction Lambda.

  9. Cochez la case pour utiliser l’intégration du proxy Lambda.

  10. Dans le champ relatif à la fonction Lambda, collez le nom de la fonction Lambda que vous avez enregistré précédemment.

  11. Enregistrez.

  12. Sélectionnez l’action pour déployer l’API.

  13. Sélectionnez ou créez une zone de préparation pour cette fonction.

  14. Cliquez sur POST puis enregistrez « Invoke URL » pour la requête POST ; placez ceci dans le champ « Resource Invocation URL » du modèle.

    Assurez-vous que l’URL d’appel inclut le nom de la ressource ; si ce n’est pas le cas, vous avez peut-être cliqué sur l’URL d’appel pour la zone de préparation plutôt que pour la ressource.

Sécurisez votre point de terminaison du proxy AWS API Gateway

Pour un aperçu de la sécurisation des points de terminaison du service proxy, voir Sécurisez votre point de terminaison de service proxy.

Pour sécuriser un point de terminaison AWS API Gateway :

  1. Spécifiez que la « demande de méthode » nécessite une autorisation AWS_IAM.

    Enregistrez l’ARN de la demande de méthode dans le modèle.

  2. Définissez la politique de ressource pour API Gateway afin de spécifier qui est autorisé à appeler le point de terminaison de la passerelle.

    La politique de ressource ressemble généralement à ce qui suit (sauf que le principal et la ressource doivent être personnalisés) :

    {
        "Version": "2012-10-17",
        "Statement":
        [
            {
            "Effect": "Allow",
            "Principal":
                {
                "AWS": "arn:aws:sts::<12-digit-number>:assumed-role/<external_function_role>/snowflake"
                },
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:us-west-2:123456789012:ljkfds890a/*/POST/MyResourceName"
            }
        ]
    }
    

    Remplacez les parties suivantes de la politique de ressource :

    • Remplacez le <nombre à 12chiffres> par le nombre à 12 chiffres de l’ARN du rôle Cloud Platform (IAM) En règle générale, il s’agit du même que celui de l’ID du compte Cloud Platform (IAM).

    • Remplacez <rôle_fonction_externe> par le nom de rôle de l’ARN du rôle Cloud Platform (IAM). En règle générale, il s’agit du même nom que le nouveau nom de rôle IAM.

      Par exemple, si votre nom de rôle Cloud Platform (IAM) est :

      arn:aws:iam::987654321098:role/MyNewIAMRole
      

      alors le résultat devrait être :

      "AWS": "arn:aws:sts::987654321098:assumed-role/MyNewIAMRole/snowflake"
      
    • La « ressource » doit être définie sur l’ARN de la demande de méthode pour la commande POST de la ressource.

      Note

      La définition de la ressource sur l’ARN de la demande de méthode spécifie que le service proxy doit autoriser les appels uniquement vers la ressource spécifiée. Il est possible de spécifier un sous-ensemble de l’ARN de la demande de méthode comme préfixe, ce qui permet d’appeler plusieurs ressources à partir du même service proxy.

      Par exemple, si l’ARN de la demande de méthode est :

      arn:aws:execute-api:us-west-1:123456789012:a1b2c3d4e5/*/POST/MyResource
      

      alors vous pouvez spécifier simplement le préfixe suivant :

      arn:aws:execute-api:us-west-1:123456789012:a1b2c3d4e5/*/
      
  3. Déployez l’API.

Dans les étapes suivantes, vous créez un objet d’intégration API Snowflake. Ne fermez pas la fenêtre d’administration de Cloud Platform maintenant ; vous devez y revenir plus tard.

Créer un objet d’intégration API dans Snowflake

  1. Ouvrez (si vous ne l’avez pas déjà fait) une session Snowflake, généralement une session GUI.

  2. Utilisez un rôle Snowflake avec des privilèges ACCOUNTADMIN ou un rôle avec le privilège CREATE INTEGRATION, par exemple :

    use role has_accountadmin_privileges;
    
  3. Tapez la commande CREATE API INTEGRATION pour créer une intégration API. La commande doit ressembler à ce qui suit :

    CREATE OR REPLACE API INTEGRATION my_api_integration_01
      api_provider = aws_api_gateway
      api_aws_role_arn = '<cloud_platform_role_ARN>'
      enabled = true
      api_allowed_prefixes = ('https://')
    ;
    

    L’ARN <rôle_cloud_platform> doit être l’ARN du rôle Cloud Platform (IAM) que vous avez enregistré précédemment.

    Le champ préfixes_autorisés_api doit contenir l’URL de l’appel de ressource que vous avez enregistré précédemment.

    Vous pouvez également souhaiter personnaliser le nom de l’intégration API, plutôt que d’utiliser la valeur dans l’exemple.

    Voici un exemple d’une instruction CREATE API INTEGRATION complète :

    create or replace api integration demonstration_external_api_integration_01
        api_provider=aws_api_gateway
        api_aws_role_arn='arn:aws:iam::123456789012:role/my_cloud_account_role'
        api_allowed_prefixes=('https://xyz.execute-api.us-west-2.amazonaws.com/production/')
        enabled=true;
    
  4. Exécutez la commande CREATE API INTEGRATION que vous avez tapée ci-dessus.

  5. Exécutez la commande DESCRIBE INTEGRATION.

    DESCRIBE INTEGRATION <my_integration_name>;
    

    Par exemple :

    DESCRIBE INTEGRATION my_api_integration_01;
    
  6. Recherchez la propriété nommée « API_AWS_IAM_USER_ARN », puis enregistrez la valeur_propriété de cette propriété pour une utilisation ultérieure.

  7. Recherchez la propriété nommée « API_AWS_EXTERNAL_ID » et enregistrez la valeur_propriété de cette propriété pour une utilisation ultérieure.

    Notez que la valeur_propriété de API_AWS_EXTERNAL_ID se termine souvent par un signe égal (« = »). Ce signe égal fait partie de la valeur ; assurez-vous de le couper et de le coller avec le reste de la valeur_propriété.

Au cours des prochaines étapes, vous reviendrez à la fenêtre d’administration de Cloud Platform. Ne fermez pas votre fenêtre d’administration Snowflake maintenant ; vous devez y revenir plus tard.

Configurer la relation de confiance entre Snowflake et le nouveau rôle IAM

Dans la console de gestion AWS :

  1. Sélectionnez IAM.

  2. Sélectionnez les rôles.

  3. Recherchez le nouveau nom de rôle IAM que vous avez créé et enregistré précédemment, puis sélectionnez-le.

  4. Cliquez sur l’onglet « Relations de confiance », puis cliquez sur le bouton pour modifier la relation de confiance.

    Cela devrait ouvrir le document de politique dans lequel vous pouvez ajouter des informations d’authentification.

  5. Dans le document de stratégie, recherchez le champ Statement.Principal.AWS et remplacez la valeur (pas la clé) par l’API_AWS_IAM_USER_ARN que vous avez enregistré précédemment.

  6. Recherchez le champ Statement.Condition. Initialement, cela ne devrait contenir que des accolades (« {} »).

  7. Collez ce qui suit entre les accolades : "StringEquals": { "sts:ExternalId": "xxx" }.

  8. Remplacez le « xxx » par la valeur de API_AWS_EXTERNAL_ID que vous avez enregistré précédemment.

  9. Une fois que vous avez terminé de modifier le document de stratégie pour la relation de confiance, cela devrait ressembler à ce qui suit :

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::1234567898012:user/development/development_user"
          },
          "Action": "sts:AssumeRole",
          "Condition": {"StringEquals": { "sts:ExternalId": "EXTERNAL_FUNCTIONS_SFCRole=3_8Hcmbi9halFOkt+MdilPi7rdgOv=" }}
        }
      ]
    }
    
  10. Cliquez sur le bouton pour mettre à jour la stratégie de confiance.

Etape 3 : Créer la fonction externe

Revenez maintenant à votre fenêtre d’administration Snowflake (où vous avez précédemment tapé la commande CREATE API INTEGRATION).

  1. Tapez la commande CREATE EXTERNAL FUNCTION. Cela devrait ressembler à ce qui suit :

    CREATE EXTERNAL FUNCTION my_external_function(n INTEGER, v VARCHAR)
        RETURNS VARIANT
        API_INTEGRATION = <api_integration_name>
        AS '<invocation_url>'
        ;
    

    La valeur <api_integration_name> doit contenir le nom de l’intégration API que vous avez créée précédemment.

    La valeur <invocation_url> doit être l’URL d’appel de ressource que vous avez enregistré précédemment. Assurez-vous que cela inclut le nom de la ressource, pas seulement le nom de la zone de préparation.

    Cet exemple transmet deux arguments (un INTEGER et un VARCHAR) car ce sont les arguments que le service distant attend. Lorsque vous créez votre propre service distant, vous transmettez les arguments appropriés pour votre service distant.

  2. Si vous n’avez pas encore exécuté la commande CREATE EXTERNAL FUNCTION que vous avez tapée ci-dessus, exécutez-la maintenant.

Etape 4 : Appeler la fonction externe

  1. Le cas échéant, accordez le privilège USAGE sur la fonction externe à un ou plusieurs rôles afin que ces rôles puissent appeler la fonction externe. (Un rôle doit avoir des privilèges USAGE ou OWNERSHIP sur cette fonction externe.)

  2. Exécutez votre fonction en appelant :

    SELECT my_external_function(99, 'Luftballoons');
    

Dépannage

Symptôme :

Lorsque vous essayez d’appeler la fonction externe, vous obtenez le message d’erreur suivant :

« Erreur d’exécution SQL : erreur en supposant AWS_ROLE. Veuillez vérifier que le rôle et l’IDexterne sont correctement configurés dans votre politique AWS. »

Causes possibles :

  • Dans la stratégie de relation de confiance AWS pour votre rôle, l’ARN AWS est incorrect. Les causes possibles de cela comprennent :

    • Vous ne l’avez pas défini.

    • Vous l’avez défini, mais vous avez utilisé l’ARN du rôle Cloud Platform (incorrect) au lieu de l’ARN utilisateur, que vous pouvez voir à partir de la commande « DESCRIBE INTEGRATION » dans Snowflake. Assurez-vous que vous utilisez API_AWS_IAM_USER_ARN plutôt que API_AWS_ROLE_ARN.

  • Dans votre stratégie de relation de confiance AWS, le std:ExternalId est incorrect. Les causes possibles de cela comprennent :

    • Vous ne l’avez pas défini.

    • Vous avez recréé l’objet d’intégration API. La recréation de l’objet API modifie son ID externe.

Symptôme :

Lorsque vous essayez d’appeler la fonction externe, vous obtenez le message d’erreur suivant :

Echec de la demande pour la fonction externe <function_name>. Erreur : 403 “{« Message » : « L’utilisateur : <ARN> n’est pas autorisé à exécuter : execute-api:Invoke sur la ressource : <MethodRequestARN> »}”

Cause(s) possible(s) :

Utilisation du mauvais ARN de rôle dans la stratégie de ressources pour API Gateway.

Solution(s) possible(s) :

Assurez-vous que vous avez suivi le modèle, mais :

  • Le numéro à 12 chiffres a été remplacé par l’ID de compte IAM.

  • Le <external_user_role> a été remplacé par le nouveau nom de rôle IAM.

Assurez-vous également que la ressource est correcte ; il doit s’agir de l’ARN de la demande de méthode.