Recursively combine dictionaries

I think this does what you want:

def resolve(groups, hosts):
    # Groups that have already been resolved
    resolved_groups = {}
    # Group names that are not root
    non_root = set()
    # Make dict with resolution of each group
    result = {}
    for name in groups:
        result[name] = _resolve_rec(name, groups, hosts, resolved_groups, non_root)
    # Remove groups that are not root
    for name in groups:
        if name in non_root:
            del result[name]
    return result

def _resolve_rec(name, groups, hosts, resolved_groups, non_root):
    # If group has already been resolved finish
    if name in resolved_groups:
        return resolved_groups[name]
    # If it is a host finish
    if name in hosts:
        return hosts[name]
    # New group resolution
    resolved = {}
    for child in groups[name]:
        # Resolve each child
        resolved[child] = _resolve_rec(child, groups, hosts, resolved_groups, non_root)
        # Mark child as non-root
        non_root.add(child)
    # Save to resolved groups
    resolved_groups[name] = resolved
    return resolved

With your example:

groups = {
    'servers': ['unix_servers', 'windows_servers'],
    'unix_servers': ['server_a', 'server_b', 'server_group'],
    'windows_servers': ['server_c', 'server_d'],
    'server_group': ['server_e', 'server_f']
}

hosts = {
    'server_a': '10.0.0.1',
    'server_b': '10.0.0.2',
    'server_c': '10.0.0.3',
    'server_d': '10.0.0.4',
    'server_e': '10.0.0.5',
    'server_f': '10.0.0.6'
}

d3 = {
    'servers': {
        'unix_servers': {
            'server_a': '10.0.0.1',
            'server_b': '10.0.0.2',
            'server_group': {
                'server_e': '10.0.0.5',
                'server_f': '10.0.0.6'
            }
        },
        'windows_servers': {
            'server_c': '10.0.0.3',
            'server_d': '10.0.0.4'
        }
    }
}


print(resolve(groups, hosts) == d3)
# True

Note this can fall into infinite recursion for malformed inputs, if you have for example group A containing group B but then group B contains group A.