aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBevan Weiss <bevan.weiss@gmail.com>2024-08-04 21:13:44 +1000
committerRob Mensching <rob@firegiant.com>2025-02-11 23:14:49 -0800
commit5b4a6538ee06988c75b717bd905197fb670e6142 (patch)
treeeb078854f258ebdabaf206282d56cbdcf87759ef
parent2c5bb89424b12de812498d568bc1aae2d4098e60 (diff)
downloadwix-5b4a6538ee06988c75b717bd905197fb670e6142.tar.gz
wix-5b4a6538ee06988c75b717bd905197fb670e6142.tar.bz2
wix-5b4a6538ee06988c75b717bd905197fb670e6142.zip
Add/Remove Group Membership rollback handled.
Fixups to a few test cases. Signed-off-by: Bevan Weiss <bevan.weiss@gmail.com>
-rw-r--r--src/ext/Util/ca/scaexec.cpp128
-rw-r--r--src/ext/Util/ca/scagroup.cpp159
-rw-r--r--src/ext/Util/ca/scasched.cpp2
-rw-r--r--src/test/burn/WixTestTools/RuntimeFactAttribute.cs32
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductA/product.wxs9
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductAddCommentToExistingGroup/product.wxs9
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentDelete/product.wxs4
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs4
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs2
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductFailIfExists/FailIfExists.wxs2
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs7
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductNewGroupWithComment/product.wxs9
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductNonVitalGroup/NonVitalUserGroup.wxs2
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductRestrictedDomain/RestrictedDomain.wxs4
-rw-r--r--src/test/msi/TestData/UtilExtensionGroupTests/ProductWithCommandLineParameters/ProductWithCommandLineParameters.wxs11
-rw-r--r--src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs95
-rw-r--r--src/test/msi/WixToolsetTest.MsiE2E/runtests.cmd1
17 files changed, 346 insertions, 134 deletions
diff --git a/src/ext/Util/ca/scaexec.cpp b/src/ext/Util/ca/scaexec.cpp
index 64f4c823..fea60b01 100644
--- a/src/ext/Util/ca/scaexec.cpp
+++ b/src/ext/Util/ca/scaexec.cpp
@@ -716,7 +716,7 @@ static HRESULT RemoveGroupInternal(
716 // 716 //
717 if (!(SCAG_DONT_CREATE_GROUP & iAttributes)) 717 if (!(SCAG_DONT_CREATE_GROUP & iAttributes))
718 { 718 {
719 GetDomainServerName(wzDomain, &pwzServerName, DS_WRITABLE_REQUIRED); 719 hr = GetDomainFromServerName(&pwzServerName, wzDomain, DS_WRITABLE_REQUIRED);
720 720
721 NET_API_STATUS er = ::NetLocalGroupDel(pwzServerName, wzName); 721 NET_API_STATUS er = ::NetLocalGroupDel(pwzServerName, wzName);
722 hr = HRESULT_FROM_WIN32(er); 722 hr = HRESULT_FROM_WIN32(er);
@@ -1218,9 +1218,13 @@ LExit:
1218 1218
1219 1219
1220/******************************************************************** 1220/********************************************************************
1221 CreateGroup - CUSTOM ACTION ENTRY POINT for creating groups 1221 CreateGroup - CUSTOM ACTION ENTRY POINT for creating groups
1222 For domain parent group, must be run as Impersonated=true
1223 For non-domain parent group, must be run as Impersonated=false (for elevation)
1222 1224
1223 Input: deferred CustomActionData - GroupName\tDomain\tComment\tAttributes 1225 Input: deferred CustomActionData - GroupName\tDomain\tComment\tAttributes\tScriptKey(empty for no rollback)
1226
1227 Output: Script for RollbackCreateGroup - OriginalComment\tRollbackAttributes
1224 * *****************************************************************/ 1228 * *****************************************************************/
1225extern "C" UINT __stdcall CreateGroup( 1229extern "C" UINT __stdcall CreateGroup(
1226 __in MSIHANDLE hInstall 1230 __in MSIHANDLE hInstall
@@ -1281,8 +1285,8 @@ extern "C" UINT __stdcall CreateGroup(
1281 1285
1282 if (!(SCAG_DONT_CREATE_GROUP & iAttributes)) 1286 if (!(SCAG_DONT_CREATE_GROUP & iAttributes))
1283 { 1287 {
1284 hr = GetDomainServerName(pwzDomain, &pwzServerName, DS_WRITABLE_REQUIRED); 1288 hr = GetDomainFromServerName(&pwzServerName, pwzDomain, DS_WRITABLE_REQUIRED);
1285 ExitOnFailure(hr, "failed to find Domain %ls.", pwzDomain); 1289 ExitOnFailure(hr, "failed to find writable server for domain %ls.", pwzDomain);
1286 1290
1287 // Set the group's comment 1291 // Set the group's comment
1288 if (SCAG_REMOVE_COMMENT & iAttributes) 1292 if (SCAG_REMOVE_COMMENT & iAttributes)
@@ -1304,7 +1308,7 @@ extern "C" UINT __stdcall CreateGroup(
1304 { 1308 {
1305 if (SCAG_FAIL_IF_EXISTS & iAttributes) 1309 if (SCAG_FAIL_IF_EXISTS & iAttributes)
1306 { 1310 {
1307 MessageExitOnFailure(hr, msierrGRPFailedGroupCreateExists, "Group (%ls) was not supposed to exist, but does", pwzName); 1311 MessageExitOnFailure(hr, msierrGRPFailedGroupCreateExists, "Group (%ls\\%ls) was not supposed to exist, but does", pwzDomain, pwzName);
1308 } 1312 }
1309 1313
1310 hr = S_OK; // Make sure that we don't report this situation as an error 1314 hr = S_OK; // Make sure that we don't report this situation as an error
@@ -1359,7 +1363,7 @@ extern "C" UINT __stdcall CreateGroup(
1359 hr = SetGroupComment(pwzServerName, pwzName, L""); 1363 hr = SetGroupComment(pwzServerName, pwzName, L"");
1360 if (FAILED(hr)) 1364 if (FAILED(hr))
1361 { 1365 {
1362 WcaLogError(hr, "failed to clear comment for group %ls\\%ls, continuing anyway.", pwzServerName, pwzName); 1366 WcaLogError(hr, "failed to clear comment for group %ls\\%ls, continuing anyway.", pwzDomain, pwzName);
1363 hr = S_OK; 1367 hr = S_OK;
1364 } 1368 }
1365 } 1369 }
@@ -1368,14 +1372,14 @@ extern "C" UINT __stdcall CreateGroup(
1368 hr = SetGroupComment(pwzServerName, pwzName, pwzComment); 1372 hr = SetGroupComment(pwzServerName, pwzName, pwzComment);
1369 if (FAILED(hr)) 1373 if (FAILED(hr))
1370 { 1374 {
1371 WcaLogError(hr, "failed to set comment to %ls for group %ls\\%ls, continuing anyway.", pwzComment, pwzServerName, pwzName); 1375 WcaLogError(hr, "failed to set comment to '%ls' for group %ls\\%ls, continuing anyway.", pwzComment, pwzDomain, pwzName);
1372 hr = S_OK; 1376 hr = S_OK;
1373 } 1377 }
1374 } 1378 }
1375 } 1379 }
1376 } 1380 }
1377 } 1381 }
1378 MessageExitOnFailure(hr, msierrGRPFailedGroupCreate, "failed to create group: %ls", pwzName); 1382 MessageExitOnFailure(hr, msierrGRPFailedGroupCreate, "failed to create group: %ls\\%ls", pwzDomain, pwzName);
1379 } 1383 }
1380 1384
1381LExit: 1385LExit:
@@ -1391,6 +1395,7 @@ LExit:
1391 ReleaseStr(pwzDomain); 1395 ReleaseStr(pwzDomain);
1392 ReleaseStr(pwzComment); 1396 ReleaseStr(pwzComment);
1393 ReleaseStr(pwzScriptKey); 1397 ReleaseStr(pwzScriptKey);
1398 ReleaseStr(pwzServerName);
1394 1399
1395 if (fInitializedCom) 1400 if (fInitializedCom)
1396 { 1401 {
@@ -1412,6 +1417,11 @@ LExit:
1412 1417
1413/******************************************************************** 1418/********************************************************************
1414 CreateGroupRollback - CUSTOM ACTION ENTRY POINT for CreateGroup rollback 1419 CreateGroupRollback - CUSTOM ACTION ENTRY POINT for CreateGroup rollback
1420 For domain parent group, must be run as Impersonated=true
1421 For non-domain parent group, must be run as Impersonated=false (for elevation)
1422
1423 Input: rollback CustomActionData - ScriptKey\tGroupName\tDomain\tComment\tRollbackAttributes
1424 rollback script - OriginalComment\tRollbackAttributes
1415 1425
1416 * *****************************************************************/ 1426 * *****************************************************************/
1417extern "C" UINT __stdcall CreateGroupRollback( 1427extern "C" UINT __stdcall CreateGroupRollback(
@@ -1429,7 +1439,7 @@ extern "C" UINT __stdcall CreateGroupRollback(
1429 LPWSTR pwzName = NULL; 1439 LPWSTR pwzName = NULL;
1430 LPWSTR pwzDomain = NULL; 1440 LPWSTR pwzDomain = NULL;
1431 LPWSTR pwzComment = NULL; 1441 LPWSTR pwzComment = NULL;
1432 int iAttributes = 0; 1442 int iRollbackAttributes = 0;
1433 BOOL fInitializedCom = FALSE; 1443 BOOL fInitializedCom = FALSE;
1434 1444
1435 WCA_CASCRIPT_HANDLE hRollbackScript = NULL; 1445 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
@@ -1454,7 +1464,7 @@ extern "C" UINT __stdcall CreateGroupRollback(
1454 // 1464 //
1455 pwz = pwzData; 1465 pwz = pwzData;
1456 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey); 1466 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
1457 ExitOnFailure(hr, "failed to read encoding key from custom action data"); 1467 ExitOnFailure(hr, "failed to read script key from custom action data");
1458 1468
1459 hr = WcaReadStringFromCaData(&pwz, &pwzName); 1469 hr = WcaReadStringFromCaData(&pwz, &pwzName);
1460 ExitOnFailure(hr, "failed to read name from custom action data"); 1470 ExitOnFailure(hr, "failed to read name from custom action data");
@@ -1465,8 +1475,8 @@ extern "C" UINT __stdcall CreateGroupRollback(
1465 hr = WcaReadStringFromCaData(&pwz, &pwzComment); 1475 hr = WcaReadStringFromCaData(&pwz, &pwzComment);
1466 ExitOnFailure(hr, "failed to read comment from custom action data"); 1476 ExitOnFailure(hr, "failed to read comment from custom action data");
1467 1477
1468 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); 1478 hr = WcaReadIntegerFromCaData(&pwz, &iRollbackAttributes);
1469 ExitOnFailure(hr, "failed to read attributes from custom action data"); 1479 ExitOnFailure(hr, "failed to read rollback attributes from custom action data");
1470 1480
1471 // Best effort to read original configuration from CreateUser. 1481 // Best effort to read original configuration from CreateUser.
1472 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript); 1482 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
@@ -1502,12 +1512,12 @@ extern "C" UINT __stdcall CreateGroupRollback(
1502 } 1512 }
1503 else 1513 else
1504 { 1514 {
1505 iAttributes |= iOriginalAttributes; 1515 iRollbackAttributes |= iOriginalAttributes;
1506 } 1516 }
1507 } 1517 }
1508 } 1518 }
1509 1519
1510 hr = RemoveGroupInternal(pwzDomain, pwzName, iAttributes); 1520 hr = RemoveGroupInternal(pwzDomain, pwzName, iRollbackAttributes);
1511 1521
1512LExit: 1522LExit:
1513 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE); 1523 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
@@ -1535,9 +1545,12 @@ LExit:
1535 1545
1536 1546
1537/******************************************************************** 1547/********************************************************************
1538 RemoveGroup - CUSTOM ACTION ENTRY POINT for removing groups 1548 RemoveGroup - CUSTOM ACTION ENTRY POINT for removing groups
1549 For domain parent group, must be run as Impersonated=true
1550 For non-domain parent group, must be run as Impersonated=false (for elevation)
1551 NOTE: This action can't be rolled back, so should only be performed in commit phase
1539 1552
1540 Input: deferred CustomActionData - Name\tDomain 1553 Input: commit CustomActionData - Name\tDomain\tComment\tAttributes
1541 * *****************************************************************/ 1554 * *****************************************************************/
1542extern "C" UINT __stdcall RemoveGroup( 1555extern "C" UINT __stdcall RemoveGroup(
1543 MSIHANDLE hInstall 1556 MSIHANDLE hInstall
@@ -1605,7 +1618,7 @@ LExit:
1605 return WcaFinalize(er); 1618 return WcaFinalize(er);
1606} 1619}
1607 1620
1608HRESULT AlterGroupMembership(bool remove, bool isRollback = false) 1621HRESULT AlterGroupMembership(bool remove, bool isRollback)
1609{ 1622{
1610 HRESULT hr = S_OK; 1623 HRESULT hr = S_OK;
1611 NET_API_STATUS er = ERROR_SUCCESS; 1624 NET_API_STATUS er = ERROR_SUCCESS;
@@ -1617,22 +1630,13 @@ HRESULT AlterGroupMembership(bool remove, bool isRollback = false)
1617 LPWSTR pwzChildName = NULL; 1630 LPWSTR pwzChildName = NULL;
1618 LPWSTR pwzChildDomain = NULL; 1631 LPWSTR pwzChildDomain = NULL;
1619 int iAttributes = 0; 1632 int iAttributes = 0;
1633 LPWSTR pwzScriptKey = NULL;
1620 LPWSTR pwzChildFullName = NULL; 1634 LPWSTR pwzChildFullName = NULL;
1621 LPWSTR pwzServerName = NULL; 1635 LPWSTR pwzServerName = NULL;
1622 LOCALGROUP_MEMBERS_INFO_3 memberInfo3 = {}; 1636 LOCALGROUP_MEMBERS_INFO_3 memberInfo3 = {};
1623 WCA_CASCRIPT_HANDLE phRollbackScript = NULL; 1637 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
1624 1638
1625 if (isRollback) 1639 hr = WcaGetProperty(L"CustomActionData", &pwzData);
1626 {
1627 // Get a CaScript key
1628 hr = WcaCaScriptOpen(WCA_ACTION_NONE, WCA_CASCRIPT_ROLLBACK, FALSE, remove ? L"AddGroupMembershipRollback" : L"RemoveGroupMembershipRollback", &phRollbackScript);
1629 hr = WcaCaScriptReadAsCustomActionData(phRollbackScript, &pwzData);
1630 }
1631 else
1632 {
1633 hr = WcaCaScriptCreate(WCA_ACTION_NONE, WCA_CASCRIPT_ROLLBACK, FALSE, remove ? L"RemoveGroupMembershipRollback" : L"AddGroupMembershipRollback", TRUE, &phRollbackScript);
1634 hr = WcaGetProperty(L"CustomActionData", &pwzData);
1635 }
1636 ExitOnFailure(hr, "failed to get CustomActionData"); 1640 ExitOnFailure(hr, "failed to get CustomActionData");
1637 1641
1638 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); 1642 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
@@ -1656,8 +1660,28 @@ HRESULT AlterGroupMembership(bool remove, bool isRollback = false)
1656 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); 1660 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
1657 ExitOnFailure(hr, "failed to read attributes from custom action data"); 1661 ExitOnFailure(hr, "failed to read attributes from custom action data");
1658 1662
1659 hr = GetDomainServerName(pwzParentDomain, &pwzServerName, DS_WRITABLE_REQUIRED); 1663 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
1660 ExitOnFailure(hr, "failed to contact domain server %ls", pwzParentDomain); 1664 ExitOnFailure(hr, "failed to read scriptkey from custom action data");
1665
1666 if (isRollback)
1667 {
1668 // if the script file doesn't exist, then we'll abandon this rollback
1669 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
1670 if (S_OK == hr)
1671 {
1672 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
1673 }
1674 else
1675 {
1676 WcaLog(LOGMSG_VERBOSE, "Rollback of parent: %ls\\%ls, child: %ls\\%ls relationship not performed, rollback script not found", pwzParentDomain, pwzParentName, pwzChildDomain, pwzChildName);
1677 hr = S_OK;
1678 ExitFunction();
1679 }
1680 }
1681
1682
1683 hr = GetDomainFromServerName(&pwzServerName, pwzParentDomain, DS_WRITABLE_REQUIRED);
1684 ExitOnFailure(hr, "failed to obtain writable server for domain %ls", pwzParentDomain);
1661 1685
1662 if (*pwzChildDomain) 1686 if (*pwzChildDomain)
1663 { 1687 {
@@ -1679,16 +1703,13 @@ HRESULT AlterGroupMembership(bool remove, bool isRollback = false)
1679 } 1703 }
1680 hr = HRESULT_FROM_WIN32(er); 1704 hr = HRESULT_FROM_WIN32(er);
1681 1705
1706 // if there was no error, the action succeeded, and we should flag that it's something which might need
1707 // to be rolled back
1682 if (S_OK == hr && !isRollback) 1708 if (S_OK == hr && !isRollback)
1683 { 1709 {
1684 // we need to log rollback data, we can just use exactly the same data we used to do the initial action though 1710 // we create a script file, the rollback matching this scriptkey will occur if the file exists
1685 WcaCaScriptWriteString(phRollbackScript, pwzParentName); 1711 hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
1686 WcaCaScriptWriteString(phRollbackScript, pwzParentDomain); 1712 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
1687 WcaCaScriptWriteString(phRollbackScript, pwzChildName);
1688 WcaCaScriptWriteString(phRollbackScript, pwzChildDomain);
1689 WcaCaScriptWriteNumber(phRollbackScript, iAttributes);
1690 WcaCaScriptFlush(phRollbackScript);
1691 WcaCaScriptClose(phRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
1692 } 1713 }
1693 1714
1694 if (remove) 1715 if (remove)
@@ -1716,6 +1737,7 @@ LExit:
1716 ReleaseStr(pwzChildDomain); 1737 ReleaseStr(pwzChildDomain);
1717 ReleaseStr(pwzChildFullName); 1738 ReleaseStr(pwzChildFullName);
1718 ReleaseStr(pwzServerName); 1739 ReleaseStr(pwzServerName);
1740 ReleaseStr(pwzScriptKey);
1719 1741
1720 if (SCAG_NON_VITAL & iAttributes) 1742 if (SCAG_NON_VITAL & iAttributes)
1721 { 1743 {
@@ -1725,10 +1747,12 @@ LExit:
1725} 1747}
1726 1748
1727/******************************************************************** 1749/********************************************************************
1728 AddGroupmembership - CUSTOM ACTION ENTRY POINT for creating groups 1750 AddGroupMembership - CUSTOM ACTION ENTRY POINT for adding Group Membership
1751 For domain parent group, must be run as Impersonated=true
1752 For non-domain parent group, must be run as Impersonated=false (for elevation)
1729 1753
1730 Input: deferred CustomActionData - 1754 Input: deferred CustomActionData -
1731 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes 1755 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
1732 * *****************************************************************/ 1756 * *****************************************************************/
1733extern "C" UINT __stdcall AddGroupMembership( 1757extern "C" UINT __stdcall AddGroupMembership(
1734 __in MSIHANDLE hInstall 1758 __in MSIHANDLE hInstall
@@ -1758,10 +1782,13 @@ LExit:
1758} 1782}
1759 1783
1760/******************************************************************** 1784/********************************************************************
1761 AddGroupmembership - CUSTOM ACTION ENTRY POINT for creating groups 1785 AddGroupMembershipRollback - CUSTOM ACTION ENTRY POINT for rolling back
1786 adding Group Membership
1787 For domain parent group, must be run as Impersonated=true
1788 For non-domain parent group, must be run as Impersonated=false (for elevation)
1762 1789
1763 Input: deferred CustomActionData - 1790 Input: deferred CustomActionData -
1764 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes 1791 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
1765 * *****************************************************************/ 1792 * *****************************************************************/
1766extern "C" UINT __stdcall AddGroupMembershipRollback( 1793extern "C" UINT __stdcall AddGroupMembershipRollback(
1767 __in MSIHANDLE hInstall 1794 __in MSIHANDLE hInstall
@@ -1791,10 +1818,12 @@ LExit:
1791} 1818}
1792 1819
1793/******************************************************************** 1820/********************************************************************
1794 RemoveGroupMembership - CUSTOM ACTION ENTRY POINT for creating groups 1821 RemoveGroupMembership - CUSTOM ACTION ENTRY POINT for removing group memberships
1822 For domain parent group, must be run as Impersonated=true
1823 For non-domain parent group, must be run as Impersonated=false (for elevation)
1795 1824
1796 Input: deferred CustomActionData - 1825 Input: deferred CustomActionData -
1797 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes 1826 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
1798 * *****************************************************************/ 1827 * *****************************************************************/
1799extern "C" UINT __stdcall RemoveGroupMembership( 1828extern "C" UINT __stdcall RemoveGroupMembership(
1800 __in MSIHANDLE hInstall 1829 __in MSIHANDLE hInstall
@@ -1824,10 +1853,13 @@ LExit:
1824} 1853}
1825 1854
1826/******************************************************************** 1855/********************************************************************
1827 RemoveGroupMembershipRollback - CUSTOM ACTION ENTRY POINT for creating groups 1856 RemoveGroupMembershipRollback - CUSTOM ACTION ENTRY POINT for rolling back
1857 removing group memberships
1858 For domain parent group, must be run as Impersonated=true
1859 For non-domain parent group, must be run as Impersonated=false (for elevation)
1828 1860
1829 Input: deferred CustomActionData - 1861 Input: deferred CustomActionData -
1830 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes 1862 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
1831 * *****************************************************************/ 1863 * *****************************************************************/
1832extern "C" UINT __stdcall RemoveGroupMembershipRollback( 1864extern "C" UINT __stdcall RemoveGroupMembershipRollback(
1833 __in MSIHANDLE hInstall 1865 __in MSIHANDLE hInstall
diff --git a/src/ext/Util/ca/scagroup.cpp b/src/ext/Util/ca/scagroup.cpp
index 699d9db7..bab438ea 100644
--- a/src/ext/Util/ca/scagroup.cpp
+++ b/src/ext/Util/ca/scagroup.cpp
@@ -220,13 +220,13 @@ HRESULT ScaGroupGetParents(
220 ExitOnFailure(hr, "failed to get Component state for Wix4Group"); 220 ExitOnFailure(hr, "failed to get Component state for Wix4Group");
221 } 221 }
222 222
223 hr = WcaGetRecordString(hRec, vgpqParentName, &pwzTempStr); 223 hr = WcaGetRecordFormattedString(hRec, vgpqParentName, &pwzTempStr);
224 ExitOnFailure(hr, "failed to get Wix4Group.Name"); 224 ExitOnFailure(hr, "failed to get Wix4Group.Name");
225 wcsncpy_s(psgParent->wzName, pwzTempStr, MAX_DARWIN_COLUMN); 225 wcsncpy_s(psgParent->wzName, pwzTempStr, MAX_DARWIN_COLUMN);
226 ReleaseNullStr(pwzTempStr); 226 ReleaseNullStr(pwzTempStr);
227 227
228 228
229 hr = WcaGetRecordString(hRec, vgpqParentDomain, &pwzTempStr); 229 hr = WcaGetRecordFormattedString(hRec, vgpqParentDomain, &pwzTempStr);
230 ExitOnFailure(hr, "failed to get Wix4Group.Domain"); 230 ExitOnFailure(hr, "failed to get Wix4Group.Domain");
231 wcsncpy_s(psgParent->wzDomain, pwzTempStr, MAX_DARWIN_COLUMN); 231 wcsncpy_s(psgParent->wzDomain, pwzTempStr, MAX_DARWIN_COLUMN);
232 ReleaseNullStr(pwzTempStr); 232 ReleaseNullStr(pwzTempStr);
@@ -288,13 +288,13 @@ HRESULT ScaGroupGetChildren(
288 ExitOnFailure(hr, "failed to get Component state for Wix4Group"); 288 ExitOnFailure(hr, "failed to get Component state for Wix4Group");
289 } 289 }
290 290
291 hr = WcaGetRecordString(hRec, vgcqChildName, &pwzTempStr); 291 hr = WcaGetRecordFormattedString(hRec, vgcqChildName, &pwzTempStr);
292 ExitOnFailure(hr, "failed to get Wix4Group.Name"); 292 ExitOnFailure(hr, "failed to get Wix4Group.Name");
293 wcsncpy_s(psgChild->wzName, pwzTempStr, MAX_DARWIN_COLUMN); 293 wcsncpy_s(psgChild->wzName, pwzTempStr, MAX_DARWIN_COLUMN);
294 ReleaseNullStr(pwzTempStr); 294 ReleaseNullStr(pwzTempStr);
295 295
296 296
297 hr = WcaGetRecordString(hRec, vgcqChildDomain, &pwzTempStr); 297 hr = WcaGetRecordFormattedString(hRec, vgcqChildDomain, &pwzTempStr);
298 ExitOnFailure(hr, "failed to get Wix4Group.Domain"); 298 ExitOnFailure(hr, "failed to get Wix4Group.Domain");
299 wcsncpy_s(psgChild->wzDomain, pwzTempStr, MAX_DARWIN_COLUMN); 299 wcsncpy_s(psgChild->wzDomain, pwzTempStr, MAX_DARWIN_COLUMN);
300 ReleaseNullStr(pwzTempStr); 300 ReleaseNullStr(pwzTempStr);
@@ -412,8 +412,11 @@ LExit:
412} 412}
413 413
414/* **************************************************************** 414/* ****************************************************************
415ScaGroupExecute - Schedules group account creation or removal based on 415ScaGroupExecute - Schedules group account creation or removal based on component state.
416component state. 416
417 Output: CustomData for CreateGroup - Name\tDomain\tComment\tAttributes\tScriptKey
418 CustomData for RollbackCreateGroup - ScriptKey\tName\tDomain\tComment\tRollbackAttributes
419 CustomData for RemoveGroup - Name\tDomain\tComment\tAttributes
417******************************************************************/ 420******************************************************************/
418HRESULT ScaGroupExecute( 421HRESULT ScaGroupExecute(
419 __in SCA_GROUP *psgList 422 __in SCA_GROUP *psgList
@@ -455,8 +458,7 @@ HRESULT ScaGroupExecute(
455 // and removing groups. Note: MSDN says that it is safe to call these APIs from any 458 // and removing groups. Note: MSDN says that it is safe to call these APIs from any
456 // user, so we should be safe calling it during immediate mode. 459 // user, so we should be safe calling it during immediate mode.
457 460
458 LPCWSTR wzDomain = psg->wzDomain; 461 hr = GetDomainServerName(psg->wzDomain, &pwzServerName);
459 hr = GetDomainServerName(wzDomain, &pwzServerName);
460 462
461 er = ::NetLocalGroupGetInfo(pwzServerName, psg->wzName, 0, reinterpret_cast<LPBYTE*>(&pGroupInfo)); 463 er = ::NetLocalGroupGetInfo(pwzServerName, psg->wzName, 0, reinterpret_cast<LPBYTE*>(&pGroupInfo));
462 if (NERR_Success == er) 464 if (NERR_Success == er)
@@ -471,7 +473,7 @@ HRESULT ScaGroupExecute(
471 { 473 {
472 geGroupExists = GROUP_EXISTS_INDETERMINATE; 474 geGroupExists = GROUP_EXISTS_INDETERMINATE;
473 hr = HRESULT_FROM_WIN32(er); 475 hr = HRESULT_FROM_WIN32(er);
474 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, group: %ls (error code 0x%x) - continuing", wzDomain, psg->wzName, hr); 476 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of group: %ls\\%ls (error code 0x%x) - continuing", psg->wzDomain, psg->wzName, hr);
475 hr = S_OK; 477 hr = S_OK;
476 er = ERROR_SUCCESS; 478 er = ERROR_SUCCESS;
477 } 479 }
@@ -498,7 +500,7 @@ HRESULT ScaGroupExecute(
498 && !(SCAG_UPDATE_IF_EXISTS & psg->iAttributes)) 500 && !(SCAG_UPDATE_IF_EXISTS & psg->iAttributes))
499 { 501 {
500 hr = HRESULT_FROM_WIN32(NERR_GroupExists); 502 hr = HRESULT_FROM_WIN32(NERR_GroupExists);
501 MessageExitOnFailure(hr, msierrGRPFailedGroupCreateExists, "Failed to create group: %ls because group already exists.", psg->wzName); 503 MessageExitOnFailure(hr, msierrGRPFailedGroupCreateExists, "Failed to create group: %ls\\%ls because group already exists.", psg->wzDomain, psg->wzName);
502 } 504 }
503 } 505 }
504 506
@@ -537,10 +539,19 @@ HRESULT ScaGroupExecute(
537 ExitOnFailure(hr, "Failed to add group name to rollback custom action data: %ls", psg->wzName); 539 ExitOnFailure(hr, "Failed to add group name to rollback custom action data: %ls", psg->wzName);
538 hr = WcaWriteStringToCaData(psg->wzDomain, &pwzRollbackData); 540 hr = WcaWriteStringToCaData(psg->wzDomain, &pwzRollbackData);
539 ExitOnFailure(hr, "Failed to add group domain to rollback custom action data: %ls", psg->wzDomain); 541 ExitOnFailure(hr, "Failed to add group domain to rollback custom action data: %ls", psg->wzDomain);
542 hr = WcaWriteStringToCaData(psg->wzComment, &pwzRollbackData);
543 ExitOnFailure(hr, "Failed to add group comment to rollback custom action data: %ls", psg->wzComment);
540 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData); 544 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
541 ExitOnFailure(hr, "failed to add group attributes to rollback custom action data for group: %ls", psg->wzKey); 545 ExitOnFailure(hr, "failed to add group rollback attributes to rollback custom action data: %i", iRollbackUserAttributes);
542 546
543 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"CreateGroupRollback"), pwzRollbackData, COST_GROUP_DELETE); 547 if (*psg->wzDomain)
548 {
549 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"CreateDomainGroupRollback"), pwzRollbackData, COST_GROUP_DELETE);
550 }
551 else
552 {
553 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"CreateGroupRollback"), pwzRollbackData, COST_GROUP_DELETE);
554 }
544 ExitOnFailure(hr, "failed to schedule CreateGroupRollback"); 555 ExitOnFailure(hr, "failed to schedule CreateGroupRollback");
545 } 556 }
546 else 557 else
@@ -571,11 +582,10 @@ HRESULT ScaGroupExecute(
571 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); 582 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData);
572 ExitOnFailure(hr, "failed to add group attributes to custom action data for group: %ls", psg->wzKey); 583 ExitOnFailure(hr, "failed to add group attributes to custom action data for group: %ls", psg->wzKey);
573 584
574 // Schedule the removal because the group exists and we don't have any flags set 585 // Schedule the removal because the group exists and we don't have any flags set that say not to remove the group
575 // that say not to remove the group on uninstall. 586 // on uninstall.
576 // 587 //
577 // Note: We can't rollback the removal of a group which is why RemoveGroup is a commit 588 // Note: We can't rollback the removal of a group which is why RemoveGroup is a commit CustomAction.
578 // CustomAction.
579 if (psg->wzDomain && *psg->wzDomain) 589 if (psg->wzDomain && *psg->wzDomain)
580 { 590 {
581 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroup"), pwzActionData, COST_GROUP_DELETE); 591 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroup"), pwzActionData, COST_GROUP_DELETE);
@@ -631,15 +641,31 @@ LExit:
631/* **************************************************************** 641/* ****************************************************************
632ScaGroupMembershipRemoveParentsExecute - Schedules group membership removal 642ScaGroupMembershipRemoveParentsExecute - Schedules group membership removal
633based on parent/child component state 643based on parent/child component state
644
645 Output: deferred CustomActionData -
646 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
647 rollback CustomActionData -
648 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
634******************************************************************/ 649******************************************************************/
635HRESULT ScaGroupMembershipRemoveParentsExecute( 650HRESULT ScaGroupMembershipRemoveParentsExecute(
636 __in SCA_GROUP* psg 651 __in SCA_GROUP* psg,
652 __inout DWORD &cScriptIndex
637) 653)
638{ 654{
639 HRESULT hr = S_OK; 655 HRESULT hr = S_OK;
640 LPWSTR pwzActionData = NULL; 656 LPWSTR pwzActionData = NULL;
657 LPWSTR pwzBaseScriptKey = NULL;
658 LPWSTR pwzScriptKey = NULL;
659 SCA_GROUP* psgp = NULL;
641 660
642 for (SCA_GROUP* psgp = psg->psgParents; psgp; psgp = psgp->psgNext) 661 ++cScriptIndex;
662 // Get the base script key for this CustomAction.
663 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
664 ExitOnFailure(hr, "Failed to get encoding key.");
665 hr = StrAllocFormatted(&pwzScriptKey, L"%ls_removemember_%u", pwzBaseScriptKey, cScriptIndex);
666 ExitOnFailure(hr, "Failed to create script key");
667
668 for (psgp = psg->psgParents; psgp; psgp = psgp->psgNext)
643 { 669 {
644 Assert(psgp->wzName); 670 Assert(psgp->wzName);
645 if (WcaIsUninstalling(psg->isInstalled, psg->isAction) 671 if (WcaIsUninstalling(psg->isInstalled, psg->isAction)
@@ -655,18 +681,24 @@ HRESULT ScaGroupMembershipRemoveParentsExecute(
655 ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psg->wzDomain); 681 ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psg->wzDomain);
656 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); 682 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData);
657 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); 683 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes);
684 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
685 ExitOnFailure(hr, "Failed to add script key to custom action data: %i", pwzScriptKey);
658 686
659 if (psgp->wzDomain && *psgp->wzDomain) 687 if (psgp->wzDomain && *psgp->wzDomain)
660 { 688 {
661 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE); 689 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE);
690 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
662 } 691 }
663 else 692 else
664 { 693 {
665 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE); 694 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE);
695 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
666 } 696 }
667 697
668 LExit: 698 LExit:
669 ReleaseNullStr(pwzActionData); 699 ReleaseNullStr(pwzActionData);
700 ReleaseNullStr(pwzBaseScriptKey);
701 ReleaseNullStr(pwzScriptKey);
670 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) 702 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL))
671 { 703 {
672 return hr; 704 return hr;
@@ -677,16 +709,32 @@ HRESULT ScaGroupMembershipRemoveParentsExecute(
677} 709}
678 710
679/* **************************************************************** 711/* ****************************************************************
680ScaGroupMembershipRemoveChildrenExecute - 712ScaGroupMembershipRemoveChildrenExecute -
713
714 Output: deferred CustomActionData -
715 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
716 rollback CustomActionData -
717 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
681******************************************************************/ 718******************************************************************/
682HRESULT ScaGroupMembershipRemoveChildrenExecute( 719HRESULT ScaGroupMembershipRemoveChildrenExecute(
683 __in SCA_GROUP* psg 720 __in SCA_GROUP* psg,
721 __inout DWORD &cScriptIndex
684) 722)
685{ 723{
686 HRESULT hr = S_OK; 724 HRESULT hr = S_OK;
687 LPWSTR pwzActionData = NULL; 725 LPWSTR pwzActionData = NULL;
726 LPWSTR pwzBaseScriptKey = NULL;
727 LPWSTR pwzScriptKey = NULL;
728 SCA_GROUP* psgc = NULL;
688 729
689 for (SCA_GROUP* psgc = psg->psgChildren; psgc; psgc = psgc->psgNext) 730 ++cScriptIndex;
731 // Get the base script key for this CustomAction.
732 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
733 ExitOnFailure(hr, "Failed to get encoding key.");
734 hr = StrAllocFormatted(&pwzScriptKey, L"%ls_removemember_%u", pwzBaseScriptKey, cScriptIndex);
735 ExitOnFailure(hr, "Failed to create script key");
736
737 for (psgc = psg->psgChildren; psgc; psgc = psgc->psgNext)
690 { 738 {
691 Assert(psgc->wzName); 739 Assert(psgc->wzName);
692 if (WcaIsUninstalling(psg->isInstalled, psg->isAction) 740 if (WcaIsUninstalling(psg->isInstalled, psg->isAction)
@@ -702,17 +750,23 @@ HRESULT ScaGroupMembershipRemoveChildrenExecute(
702 ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psgc->wzDomain); 750 ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psgc->wzDomain);
703 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); 751 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData);
704 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); 752 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes);
753 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
754 ExitOnFailure(hr, "Failed to add script key to custom action data: %i", pwzScriptKey);
705 if (psg->wzDomain && *psg->wzDomain) 755 if (psg->wzDomain && *psg->wzDomain)
706 { 756 {
707 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE); 757 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE);
758 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveDomainGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
708 } 759 }
709 else 760 else
710 { 761 {
711 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE); 762 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_DELETE);
763 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RemoveGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
712 } 764 }
713 765
714 LExit: 766 LExit:
715 ReleaseNullStr(pwzActionData); 767 ReleaseNullStr(pwzActionData);
768 ReleaseNullStr(pwzBaseScriptKey);
769 ReleaseNullStr(pwzScriptKey);
716 770
717 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) 771 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL))
718 { 772 {
@@ -726,23 +780,29 @@ HRESULT ScaGroupMembershipRemoveChildrenExecute(
726/* **************************************************************** 780/* ****************************************************************
727ScaGroupMembershipRemoveExecute - Schedules group membership removal 781ScaGroupMembershipRemoveExecute - Schedules group membership removal
728based on parent/child component state 782based on parent/child component state
783
784 Output: deferred CustomActionData -
785 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
786 rollback CustomActionData -
787 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
729******************************************************************/ 788******************************************************************/
730HRESULT ScaGroupMembershipRemoveExecute( 789HRESULT ScaGroupMembershipRemoveExecute(
731 __in SCA_GROUP* psgList 790 __in SCA_GROUP* psgList
732) 791)
733{ 792{
734 HRESULT hr = S_OK; 793 HRESULT hr = S_OK;
794 static DWORD iScriptIndex = 0;
735 795
736 // Loop through all the users to be configured. 796 // Loop through all the users to be configured.
737 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext) 797 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
738 { 798 {
739 Assert(psg->wzName); 799 Assert(psg->wzName);
740 // first we loop through the Parents 800 // first we loop through the Parents
741 hr = ScaGroupMembershipRemoveParentsExecute(psg); 801 hr = ScaGroupMembershipRemoveParentsExecute(psg, iScriptIndex);
742 ExitOnFailure(hr, "Failed to remove parent membership for vital group: %ls", psg->wzKey); 802 ExitOnFailure(hr, "Failed to remove parent membership for vital group: %ls", psg->wzKey);
743 803
744 // then through the Children 804 // then through the Children
745 hr = ScaGroupMembershipRemoveChildrenExecute(psg); 805 hr = ScaGroupMembershipRemoveChildrenExecute(psg, iScriptIndex);
746 ExitOnFailure(hr, "Failed to remove child membership for vital group: %ls", psg->wzKey); 806 ExitOnFailure(hr, "Failed to remove child membership for vital group: %ls", psg->wzKey);
747 } 807 }
748 808
@@ -755,13 +815,24 @@ ScaGroupMembershipAddParentsExecute - Schedules group membership removal
755based on parent/child component state 815based on parent/child component state
756******************************************************************/ 816******************************************************************/
757HRESULT ScaGroupMembershipAddParentsExecute( 817HRESULT ScaGroupMembershipAddParentsExecute(
758 __in SCA_GROUP* psg 818 __in SCA_GROUP* psg,
819 __inout DWORD &cScriptIndex
759) 820)
760{ 821{
761 HRESULT hr = S_OK; 822 HRESULT hr = S_OK;
762 LPWSTR pwzActionData = NULL; 823 LPWSTR pwzActionData = NULL;
824 LPWSTR pwzBaseScriptKey = NULL;
825 LPWSTR pwzScriptKey = NULL;
826 SCA_GROUP* psgp = NULL;
827
828 ++cScriptIndex;
829 // Get the base script key for this CustomAction.
830 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
831 ExitOnFailure(hr, "Failed to get encoding key.");
832 hr = StrAllocFormatted(&pwzScriptKey, L"%ls_addmember_%u", pwzBaseScriptKey, cScriptIndex);
833 ExitOnFailure(hr, "Failed to create script key");
763 834
764 for (SCA_GROUP* psgp = psg->psgParents; psgp; psgp = psgp->psgNext) 835 for (psgp = psg->psgParents; psgp; psgp = psgp->psgNext)
765 { 836 {
766 Assert(psgp->wzName); 837 Assert(psgp->wzName);
767 if (WcaIsInstalling(psg->isInstalled, psg->isAction) 838 if (WcaIsInstalling(psg->isInstalled, psg->isAction)
@@ -777,17 +848,23 @@ HRESULT ScaGroupMembershipAddParentsExecute(
777 ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psg->wzDomain); 848 ExitOnFailure(hr, "Failed to add child group domain to custom action data: %ls", psg->wzDomain);
778 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); 849 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData);
779 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); 850 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes);
851 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
852 ExitOnFailure(hr, "Failed to add script key to custom action data: %i", pwzScriptKey);
780 if (psgp->wzDomain&&* psgp->wzDomain) 853 if (psgp->wzDomain&&* psgp->wzDomain)
781 { 854 {
782 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD); 855 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
856 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddDomainGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
783 } 857 }
784 else 858 else
785 { 859 {
786 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD); 860 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
861 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
787 } 862 }
788 863
789 LExit: 864 LExit:
790 ReleaseNullStr(pwzActionData); 865 ReleaseNullStr(pwzActionData);
866 ReleaseNullStr(pwzBaseScriptKey);
867 ReleaseNullStr(pwzScriptKey);
791 868
792 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) 869 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL))
793 { 870 {
@@ -801,16 +878,32 @@ HRESULT ScaGroupMembershipAddParentsExecute(
801/* **************************************************************** 878/* ****************************************************************
802ScaGroupMembershipAddChildrenExecute - Schedules group membership removal 879ScaGroupMembershipAddChildrenExecute - Schedules group membership removal
803based on parent/child component state 880based on parent/child component state
881
882 Output: deferred CustomActionData -
883 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
884 rollback CustomActionData -
885 ParentGroupName\tParentGroupDomain\tChildGroupName\tChildGroupDomain\tAttributes\tScriptkey
804******************************************************************/ 886******************************************************************/
805HRESULT ScaGroupMembershipAddChildrenExecute( 887HRESULT ScaGroupMembershipAddChildrenExecute(
806 __in SCA_GROUP* psg 888 __in SCA_GROUP* psg,
889 __inout DWORD &cScriptIndex
807) 890)
808{ 891{
809 HRESULT hr = S_OK; 892 HRESULT hr = S_OK;
810 LPWSTR pwzActionData = NULL; 893 LPWSTR pwzActionData = NULL;
894 LPWSTR pwzBaseScriptKey = NULL;
895 LPWSTR pwzScriptKey = NULL;
896 SCA_GROUP* psgc = NULL;
897
898 ++cScriptIndex;
899 // Get the base script key for this CustomAction.
900 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
901 ExitOnFailure(hr, "Failed to get encoding key.");
902 hr = StrAllocFormatted(&pwzScriptKey, L"%ls_addmember_%u", pwzBaseScriptKey, cScriptIndex);
903 ExitOnFailure(hr, "Failed to create script key");
811 904
812 // then through the Children 905 // then through the Children
813 for (SCA_GROUP* psgc = psg->psgChildren; psgc; psgc = psgc->psgNext) 906 for (psgc = psg->psgChildren; psgc; psgc = psgc->psgNext)
814 { 907 {
815 Assert(psgc->wzName); 908 Assert(psgc->wzName);
816 if (WcaIsInstalling(psg->isInstalled, psg->isAction) 909 if (WcaIsInstalling(psg->isInstalled, psg->isAction)
@@ -826,17 +919,24 @@ HRESULT ScaGroupMembershipAddChildrenExecute(
826 ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psgc->wzDomain); 919 ExitOnFailure(hr, "Failed to add parent group domain to custom action data: %ls", psgc->wzDomain);
827 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData); 920 hr = WcaWriteIntegerToCaData(psg->iAttributes, &pwzActionData);
828 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes); 921 ExitOnFailure(hr, "Failed to add group attributes to custom action data: %i", psg->iAttributes);
922 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
923 ExitOnFailure(hr, "Failed to add script key to custom action data: %i", pwzScriptKey);
829 if (psg->wzDomain && *psg->wzDomain) 924 if (psg->wzDomain && *psg->wzDomain)
830 { 925 {
831 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD); 926 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddDomainGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
927 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddDomainGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
832 } 928 }
833 else 929 else
834 { 930 {
835 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD); 931 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembership"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
932 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"AddGroupMembershipRollback"), pwzActionData, COST_GROUPMEMBERSHIP_ADD);
836 } 933 }
837 934
838 LExit: 935 LExit:
839 ReleaseNullStr(pwzActionData); 936 ReleaseNullStr(pwzActionData);
937 ReleaseNullStr(pwzBaseScriptKey);
938 ReleaseNullStr(pwzScriptKey);
939
840 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL)) 940 if (hr != S_OK && !(psg->iAttributes & SCAG_NON_VITAL))
841 { 941 {
842 return hr; 942 return hr;
@@ -855,17 +955,18 @@ HRESULT ScaGroupMembershipAddExecute(
855) 955)
856{ 956{
857 HRESULT hr = S_OK; 957 HRESULT hr = S_OK;
958 static DWORD iScriptIndex = 0;
858 959
859 // Loop through all the users to be configured. 960 // Loop through all the users to be configured.
860 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext) 961 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
861 { 962 {
862 Assert(psg->wzName); 963 Assert(psg->wzName);
863 // first we loop through the Parents 964 // first we loop through the Parents
864 hr = ScaGroupMembershipAddParentsExecute(psg); 965 hr = ScaGroupMembershipAddParentsExecute(psg, iScriptIndex);
865 ExitOnFailure(hr, "Failed to add parent membership for vital group: %ls", psg->wzKey); 966 ExitOnFailure(hr, "Failed to add parent membership for vital group: %ls", psg->wzKey);
866 967
867 // then through the Children 968 // then through the Children
868 hr = ScaGroupMembershipAddChildrenExecute(psg); 969 hr = ScaGroupMembershipAddChildrenExecute(psg, iScriptIndex);
869 ExitOnFailure(hr, "Failed to add child membership for vital group: %ls", psg->wzKey); 970 ExitOnFailure(hr, "Failed to add child membership for vital group: %ls", psg->wzKey);
870 } 971 }
871 972
diff --git a/src/ext/Util/ca/scasched.cpp b/src/ext/Util/ca/scasched.cpp
index cef7fecb..d7378b13 100644
--- a/src/ext/Util/ca/scasched.cpp
+++ b/src/ext/Util/ca/scasched.cpp
@@ -134,7 +134,7 @@ extern "C" UINT __stdcall ConfigureGroups(
134 __in MSIHANDLE hInstall 134 __in MSIHANDLE hInstall
135) 135)
136{ 136{
137 //AssertSz(0, "Debug ConfigureGroups"); 137 AssertSz(0, "Debug ConfigureGroups");
138 138
139 HRESULT hr = S_OK; 139 HRESULT hr = S_OK;
140 UINT er = ERROR_SUCCESS; 140 UINT er = ERROR_SUCCESS;
diff --git a/src/test/burn/WixTestTools/RuntimeFactAttribute.cs b/src/test/burn/WixTestTools/RuntimeFactAttribute.cs
index 573a9de2..76004f26 100644
--- a/src/test/burn/WixTestTools/RuntimeFactAttribute.cs
+++ b/src/test/burn/WixTestTools/RuntimeFactAttribute.cs
@@ -3,6 +3,7 @@
3namespace WixTestTools 3namespace WixTestTools
4{ 4{
5 using System; 5 using System;
6 using System.DirectoryServices.ActiveDirectory;
6 using System.Security.Principal; 7 using System.Security.Principal;
7 using WixInternal.TestSupport.XunitExtensions; 8 using WixInternal.TestSupport.XunitExtensions;
8 using System.Runtime.InteropServices; 9 using System.Runtime.InteropServices;
@@ -10,10 +11,13 @@ namespace WixTestTools
10 public class RuntimeFactAttribute : SkippableFactAttribute 11 public class RuntimeFactAttribute : SkippableFactAttribute
11 { 12 {
12 const string RequiredEnvironmentVariableName = "RuntimeTestsEnabled"; 13 const string RequiredEnvironmentVariableName = "RuntimeTestsEnabled";
14 const string RequiredDomainEnvironmentVariableName = "RuntimeDomainTestsEnabled";
13 15
14 public static bool RuntimeTestsEnabled { get; } 16 public static bool RuntimeTestsEnabled { get; }
17 public static bool RuntimeDomainTestsEnabled { get; }
15 public static bool RunningAsAdministrator { get; } 18 public static bool RunningAsAdministrator { get; }
16 public static bool RunningOnWindowsServer { get; } 19 public static bool RunningOnWindowsServer { get; }
20 public static bool RunningInDomain { get; }
17 21
18 [DllImport("shlwapi.dll", SetLastError = true, EntryPoint = "#437")] 22 [DllImport("shlwapi.dll", SetLastError = true, EntryPoint = "#437")]
19 private static extern bool IsOS(int os); 23 private static extern bool IsOS(int os);
@@ -23,7 +27,6 @@ namespace WixTestTools
23 return IsOS(OS_ANYSERVER); 27 return IsOS(OS_ANYSERVER);
24 } 28 }
25 29
26
27 static RuntimeFactAttribute() 30 static RuntimeFactAttribute()
28 { 31 {
29 using var identity = WindowsIdentity.GetCurrent(); 32 using var identity = WindowsIdentity.GetCurrent();
@@ -33,6 +36,33 @@ namespace WixTestTools
33 var testsEnabledString = Environment.GetEnvironmentVariable(RequiredEnvironmentVariableName); 36 var testsEnabledString = Environment.GetEnvironmentVariable(RequiredEnvironmentVariableName);
34 RuntimeTestsEnabled = Boolean.TryParse(testsEnabledString, out var testsEnabled) && testsEnabled; 37 RuntimeTestsEnabled = Boolean.TryParse(testsEnabledString, out var testsEnabled) && testsEnabled;
35 38
39 RunningInDomain = false;
40 try
41 {
42 RunningInDomain = !String.IsNullOrEmpty(System.DirectoryServices.ActiveDirectory.Domain.GetComputerDomain().Name);
43 }
44 catch (ActiveDirectoryObjectNotFoundException) { }
45
46 var domainTestsEnabledString = Environment.GetEnvironmentVariable(RequiredDomainEnvironmentVariableName);
47 RuntimeDomainTestsEnabled = Boolean.TryParse(domainTestsEnabledString, out var domainTestsEnabled) && domainTestsEnabled;
48 }
49
50 private bool _domainRequired;
51 public bool DomainRequired
52 {
53 get
54 {
55 return _domainRequired;
56 }
57 set
58 {
59 _domainRequired = value;
60 if (_domainRequired && String.IsNullOrEmpty(this.Skip) && (!RunningInDomain || !RuntimeDomainTestsEnabled))
61 {
62 this.Skip = $"These tests require the test host to be running as a domain member ({(RunningInDomain ? "passed" : "failed")}). These tests affect both MACHINE AND DOMAIN state. To accept the consequences, set the {RequiredDomainEnvironmentVariableName} environment variable to true ({(RuntimeDomainTestsEnabled ? "passed" : "failed")}).";
63 }
64 }
65
36 RunningOnWindowsServer = IsWindowsServer(); 66 RunningOnWindowsServer = IsWindowsServer();
37 } 67 }
38 68
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductA/product.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductA/product.wxs
index e3c143e6..f7f60fdb 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductA/product.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductA/product.wxs
@@ -7,19 +7,18 @@
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9 9
10 <Property Id="TEMPDOMAIN" Secure="yes" /> 10 <Property Id="TESTDOMAIN" Secure="yes" />
11 <Property Id="TEMPGROUPNAME" Secure="yes" />
12 </Fragment> 11 </Fragment>
13 12
14 <Fragment> 13 <Fragment>
15 <Component Id="Component1" Guid="09624A9A-4BBC-4126-BBF9-0713C5217DB1" Directory="INSTALLFOLDER"> 14 <Component Id="Component1" Guid="09624A9A-4BBC-4126-BBF9-0713C5217DB1" Directory="INSTALLFOLDER">
16 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" /> 15 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
17 16
18 <util:Group Id="TEST_GROUP1" Name="testName1" Comment="Group1" CreateGroup="yes" RemoveOnUninstall="yes" /> 17 <util:Group Id="TEST_GROUP1" Name="testName1" Domain="[TESTDOMAIN]" Comment="Group1" CreateGroup="yes" RemoveOnUninstall="yes" />
19 18
20 <util:Group Id="TEST_GROUP2" Name="testName2" Comment="Group2" RemoveOnUninstall="no" UpdateIfExists="yes" /> 19 <util:Group Id="TEST_GROUP2" Name="testName2" Domain="[TESTDOMAIN]" Comment="Group2" RemoveOnUninstall="no" UpdateIfExists="yes" />
21 20
22 <util:Group Id="TEST_GROUP3" Name="testName3" Comment="Group3" CreateGroup="no" /> 21 <util:Group Id="TEST_GROUP3" Name="testName3" Domain="[TESTDOMAIN]" Comment="Group3" CreateGroup="no" />
23 </Component> 22 </Component>
24 </Fragment> 23 </Fragment>
25</Wix> 24</Wix>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductAddCommentToExistingGroup/product.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductAddCommentToExistingGroup/product.wxs
index e0170746..6c9d3be3 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductAddCommentToExistingGroup/product.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductAddCommentToExistingGroup/product.wxs
@@ -6,18 +6,15 @@
6 <ComponentGroup Id="ProductComponents"> 6 <ComponentGroup Id="ProductComponents">
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9
10 <Property Id="TESTDOMAIN" Secure="yes" />
9 </Fragment> 11 </Fragment>
10 12
11 <Fragment> 13 <Fragment>
12 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER"> 14 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
13 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" /> 15 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
14 16
15 <util:Group Id="TEST_GROUP1" 17 <util:Group Id="TEST_GROUP1" Name="testName1" Domain="[TESTDOMAIN]" CreateGroup="yes" UpdateIfExists="yes" RemoveOnUninstall="yes" Comment="testComment1"/>
16 Name="testName1"
17 CreateGroup="yes"
18 UpdateIfExists="yes"
19 RemoveOnUninstall="yes"
20 Comment="testComment1"/>
21 </Component> 18 </Component>
22 </Fragment> 19 </Fragment>
23</Wix> 20</Wix>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentDelete/product.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentDelete/product.wxs
index d1824890..a34a276b 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentDelete/product.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentDelete/product.wxs
@@ -6,13 +6,15 @@
6 <ComponentGroup Id="ProductComponents"> 6 <ComponentGroup Id="ProductComponents">
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9
10 <Property Id="TESTDOMAIN" Secure="yes" />
9 </Fragment> 11 </Fragment>
10 12
11 <Fragment> 13 <Fragment>
12 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER"> 14 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
13 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" /> 15 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
14 16
15 <util:Group Id="TEST_GROUP1" Name="testName1" UpdateIfExists="yes" RemoveOnUninstall="yes" RemoveComment="yes"/> 17 <util:Group Id="TEST_GROUP1" Name="testName1" Domain="[TESTDOMAIN]" UpdateIfExists="yes" RemoveOnUninstall="yes" RemoveComment="yes"/>
16 </Component> 18 </Component>
17 </Fragment> 19 </Fragment>
18</Wix> 20</Wix>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs
index 4e70717f..79396882 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductCommentFail/product_fail.wxs
@@ -10,13 +10,15 @@
10 <InstallExecuteSequence> 10 <InstallExecuteSequence>
11 <Custom Action="CaFail" After="Wix6ConfigureGroups_X86" /> 11 <Custom Action="CaFail" After="Wix6ConfigureGroups_X86" />
12 </InstallExecuteSequence> 12 </InstallExecuteSequence>
13
14 <Property Id="TESTDOMAIN" Secure="yes" />
13 </Fragment> 15 </Fragment>
14 16
15 <Fragment> 17 <Fragment>
16 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER"> 18 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
17 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" /> 19 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
18 20
19 <util:Group Id="TEST_GROUP1" Name="testName1" CreateGroup="yes" RemoveOnUninstall="yes" Comment="testComment1"/> 21 <util:Group Id="TEST_GROUP1" Name="testName1" Domain="[TESTDOMAIN]" CreateGroup="yes" RemoveOnUninstall="yes" Comment="testComment1"/>
20 </Component> 22 </Component>
21 </Fragment> 23 </Fragment>
22</Wix> 24</Wix>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs
index 3013e5a0..148f26ca 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductFail/product_fail.wxs
@@ -13,6 +13,8 @@
13 <InstallExecuteSequence> 13 <InstallExecuteSequence>
14 <Custom Action="CaFail" After="Wix6ConfigureGroups_X86" /> 14 <Custom Action="CaFail" After="Wix6ConfigureGroups_X86" />
15 </InstallExecuteSequence> 15 </InstallExecuteSequence>
16
17 <Property Id="TESTDOMAIN" Secure="yes" />
16 </Fragment> 18 </Fragment>
17 19
18 <Fragment> 20 <Fragment>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductFailIfExists/FailIfExists.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductFailIfExists/FailIfExists.wxs
index 00f8e12d..e7acb5e0 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductFailIfExists/FailIfExists.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductFailIfExists/FailIfExists.wxs
@@ -6,6 +6,8 @@
6 <ComponentGroup Id="ProductComponents"> 6 <ComponentGroup Id="ProductComponents">
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9
10 <Property Id="TESTDOMAIN" Secure="yes" />
9 </Fragment> 11 </Fragment>
10 12
11 <Fragment> 13 <Fragment>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs
index c27eb27a..f513e7c6 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNestedGroups/product.wxs
@@ -7,13 +7,14 @@
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9 9
10 <Property Id="TEMPDOMAIN" Secure="yes" Value="TESTDOMAIN" /> 10 <Property Id="TESTDOMAIN" Secure="yes" />
11 </Fragment> 11 </Fragment>
12 12
13 <Fragment> 13 <Fragment>
14 <util:Group Id="AUTH_USERS" Name="Authenticated Users" Domain="[TEMPDOMAIN]" > 14 <util:Group Id="DOMAIN_USERS" Name="Domain Users" Domain="[TESTDOMAIN]" >
15 <util:GroupRef Id="TEST_GROUP1" /> 15 <util:GroupRef Id="TEST_GROUP1" />
16 <util:GroupRef Id="TEST_GROUP2" /> 16 <util:GroupRef Id="TEST_GROUP2" />
17 <util:GroupRef Id="TEST_GROUP3" />
17 </util:Group> 18 </util:Group>
18 <util:Group Id="EVERYONE" Name="Everyone" > 19 <util:Group Id="EVERYONE" Name="Everyone" >
19 <util:GroupRef Id="TEST_GROUP1" /> 20 <util:GroupRef Id="TEST_GROUP1" />
@@ -25,6 +26,8 @@
25 <util:Group Id="TEST_GROUP1" Name="testName1" Comment="Group1" CreateGroup="yes" RemoveOnUninstall="yes" /> 26 <util:Group Id="TEST_GROUP1" Name="testName1" Comment="Group1" CreateGroup="yes" RemoveOnUninstall="yes" />
26 27
27 <util:Group Id="TEST_GROUP2" Name="testName2" Comment="Group2" RemoveOnUninstall="no" UpdateIfExists="yes" /> 28 <util:Group Id="TEST_GROUP2" Name="testName2" Comment="Group2" RemoveOnUninstall="no" UpdateIfExists="yes" />
29
30 <util:Group Id="TEST_GROUP3" Name="testName3" Domain="[TESTDOMAIN]" Comment="Group3" RemoveOnUninstall="no" UpdateIfExists="yes" />
28 </Component> 31 </Component>
29 </Fragment> 32 </Fragment>
30</Wix> 33</Wix>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNewGroupWithComment/product.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNewGroupWithComment/product.wxs
index 2d012b23..2305a80b 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNewGroupWithComment/product.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNewGroupWithComment/product.wxs
@@ -6,18 +6,15 @@
6 <ComponentGroup Id="ProductComponents"> 6 <ComponentGroup Id="ProductComponents">
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9
10 <Property Id="TESTDOMAIN" Secure="yes" />
9 </Fragment> 11 </Fragment>
10 12
11 <Fragment> 13 <Fragment>
12 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER"> 14 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
13 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" /> 15 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
14 <util:Group 16 <util:Group
15 Id="TEST_GROUP1" 17 Id="TEST_GROUP1" Name="testName1" CreateGroup="yes" UpdateIfExists="yes" RemoveOnUninstall="yes" Comment="testComment1" />
16 Name="testName1"
17 CreateGroup="yes"
18 UpdateIfExists="yes"
19 RemoveOnUninstall="yes"
20 Comment="testComment1" />
21 </Component> 18 </Component>
22 </Fragment> 19 </Fragment>
23</Wix> 20</Wix>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNonVitalGroup/NonVitalUserGroup.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNonVitalGroup/NonVitalUserGroup.wxs
index a834c76b..4922fcef 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductNonVitalGroup/NonVitalUserGroup.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductNonVitalGroup/NonVitalUserGroup.wxs
@@ -6,6 +6,8 @@
6 <ComponentGroup Id="ProductComponents"> 6 <ComponentGroup Id="ProductComponents">
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9
10 <Property Id="TESTDOMAIN" Secure="yes" />
9 </Fragment> 11 </Fragment>
10 12
11 <Fragment> 13 <Fragment>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductRestrictedDomain/RestrictedDomain.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductRestrictedDomain/RestrictedDomain.wxs
index edb3387c..04a1ac4e 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductRestrictedDomain/RestrictedDomain.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductRestrictedDomain/RestrictedDomain.wxs
@@ -7,14 +7,14 @@
7 <ComponentRef Id="Component1" /> 7 <ComponentRef Id="Component1" />
8 </ComponentGroup> 8 </ComponentGroup>
9 9
10 <Property Id="TEMPDOMAIN" Secure="yes" /> 10 <Property Id="TESTDOMAIN" Secure="yes" />
11 </Fragment> 11 </Fragment>
12 12
13 <Fragment> 13 <Fragment>
14 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER"> 14 <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
15 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" /> 15 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
16 16
17 <util:Group Id="TEST_GROUP_test" Name="testName1" Domain="[TEMPDOMAIN]" /> 17 <util:Group Id="TEST_GROUP_test" Name="testName1" Domain="[TESTDOMAIN]" />
18 </Component> 18 </Component>
19 </Fragment> 19 </Fragment>
20</Wix> 20</Wix>
diff --git a/src/test/msi/TestData/UtilExtensionGroupTests/ProductWithCommandLineParameters/ProductWithCommandLineParameters.wxs b/src/test/msi/TestData/UtilExtensionGroupTests/ProductWithCommandLineParameters/ProductWithCommandLineParameters.wxs
index 059ecee8..36d10aa3 100644
--- a/src/test/msi/TestData/UtilExtensionGroupTests/ProductWithCommandLineParameters/ProductWithCommandLineParameters.wxs
+++ b/src/test/msi/TestData/UtilExtensionGroupTests/ProductWithCommandLineParameters/ProductWithCommandLineParameters.wxs
@@ -4,16 +4,13 @@
4 <ComponentGroup Id="ProductComponents"> 4 <ComponentGroup Id="ProductComponents">
5 <ComponentRef Id="Component1" /> 5 <ComponentRef Id="Component1" />
6 </ComponentGroup> 6 </ComponentGroup>
7
8 <Property Id="TESTDOMAIN" Secure="yes" />
7 </Fragment> 9 </Fragment>
8 10
9 <Fragment> 11 <Fragment>
10 <Component Id="Component1" 12 <Component Id="Component1" Guid="1FDC6C4D-7741-4BF1-A4F0-4231879CEC45" Directory="INSTALLFOLDER">
11 Guid="1FDC6C4D-7741-4BF1-A4F0-4231879CEC45" 13 <util:Group Id="TEST_GROUP1" Name="[TESTPARAMETER1]" Domain="[TESTDOMAIN]" CreateGroup="yes" RemoveOnUninstall="yes" />
12 Directory="INSTALLFOLDER">
13 <util:Group Id="TEST_GROUP1"
14 Name="[TESTPARAMETER1]"
15 CreateGroup="yes"
16 RemoveOnUninstall="yes" />
17 </Component> 14 </Component>
18 </Fragment> 15 </Fragment>
19</Wix> 16</Wix>
diff --git a/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs b/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs
index d7cf3168..cee357a6 100644
--- a/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs
+++ b/src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionGroupTests.cs
@@ -11,9 +11,10 @@ namespace WixToolsetTest.MsiE2E
11 { 11 {
12 public UtilExtensionGroupTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } 12 public UtilExtensionGroupTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
13 13
14 #region Non Domain
14 // Verify that the users specified in the authoring are created as expected. 15 // Verify that the users specified in the authoring are created as expected.
15 [RuntimeFact] 16 [RuntimeFact]
16 public void CanInstallAndUninstallGroups() 17 public void CanInstallAndUninstallNonDomainGroups()
17 { 18 {
18 UserGroupVerifier.CreateLocalGroup("testName3"); 19 UserGroupVerifier.CreateLocalGroup("testName3");
19 var productA = this.CreatePackageInstaller("ProductA"); 20 var productA = this.CreatePackageInstaller("ProductA");
@@ -39,7 +40,7 @@ namespace WixToolsetTest.MsiE2E
39 40
40 // Verify the rollback action reverts all Users changes. 41 // Verify the rollback action reverts all Users changes.
41 [RuntimeFact] 42 [RuntimeFact]
42 public void CanRollbackGroups() 43 public void CanRollbackNonDomainGroups()
43 { 44 {
44 UserGroupVerifier.CreateLocalGroup("testName3"); 45 UserGroupVerifier.CreateLocalGroup("testName3");
45 var productFail = this.CreatePackageInstaller("ProductFail"); 46 var productFail = this.CreatePackageInstaller("ProductFail");
@@ -65,7 +66,7 @@ namespace WixToolsetTest.MsiE2E
65 // Original code signalled repair mode by using "-f ", which silently 66 // Original code signalled repair mode by using "-f ", which silently
66 // terminated the command-line parsing, ignoring any parameters that followed. 67 // terminated the command-line parsing, ignoring any parameters that followed.
67 [RuntimeFact()] 68 [RuntimeFact()]
68 public void CanRepairGroupsWithCommandLineParameters() 69 public void CanRepairNonDomainGroupsWithCommandLineParameters()
69 { 70 {
70 var arguments = new string[] 71 var arguments = new string[]
71 { 72 {
@@ -82,6 +83,10 @@ namespace WixToolsetTest.MsiE2E
82 // Repair 83 // Repair
83 productWithCommandLineParameters.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments); 84 productWithCommandLineParameters.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments);
84 85
86
87 // Install
88 productWithCommandLineParameters.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments);
89
85 // Clean up 90 // Clean up
86 UserGroupVerifier.DeleteLocalGroup("testName1"); 91 UserGroupVerifier.DeleteLocalGroup("testName1");
87 } 92 }
@@ -89,7 +94,7 @@ namespace WixToolsetTest.MsiE2E
89 94
90 // Verify that the groups specified in the authoring are created as expected on repair. 95 // Verify that the groups specified in the authoring are created as expected on repair.
91 [RuntimeFact()] 96 [RuntimeFact()]
92 public void CanRepairGroups() 97 public void CanRepairNonDomainGroups()
93 { 98 {
94 UserGroupVerifier.CreateLocalGroup("testName3"); 99 UserGroupVerifier.CreateLocalGroup("testName3");
95 var productA = this.CreatePackageInstaller("ProductA"); 100 var productA = this.CreatePackageInstaller("ProductA");
@@ -119,7 +124,7 @@ namespace WixToolsetTest.MsiE2E
119 124
120 // Verify that Installation fails if FailIfExists is set. 125 // Verify that Installation fails if FailIfExists is set.
121 [RuntimeFact] 126 [RuntimeFact]
122 public void FailsIfGroupExists() 127 public void FailsIfNonDomainGroupExists()
123 { 128 {
124 var productFailIfExists = this.CreatePackageInstaller("ProductFailIfExists"); 129 var productFailIfExists = this.CreatePackageInstaller("ProductFailIfExists");
125 130
@@ -148,7 +153,7 @@ namespace WixToolsetTest.MsiE2E
148 { 153 {
149 var productRestrictedDomain = this.CreatePackageInstaller("ProductRestrictedDomain"); 154 var productRestrictedDomain = this.CreatePackageInstaller("ProductRestrictedDomain");
150 155
151 string logFile = productRestrictedDomain.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, "TEMPDOMAIN=DOESNOTEXIST"); 156 string logFile = productRestrictedDomain.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, "TESTDOMAIN=DOESNOTEXIST");
152 157
153 // Verify expected error message in the log file 158 // Verify expected error message in the log file
154 Assert.True(LogVerifier.MessageInLogFile(logFile, "CreateGroup: Error 0x8007054b: failed to find Domain DOESNOTEXIST.")); 159 Assert.True(LogVerifier.MessageInLogFile(logFile, "CreateGroup: Error 0x8007054b: failed to find Domain DOESNOTEXIST."));
@@ -156,12 +161,13 @@ namespace WixToolsetTest.MsiE2E
156 161
157 // Verify that a group can be created with a group comment 162 // Verify that a group can be created with a group comment
158 [RuntimeFact] 163 [RuntimeFact]
159 public void CanCreateNewGroupWithComment() 164 public void CanCreateNewNonDomainGroupWithComment()
160 { 165 {
161 var productNewUserWithComment = this.CreatePackageInstaller("ProductNewGroupWithComment"); 166 var productNewUserWithComment = this.CreatePackageInstaller("ProductNewGroupWithComment");
162 167
163 productNewUserWithComment.InstallProduct(); 168 productNewUserWithComment.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
164 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1"); 169 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1");
170 productNewUserWithComment.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
165 171
166 // clean up 172 // clean up
167 UserGroupVerifier.DeleteLocalGroup("testName1"); 173 UserGroupVerifier.DeleteLocalGroup("testName1");
@@ -169,30 +175,33 @@ namespace WixToolsetTest.MsiE2E
169 175
170 // Verify that a comment can be added to an existing group 176 // Verify that a comment can be added to an existing group
171 [RuntimeFact] 177 [RuntimeFact]
172 public void CanAddCommentToExistingGroup() 178 public void CanAddCommentToExistingNonDomainGroup()
173 { 179 {
174 UserGroupVerifier.CreateLocalGroup("testName1"); 180 UserGroupVerifier.CreateLocalGroup("testName1");
175 var productAddCommentToExistingUser = this.CreatePackageInstaller("ProductAddCommentToExistingGroup"); 181 var productAddCommentToExistingUser = this.CreatePackageInstaller("ProductAddCommentToExistingGroup");
176 182
177 productAddCommentToExistingUser.InstallProduct(); 183 productAddCommentToExistingUser.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
178 184
179 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1"); 185 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1");
180 186
187 productAddCommentToExistingUser.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
188
181 // clean up 189 // clean up
182 UserGroupVerifier.DeleteLocalGroup("testName1"); 190 UserGroupVerifier.DeleteLocalGroup("testName1");
183 } 191 }
184 192
185 // Verify that a comment can be repaired for a new group 193 // Verify that a comment can be repaired for a new group
186 [RuntimeFact] 194 [RuntimeFact]
187 public void CanRepairCommentOfNewGroup() 195 public void CanRepairCommentOfNewNonDomainGroup()
188 { 196 {
189 var productNewUserWithComment = this.CreatePackageInstaller("ProductNewGroupWithComment"); 197 var productNewUserWithComment = this.CreatePackageInstaller("ProductNewGroupWithComment");
190 198
191 productNewUserWithComment.InstallProduct(); 199 productNewUserWithComment.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
192 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", ""); 200 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", "");
193 201
194 productNewUserWithComment.RepairProduct(); 202 productNewUserWithComment.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS);
195 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1"); 203 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1");
204 productNewUserWithComment.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
196 205
197 // clean up 206 // clean up
198 UserGroupVerifier.DeleteLocalGroup("testName1"); 207 UserGroupVerifier.DeleteLocalGroup("testName1");
@@ -200,14 +209,15 @@ namespace WixToolsetTest.MsiE2E
200 209
201 // Verify that a comment can be changed for an existing group 210 // Verify that a comment can be changed for an existing group
202 [RuntimeFact] 211 [RuntimeFact]
203 public void CanChangeCommentOfExistingGroup() 212 public void CanChangeCommentOfExistingNonDomainGroup()
204 { 213 {
205 UserGroupVerifier.CreateLocalGroup("testName1"); 214 UserGroupVerifier.CreateLocalGroup("testName1");
206 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", "initialTestComment1"); 215 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", "initialTestComment1");
207 var productNewUserWithComment = this.CreatePackageInstaller("ProductNewGroupWithComment"); 216 var productNewUserWithComment = this.CreatePackageInstaller("ProductNewGroupWithComment");
208 217
209 productNewUserWithComment.InstallProduct(); 218 productNewUserWithComment.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
210 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1"); 219 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "testComment1");
220 productNewUserWithComment.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
211 221
212 // clean up 222 // clean up
213 UserGroupVerifier.DeleteLocalGroup("testName1"); 223 UserGroupVerifier.DeleteLocalGroup("testName1");
@@ -215,7 +225,7 @@ namespace WixToolsetTest.MsiE2E
215 225
216 // Verify that a comment can be rolled back for an existing group 226 // Verify that a comment can be rolled back for an existing group
217 [RuntimeFact] 227 [RuntimeFact]
218 public void CanRollbackCommentOfExistingGroup() 228 public void CanRollbackCommentOfExistingNonDomainGroup()
219 { 229 {
220 UserGroupVerifier.CreateLocalGroup("testName1"); 230 UserGroupVerifier.CreateLocalGroup("testName1");
221 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", "initialTestComment1"); 231 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", "initialTestComment1");
@@ -232,7 +242,7 @@ namespace WixToolsetTest.MsiE2E
232 242
233 // Verify that a comment can be deleted for an existing group 243 // Verify that a comment can be deleted for an existing group
234 [RuntimeFact] 244 [RuntimeFact]
235 public void CanDeleteCommentOfExistingGroup() 245 public void CanDeleteCommentOfExistingNonDomainGroup()
236 { 246 {
237 UserGroupVerifier.CreateLocalGroup("testName1"); 247 UserGroupVerifier.CreateLocalGroup("testName1");
238 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", "testComment1"); 248 UserGroupVerifier.SetGroupComment(String.Empty, "testName1", "testComment1");
@@ -243,29 +253,64 @@ namespace WixToolsetTest.MsiE2E
243 // Verify that comment was removed. 253 // Verify that comment was removed.
244 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", ""); 254 UserGroupVerifier.VerifyGroupComment(String.Empty, "testName1", "");
245 255
256
257 productCommentDelete.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
258
246 // clean up 259 // clean up
247 UserGroupVerifier.DeleteLocalGroup("testName1"); 260 UserGroupVerifier.DeleteLocalGroup("testName1");
248 } 261 }
249 262
250 // Verify that a comment can be deleted for an existing group 263 #endregion
251 [RuntimeFact] 264
252 public void CanNestGroups() 265 #region Domain
266 // Verify that a domain group can be nested within a local group
267 [RuntimeFact(DomainRequired = true)]
268 public void CanNestDomainGroups()
253 { 269 {
270 var testDomain = System.Environment.UserDomainName;
254 var productNestedGroups = this.CreatePackageInstaller("ProductNestedGroups"); 271 var productNestedGroups = this.CreatePackageInstaller("ProductNestedGroups");
255 272
256 productNestedGroups.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS); 273 productNestedGroups.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS, $"TESTDOMAIN={testDomain}");
257 274
258 // Verify group nested membership 275 // Verify group nested membership
259 UserGroupVerifier.VerifyIsMemberOf(String.Empty, "Authenticated Users", new string[] { "testName1", "testName2" }); 276 UserGroupVerifier.VerifyIsMemberOf(testDomain, "Domain Users", new string[] { "testName1", "testName2" });
260 UserGroupVerifier.VerifyIsMemberOf(String.Empty, "Everyone", new string[] { "testName1" }); 277 //UserGroupVerifier.VerifyIsMemberOf(String.Empty, "Everyone", new string[] { "testName1" });
278
279 UserGroupVerifier.VerifyIsNotMemberOf(testDomain, "Domain Users", new string[] { "testName3" });
280 //UserGroupVerifier.VerifyIsNotMemberOf(String.Empty, "Everyone", new string[] { "testName2", "testName3" });
261 281
262 UserGroupVerifier.VerifyIsNotMemberOf(String.Empty, "Authenticated Users", new string[] { "testName3" }); 282 productNestedGroups.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS, $"TESTDOMAIN={testDomain}");
263 UserGroupVerifier.VerifyIsNotMemberOf(String.Empty, "Everyone", new string[] { "testName2", "testName3" });
264 283
265 // clean up 284 // clean up
266 UserGroupVerifier.DeleteLocalGroup("testName1"); 285 UserGroupVerifier.DeleteLocalGroup("testName1");
267 UserGroupVerifier.DeleteLocalGroup("testName2"); 286 UserGroupVerifier.DeleteLocalGroup("testName2");
268 UserGroupVerifier.DeleteLocalGroup("testName3"); 287 UserGroupVerifier.DeleteLocalGroup("testName3");
269 } 288 }
289
290 // Verify the rollback action reverts all Users changes.
291 [RuntimeFact(DomainRequired = true)]
292 public void CanRollbackDomainGroups()
293 {
294 var testDomain = System.Environment.UserDomainName;
295 UserGroupVerifier.CreateLocalGroup("testName3");
296 var productFail = this.CreatePackageInstaller("ProductFail");
297
298 // make sure the user accounts are deleted before we start
299 UserGroupVerifier.DeleteLocalGroup("testName1");
300 UserGroupVerifier.DeleteLocalGroup("testName2");
301
302 productFail.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, $"TESTDOMAIN={testDomain}");
303
304 // Verify added Users were removed on rollback.
305 Assert.False(UserGroupVerifier.GroupExists(String.Empty, "testName1"), String.Format("Group '{0}' was not removed on Rollback", "testName1"));
306 Assert.False(UserGroupVerifier.GroupExists(String.Empty, "testName2"), String.Format("Group '{0}' was not removed on Rollback", "testName2"));
307
308 // clean up
309 UserGroupVerifier.DeleteLocalGroup("testName1");
310 UserGroupVerifier.DeleteLocalGroup("testName2");
311 UserGroupVerifier.DeleteLocalGroup("testName3");
312 }
313
314 #endregion
270 } 315 }
271} 316}
diff --git a/src/test/msi/WixToolsetTest.MsiE2E/runtests.cmd b/src/test/msi/WixToolsetTest.MsiE2E/runtests.cmd
index a2e67c89..ed1d50b6 100644
--- a/src/test/msi/WixToolsetTest.MsiE2E/runtests.cmd
+++ b/src/test/msi/WixToolsetTest.MsiE2E/runtests.cmd
@@ -1,2 +1,3 @@
1SET RuntimeTestsEnabled=true 1SET RuntimeTestsEnabled=true
2SET RuntimeDomainTestsEnabled=true
2dotnet test WixToolsetTest.MsiE2E.dll -v normal --logger trx 3dotnet test WixToolsetTest.MsiE2E.dll -v normal --logger trx