Blog

AWS Cognito Backup-Wiederherstellungslösung mit Python3 Lambda

Bild des ITGix-Teams
ITGix-Team
Passionierte DevOps- und Cloud-Ingenieure
18.04.2022
Lesezeit: 13 Minuten.
Zuletzt aktualisiert am: 28.02.2025

Inhaltsübersicht

Quelldateien

Sie finden alle Quelldateien für diese Sicherungs- und Wiederherstellungslösung in unserem offiziellen Repository und können sie herunterladen, verändern und für nicht-kommerzielle Zwecke verwenden.

Sie finden sie hier.

Einführung in Amazonas Kognito

Mit Amazon Cognito können Sie ganz einfach Benutzeranmeldung, Anmeldung und Zugriffskontrolle zu Ihren mobilen und Webanwendungen hinzufügen. Amazon Cognito lässt sich auf Millionen von Benutzern skalieren und ist hochgradig zugänglich, da es die Anmeldung bei sozialen Identitätsanbietern wie Facebook, Apple, Google und Amazon sowie bei Unternehmensidentitätsanbietern über OpenID Connect und SAML 2.0 unterstützt.  

Wenn Sie jedoch einen Amazon Cognito User Pool sichern müssen, werden Sie feststellen, dass es keine native Lösung von Amazon gibt. Glücklicherweise verfügt Amazon User Pool über eine sehr flexible API, die wir verwenden werden, um unsere Benutzer zu sichern.

Es ist von entscheidender Bedeutung, alle Benutzerprofile und verknüpften Daten zu sichern, da dies eine wichtige Rolle bei der Vermeidung von Unfällen (z. B. unbeabsichtigtem Datenverlust) spielt. Darüber hinaus sind sie auch in anderen Fällen unerlässlich, z. B. bei der Migration von Benutzerdaten in einen neuen Benutzerpool. Aus diesen Gründen möchte ich Ihnen heute meine selbst entwickelte Lösung vorstellen.

Maßgeschneiderte Backup-Wiederherstellungslösung für Amazon Cognito-Benutzer

Die Verwaltung von Daten ist eine schwierige Aufgabe, und genau dann kommen Backups zur Hilfe und verhindern, dass sie Probleme verursachen. Aber wie kann man Backups sicherstellen, wenn der Dienst, auf den Sie sich verlassen, keine Backup-Lösung anbietet? Die Antwort ist ziemlich einfach: Bauen Sie selbst eine! Und genau das werden wir in diesem Lernprogramm tun.

Diese Lösung ist maßgeschneidert, aber so konzipiert, dass sie für eine Vielzahl von Anwendungsfällen geeignet ist. In unserer Schritt-für-Schritt-Anleitung erfahren Sie, wie Sie eine Sicherungs- und Wiederherstellungslösung für Amazon Cognito-Benutzer entwickeln.

In diesem Lernprogramm wird der Kern der Implementierung und nicht die gesamte Einrichtung der Architektur behandelt. Weitere Informationen finden Sie in der AWS-Dokumentation.

Ich werde nicht zeigen, wie man AWS Lambda mit einem Trigger-Ereignis verknüpft. In unserem Fall werden wir Ereignisse manuell auslösen.

Um unser Ziel zu erreichen, werden wir Python-basierte Lambda-Funktionen verwenden. Eine zum Exportieren von Cognito-Benutzern in eine CSV-Datei, zum Hochladen und zum Speichern in einem S3-Bucket. Eine, um das zweite Cognito zu bereinigen, und eine, um die CSV-Datei abzurufen und sie in die Backup-Cognito-Instanz zu importieren:

Architektur-Setup für eine maßgeschneiderte Backup-Wiederherstellungslösung für Amazon Cognito-Benutzer
Beispiel für Architektur

Das Skript verwendet das Python AWS SDK mit der Bezeichnung boto3, um ListUsers API-Aufrufe durchzuführen. Mit der boto3-Bibliothek müssen Sie die Methode list_users(**args) von CognitoIdentityProvider verwenden. Weitere Methoden und Details finden Sie auf der offiziellen Dokumentationsseite.

Bereitstellen von Export-Lambda

Erstellen Sie eine Rolle. Mit dieser Rolle werden wir der Lambda-Funktion einige Berechtigungen geben:

Berechtigungen für Deploy export Lambda

 !!! Denken Sie daran, die geringstmögliche Berechtigung zu verwenden, wenn Sie in der Produktion bereitstellen wollen. Für diesen Lehrgang verwenden wir die von AWS bereitgestellten Berechtigungsrichtlinien. Sie bieten vollen Zugriff auf Ressourcen. Es ist immer eine gute Praxis, eine benutzerdefinierte Richtlinie mit den einzigen Berechtigungen zu erstellen, die der Lambda benötigt.

S3 Bucket erstellen

Vergessen Sie bei der Erstellung des Buckets nicht, den öffentlichen Zugriff zu sperren. Da unsere CSV-Dateien private Daten enthalten werden.

Lambda-Funktion erstellen

Erstellen Sie eine Lambda-Funktion von Grund auf mit Python 3.9 als Laufzeitumgebung:

Lambda-Funktion erstellen

Fügen Sie 3 Umgebungsvariablen hinzu:

  • BACKUP_BUCKET - dies ist Ihr Ziel-Bucket
  • COGNITO_ID - die ID des Quell-Cognito-Benutzerpools
  • REGION - Region Cognito
Umgebungsvariablen für die Lambda-Funktion bearbeiten

Von Konfiguration - Allgemeine Konfiguration. 

Ändern Sie den Speicher und die Zeitüberschreitung, da die Standardwerte möglicherweise nicht ausreichen, wenn Sie eine große Anzahl von Benutzern haben.

Hängen Sie die erstellte Rolle an:

Grundeinstellungen bei der Erstellung von Lambda-Funktionen bearbeiten

Der letzte Punkt, der geändert werden muss, ist der Lambda-Handler. Dies ist die Abbildung auf die Datei und die Hauptmethode, die ausgeführt werden soll.

Laufzeiteinstellungen bei der Erstellung von Lambda-Funktionen

Fügen Sie nun den Code ein. Speichern und bereitstellen.

from email import header
import boto3
import datetime
from datetime import datetime
import time
import argparse
import traceback
import os

class Logs:
    @staticmethod
    def warning(logBody):
        print("[WARNING] {}".format(logBody))

    @staticmethod
    def critical(logBody):
        print("[CRITICAL] {}".format(logBody))

    @staticmethod
    def info(logBody):
        print("[INFO] {}".format(logBody))

class Cognito:
    USERPOOLID = ""
    REGION = ""
    ATTRIBUTES = ""

    # INIT userpool id, region and column names to be exported. 
    # Currently region is not used. The export lambda must be in the same region as the Cognito service
    def __init__(self, userPoolId, region, attributes):
        self.USERPOOLID = userPoolId
        self.REGION = region
        self.ATTRIBUTES = attributes
    
    def getAttributes (self):
        try:
            boto = boto3.client('cognito-idp')
            headers = boto.get_csv_header(
                UserPoolId=self.USERPOOLID
            )
            self.ATTRIBUTES = headers["CSVHeader"]
            return headers["CSVHeader"]
        except Exception as e:
            Logs.critical("There is an error listing users attributes")
            Logs.critical(traceback.format_exc())
            exit()

    # List all cognito users with only the predefined columns in ATTRIBUTES variable
    # As there is a limit of 60 users per query, a multiple queries are executed separated in so called pages.
    def listUsers (self):
        try:
            boto = boto3.client('cognito-idp')

            users = []
            next_page = None
            kwargs = {
                'UserPoolId': self.USERPOOLID,
            }
            users_remain = True
            while(users_remain):
                if next_page:
                    kwargs['PaginationToken'] = next_page
                response = boto.list_users(**kwargs)
                users.extend(response['Users'])
                next_page = response.get('PaginationToken', None)
                users_remain = next_page is not None
                # COOL DOWN BEFORE NEXT QUERY
                time.sleep(0.15)

            return users
        except Exception as e:
            Logs.critical("There is an error listing cognito users")
            Logs.critical(traceback.format_exc())
            exit()
    
    # List all cognito groups with only the predefined columns in ATTRIBUTES variable
    # As there is a limit of 60 groups per query, a multiple queries are executed separated in so called pages.
    def listGroups (self):
        try:
            boto = boto3.client('cognito-idp')
            groups = []
            next_page = None
            kwargs = {
                'UserPoolId': self.USERPOOLID,
            }
            groups_remain = True
            while(groups_remain):
                if next_page:
                    kwargs['NextToken'] = next_page
                response = boto.list_groups(**kwargs)
                groups.extend(response['Groups'])
                next_page = response.get('NextToken', None)
                groups_remain = next_page is not None
                # COOL DOWN BEFORE NEXT QUERY
                time.sleep(0.15)
            return groups
        except Exception as e:
            Logs.critical("There is an error listing cognito groups")
            Logs.critical(traceback.format_exc())
            exit()

class CSV:
    FILENAME = ""
    FOLDER = "/tmp/"
    ATTRIBUTES = ""
    CSV_LINES = []

    # INIT titles and filename
    def __init__(self, attributes, prefix):
        self.ATTRIBUTES = attributes
        self.FILENAME = "cognito_backup_" + prefix + "_" + datetime.now().strftime("%Y%m%d-%H%M") + ".csv"
        self.CSV_LINES = []

    # Generate CSV content. Every column in a row is split with ","
    # First are added the titles and then all users are looped.
    def generateUserContent (self, records):
        try:
            #ADD TITLES
            csv_new_line = self.addTitles()

            #ADD USERS
            for user in records:
                """ Fetch Required Attributes Provided """
                csv_line = csv_new_line.copy()
                for requ_attr in self.ATTRIBUTES:
                    csv_line[requ_attr] = ''
                    if requ_attr in user.keys():
                        csv_line[requ_attr] = str(user[requ_attr])
                        continue
                    for usr_attr in user['Attributes']:
                        if usr_attr['Name'] == requ_attr:
                            csv_line[requ_attr] = str(usr_attr['Value'])
                csv_line["cognito:mfa_enabled"] = "false"
                csv_line["cognito:username"] = csv_line["email"]
                self.CSV_LINES.append(",".join(csv_line.values()) + 'n')       
            return self.CSV_LINES
        except Exception as e:
            Logs.critical("Error generating csv content")
            Logs.critical(traceback.format_exc())
            exit()
    
    # Add titles to first row and return it as a template.
    def addTitles (self):
        csv_new_line = {self.ATTRIBUTES[i]: '' for i in range(len(self.ATTRIBUTES))}
        self.CSV_LINES.append(",".join(csv_new_line) + 'n')
        return csv_new_line
    
    # Generate CSV content. Every column in a row is split with ","
    # First are added the titles and then all groups are looped.
    def generateGroupContent (self, records):
        try:
            #ADD TITLES
            csv_new_line = self.addTitles()

            #ADD GROUPS
            for group in records:
                csv_line = {}
                for groupParam in self.ATTRIBUTES:
                    csv_line[str(groupParam)] = str(group[str(groupParam)])
                self.CSV_LINES.append(",".join(csv_line.values()) + 'n')
            return self.CSV_LINES
        except Exception as e:
            Logs.critical("Error generating csv content")
            Logs.critical(traceback.format_exc())
            exit()
    
    # Save generated content to a file.
    def saveToFile(self):
        try:
            csvFile = open(self.FOLDER + "/" + self.FILENAME, 'a')
            csvFile.writelines(self.CSV_LINES)
            csvFile.close()
        except Exception as e:
            Logs.critical("Error saving csv file")
            Logs.critical(traceback.format_exc())
            exit()

class S3:
    BUCKET = ""
    REGION = ""

    # INIT bucket name and region. Currently region is not used.
    def __init__(self, bucket, region):
        self.BUCKET = bucket
        self.REGION = region
    
    # Upload fil to s3 bucket
    def uploadFile(self, src, dest):
        try:
            boto3.resource('s3').meta.client.upload_file(src, self.BUCKET, dest)
        except Exception as e:
            Logs.critical("Error uploading the backup file")
            Logs.critical(traceback.format_exc())
            exit()


def lambda_function(event, context): 
    ### MAIN ###
    # VARIABLES
    REGION = os.environ['REGION']
    COGNITO_ID = os.environ['COGNITO_ID']
    BACKUP_BUCKET = os.environ['BACKUP_BUCKET']
    GATTRIBUTES = [
        'GroupName',
        'Description',
        'Precedence'
    ]

    # INIT CLASSES
    cognito = Cognito(COGNITO_ID, REGION, [])
    cognitoS3 = S3(BACKUP_BUCKET, REGION)

    # GET USERS ITTRIBUTES AND INIT CSV CLASS
    ATTRIBUTES = cognito.getAttributes()
    csvUsers = CSV(ATTRIBUTES, "users")
    # LIST ALL USERS
    user_records = cognito.listUsers()
    # SAVE USERS TO FILE
    csvUsers.generateUserContent(user_records)
    csvUsers.saveToFile()    
    # DISPLAY INFO
    Logs.info("Total Exported User Records: "+str(len(csvUsers.CSV_LINES)))
    # UPLOAD FILE
    cognitoS3.uploadFile (csvUsers.FOLDER + "/" + csvUsers.FILENAME, csvUsers.FILENAME)

    # INIT GROUPS CSV CLASS
    csvGroups = CSV(GATTRIBUTES, "groups")
    # LIST ALL GROUPS
    group_records = cognito.listGroups()
    # SAVE GROUPS TO FILE
    csvGroups.generateGroupContent(group_records)
    csvGroups.saveToFile()
    # DISPLAY INFO
    Logs.info("Total Exported Group Records: "+str(len(csvGroups.CSV_LINES)))
    # UPLOAD FILE
    cognitoS3.uploadFile (csvGroups.FOLDER + "/" + csvGroups.FILENAME, csvGroups.FILENAME)

Drücken Sie auf der Registerkarte "Test" einfach auf die Schaltfläche "TEST", und Sie werden die erfolgreiche Ausgabe des Laufs sehen.

Was macht also dieses Lambda?

Es gibt eine Reihe von benutzerdefinierten Klassen, die mit Cognito, S3 und CSV arbeiten. Die Hauptprozedur befindet sich jedoch innerhalb der Methode lambda_function.

  • Zunächst initialisieren wir alle Klassen.
  • Innerhalb von GATTRIBUTES müssen wir die Gruppenspalten definieren, die exportiert werden sollen.
    • Gruppenname
    • Beschreibung
    • Vorrangig

Dies sind die Standardspalten, die in den meisten Fällen ausreichend sind.

  • Benutzerattribute abrufen. Dies sind die Spaltennamen, die in Cognito verwendet werden. In dieser Installation haben wir 3 benutzerdefinierte Attribute.
Abrufen benutzerdefinierter Benutzerattribute in congnito

   !!! Wenn wir benutzerdefinierte Attribute verwenden, müssen wir sie innerhalb des neuen Cognito erstellen, sonst schlägt der Import fehl.

  • Alle Benutzer auflisten
  • Erzeugen und Speichern von Benutzern in der CSV-Datei
  • Hochladen der neu erstellten Benutzerdatei in unseren Backup-Bucket
  • Alle Gruppen auflisten
  • Erzeugen und Speichern von Gruppen in der CSV-Datei.
  • Hochladen der neu erstellten Gruppendatei in unseren Backup-Bucket
  • Eine Statistik exportieren 

Wenn wir den Backup-Bucket besuchen, sehen wir die neu erstellten CSV-Dateien:

CSV-Dateien in Lambda

Cognito Lambda reinigen

Falls unser zweiter Cognito-Pool nicht leer ist oder der Import nicht vollständig erfolgreich war, müssen wir ihn bereinigen.

Erstellen Sie wieder eine Python 3.9 Lambda-Funktion von Grund auf. Ändern Sie Speicher und Timeout und fügen Sie diese Umgebungsvariablen hinzu:

  • COGNITO_ID - mit der ID des Ziel-Cognitos
  • REGION - Zielgebiet Cognito Region

Erstellen Sie außerdem eine neue Rolle mit der folgenden Berechtigung und nennen Sie sie CognitoImportRole:

  • IAMFullAccess
  • AmazonS3FullAccess
  • CloudWatchFullAccess
  • AmazonCognitoPowerUser
import boto3
import datetime
from datetime import datetime
import time
import traceback
import os

class Logs:
    @staticmethod
    def warning(logBody):
        print("[WARNING] {}".format(logBody))

    @staticmethod
    def critical(logBody):
        print("[CRITICAL] {}".format(logBody))

    @staticmethod
    def info(logBody):
        print("[INFO] {}".format(logBody))

class Cognito:
    USERPOOLID = ""
    REGION = ""
    ATTRIBUTES = ""

    # INIT userpool id, region and column names to be exported. 
    # Currently region is not used. The export lambda must be in the same region as the Cognito service
    def __init__(self, userPoolId, region, attributes):
        self.USERPOOLID = userPoolId
        self.REGION = region
        self.ATTRIBUTES = attributes
    
    def getAttributes (self):
        try:
            boto = boto3.client('cognito-idp')
            headers = boto.get_csv_header(
                UserPoolId=self.USERPOOLID
            )
            self.ATTRIBUTES = headers["CSVHeader"]
            return headers["CSVHeader"]
        except Exception as e:
            Logs.critical("There is an error listing users attributes")
            Logs.critical(traceback.format_exc())
            exit()

    # List all cognito users with only the predefined columns in ATTRIBUTES variable
    # As there is a limit of 60 users per query, a multiple queries are executed separated in so called pages.
    def listUsers (self):
        try:
            boto = boto3.client('cognito-idp')

            users = []
            next_page = None
            kwargs = {
                'UserPoolId': self.USERPOOLID,
            }
            users_remain = True
            while(users_remain):
                if next_page:
                    kwargs['PaginationToken'] = next_page
                response = boto.list_users(**kwargs)
                users.extend(response['Users'])
                next_page = response.get('PaginationToken', None)
                users_remain = next_page is not None
                # COOL DOWN BEFORE NEXT QUERY
                time.sleep(0.15)

            return users
        except Exception as e:
            Logs.critical("There is an error listing cognito users")
            Logs.critical(traceback.format_exc())
            exit()

    # List all cognito groups with only the predefined columns in ATTRIBUTES variable
    # As there is a limit of 60 groups per query, a multiple queries are executed separated in so called pages.
    def listGroups (self):
        try:
            boto = boto3.client('cognito-idp')
            groups = []
            next_page = None
            kwargs = {
                'UserPoolId': self.USERPOOLID,
            }
            groups_remain = True
            while(groups_remain):
                if next_page:
                    kwargs['NextToken'] = next_page
                response = boto.list_groups(**kwargs)
                groups.extend(response['Groups'])
                next_page = response.get('NextToken', None)
                groups_remain = next_page is not None
                # COOL DOWN BEFORE NEXT QUERY
                time.sleep(0.15)
            return groups
        except Exception as e:
            Logs.critical("There is an error listing cognito groups")
            Logs.critical(traceback.format_exc())
            exit()
    
    def deleteGroups (self, groups):
        try:
            boto = boto3.client('cognito-idp')
            for group in groups:
                response = boto.delete_group(
                    GroupName=group["GroupName"],
                    UserPoolId=self.USERPOOLID
                )
        except Exception as e:
            Logs.critical("There is an error listing cognito groups")
            Logs.critical(traceback.format_exc())
            exit()

    def deleteUsers (self, users):
        try:
            boto = boto3.client('cognito-idp')
            for user in users:
                response = boto.admin_delete_user(
                    UserPoolId=self.USERPOOLID,
                    Username=user["Username"]
                )
        except Exception as e:
            Logs.critical("There is an error listing cognito groups")
            Logs.critical(traceback.format_exc())
            exit()


def lambda_function(event, context): 
    ### MAIN ###
    # VARIABLES
    REGION = os.environ['REGION']
    COGNITO_ID = os.environ['COGNITO_ID']

    # INIT CLASSES
    cognito = Cognito(COGNITO_ID, REGION, [])
    # LIST ALL USERS
    user_records = cognito.listUsers()
    cognito.deleteUsers(user_records)


    # LIST ALL GROUPS
    group_records = cognito.listGroups()
    cognito.deleteGroups(group_records)

Was macht also dieses Lambda? Der Vorgang ist ziemlich einfach. Es listet alle Benutzer und Gruppen auf und löscht sie.

Cognito-Benutzerpool wiederherstellen

Die folgenden Schritte sind nur dann wichtig, wenn wir die auf S3 gespeicherten Benutzerdaten wiederherstellen müssen - zum Beispiel, wenn ein Benutzer versehentlich gelöscht wurde oder wenn Daten in einen anderen Cognito User Pool migriert werden müssen. Dazu haben wir einen zweiten AWS Lambda erstellt, der manuell über die AWS-Konsole ausgelöst werden muss, wenn man die entsprechenden Daten wiederherstellen möchte. Das kann ein neuer Benutzerpool oder ein alter sein. Bei dieser generischen Lösung werden alle im Bucket gespeicherten S3-Benutzer berücksichtigt, aber Cognito importiert nur die Benutzer, die nicht im UserPool vorhanden sind, und schlägt bei den vorhandenen Benutzern fehl.

Die Wiederherstellungslösung wird im Folgenden vorgestellt.

Erstellen Sie wieder eine Python 3.9 Lambda-Funktion von Grund auf. Ändern Sie Speicher und Timeout, fügen Sie die CognitoImport-Rolle hinzu und fügen Sie diese Umgebungsvariablen hinzu:

  • BACKUP_BUCKET - Sicherungseimer
  • BACKUP_FILE_GROUPS - vollständiger Dateiname cognito_backup_groups_XXXXXXXX-XXXX.csv
  • BACKUP_FILE_USERS - vollständiger Dateiname cognito_backup_users_XXXXXXXX-XXXX.csv
  • COGNITO_ID - Ziel-Cognito-ID
  • REGION - Zielgebiet cognito region

Der Quellcode ist hier ein wenig anders. Da AWS die "requests"-Bibliothek nicht unterstützt, müssen wir sie bereitstellen. 

  • Speichern Sie den folgenden Code in index.py
  • Führen Sie im selben Ordner
pip3 install requests -t .

Dadurch werden alle Pakete heruntergeladen, die zur Ausführung von "requests" benötigt werden.

import boto3
import datetime
from datetime import datetime
import time
import traceback
import csv
import requests
import os

class Logs:
    @staticmethod
    def warning(logBody):
        print("[WARNING] {}".format(logBody))

    @staticmethod
    def critical(logBody):
        print("[CRITICAL] {}".format(logBody))

    @staticmethod
    def info(logBody):
        print("[INFO] {}".format(logBody))


class S3:
    BUCKET = ""
    REGION = ""
    def __init__(self, bucket, region):
        self.BUCKET = bucket
        self.REGION = region

    def downloadFile (self, src, dest):
        try:
            boto3.resource('s3').meta.client.download_file(self.BUCKET, src, dest)
        except Exception as e:
            Logs.critical("Error downloading file")
            Logs.critical(traceback.format_exc())
            exit()
class CSV:
    FILENAME = ""
    FOLDER = "/tmp/"
    def __init__(self, filename):
        self.FILENAME = filename
    
    def readBackup(self):
        row_count = 0
        groups = []
        import csv
        with open(self.FILENAME, 'r') as file:
            csv_file = csv.DictReader(file)
            for row in csv_file:
                groups.append(dict(row))
        return groups

class Cognito:
    USERPOOLID = ""
    REGION = ""
    ATTRIBUTES = ""
    def __init__(self, userPoolId, region, attributes):
        self.USERPOOLID = userPoolId
        self.REGION = region
        self.ATTRIBUTES = attributes
    
    def importUsers (self):
        try:
            print("importUsers")
        except Exception as e:
            Logs.critical("Error importing users")
            Logs.critical(traceback.format_exc())
            exit()
    
    def importGroups (self, groups):
        try:
            boto = boto3.client('cognito-idp')
            for group in groups:
                print(group)
                if not self.checkIfGroupExists(group["GroupName"]):
                    kwargs = {
                        'UserPoolId': self.USERPOOLID
                    }
                    for attribute in self.ATTRIBUTES:
                        if (group[str(attribute)].isnumeric()):
                            kwargs[str(attribute)] = int(group[attribute])
                        else:
                            kwargs[str(attribute)] = str(group[attribute])
                    response = boto.create_group(**kwargs)
                else:
                    Logs.info("Group {} already exists".format(group["GroupName"]))
        except Exception as e:
            Logs.critical("Error importing groups")
            Logs.critical(traceback.format_exc())
            exit()

    def checkIfGroupExists(self, groupName):
        try:
            boto = boto3.client('cognito-idp')
            response = boto.get_group(
                GroupName=groupName,
                UserPoolId=self.USERPOOLID
            )
            return True
        except Exception as e:
            return False
    
    def importUsers(self, filename):
        try:
            #client = boto3.client("cognito-idp", region_name="ap-south-1")
            boto = boto3.client('cognito-idp')
            
            response = boto.get_csv_header(
                UserPoolId=self.USERPOOLID
            )

            response = boto.create_user_import_job(
                JobName='Import-Test-Job',
                UserPoolId=self.USERPOOLID,
                CloudWatchLogsRoleArn='arn:aws:iam::615124646879:role/CognitoImportRole'
            )
            #print(response)

            #UPLOAD CSV File
            content_deposition = 'attachment;filename='+filename;
            presigned_url = response['UserImportJob']['PreSignedUrl']
            print(presigned_url)
            headers_dict = {
                'x-amz-server-side-encryption': 'aws:kms',
                'Content-Disposition': content_deposition
            }
            with open(filename, 'rb') as csvFile:
                file_upload_response = requests.put(
                    presigned_url, 
                    data=csvFile, 
                    headers=headers_dict
                )

            response2 = boto.start_user_import_job(
                UserPoolId=self.USERPOOLID,
                JobId=response["UserImportJob"]["JobId"]
            )
            print(response2)
        except Exception as e:
            Logs.critical("Error importing users")
            Logs.critical(traceback.format_exc())
            exit()


def lambda_function(event, context): 
    REGION = os.environ['REGION']
    COGNITO_ID = os.environ['COGNITO_ID']
    BACKUP_FILE_USERS = os.environ['BACKUP_FILE_USERS']
    BACKUP_FILE_GROUPS = os.environ['BACKUP_FILE_GROUPS']
    BACKUP_BUCKET = os.environ['BACKUP_BUCKET']
    cognitS3 = S3(BACKUP_BUCKET, REGION)

    # DOWNLOAD GROUPS
    cognitS3.downloadFile(BACKUP_FILE_GROUPS, "/tmp/"+BACKUP_FILE_GROUPS)
    # IMPORT GROUPS
    csvGroups = CSV("/tmp/"+BACKUP_FILE_GROUPS)
    groups = csvGroups.readBackup()
    GATTRIBUTES = [
        'GroupName',
        'Description',
        'Precedence'
    ]
    cognito = Cognito(COGNITO_ID, REGION, GATTRIBUTES)
    cognito.importGroups(groups)


    # DOWNLOAD USERS
    cognitS3.downloadFile(BACKUP_FILE_USERS, "/tmp/"+BACKUP_FILE_USERS)

    #csvUsers = CSV("/tmp/"+BACKUP_FILE_USERS)
    #users = csvUsers.readBackup()
    ATTRIBUTES = [
        'email',
        'given_name',
        'family_name'
    ]
    cognitoUsers = Cognito(COGNITO_ID, REGION, ATTRIBUTES)
    cognitoUsers.importUsers("/tmp/"+BACKUP_FILE_USERS)

    # IMPORT USERS

Zippen Sie nun das gesamte Verzeichnis und verteilen Sie den Code aus der Zip-Datei.

Zip-Datei zur Wiederherstellung des Cognito-Benutzerpools bereitstellen

Was macht das Lambda? 

  • Lädt die Gruppendatei herunter.
  • Gruppen importieren
  • Benutzerdatei herunterladen
  • Erstellt einen Cognito-Importauftrag, fügt eine Datei an und führt ihn aus.

Da der Importauftrag asynchron ist, wird der Lambda an dieser Stelle angehalten und gilt als erfolgreich. 

Wir können Importaufträge von Cognito User Pool - Benutzer und Gruppen - Benutzer importieren verfolgen

Hier sehen wir die Anzahl der erfolgreich importierten Benutzer. Aus irgendeinem Grund kann ein Benutzer nicht importiert werden.

Importieren eines Testauftrags zur Wiederherstellung des Cognito-Benutzerpools

Beobachtung der Sicherungs- und Wiederherstellungslösung mit Python3 Lambda

Leider gibt es beim Exportieren/Importieren von Benutzern aus einem Benutzerpool einige Einschränkungen. Einige sind recht einfach, andere nicht. Im Folgenden werden die Einschränkungen aufgeführt, die einen größeren Einfluss auf die Lösung haben:

  • Passwort-Backups: Es gibt keine Sicherungen für Passwörter, und die Benutzer müssten sie zurücksetzen.
  • Cognito-Unterattribute werden erneuert: Wenn die Software auf diese Attribute angewiesen ist, können sie in ein benutzerdefiniertes Attribut kopiert werden, oder alternativ können benutzerdefinierte Attribute anstelle von Unterattributen in der Softwarelösung verwendet werden.
  • MFA - alle MFA würde in dem neu erstellten Pool deaktiviert werden und muss wieder aktiviert werden.
  • Anbieter von Drittanbietern wie Facebook werden nicht funktionieren.

   Das Problem ist, dass jeder Cognito-Pool einen eigenen Salzschlüssel hat. Er wird automatisch generiert und wir haben nicht die Möglichkeit, ihn zu kopieren oder zu ändern.

Wenn Sie Fragen haben, können Sie uns gerne kontaktieren.

Eine Antwort hinterlassen

Newsletter für Tech-Experten

Signal, kein Rauschen –

direkt in Ihren Posteingang.

Schließen Sie sich mehr als 12.000 Ingenieuren und Führungskräften aus der Wirtschaft an, die Praxisberichte zu SRE, DevOps und Cloud-nativer Zuverlässigkeit erhalten.

Tech-Blogs mit tiefgehenden Einblicken und Fallstudien
Neue Technologien, sorgfältig ausgewählt

Ihre geschäftliche E-Mail-Adresse

Wir gehen respektvoll mit Ihrem Posteingang um. Lesen Sie unsere Datenschutzerklärung.

Mehr Beiträge

AI SRE Agent: Die ersten 20 Minuten eines Vorfalls automatisieren Es ist 3 Uhr morgens. Ein Alarm wird ausgelöst. Sie klappen Ihren Laptop auf, und die nächsten zwanzig Minuten verlaufen wie immer...
Lesen
Bei der Arbeit mit Terraform sollte das Standard-Meta-Argument zum Erstellen von Ressourcen fast immer „for_each“ lauten – insbesondere bei Infrastruktur, die voraussichtlich lange bestehen bleibt und sich weiterentwickelt...
Lesen
Kontakt aufnehmen
ITGix bietet Ihnen fachkundige Beratung und maßgeschneiderte DevOps-Services, um Ihr Unternehmenswachstum zu beschleunigen.
Newsletter für
Technik-Experten
Schließen Sie sich 12.000+ Geschäftsführern und Ingenieuren an, die Blogs, e-Books und Fallstudien Fallstudien über neue Technologie erhalten.