示例#1
0
    def _testRegistryPolicy(self, policy_name, policy_config,
                            registry_value_hive, registry_value_path,
                            registry_value_vname, expected_value_data):
        '''
        Takes a registry based policy name and config and validates that the
        expected registry value exists and has the correct data

        policy_name
            name of the registry based policy to configure
        policy_config
            the configuration of the policy
        registry_value_hive
            the registry hive that the policy registry path is in
        registry_value_path
            the registry value path that the policy updates
        registry_value_vname
            the registry value name
        expected_value_data
            the expected data that the value will contain
        '''
        ret = self.run_function('lgpo.set_computer_policy',
                                (policy_name, policy_config))
        self.assertTrue(ret)
        val = reg.read_value(hive=registry_value_hive,
                             key=registry_value_path,
                             vname=registry_value_vname)
        self.assertTrue(
            val['success'],
            msg='Failed to obtain the registry data for policy {0}'.format(
                policy_name))
        if val['success']:
            self.assertEqual(
                val['vdata'], expected_value_data,
                'The registry value data {0} does not match the expected value {1} for policy {2}'
                .format(val['vdata'], expected_value_data, policy_name))
示例#2
0
 def test_set_value_default(self):
     '''
     Test the set_value function on the default value
     '''
     try:
         self.assertTrue(
             win_reg.set_value(
                 hive='HKLM',
                 key=FAKE_KEY,
                 vdata='fake_default_data'
             )
         )
         expected = {
             'hive': 'HKLM',
             'key': FAKE_KEY,
             'success': True,
             'vdata': 'fake_default_data',
             'vname': '(Default)',
             'vtype': 'REG_SZ'
         }
         self.assertEqual(
             win_reg.read_value(
                 hive='HKLM',
                 key=FAKE_KEY,
             ),
             expected
         )
     finally:
         win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY)
示例#3
0
 def test_set_value_reg_qword(self):
     '''
     Test the set_value function on a unicode value
     '''
     try:
         self.assertTrue(
             win_reg.set_value(hive='HKLM',
                               key=FAKE_KEY,
                               vname='qword_value',
                               vdata=123,
                               vtype='REG_QWORD'))
         expected = {
             'hive': 'HKLM',
             'key': FAKE_KEY,
             'success': True,
             'vdata': 123,
             'vname': 'qword_value',
             'vtype': 'REG_QWORD'
         }
         self.assertEqual(
             win_reg.read_value(hive='HKLM',
                                key=FAKE_KEY,
                                vname='qword_value'), expected)
     finally:
         win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY)
示例#4
0
    def test_present_reg_binary(self):
        '''
        Testing reg.present with REG_BINARY
        '''
        test_data = 'Salty Test'
        log.debug('Testing reg.present with REG_BINARY')
        # default type is 'REG_SZ'
        # Does the state return the correct data
        ret = self.run_state('reg.present',
                             name='HKLM\\{0}'.format(FAKE_KEY),
                             vname='test_reg_binary',
                             vtype='REG_BINARY',
                             vdata=test_data)
        expected = {
            'reg': {
                'Added': {
                    'Entry': 'test_reg_binary',
                    'Key': 'HKLM\\{0}'.format(FAKE_KEY),
                    'Value': test_data}}}
        self.assertSaltStateChangesEqual(ret, expected)

        # Is the value actually set
        ret = reg.read_value(hive='HKLM', key=FAKE_KEY, vname='test_reg_binary')
        expected = {
            'vtype': 'REG_BINARY',
            'vname': 'test_reg_binary',
            'success': True,
            'hive': 'HKLM',
            'vdata': test_data.encode('utf-8'),
            'key': FAKE_KEY}
        self.assertEqual(ret, expected)
示例#5
0
 def test_set_value_unicode_key(self):
     '''
     Test the set_value function on a unicode key
     '''
     try:
         self.assertTrue(
             win_reg.set_value(hive='HKLM',
                               key='{0}\\{1}'.format(FAKE_KEY, UNICODE_KEY),
                               vname='fake_name',
                               vdata='fake_value'))
         expected = {
             'hive': 'HKLM',
             'key': '{0}\\{1}'.format(FAKE_KEY, UNICODE_KEY),
             'success': True,
             'vdata': 'fake_value',
             'vname': 'fake_name',
             'vtype': 'REG_SZ'
         }
         self.assertEqual(
             win_reg.read_value(hive='HKLM',
                                key='{0}\\{1}'.format(
                                    FAKE_KEY, UNICODE_KEY),
                                vname='fake_name'), expected)
     finally:
         win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY)
示例#6
0
    def test_present_reg_sz_unicode_value(self):
        '''
        Testing reg.present with REG_SZ and a unicode value
        '''
        log.debug('Testing reg.present with REG_SZ and a unicode value')
        # default type is 'REG_SZ'
        # Does the state return the correct data
        ret = self.run_state('reg.present',
                             name='HKLM\\{0}'.format(FAKE_KEY),
                             vname='test_reg_sz',
                             vdata=UNICODE_VALUE)
        expected = {
            'reg': {
                'Added': {
                    'Entry': 'test_reg_sz',
                    'Key': 'HKLM\\{0}'.format(FAKE_KEY),
                    'Value': UNICODE_VALUE}}}
        self.assertSaltStateChangesEqual(ret, expected)

        # Is the value actually set
        ret = reg.read_value(hive='HKLM', key=FAKE_KEY, vname='test_reg_sz')
        expected = {
            'vtype': 'REG_SZ',
            'vname': 'test_reg_sz',
            'success': True,
            'hive': 'HKLM',
            'vdata': UNICODE_VALUE,
            'key': FAKE_KEY}
        self.assertEqual(ret, expected)
示例#7
0
    def test_present_32_bit(self):
        '''
        Testing reg.present with REG_SZ using 32bit registry
        '''
        log.debug('Testing reg.present with REG_SZ using 32bit registry')
        # default type is 'REG_SZ'
        # Does the state return the correct data
        ret = self.run_state('reg.present',
                             name='HKLM\\{0}'.format(FAKE_KEY),
                             vname='test_reg_sz',
                             vdata='fake string data',
                             use_32bit_registry=True)

        expected = {
            'reg': {
                'Added': {
                    'Entry': 'test_reg_sz',
                    'Key': 'HKLM\\{0}'.format(FAKE_KEY),
                    'Value': 'fake string data'}}}
        self.assertSaltStateChangesEqual(ret, expected)

        # Is the value actually set
        ret = reg.read_value(hive='HKLM',
                             key=FAKE_KEY,
                             vname='test_reg_sz',
                             use_32bit_registry=True)
        expected = {
            'vtype': 'REG_SZ',
            'vname': 'test_reg_sz',
            'success': True,
            'hive': 'HKLM',
            'vdata': 'fake string data',
            'key': FAKE_KEY}
        self.assertEqual(ret, expected)
示例#8
0
 def test_read_value_multi_sz_empty_list(self):
     """
     An empty REG_MULTI_SZ value should return an empty list, not None
     """
     try:
         self.assertTrue(
             win_reg.set_value(
                 hive="HKLM",
                 key=FAKE_KEY,
                 vname="empty_list",
                 vdata=[],
                 vtype="REG_MULTI_SZ",
             ))
         expected = {
             "hive": "HKLM",
             "key": FAKE_KEY,
             "success": True,
             "vdata": [],
             "vname": "empty_list",
             "vtype": "REG_MULTI_SZ",
         }
         self.assertEqual(
             win_reg.read_value(
                 hive="HKLM",
                 key=FAKE_KEY,
                 vname="empty_list",
             ),
             expected,
         )
     finally:
         win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)
示例#9
0
 def test_set_value_unicode_value(self):
     """
     Test the set_value function on a unicode value
     """
     try:
         self.assertTrue(
             win_reg.set_value(hive="HKLM",
                               key=FAKE_KEY,
                               vname="fake_unicode",
                               vdata=UNICODE_VALUE))
         expected = {
             "hive": "HKLM",
             "key": FAKE_KEY,
             "success": True,
             "vdata": UNICODE_VALUE,
             "vname": "fake_unicode",
             "vtype": "REG_SZ",
         }
         self.assertEqual(
             win_reg.read_value(hive="HKLM",
                                key=FAKE_KEY,
                                vname="fake_unicode"),
             expected,
         )
     finally:
         win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)
示例#10
0
 def test_set_value_reg_qword(self):
     """
     Test the set_value function on a REG_QWORD value
     """
     try:
         self.assertTrue(
             win_reg.set_value(
                 hive="HKLM",
                 key=FAKE_KEY,
                 vname="qword_value",
                 vdata=123,
                 vtype="REG_QWORD",
             ))
         expected = {
             "hive": "HKLM",
             "key": FAKE_KEY,
             "success": True,
             "vdata": 123,
             "vname": "qword_value",
             "vtype": "REG_QWORD",
         }
         self.assertEqual(
             win_reg.read_value(hive="HKLM",
                                key=FAKE_KEY,
                                vname="qword_value"),
             expected,
         )
     finally:
         win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)
示例#11
0
 def test_read_value_multi_sz_empty_list(self):
     '''
     An empty REG_MULTI_SZ value should return an empty list, not None
     '''
     try:
         self.assertTrue(
             win_reg.set_value(hive='HKLM',
                               key=FAKE_KEY,
                               vname='empty_list',
                               vdata=[],
                               vtype='REG_MULTI_SZ'))
         expected = {
             'hive': 'HKLM',
             'key': FAKE_KEY,
             'success': True,
             'vdata': [],
             'vname': 'empty_list',
             'vtype': 'REG_MULTI_SZ'
         }
         self.assertEqual(
             win_reg.read_value(
                 hive='HKLM',
                 key=FAKE_KEY,
                 vname='empty_list',
             ), expected)
     finally:
         win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY)
示例#12
0
 def test_read_value_non_existing(self):
     """
     Test the read_value function using a non existing value pair
     """
     expected = {
         "comment": ("Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\"
                     "Windows\\CurrentVersion"),
         "vdata":
         None,
         "vname":
         "fake_name",
         "success":
         False,
         "hive":
         "HKLM",
         "key":
         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
     }
     self.assertDictEqual(
         win_reg.read_value(
             hive="HKLM",
             key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
             vname="fake_name",
         ),
         expected,
     )
示例#13
0
 def test_read_value_default(self):
     '''
     Test the read_value function reading the default value using a well
     known registry key
     '''
     ret = win_reg.read_value(
         hive='HKLM', key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion')
     self.assertEqual(ret['vdata'], '(value not set)')
示例#14
0
 def test_read_value_default(self):
     """
     Test the read_value function reading the default value using a well
     known registry key
     """
     ret = win_reg.read_value(
         hive="HKLM", key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion")
     self.assertEqual(ret["vdata"], "(value not set)")
示例#15
0
 def test_read_value_existing(self):
     '''
     Test the read_value function using a well known registry value
     '''
     ret = win_reg.read_value(
         hive='HKLM',
         key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion',
         vname='ProgramFilesPath')
     self.assertEqual(ret['vdata'], '%ProgramFiles%')
示例#16
0
    def _testRegistryPolicy(
        self,
        policy_name,
        policy_config,
        registry_value_hive,
        registry_value_path,
        registry_value_vname,
        expected_value_data,
        expected_value_type=None,
        expect_value_exists=True,
    ):
        """
        Takes a registry based policy name and config and validates that the
        expected registry value exists and has the correct data

        policy_name
            name of the registry based policy to configure
        policy_config
            the configuration of the policy
        registry_value_hive
            the registry hive that the policy registry path is in
        registry_value_path
            the registry value path that the policy updates
        registry_value_vname
            the registry value name
        expected_value_data
            the expected data that the value will contain
        expected_value_type
            the registry value type (i.e. REG_SZ, REG_DWORD, etc)
        expect_value_exists
            define if it expected for a registry value to exist
            some policies when set to 'Not Defined' delete the registry value
        """
        ret = self.run_function("lgpo.set_computer_policy",
                                (policy_name, policy_config))
        self.assertTrue(ret)
        val = reg.read_value(registry_value_hive, registry_value_path,
                             registry_value_vname)
        if expect_value_exists:
            self.assertTrue(
                val["success"],
                msg="Failed to obtain the registry data for policy {}".format(
                    policy_name),
            )
        self.assertEqual(
            val["vdata"],
            expected_value_data,
            "The registry value data {} does not match the expected value {} for policy {}"
            .format(val["vdata"], expected_value_data, policy_name),
        )
        if expected_value_type:
            self.assertEqual(
                val["vtype"],
                expected_value_type,
                "The registry value type {} does not match the expected type {} for policy {}"
                .format(val["vtype"], expected_value_type, policy_name),
            )
示例#17
0
 def test_read_value_existing(self):
     """
     Test the read_value function using a well known registry value
     """
     ret = win_reg.read_value(
         hive="HKLM",
         key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
         vname="ProgramFilesPath",
     )
     self.assertEqual(ret["vdata"], "%ProgramFiles%")
示例#18
0
 def test_read_value_non_existing_key(self):
     """
     Test the read_value function using a non existing registry key
     """
     expected = {
         "comment": "Cannot find key: HKLM\\{0}".format(FAKE_KEY),
         "vdata": None,
         "vname": "fake_name",
         "success": False,
         "hive": "HKLM",
         "key": FAKE_KEY,
     }
     self.assertDictEqual(
         win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_name"),
         expected)
示例#19
0
 def test_read_value_non_existing_key(self):
     '''
     Test the read_value function using a non existing registry key
     '''
     expected = {
         'comment': 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY),
         'vdata': None,
         'vname': 'fake_name',
         'success': False,
         'hive': 'HKLM',
         'key': FAKE_KEY
     }
     self.assertEqual(
         win_reg.read_value(hive='HKLM', key=FAKE_KEY, vname='fake_name'),
         expected)
示例#20
0
    def test_present_32_bit(self):
        """
        Testing reg.present with REG_SZ using 32bit registry
        """
        log.debug("Testing reg.present with REG_SZ using 32bit registry")
        # default type is 'REG_SZ'
        # Does the state return the correct data
        ret = self.run_state(
            "reg.present",
            name="HKLM\\{}".format(FAKE_KEY),
            vname="test_reg_sz",
            vdata="fake string data",
            use_32bit_registry=True,
        )

        expected = {
            "reg": {
                "Added": {
                    "Entry": "test_reg_sz",
                    "Inheritance": True,
                    "Key": "HKLM\\{}".format(FAKE_KEY),
                    "Owner": None,
                    "Perms": {
                        "Deny": None,
                        "Grant": None
                    },
                    "Value": "fake string data",
                }
            }
        }
        self.assertSaltStateChangesEqual(ret, expected)

        # Is the value actually set
        ret = reg.read_value(hive="HKLM",
                             key=FAKE_KEY,
                             vname="test_reg_sz",
                             use_32bit_registry=True)
        expected = {
            "vtype": "REG_SZ",
            "vname": "test_reg_sz",
            "success": True,
            "hive": "HKLM",
            "vdata": "fake string data",
            "key": FAKE_KEY,
        }
        self.assertEqual(ret, expected)
示例#21
0
    def test_present_reg_binary(self):
        """
        Testing reg.present with REG_BINARY
        """
        test_data = "Salty Test"
        log.debug("Testing reg.present with REG_BINARY")
        # default type is 'REG_SZ'
        # Does the state return the correct data
        ret = self.run_state(
            "reg.present",
            name="HKLM\\{}".format(FAKE_KEY),
            vname="test_reg_binary",
            vtype="REG_BINARY",
            vdata=test_data,
        )
        expected = {
            "reg": {
                "Added": {
                    "Entry": "test_reg_binary",
                    "Inheritance": True,
                    "Key": "HKLM\\{}".format(FAKE_KEY),
                    "Owner": None,
                    "Perms": {
                        "Deny": None,
                        "Grant": None
                    },
                    "Value": test_data,
                }
            }
        }
        self.assertSaltStateChangesEqual(ret, expected)

        # Is the value actually set
        ret = reg.read_value(hive="HKLM",
                             key=FAKE_KEY,
                             vname="test_reg_binary")
        expected = {
            "vtype": "REG_BINARY",
            "vname": "test_reg_binary",
            "success": True,
            "hive": "HKLM",
            "vdata": test_data.encode("utf-8"),
            "key": FAKE_KEY,
        }
        self.assertEqual(ret, expected)
示例#22
0
 def test_read_value_non_existing(self):
     '''
     Test the read_value function using a non existing value pair
     '''
     expected = {
         'comment':
         'Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion',
         'vdata': None,
         'vname': 'fake_name',
         'success': False,
         'hive': 'HKLM',
         'key': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
     }
     self.assertEqual(
         win_reg.read_value(
             hive='HKLM',
             key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion',
             vname='fake_name'), expected)
示例#23
0
    def test_present_reg_sz_unicode_value_name(self):
        """
        Testing reg.present with REG_SZ and a unicode value name
        """
        log.debug("Testing reg.present with REG_SZ and a unicode value name")
        # default type is 'REG_SZ'
        # Does the state return the correct data
        ret = self.run_state(
            "reg.present",
            name="HKLM\\{0}".format(FAKE_KEY),
            vname=UNICODE_VALUE_NAME,
            vdata="fake string data",
        )
        expected = {
            "reg": {
                "Added": {
                    "Entry": UNICODE_VALUE_NAME,
                    "Inheritance": True,
                    "Key": "HKLM\\{0}".format(FAKE_KEY),
                    "Owner": None,
                    "Perms": {
                        "Deny": None,
                        "Grant": None
                    },
                    "Value": "fake string data",
                }
            }
        }
        self.assertSaltStateChangesEqual(ret, expected)

        # Is the value actually set
        ret = reg.read_value(hive="HKLM",
                             key=FAKE_KEY,
                             vname=UNICODE_VALUE_NAME)

        expected = {
            "vtype": "REG_SZ",
            "vname": UNICODE_VALUE_NAME,
            "success": True,
            "hive": "HKLM",
            "vdata": "fake string data",
            "key": FAKE_KEY,
        }
        self.assertEqual(ret, expected)
示例#24
0
    def test_present_reg_multi_sz(self):
        """
        Testing reg.present with REG_MULTI_SZ
        """
        log.debug("Testing reg.present with REG_MULTI_SZ")
        # default type is 'REG_SZ'
        # Does the state return the correct data
        ret = self.run_state(
            "reg.present",
            name="HKLM\\{0}".format(FAKE_KEY),
            vname="test_reg_multi_sz",
            vtype="REG_MULTI_SZ",
            vdata=["item1", "item2"],
        )
        expected = {
            "reg": {
                "Added": {
                    "Entry": "test_reg_multi_sz",
                    "Inheritance": True,
                    "Key": "HKLM\\{0}".format(FAKE_KEY),
                    "Owner": None,
                    "Perms": {
                        "Deny": None,
                        "Grant": None
                    },
                    "Value": ["item1", "item2"],
                }
            }
        }
        self.assertSaltStateChangesEqual(ret, expected)

        # Is the value actually set
        ret = reg.read_value(hive="HKLM",
                             key=FAKE_KEY,
                             vname="test_reg_multi_sz")
        expected = {
            "vtype": "REG_MULTI_SZ",
            "vname": "test_reg_multi_sz",
            "success": True,
            "hive": "HKLM",
            "vdata": ["item1", "item2"],
            "key": FAKE_KEY,
        }
        self.assertEqual(ret, expected)
示例#25
0
 def test_set_value_default(self):
     """
     Test the set_value function on the default value
     """
     try:
         self.assertTrue(
             win_reg.set_value(hive="HKLM", key=FAKE_KEY, vdata="fake_default_data")
         )
         expected = {
             "hive": "HKLM",
             "key": FAKE_KEY,
             "success": True,
             "vdata": "fake_default_data",
             "vname": "(Default)",
             "vtype": "REG_SZ",
         }
         self.assertEqual(win_reg.read_value(hive="HKLM", key=FAKE_KEY), expected)
     finally:
         win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)
示例#26
0
def versions():
    '''
    Figure out what versions of .NET are installed on the system

    Returns:
        dict: A dictionary containing two keys:
            - versions: A list of versions installed on the system
            - details: A dictionary with details about the versions installed on
              the system
    '''
    hive = 'HKLM'
    key = 'SOFTWARE\\Microsoft\\NET Framework Setup\\NDP'
    ver_keys = win_reg.list_keys(hive=hive, key=key)

    def dotnet_45_plus_versions(release):
        if release >= 461808:
            return '4.7.2'
        if release >= 461308:
            return '4.7.1'
        if release >= 460798:
            return '4.7'
        if release >= 394802:
            return '4.6.2'
        if release >= 394254:
            return '4.6.1'
        if release >= 393295:
            return '4.6'
        if release >= 379893:
            return '4.5.2'
        if release >= 378675:
            return '4.5.1'
        if release >= 378389:
            return '4..5'

    return_dict = {'versions': [], 'details': {}}
    for ver_key in ver_keys:

        if ver_key.startswith('v'):
            if win_reg.value_exists(hive=hive,
                                    key='\\'.join([key, ver_key]),
                                    vname='Version'):
                # https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#find-net-framework-versions-1-4-with-codep
                install = win_reg.read_value(hive=hive,
                                             key='\\'.join([key, ver_key]),
                                             vname='Install')['vdata']
                if not install:
                    continue
                version = win_reg.read_value(hive=hive,
                                             key='\\'.join([key, ver_key]),
                                             vname='Version')['vdata']
                sp = win_reg.read_value(hive=hive,
                                        key='\\'.join([key, ver_key]),
                                        vname='SP')['vdata']
            elif win_reg.value_exists(hive=hive,
                                      key='\\'.join([key, ver_key, 'Full']),
                                      vname='Release'):
                # https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#find-net-framework-versions-45-and-later-with-code
                install = win_reg.read_value(hive=hive,
                                             key='\\'.join(
                                                 [key, ver_key, 'Full']),
                                             vname='Install')['vdata']
                if not install:
                    continue
                version = dotnet_45_plus_versions(
                    win_reg.read_value(hive=hive,
                                       key='\\'.join([key, ver_key, 'Full']),
                                       vname='Release')['vdata'])
                sp = 'N/A'
            else:
                continue

            service_pack = ' SP{0}'.format(sp) if sp != 'N/A' else ''
            return_dict['versions'].append(version)
            return_dict['details'][ver_key] = {
                'version': version,
                'service_pack': sp,
                'full': '{0}{1}'.format(version, service_pack)
            }

    return return_dict
示例#27
0
def versions():
    """
    Figure out what versions of .NET are installed on the system

    Returns:
        dict: A dictionary containing two keys:
            - versions: A list of versions installed on the system
            - details: A dictionary with details about the versions installed on
              the system
    """
    hive = "HKLM"
    key = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP"
    ver_keys = win_reg.list_keys(hive=hive, key=key)

    def dotnet_45_plus_versions(release):
        if release >= 528040:
            return "4.8"
        if release >= 461808:
            return "4.7.2"
        if release >= 461308:
            return "4.7.1"
        if release >= 460798:
            return "4.7"
        if release >= 394802:
            return "4.6.2"
        if release >= 394254:
            return "4.6.1"
        if release >= 393295:
            return "4.6"
        if release >= 379893:
            return "4.5.2"
        if release >= 378675:
            return "4.5.1"
        if release >= 378389:
            return "4.5"

    return_dict = {"versions": [], "details": {}}
    for ver_key in ver_keys:

        if ver_key.startswith("v"):
            if win_reg.value_exists(
                hive=hive, key="\\".join([key, ver_key]), vname="Version"
            ):
                # https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#find-net-framework-versions-1-4-with-codep
                install = win_reg.read_value(
                    hive=hive, key="\\".join([key, ver_key]), vname="Install"
                )["vdata"]
                if not install:
                    continue
                version = win_reg.read_value(
                    hive=hive, key="\\".join([key, ver_key]), vname="Version"
                )["vdata"]
                sp = win_reg.read_value(
                    hive=hive, key="\\".join([key, ver_key]), vname="SP"
                )["vdata"]
            elif win_reg.value_exists(
                hive=hive, key="\\".join([key, ver_key, "Full"]), vname="Release"
            ):
                # https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#find-net-framework-versions-45-and-later-with-code
                install = win_reg.read_value(
                    hive=hive, key="\\".join([key, ver_key, "Full"]), vname="Install"
                )["vdata"]
                if not install:
                    continue
                version = dotnet_45_plus_versions(
                    win_reg.read_value(
                        hive=hive,
                        key="\\".join([key, ver_key, "Full"]),
                        vname="Release",
                    )["vdata"]
                )
                sp = "N/A"
            else:
                continue

            service_pack = " SP{0}".format(sp) if sp != "N/A" else ""
            return_dict["versions"].append(version)
            return_dict["details"][ver_key] = {
                "version": version,
                "service_pack": sp,
                "full": "{0}{1}".format(version, service_pack),
            }

    return return_dict