This is an old post. Microsoft now provide out-of-the-box mechanisms for achieving this outcome.
It sounds simple. You want to prevent the "first-run" welcome page from appearing for new users when they open Microsoft Edge on Windows 10. Unfortunately, I couldn't find a group policy setting, or any other simple registry hack, that would to do the trick.
After digging about with Process Monitor, I noticed that on start, Edge looks for a registry value named IE10TourNoShow. This value is stored in the registry under:
HKEY_CURRENT_USER\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge\Main
The most common method for setting a specific registry value for new users is to predefine the value in C:\Users\Default\ntuser.dat
. However, the SOFTWARE\Classes
key is actually a mount point for a separate registry hive that is not based on ntuser.dat. This second hive is created dynamically at first logon; it is not based on a pre-existing template. Note: The hive is also mounted under HKEY_USERS, where it is named after the user's SID, with a _CLASSES suffix. It is stored as %LOCALAPPDATA%\Microsoft\Windows\UsrClass.dat
.
As we cannot predefine the required registry value for new users, we will need to dynamically configure it during first logon. Perhaps the easiest way of doing so would be to trigger the execution of a script via the RunOnce registry key (which can be pre-staged via ntuser.dat). However, it's not uncommon for this method to be disabled (as it is sometimes used as a vector for running malware).
An alternate method is to apply the change using Windows Installer via Active Setup. For this method to work, we first need to create a custom MSI.
The following PowerShell code will create a minimalist MSI which we will subsequently extend to configure Edge using Active Setup. I've isolated the code, as it is useful on its own for creating an installer that can be used as the starting point for a wide range of "setup" activities. Note: The code has only been tested on Windows 10.
function guid() {
return ("{" + ([guid]::NewGuid()).guid + "}").ToUpper()
}
function query($sql) {
$view = $db.OpenView($sql)
$view.Execute()
$view.Close()
}
function ies($a, $c, $s) {
query "INSERT INTO InstallExecuteSequence (Action, Condition, Sequence) VALUES ('$a', '$c', '$s')"
}
$name = "empty.msi"
$author = "Peter Hinchley"
$title = "Empty"
$version = "1.0"
$msi = New-Object -ComObject WindowsInstaller.Installer
$db = $msi.OpenDatabase($name, 3)
query "CREATE TABLE Component (Component char(72) NOT NULL, ComponentId char(38), Directory_ char(72) NOT NULL, Attributes short NOT NULL, Condition char(255), KeyPath char(72) PRIMARY KEY Component)"
query "CREATE TABLE CustomAction (Action char(72) NOT NULL, Type short NOT NULL, Source char(72), Target char(255), ExtendedType long PRIMARY KEY Action)"
query "CREATE TABLE Directory (Directory char(72) NOT NULL, Directory_Parent char(72), DefaultDir char(255) NOT NULL LOCALIZABLE PRIMARY KEY Directory)"
query "CREATE TABLE Feature (Feature char(38) NOT NULL, Feature_Parent char(38), Title char(64) LOCALIZABLE, Description char(255) LOCALIZABLE, Display short, Level short NOT NULL, Directory_ char(72), Attributes short NOT NULL PRIMARY KEY Feature)"
query "CREATE TABLE FeatureComponents (Feature_ char(38) NOT NULL, Component_ char(72) NOT NULL PRIMARY KEY Feature_, Component_)"
query "CREATE TABLE InstallExecuteSequence (Action char(72) NOT NULL, Condition char(255), Sequence short PRIMARY KEY Action)"
query "CREATE TABLE Media (DiskId short NOT NULL, LastSequence short NOT NULL, DiskPrompt char(64) LOCALIZABLE, Cabinet char(255), VolumeLabel char(32), Source char(72) PRIMARY KEY DiskId)"
query "CREATE TABLE Property (Property char(72) NOT NULL, Value char NOT NULL LOCALIZABLE PRIMARY KEY Property)"
query "CREATE TABLE Registry (Registry char(72) NOT NULL, Root short NOT NULL, ``Key`` char(255) NOT NULL LOCALIZABLE, Name char(255) LOCALIZABLE, Value char LOCALIZABLE, Component_ char(72) NOT NULL PRIMARY KEY Registry)"
query "INSERT INTO Component (Component, ComponentId, Directory_, Attributes) VALUES ('DEFAULTCOMPONENT', '$(guid)', 'TARGETDIR', 2)"
query "INSERT INTO Directory (Directory, DefaultDir) VALUES ('TARGETDIR', 'SourceDir')"
query "INSERT INTO Feature (Feature, Level, Attributes) VALUES ('DEFAULTFEATURE', 1, 0)"
query "INSERT INTO FeatureComponents (Feature_, Component_) VALUES ('DEFAULTFEATURE', 'DEFAULTCOMPONENT')"
query "INSERT INTO Media (DiskId, LastSequence) VALUES (1, 1)"
query "INSERT INTO Property (Property, Value) VALUES ('ProductName', '$title')"
query "INSERT INTO Property (Property, Value) VALUES ('ProductVersion', '$version')"
query "INSERT INTO Property (Property, Value) VALUES ('ProductCode', '$(guid)')"
query "INSERT INTO Property (Property, Value) VALUES ('ProductLanguage', '1033')"
query "INSERT INTO Property (Property, Value) VALUES ('ProductID', 'none')"
query "INSERT INTO Property (Property, Value) VALUES ('Manufacturer', '$author')"
query "INSERT INTO Property (Property, Value) VALUES ('UpgradeCode', '$(guid)')"
query "INSERT INTO Property (Property, Value) VALUES ('ARPSYSTEMCOMPONENT', '0')"
ies 'AllocateRegistrySpace' 'NOT Installed' '1550'
ies 'AppSearch' '' '400'
ies 'BindImage' '' '4300'
ies 'CCPSearch' 'NOT Installed' '500'
ies 'CostFinalize' '' '1000'
ies 'CostInitialize' '' '800'
ies 'CreateFolders' '' '3700'
ies 'CreateShortcuts' '' '4500'
ies 'DeleteServices' 'VersionNT' '2000'
ies 'DuplicateFiles' '' '4210'
ies 'FileCost' '' '900'
ies 'InstallFiles' '' '4000'
ies 'InstallFinalize' '' '6600'
ies 'InstallInitialize' '' '1500'
ies 'InstallODBC' '' '5400'
ies 'InstallServices' 'VersionNT' '5800'
ies 'InstallValidate' '' '1400'
ies 'LaunchConditions' '' '100'
ies 'MoveFiles' '' '3800'
ies 'MsiPublishAssemblies' '' '6250'
ies 'PatchFiles' '' '4090'
ies 'ProcessComponents' '' '1600'
ies 'PublishComponents' '' '6200'
ies 'PublishFeatures' '' '6300'
ies 'PublishProduct' '' '6400'
ies 'RMCCPSearch' 'NOT Installed' '600'
ies 'RegisterClassInfo' '' '4600'
ies 'RegisterComPlus' '' '5700'
ies 'RegisterExtensionInfo' '' '4700'
ies 'RegisterFonts' '' '5300'
ies 'RegisterMIMEInfo' '' '4900'
ies 'RegisterProduct' '' '6100'
ies 'RegisterProgIdInfo' '' '4800'
ies 'RegisterTypeLibraries' '' '5500'
ies 'RegisterUser' '' '6000'
ies 'RemoveDuplicateFiles' '' '3400'
ies 'RemoveEnvironmentStrings' '' '3300'
ies 'RemoveFiles' '' '3500'
ies 'RemoveFolders' '' '3600'
ies 'RemoveIniValues' '' '3100'
ies 'RemoveODBC' '' '2400'
ies 'RemoveRegistryValues' '' '2600'
ies 'RemoveShortcuts' '' '3200'
ies 'SelfRegModules' '' '5600'
ies 'SelfUnregModules' '' '2200'
ies 'SetODBCFolders' '' '1100'
ies 'StartServices' 'VersionNT' '5900'
ies 'StopServices' 'VersionNT' '1900'
ies 'UnpublishComponents' '' '1700'
ies 'UnpublishFeatures' '' '1800'
ies 'UnregisterClassInfo' '' '2700'
ies 'UnregisterComPlus' '' '2100'
ies 'UnregisterExtensionInfo' '' '2800'
ies 'UnregisterFonts' '' '2500'
ies 'UnregisterMIMEInfo' '' '3000'
ies 'UnregisterProgIdInfo' '' '2900'
ies 'UnregisterTypeLibraries' '' '2300'
ies 'ValidateProductID' '' '700'
ies 'WriteEnvironmentStrings' '' '5200'
ies 'WriteIniValues' '' '5100'
ies 'WriteRegistryValues' '' '5000'
$db.Commit()
$db = $null
[GC]::Collect()
$msi = New-Object -ComObject WindowsInstaller.Installer
$info = $msi.SummaryInformation($name, 9)
$info.Property(1) = 1252
$info.Property(2) = $title
$info.Property(3) = "Empty"
$info.Property(4) = $author
$info.Property(7) = "Intel;1033"
$info.Property(9) = $(guid)
$info.Property(14) = 200 # Minimum required for 64bit. Windows Installer 2.0.
$info.Property(15) = 0
$info.Property(18) = "Windows Installer"
$info.Persist()
Most of the code in the above script is used to construct the database tables required by a valid MSI. I won't go into the gory details, but if you are after more information, take a look at MSDN. It covers everything you need, whether it be working out how to interact with the MSI database, or deciphering the properties within the Summary Information table.
Having run the previous code to create empty.msi, let's now modify the MSI for the purpose of configuring Microsoft Edge. In particular, we will:
- Update the ProductName and ProductCode in the Property table.
- Update the ComponentId in the Component table. Note: You should always refresh all GUIDs in the database when creating a new MSI.
- Add a custom action to the CustomAction table. More on this later.
- Add a row to the InstallExecuteSequence table to call the custom action.
- Add several rows to the Registry table; two to configure Active Setup, and the third to configure Edge.
Here's the code:
function guid() {
return ("{" + ([guid]::NewGuid()).guid + "}").ToUpper()
}
function query($sql) {
$view = $db.OpenView($sql)
$view.Execute()
$view.Close()
}
function ies($a, $c, $s) {
query "INSERT INTO InstallExecuteSequence (Action, Condition, Sequence) VALUES ('$a', '$c', '$s')"
}
$source = "empty.msi"
$target = "edge.msi"
$title = "Disable Microsoft Edge Welcome"
Copy-Item $source $target
$msi = New-Object -ComObject WindowsInstaller.Installer
$db = $msi.OpenDatabase($target, 2)
query "UPDATE Property SET Value = '$title' WHERE Property = 'ProductName'"
query "UPDATE Property SET Value = '$(guid)' WHERE Property = 'ProductCode'"
query "UPDATE Component SET ComponentId = '$(guid)' WHERE Component = 'DEFAULTCOMPONENT'"
query "INSERT INTO CustomAction (Action, Type, Target) VALUES ('CUSTOMACTION1', '37', 'var k,r=GetObject(`"winmgmts:!root/default:StdRegProv`");while(r.EnumKey(0x80000001,`"SOFTWARE\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\microsoft.microsoftedge_8wekyb3d8bbwe`",k)){}')"
query "INSERT INTO Registry (Registry, Root, ``Key``, Name, Value, Component_) VALUES ('ACTIVESETUP.VERSION', '2', 'Software\Microsoft\Active Setup\Installed Components\[ProductCode]', 'Version', '1.0', 'DEFAULTCOMPONENT')"
query "INSERT INTO Registry (Registry, Root, ``Key``, Name, Value, Component_) VALUES ('ACTIVESETUP.STUBPATH', '2', 'Software\Microsoft\Active Setup\Installed Components\[ProductCode]', 'StubPath', 'msiexec.exe /fu [ProductCode]', 'DEFAULTCOMPONENT')"
query "INSERT INTO Registry (Registry, Root, ``Key``, Name, Value, Component_) VALUES ('REG1', '1', 'Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge\Main', 'IE10TourNoShow', '`#1', 'DEFAULTCOMPONENT')"
ies 'CUSTOMACTION1' 'Installed' '4950'
$db.Commit()
$db = $null
[GC]::Collect()
$msi = New-Object -ComObject WindowsInstaller.Installer
$info = $msi.SummaryInformation($target, 3)
$info.Property(2) = $title
$info.Property(9) = $(guid)
$info.Persist()
There are a few items in the above script that require further explanation:
- The [ProductCode] references in the registry table will be dynamically replaced (during installation) with the product code from the Property table.
- The custom action is necessary to delay the addition of the registry value needed by Edge. This is required because, during testing, I noticed that Active Setup was adding the registry value prior to the user-based installation of Edge that occurs at first logon. This resulted in Edge overwriting (i.e. removing) the IE10TourNoShow value.
- The custom action has a Type of 37. This indicates that the Target property of the Custom Action table holds inline JScript. Refer to Custom Action Types for further information.
- I used JScript, rather than VBScript, because the Target property can only contain a single line of code; and the code that I required could only be coerced into a single line by using semicolons (which is only possible with JScript).
- The Target property is also limited to 255 characters; hence the rather terse JScript syntax. Note: The code fortunately came in at 232 characters.
- The JScript code basically pauses the execution of Windows Installer until Edge has provisioned the registry key under which IE10TourNoShow value is to be added.
- The custom action is sequenced to execute before the values in the Registry table are processed. This is achieved by configuring a Sequence value (4950) just lower than that assigned to the WriteRegistryValues action (5000).
- When a new user logs onto the computer where the MSI has been installed, Active Setup will run msiexec.exe /fu [guid] (where [guid] is the product key of the MSI) to write the user-based registry values (i.e. IE10TourNoShow) from the Registry table of the MSI into the user's profile.
Having run the above two scripts, you should have a file named edge.msi. To install it, open an elevated command prompt and run:
msiexec.exe /i Edge.msi /qb ALLUSERS=1
Now log into the same computer with a new user account (a user that hasn't previously logged on, or at the very least, as a user that hasn't launched Edge). Now, try opening Edge. If everything has gone to plan, the annoying welcome page won't be displayed.