def team_present(
        name,
        description=None,
        repo_names=None,
        privacy='secret',
        permission='pull',
        members=None,
        enforce_mfa=False,
        no_mfa_grace_seconds=0,
        profile="github",
        **kwargs):
    '''
    Ensure a team is present

    name
        This is the name of the team in the organization.

    description
        The description of the team.

    repo_names
        The names of repositories to add the team to.

    privacy
        The level of privacy for the team, can be 'secret' or 'closed'. Defaults
        to secret.

    permission
        The default permission for new repositories added to the team, can be
        'pull', 'push' or 'admin'. Defaults to pull.

    members
        The members belonging to the team, specified as a dict of member name to
        optional configuration. Options include 'enforce_mfa_from' and 'mfa_exempt'.

    enforce_mfa
        Whether to enforce MFA requirements on members of the team. If True then
        all members without `mfa_exempt: True` configured will be removed from
        the team. Note that `no_mfa_grace_seconds` may be set to allow members
        a grace period.

    no_mfa_grace_seconds
        The number of seconds of grace time that a member will have to enable MFA
        before being removed from the team. The grace period will begin from
        `enforce_mfa_from` on the member configuration, which defaults to
        1970/01/01.

    Example:

    .. code-block:: yaml

        Ensure team test is present in github:
            github.team_present:
                - name: 'test'
                - members:
                    user1: {}
                    user2: {}

        Ensure team test_mfa is present in github:
            github.team_present:
                - name: 'test_mfa'
                - members:
                    user1:
                        enforce_mfa_from: 2016/06/15
                - enforce_mfa: True

    .. versionadded:: 2016.11.0
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': True,
        'comment': ''
    }

    target = __salt__['github.get_team'](name, profile=profile, **kwargs)
    test_comments = []

    if target:  # Team already exists
        parameters = {}
        if description is not None and target['description'] != description:
            parameters['description'] = description
        if permission is not None and target['permission'] != permission:
            parameters['permission'] = permission
        if privacy is not None and target['privacy'] != privacy:
            parameters['privacy'] = privacy

        if parameters:
            if __opts__['test']:
                test_comments.append('Team properties are set to be edited: {0}'
                                     .format(parameters))
                ret['result'] = None
            else:
                result = __salt__['github.edit_team'](name, profile=profile,
                                                      **parameters)
                if result:
                    ret['changes']['team'] = {
                        'old': 'Team properties were {0}'.format(target),
                        'new': 'Team properties (that changed) are {0}'.format(parameters)
                    }
                else:
                    ret['result'] = False
                    ret['comment'] = 'Failed to update team properties.'
                    return ret

        manage_repos = repo_names is not None
        current_repos = set(__salt__['github.list_team_repos'](name, profile=profile)
                            .keys())
        repo_names = set(repo_names or [])

        repos_to_add = repo_names - current_repos
        repos_to_remove = current_repos - repo_names if repo_names else []

        if repos_to_add:
            if __opts__['test']:
                test_comments.append('Team {0} will have the following repos '
                                     'added: {1}.'.format(name, list(repos_to_add)))
                ret['result'] = None
            else:
                for repo_name in repos_to_add:
                    result = (__salt__['github.add_team_repo']
                              (repo_name, name, profile=profile, **kwargs))
                    if result:
                        ret['changes'][repo_name] = {
                            'old': 'Repo {0} is not in team {1}'.format(repo_name, name),
                            'new': 'Repo {0} is in team {1}'.format(repo_name, name)
                        }
                    else:
                        ret['result'] = False
                        ret['comment'] = ('Failed to add repo {0} to team {1}.'
                                          .format(repo_name, name))
                        return ret

        if repos_to_remove:
            if __opts__['test']:
                test_comments.append('Team {0} will have the following repos '
                                     'removed: {1}.'.format(name, list(repos_to_remove)))
                ret['result'] = None
            else:
                for repo_name in repos_to_remove:
                    result = (__salt__['github.remove_team_repo']
                              (repo_name, name, profile=profile, **kwargs))
                    if result:
                        ret['changes'][repo_name] = {
                            'old': 'Repo {0} is in team {1}'.format(repo_name, name),
                            'new': 'Repo {0} is not in team {1}'.format(repo_name, name)
                        }
                    else:
                        ret['result'] = False
                        ret['comment'] = ('Failed to remove repo {0} from team {1}.'
                                          .format(repo_name, name))
                        return ret

    else:  # Team does not exist - it will be created.
        if __opts__['test']:
            ret['comment'] = 'Team {0} is set to be created.'.format(name)
            ret['result'] = None
            return ret

        result = __salt__['github.add_team'](
            name,
            description=description,
            repo_names=repo_names,
            permission=permission,
            privacy=privacy,
            profile=profile,
            **kwargs
        )
        if result:
            ret['changes']['team'] = {}
            ret['changes']['team']['old'] = None
            ret['changes']['team']['new'] = 'Team {0} has been created'.format(name)
        else:
            ret['result'] = False
            ret['comment'] = 'Failed to create team {0}.'.format(name)
            return ret

    manage_members = members is not None

    mfa_deadline = datetime.datetime.utcnow() - datetime.timedelta(seconds=no_mfa_grace_seconds)
    members_no_mfa = __salt__['github.list_members_without_mfa'](profile=profile)

    members_lower = {}
    for member_name, info in six.iteritems(members or {}):
        members_lower[member_name.lower()] = info

    member_change = False
    current_members = __salt__['github.list_team_members'](name, profile=profile)

    for member, member_info in six.iteritems(members or {}):
        log.info('Checking member %s in team %s', member, name)

        if member.lower() not in current_members:
            if (enforce_mfa and _member_violates_mfa(member, member_info,
                                                     mfa_deadline, members_no_mfa)):
                if __opts__['test']:
                    test_comments.append('User {0} will not be added to the '
                                         'team because they do not have MFA.'
                                         ''.format(member))
            else:  # Add to team
                member_change = True
                if __opts__['test']:
                    test_comments.append('User {0} set to be added to the '
                                         'team.'.format(member))
                    ret['result'] = None
                else:
                    result = (__salt__['github.add_team_member']
                              (member, name, profile=profile, **kwargs))
                    if result:
                        ret['changes'][member] = {}
                        ret['changes'][member]['old'] = (
                            'User {0} is not in team {1}'.format(member, name))
                        ret['changes'][member]['new'] = (
                            'User {0} is in team {1}'.format(member, name))
                    else:
                        ret['result'] = False
                        ret['comment'] = ('Failed to add user {0} to team '
                                          '{1}.'.format(member, name))
                        return ret

    for member in current_members:
        mfa_violation = False
        if member in members_lower:
            mfa_violation = _member_violates_mfa(member, members_lower[member],
                                                 mfa_deadline, members_no_mfa)
        if (manage_members and member not in members_lower or
            (enforce_mfa and mfa_violation)):
            # Remove from team
            member_change = True
            if __opts__['test']:
                if mfa_violation:
                    test_comments.append('User {0} set to be removed from the '
                                         'team because they do not have MFA.'
                                         .format(member))
                else:
                    test_comments.append('User {0} set to be removed from '
                                         'the team.'.format(member))
                ret['result'] = None
            else:
                result = (__salt__['github.remove_team_member']
                          (member, name, profile=profile, **kwargs))
                if result:
                    extra_changes = ' due to MFA violation' if mfa_violation else ''
                    ret['changes'][member] = {
                        'old': 'User {0} is in team {1}'.format(member, name),
                        'new': 'User {0} is not in team {1}{2}'.format(member, name, extra_changes)
                    }
                else:
                    ret['result'] = False
                    ret['comment'] = ('Failed to remove user {0} from team {1}.'
                                      .format(member, name))
                    return ret

    if member_change:  # Refresh team cache
        __salt__['github.list_team_members'](name, profile=profile,
                                             ignore_cache=False, **kwargs)

    if test_comments:
        ret['comment'] = '\n'.join(test_comments)
    return ret
def team_present ( name , description = None , repo_names = None , privacy = 'secret' , permission = 'pull' , members = None , enforce_mfa = False , no_mfa_grace_seconds = 0 , profile = "github" , ** kwargs ) : ret = { 'name' : name , 'changes' : { } , 'result' : True , 'comment' : '' } target = __salt__ [ 'github.get_team' ] ( name , profile = profile , ** kwargs ) test_comments = [ ] if target : parameters = { } if description is not None and target [ 'description' ] != description : parameters [ 'description' ] = description if permission is not None and target [ 'permission' ] != permission : parameters [ 'permission' ] = permission if privacy is not None and target [ 'privacy' ] != privacy : parameters [ 'privacy' ] = privacy if parameters : if __opts__ [ 'test' ] : test_comments . append ( 'Team properties are set to be edited: {0}' . format ( parameters ) ) ret [ 'result' ] = None else : result = __salt__ [ 'github.edit_team' ] ( name , profile = profile , ** parameters ) if result : ret [ 'changes' ] [ 'team' ] = { 'old' : 'Team properties were {0}' . format ( target ) , 'new' : 'Team properties (that changed) are {0}' . format ( parameters ) } else : ret [ 'result' ] = False ret [ 'comment' ] = 'Failed to update team properties.' return ret manage_repos = repo_names is not None current_repos = set ( __salt__ [ 'github.list_team_repos' ] ( name , profile = profile ) . keys ( ) ) repo_names = set ( repo_names or [ ] ) repos_to_add = repo_names - current_repos repos_to_remove = current_repos - repo_names if repo_names else [ ] if repos_to_add : if __opts__ [ 'test' ] : test_comments . append ( 'Team {0} will have the following repos ' 'added: {1}.' . format ( name , list ( repos_to_add ) ) ) ret [ 'result' ] = None else : for repo_name in repos_to_add : result = ( __salt__ [ 'github.add_team_repo' ] ( repo_name , name , profile = profile , ** kwargs ) ) if result : ret [ 'changes' ] [ repo_name ] = { 'old' : 'Repo {0} is not in team {1}' . format ( repo_name , name ) , 'new' : 'Repo {0} is in team {1}' . format ( repo_name , name ) } else : ret [ 'result' ] = False ret [ 'comment' ] = ( 'Failed to add repo {0} to team {1}.' . format ( repo_name , name ) ) return ret if repos_to_remove : if __opts__ [ 'test' ] : test_comments . append ( 'Team {0} will have the following repos ' 'removed: {1}.' . format ( name , list ( repos_to_remove ) ) ) ret [ 'result' ] = None else : for repo_name in repos_to_remove : result = ( __salt__ [ 'github.remove_team_repo' ] ( repo_name , name , profile = profile , ** kwargs ) ) if result : ret [ 'changes' ] [ repo_name ] = { 'old' : 'Repo {0} is in team {1}' . format ( repo_name , name ) , 'new' : 'Repo {0} is not in team {1}' . format ( repo_name , name ) } else : ret [ 'result' ] = False ret [ 'comment' ] = ( 'Failed to remove repo {0} from team {1}.' . format ( repo_name , name ) ) return ret else : if __opts__ [ 'test' ] : ret [ 'comment' ] = 'Team {0} is set to be created.' . format ( name ) ret [ 'result' ] = None return ret result = __salt__ [ 'github.add_team' ] ( name , description = description , repo_names = repo_names , permission = permission , privacy = privacy , profile = profile , ** kwargs ) if result : ret [ 'changes' ] [ 'team' ] = { } ret [ 'changes' ] [ 'team' ] [ 'old' ] = None ret [ 'changes' ] [ 'team' ] [ 'new' ] = 'Team {0} has been created' . format ( name ) else : ret [ 'result' ] = False ret [ 'comment' ] = 'Failed to create team {0}.' . format ( name ) return ret manage_members = members is not None mfa_deadline = datetime . datetime . utcnow ( ) - datetime . timedelta ( seconds = no_mfa_grace_seconds ) members_no_mfa = __salt__ [ 'github.list_members_without_mfa' ] ( profile = profile ) members_lower = { } for member_name , info in six . iteritems ( members or { } ) : members_lower [ member_name . lower ( ) ] = info member_change = False current_members = __salt__ [ 'github.list_team_members' ] ( name , profile = profile ) for member , member_info in six . iteritems ( members or { } ) : log . info ( 'Checking member %s in team %s' , member , name ) if member . lower ( ) not in current_members : if ( enforce_mfa and _member_violates_mfa ( member , member_info , mfa_deadline , members_no_mfa ) ) : if __opts__ [ 'test' ] : test_comments . append ( 'User {0} will not be added to the ' 'team because they do not have MFA.' '' . format ( member ) ) else : member_change = True if __opts__ [ 'test' ] : test_comments . append ( 'User {0} set to be added to the ' 'team.' . format ( member ) ) ret [ 'result' ] = None else : result = ( __salt__ [ 'github.add_team_member' ] ( member , name , profile = profile , ** kwargs ) ) if result : ret [ 'changes' ] [ member ] = { } ret [ 'changes' ] [ member ] [ 'old' ] = ( 'User {0} is not in team {1}' . format ( member , name ) ) ret [ 'changes' ] [ member ] [ 'new' ] = ( 'User {0} is in team {1}' . format ( member , name ) ) else : ret [ 'result' ] = False ret [ 'comment' ] = ( 'Failed to add user {0} to team ' '{1}.' . format ( member , name ) ) return ret for member in current_members : mfa_violation = False if member in members_lower : mfa_violation = _member_violates_mfa ( member , members_lower [ member ] , mfa_deadline , members_no_mfa ) if ( manage_members and member not in members_lower or ( enforce_mfa and mfa_violation ) ) : member_change = True if __opts__ [ 'test' ] : if mfa_violation : test_comments . append ( 'User {0} set to be removed from the ' 'team because they do not have MFA.' . format ( member ) ) else : test_comments . append ( 'User {0} set to be removed from ' 'the team.' . format ( member ) ) ret [ 'result' ] = None else : result = ( __salt__ [ 'github.remove_team_member' ] ( member , name , profile = profile , ** kwargs ) ) if result : extra_changes = ' due to MFA violation' if mfa_violation else '' ret [ 'changes' ] [ member ] = { 'old' : 'User {0} is in team {1}' . format ( member , name ) , 'new' : 'User {0} is not in team {1}{2}' . format ( member , name , extra_changes ) } else : ret [ 'result' ] = False ret [ 'comment' ] = ( 'Failed to remove user {0} from team {1}.' . format ( member , name ) ) return ret if member_change : __salt__ [ 'github.list_team_members' ] ( name , profile = profile , ignore_cache = False , ** kwargs ) if test_comments : ret [ 'comment' ] = '\n' . join ( test_comments ) return ret
Ensure a team is present

    name
        This is the name of the team in the organization.

    description
        The description of the team.

    repo_names
        The names of repositories to add the team to.

    privacy
        The level of privacy for the team, can be 'secret' or 'closed'. Defaults
        to secret.

    permission
        The default permission for new repositories added to the team, can be
        'pull', 'push' or 'admin'. Defaults to pull.

    members
        The members belonging to the team, specified as a dict of member name to
        optional configuration. Options include 'enforce_mfa_from' and 'mfa_exempt'.

    enforce_mfa
        Whether to enforce MFA requirements on members of the team. If True then
        all members without `mfa_exempt: True` configured will be removed from
        the team. Note that `no_mfa_grace_seconds` may be set to allow members
        a grace period.

    no_mfa_grace_seconds
        The number of seconds of grace time that a member will have to enable MFA
        before being removed from the team. The grace period will begin from
        `enforce_mfa_from` on the member configuration, which defaults to
        1970/01/01.

    Example:

    .. code-block:: yaml

        Ensure team test is present in github:
            github.team_present:
                - name: 'test'
                - members:
                    user1: {}
                    user2: {}

        Ensure team test_mfa is present in github:
            github.team_present:
                - name: 'test_mfa'
                - members:
                    user1:
                        enforce_mfa_from: 2016/06/15
                - enforce_mfa: True

    .. versionadded:: 2016.11.0