def lambda_handler()

in granular_access/lambda_functions/granular_user_govenance/granular_user_govenance.py [0:0]


def lambda_handler(event, context):
    # get account_id
    sts_client = boto3.client("sts", region_name=aws_region)
    account_id = sts_client.get_caller_identity()["Account"]

    # call s3 bucket to get group information
    s3 = boto3.resource('s3', region_name=lambda_aws_region)
    bucketname = get_ssm_parameters('/qs/config/groups')
    bucketname = bucketname['bucket-name']
    bucket = s3.Bucket(bucketname)
    mkey = 'membership/membership.csv'
    tmpdir = tempfile.mkdtemp()
    local_file_name = 'membership.csv'
    path = os.path.join(tmpdir, local_file_name)
    bucket.download_file(mkey, path)

    # qs_client is in us-east-1 region for users/groups;
    # qs_local_client is the local region of lambda function for other qs assets
    qs_client = boto3.client('quicksight', region_name='us-east-1')
    qs_local_client = boto3.client('quicksight', region_name=lambda_aws_region)

    # define which iam role to register user according to region
    if lambda_aws_region == 'us-east-1':
        iamrole = 'quicksight-fed-us-users'
        arn = 'arn:aws:iam::' + account_id + ':role/quicksight-fed-us-users'
    elif lambda_aws_region == 'eu-west-1':
        iamrole = 'quicksight-fed-eu-users'
        arn = 'arn:aws:iam::' + account_id + ':role/quicksight-fed-eu-users'

    nslogkey = 'monitoring/quicksight/logs/create_namespace_log.csv'
    local_file_name2 = 'create_namespace_log.csv'
    nslogpath = os.path.join(tmpdir, local_file_name2)

    # load qs namespace information. in QS account, there might be some namespaces which are not list in the ssm.
    # But this solution only controls the namespaces stored in ssm.
    ns_list = get_ssm_parameters('/qs/config/ns')
    ns_list = ns_list['ns']
    print(ns_list)

    nslog=[]
    currentnslist=[]
    lscurrentnsres=list_namespaces(account_id, aws_region)
    for i in lscurrentnsres:
        currentnslist.append(i['Name'])
    for i in ns_list:
        if i in currentnslist:
            if lscurrentnsres[currentnslist.index(i)]['CreationStatus']=='CREATED':
                nslog.append(['CREATED', i, 'N/A'])
            continue
        elif i not in currentnslist:
            create_namespace(account_id, aws_region, i, 'QUICKSIGHT')
            desnsres=describe_namespace(i, account_id, aws_region)
            while (desnsres['Namespace']['CreationStatus'] == 'CREATING'):
                desnsres = describe_namespace(i, account_id, aws_region)
            else:
                if desnsres['Namespace']['CreationStatus'] == 'CREATED':
                    nslog.append(['CREATED', i, datetime.now().strftime("%d/%m/%Y %H:%M:%S")])
                    continue
                else:
                    nslog.append([desnsres['Namespace']['CreationStatus'], i, datetime.now().strftime("%d/%m/%Y %H:%M:%S")])
                    ns_list.remove(i)

    #upload namespace creation results:
    with open(nslogpath, 'w', newline='') as outfile:
        writer = csv.writer(outfile)
        for line in nslog:
            writer.writerow(line)
    bucket.upload_file(nslogpath, nslogkey)

    #place holder to store the group/users info
    new = []

    #dict to store the current group/user info
    currentmembership = {}

    # get current quicksight groups
    for namespace in ns_list:
        groups = list_groups(account_id, aws_region, namespace)
        for group in groups:
            new.append(namespace + '_' + group['GroupName']) #['default_quicksight-fed-critical','3rd-party_3rd-party']

            # get current quicksight users-groups mapping
            if (namespace + '_' + group['GroupName']) in currentmembership:
                pass
            else:
                currentmembership[namespace + '_' + group['GroupName']] = []
            #members=list_group_memberships(group['GroupName'],account_id,aws_region,namespace)

    groups = new # an array of groups with ns information
    new = {} # a dict placeholder to store user alias and QS username mapping

    #get users in current QS account, per different namespace
    for namespace in ns_list:
        users = list_users(account_id, aws_region, namespace)
    # print(users)
        for user in users:
            useralias = user['UserName'].split('/')[-1]
            useralias = useralias.split('@')[0]
            useralias = namespace + '_' + useralias #default_yingwang for example
            new[useralias] = user['UserName'] #{'default_yingwang': 'yingwang@sample.com'}
            # print(user['UserName'])
            usergroups = list_user_groups(user['UserName'], account_id, aws_region, namespace)
            if len(usergroups) == 0:
                if (namespace+'_None') in currentmembership:
                    currentmembership[namespace+'_None'].append(user['UserName'])
                else:
                    currentmembership[namespace+'_None'] = []
                    currentmembership[namespace+'_None'].append(user['UserName'])
            else:
                for group in usergroups:
                    # print(group)
                    fullgroupname = namespace + '_' + group['GroupName']
                    if fullgroupname in currentmembership:
                        currentmembership[fullgroupname].append(user['UserName'])
                    else:
                        currentmembership[fullgroupname] = []
                        currentmembership[fullgroupname].append(user['UserName'])
    users = new #{'default_yingwang': 'yingwang@sample.com', ...}
    print(users)
    print(currentmembership) #{'default_groupname': ['qsusrname', ...], }

    # build new group-members mapping from membership.csv file
    memberships = {}

    # load qs user role information
    roles = get_ssm_parameters('/qs/config/roles')
    # dict {groupname:role} {'default_bi-developer': 'AUTHOR',
    #                                'default_bi-admin': 'ADMIN',
    print(roles)

    # load group mapping information
    with open(path) as csv_file:
        members = csv.reader(csv_file, delimiter=',')
        for member in members:
            namespace = member[0]
            alias = member[2]
            email = member[3]
            # create group
            group = member[1]
            nsplusgroup = namespace + '_' + group.lower() #ns_groupname without qs-fed.
            group = 'quicksight-fed-' + group.lower()
            fullgroupname = namespace + '_' + group
            # print("process this group: " + newgroup)
            if fullgroupname not in groups:
                print("Creating this new group: " + group + "in namespace: " + namespace)
                try:
                    response = create_group(group, account_id, aws_region, namespace)
                    currentmembership[fullgroupname] = []
                    groups.append(fullgroupname)
                except Exception as e:
                    if str(e).find('already exists.'):
                        # print(e)
                        pass
                    else:
                        raise e

            # handle every user
            if alias:
                # register user is user is new
                fullalias = namespace + '_' + alias
                if fullalias not in users:
                    try:
                        response = register_user(aws_region, 'IAM', email, alias, account_id,
                                                 Arn=arn, Session=email, NS=namespace, Role=roles[nsplusgroup])
                        qs_usr_name = iamrole + '/' + email
                        print(qs_usr_name + " is registered")
                        users[fullalias] = qs_usr_name
                    except Exception as e:
                        print(e)
                        pass
                    #assign user into group
                    try:
                        response = create_group_membership(qs_usr_name, group, account_id, aws_region, namespace)
                        print(qs_usr_name + " is added into " + group + "of namespace: " + namespace)
                        currentmembership[fullgroupname].append(qs_usr_name)

                    except Exception as e:
                        if str(e).find('does not exist'):
                            # print(e)
                            pass
                        else:
                            raise e

                # add user into the group if user exists
                if fullalias in users: #existing user
                    qs_usr_name = users[fullalias]
                    email = users[fullalias].split('/')[-1]
                    # print(qs_usr_name)
                    # print(email)
                    if qs_usr_name not in currentmembership[fullgroupname]:
                        # print(qs_usr_name)
                        try:
                            response = create_group_membership(qs_usr_name, group, account_id, aws_region, namespace)
                            print(qs_usr_name + " is added into " + group + "of namespace: " + namespace)
                            currentmembership[fullgroupname].append(qs_usr_name)
                        except Exception as e:
                            if str(e).find('does not exist'):
                                # print(e)
                                pass
                            else:
                                raise e
                    if ((nsplusgroup in roles) and (roles[nsplusgroup] != 'READER')):
                        print("update role")
                        try:
                            qs_client.update_user(
                                UserName=qs_usr_name,
                                AwsAccountId=account_id,
                                Namespace=namespace,
                                Email=email,
                                Role=roles[nsplusgroup]
                            )
                        except Exception as e:
                            if str(e).find('does not exist'):
                                # print(e)
                                pass
                            else:
                                raise e
                        """elif newgroup in ['quicksight-fed-bi-developer',
                                          'quicksight-fed-power-reader']:
                            print("add new dev")
                            if qs_usr_name not in memberships['quicksight-fed-bi-admin']:
                                try:
                                    qs_client.update_user(
                                        UserName=qs_usr_name,
                                        AwsAccountId=account_id,
                                        Namespace='default',
                                        Email=email,
                                        Role='AUTHOR'
                                    )
                                except Exception as e:
                                    if str(e).find('does not exist'):
                                        # print(e)
                                        pass
                                    else:
                                        raise e"""

                # write current group-users mapping into a dict "membership"
                if fullgroupname in memberships:
                    if 'qs_usr_name' in locals():  # User in membership.csv already registered as a QS user
                        memberships[fullgroupname].append(qs_usr_name)
                    #else:  # User in membership.csv do not register as a QS user yet
                        #memberships[fullgroupname].append(alias)

                else:
                    memberships[fullgroupname] = []
                    if 'qs_usr_name' in locals():
                        memberships[fullgroupname].append(qs_usr_name)
                    #else:
                        #memberships[fullgroupname].append(alias)

    # remove current user permissions or memberships if membership file changed
    print(memberships)
    for namespace in ns_list:
        users = list_users(account_id, aws_region, namespace)

        for user in users:
            # print(user['UserName'])
            groups = list_user_groups(user['UserName'], account_id, aws_region, namespace)
            # print(groups)
            if len(groups) == 0:
                if user['UserName'].split("/", 1)[0] != 'Administrator':
                    #delete orphan user who is not in any group
                    delete_user(user['UserName'], account_id, aws_region, namespace)
            else:
                for group in groups:
                    # currentmembership[group['GroupName']] = user['UserName']
                    nsplusgroup = namespace + '_' + group['GroupName']
                    if nsplusgroup in memberships:
                        # print(group['GroupName'])
                        if user['UserName'] not in memberships[nsplusgroup]:
                            delete_group_membership(user['UserName'], group['GroupName'], account_id, aws_region,
                                                    namespace)
                            print(user['UserName'] + " is removed from " + group['GroupName']
                                  + 'of namespace: ' + namespace)