Create the Configuration Store

Applies To: Windows 8.1

For a Windows RT 8.1 deployment in education you use the same configuration store for both deployment scenarios.

The configuration store is where you store the source files and scripts that configure each device. Use the same configuration store for both deployment scenarios. You can locate the configuration store on a USB flash drive or a network share. (You can also keep a master copy of the configuration store on a network share and copy it to USB flash drives to expedite configuration for each installer.) This guide assumes that the configuration store is on a USB flash drive in D:\Store. By default, the scripts in this guide look for files in the following subfolders:

  • Apps. Windows Store apps to sideload on devices

  • Files. Extra files to copy to devices

  • Logs. Target location in which to log devices’ names, media access control (MAC) addresses, and serial numbers

  • Policies. Local Group Policy settings to copy to devices

  • Profiles. Wireless networking profiles to add to devices

  • Scripts. Scripts required to configure devices

  • Settings. REG files to import into devices’ registries

  • Tasks. Scheduled tasks to import on devices

  • Updates. Update packages (MSU files) to install on devices

If you store the configuration store on a network share, guest access must be enabled on the configuration store. Otherwise, installers must provide domain credentials when preparing devices. Enabling guest access on the configuration store helps streamline the process. Because Microsoft recommends guest access for the configuration store, Microsoft also recommends creating it on a stand-alone server or PC in the lab, which you can take down after you are done. A laptop or network-attached storage device is perfect for this purpose.

Warning

The scripts and source files contain passwords in plain text—for example, the password to use for shared accounts, wireless network passphrases, and the credentials under which to run scheduled tasks. Therefore, you must ensure that students do not have access to the configuration store.

The following subsections tackle individual folders in the configuration store separately, including the sample scripts that drive each. The Preparing Shared Devices for Delivery and Preparing Personal Devices for Delivery sections give end-to-end examples that tie everything together.

Note

The scripts that this guide provides are samples. Schools must customize and test these scripts prior to using them in any Windows RT device deployment. Although Microsoft has tested these scripts and they do work properly, they are not suitable for use as-is without careful consideration.

Apps

Installing apps from the Windows Store requires a Microsoft account. If you are not using Microsoft accounts (for example, you are deploying shared devices), an alternative is to sideload apps. Sideloading an app means installing it directly on the device without buying it from the Windows Store. For more information, see Windows RT in the Enterprise.

Note

Sideloading apps on Windows RT devices requires that you first install a sideloading product key. Contact your account team about acquiring sideloading product keys if you must sideload apps.

You need the apps’ package files to sideload them. Package files are files with the .appx file name extension, and you obtain them from the apps’ developers. Keep in mind that few developers provide package files for their apps outside of the Windows Store, but if you have an arrangement to sideload developers’ apps or your school has developed Windows Store apps internally, copy the package files they provide to the Apps subfolder of the configuration store. The script in Listing 1 provisions each app in the given path on the target device, meaning that those apps will be available for all users who sign in to the device.

Listing 1. Apply-AppxPackages.ps1

function Apply-AppxPackages {

    # Install each Windows Store app from the configuration store.
    # Make sure the Group Policy setting AllowAllTrustedApps is enabled
    # and a sideloading product key is installed on the device.

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Path of the folder containing Windows Store app (APPX) packages")][string] $Path
    )

    if ((Test-Path -Path $Path -PathType Container)) {

        Push-Location -Path $Path
    
        $AppPackages = Get-ChildItem -Filter *.appx
    
        $PackageCount = ($AppPackages | Measure-Object).Count
        Write-Output "Installing ($PackageCount) apps from the configuration store."

        $AppPackages | ForEach-Object {
            Write-Output "...$_"
            Add-AppxProvisionedPackage -Online -PackagePath $_ -SkipLicense
        }

        Pop-Location
        Write-Output "Finished installing app packages on the device."
    }
    else {
        Write-Output "Skipping Windows Store apps because path was not found."
    }
}

Files

The script in Listing 2 has a simple but useful role. It replicates the contents of the Files subfolder in the configuration store to the target device. To prepare the Files subfolder in the configuration store, create the file and folder structure you want to replicate on the target device. Imagine that the Files subfolder of the configuration store is the root of the system drive on the target device. For example, if you create a text file named helloworld.txt in Files\Users\Public\Desktop, the script in Listing 2 copies that file to the public desktop on each device it runs. The script retains file attributes and overwrites system, read-only, and hidden files.

Listing 2. Apply-LocalFiles.ps1

function Apply-LocalFiles {

    # Copy files and folders from the configuration store.

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Path of the folder containing folders and files to copy")][string] $Path,
        [Parameter(Mandatory=$true, HelpMessage = "Target path to which to copy the source folders and files")][string] $Target
    )

    if ((Test-Path -Path $Path -PathType Container)) {
        Write-Output "Applying files and folders to this device."
        xcopy.exe $Path\*.* $Target\*.* /s /d /e /h /r /k /y | Tee-Object -Variable Results | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw $Results
        }
        Write-Output "Finished applying files and folders to this device."
    }
    else {
        Write-Output "Skipping local files because path was not found."
    }
}

Logs

Some schools need to collect the MAC address of each Windows RT device as they configure it. For example, schools that have network filters might prefer to use MAC security filtering rather than requiring students to provide their domain credentials to get through the firewall. Collecting the name and MAC address of each device during installation helps simplify the configuration of MAC security filtering.

The script in Listing 3 logs the computer name, its MAC address, and its serial number in the Logs subfolder of the configuration store. Because you might have multiple installers setting up devices, this script creates a separate text file for each device to avoid multiuser conflicts if you are storing them in a network share. The file name is the MAC address appended to the computer name. The text file contains the name, MAC address, and serial number separated by commas. Not only does this approach prevent multiuser collisions, it enables you to easily import the comma-delimited text files in to a Microsoft Excel spreadsheet to aggregate them.

Note

You can customize Listing 3 to collect additional information about devices.

Listing 3. Log-DeviceWithMac.ps1

Function Log-DeviceWithMac {

    # Create a file containing the computer name, MAC address
    # of the first Wi-Fi adapter, and the device's serial number.

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Path in which to log the computer's name, MAC address, and serial number")][string] $Path
    )

    if ((Test-Path -Path $Path -PathType Container)) {
        Write-Output "Logging the computer name and MAC address in the configuration store."

        $FileName = $env:ComputerName + "_" + $((Get-NetAdapter -Name "Wi-FI").MacAddress ) + ".txt"
        $FullFilePath = Join-Path $Path $FileName
    
        # Check if the file exists do not write a new file, otherwise write the file.

        If (!(Test-Path $FullFilePath)) {
            $Content = $env:ComputerName + ", " + $((Get-NetAdapter -Name "Wi-FI").MacAddress ) + ", " + (Get-WmiObject -Class Win32_BIOS).SerialNumber
            Add-Content -Path $FullFilePath -Value $Content
            Write-Output "...$Content"
        }
        Write-Output "Finished logging the computer name and MAC address in the configuration store."
    }
    else {
        Write-Output "Did not log the device because path was not found."
    }
}

Policies

Windows RT devices do not support domain join, so they do not support domain-based Group Policy. These devices do support local Group Policy, though, and you can create a local Group Policy configuration that you can apply to many devices. The procedure is as follows:

  1. On a reference device, configure local Group Policy.

  2. Copy the local Group Policy from the reference device to the configuration store.

  3. Apply the local Group Policy from the configuration store to each device.

Note

See the Local Group Policy Settings section for a list of settings that you might explore for your school’s devices. Click here to learn more about local Group Policy.

By default, Group Policy is not enabled on Windows RT devices. You must enable it by starting the Group Policy service (GPSVC). Although you can do this by using the Services console (see Local Group Policy Support for Windows RT), the script in Listing 4 demonstrates how to enable the Group Policy service by using Windows PowerShell. If you configure a policy and it seems to have no effect on the system, make sure that you have enabled the Group Policy service.

Listing 4. Enable-GroupPolicy.ps1

function Enable-GroupPolicy {

    # Enable and start the Group Policy service.

    Set-Service -Name gpsvc -StartupType auto
    Start-Service -Name gpsvc
}

To configure policies on a reference device, use the Local Group Policy Editor. To access it, type gpedit.msc on the Start screen, and then press Enter. You can configure security policy and Administrative Templates for both the computer and users. Examine each policy’s description to determine whether it supports Windows RT devices. You can also filter settings in Administrative Templates to show only those policies that Windows RT 8.1 devices support. For detailed step-by-step instructions, see Local Group Policy Editor. Thoroughly test each policy to ensure that it works as expected on your devices. The Local Group Policy Settings section lists many policies interesting to schools deploying Windows RT devices.

After you have configured policies on the reference device, copy them from the reference device to the configuration store. The script in Listing 5 is an example. (If the configuration store is on a network share, you must connect to the configuration store by using an account that can write to it.) This script uses the command-line tool Secedit.exe to export the device’s security configuration to an INF file (see Secedit). It also uses the Export-StartLayout Windows PowerShell cmdlet to export the current Start screen layout to an XML file. The script stores both files in the local Group Policy Object (GPO—in other words, %SystemRoot%\System32\GroupPolicy). Then, it copies the local GPO from the reference device to the configuration store.

Listing 5. Gather-GroupPolicy.ps1

function Gather-GroupPolicy {

    # Capture local Group Policy and save in the configuration store.
    # Important: Make sure that the path and file name of $StartLayoutFile
    #            is the same as used in the Start Menu Layout Group Policy setting.
    #            By default, these scripts create the file layout.xml in the path
    #            C:\Windows\System32\GroupPolicy. For more information, see 
    #            "Deploying Windows RT 8.1 in education."

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Path to the folder in which to store the local Group Policy object")][string] $Path
    )
    $PolicySource    = "C:\Windows\System32\GroupPolicy"
    $SecurityInfFile = Join-Path $PolicySource "security.inf"
    $StartLayoutFile = Join-Path $PolicySource "layout.xml"

    Write-Output "Gathering Group Policy settings to $Path."

    secedit /export /cfg $SecurityInfFile | Tee-Object -Variable Results | Out-Null
    if ($LASTEXITCODE -ne 0) {
        throw $Results
    }

    Export-StartLayout –path $StartLayoutFile –as XML
    xcopy $PolicySource\*.* $Path\*.* /s /d /h /r /y  | Tee-Object -Variable Results | Out-Null
    if ($LASTEXITCODE -ne 0) {
        throw $Results
    }

    Write-Output "Finished gathering Group Policy settings to $Path"
}

The script in Listing 6 reverses the process. It copies the local GPO from the configuration store to the target device, then it uses Secedit to import security policy from the local GPO (in other words, %SystemRoot%\System32\GroupPolicy). Finally, it configures and starts the Group Policy service and runs Gpupdate.exe to refresh Group Policy on the device.

Notice that Listing 6 does not import the Start screen layout. Instead, the layout file is stored within the local GPO, and you must define the policy setting named Start Screen Layout using the path to the layout file (in other words, %SystemRoot%\System32\GroupPolicy\layout.xml). Windows RT 8.1 does support this policy, but it works only if sideloading is enabled on the device. For more information about managing the Start screen layout, see Customize Windows 8.1 Start Screens by Using Group Policy.

Listing 6. Apply-GroupPolicy.ps1

function Apply-GroupPolicy {

    # Apply local Group Policy settings from A configuration store
    # to the local computer, and start the Group Policy service.

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Path of the folder containing the local Group Policy object to copy")][string] $Path
    )
    $Target          = "C:\Windows\System32\GroupPolicy"
    $SecurityInfPath = Join-Path $Target "security.inf"
    $SecuritySdbPath = Join-Path $Target "secedit.sdb"

    if ((Test-Path -Path $Path -PathType Container)) {
        Write-Output "Configuring Group Policy on this device."

        Write-Output "...Copying policy settings to the device."
        xcopy $Path\*.* $Target\*.* /s /d /h /r /y | Tee-Object -Variable Results | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw $Results
        }

        Write-Output "...Configuring security policy on the device."
        secedit /configure /db $SecuritySdbPath /cfg $SecurityInfPath | Out-Null

        Write-Output "...Enabling and starting the Group Policy service."
        Enable-GroupPolicy

        Write-Output "...Updating Group Policy on the device."
        gpupdate /force | Tee-Object -Variable Results | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw $Results
        }

        Write-Output "Finished configuring Group Policy on the device."
    }
    else {
        Write-Output "Skipping Group Policy because path was not found."
    }
}

Profiles

The Profiles folder of the configuration store contains wireless networking profiles. The process is as follows:

  1. On a reference device, connect to each wireless network.

  2. Use the example script in Listing 7 to create a wireless network profile for each connection in the configuration store. The script creates one XML file for each profile.

    Listing 7. Gather-WirelessProfiles.ps1

    function Gather-WirelessProfiles {
    
        # Export all wireless profiles on the device to the given $Path.
    
        param (
            [Parameter(Mandatory=$true, HelpMessage = "Path to the folder in which to store wireless profiles")][string] $Path
        )
    
        if (!(Test-Path $Path)) {
            throw "Unable to export wireless profiles. $Path was not found."
        }
    
        Write-Output "Gathering wireless profiles to $Path."
    
        netsh.exe wlan export profile folder="$Path" key=clear | Tee-Object -Variable Results | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw $Results
        }
    
        Write-Output "Finished gathering wireless profiles to $Path."
    }
    
  3. Use the example script in Listing 8 to add each wireless network profile in the Profiles folder to the Windows RT device.

    On devices that have a single wireless interface, such as the Surface, Windows RT will automatically connect to network automatically.

    Listing 8. Apply-WirelessProfiles.ps1

    function Apply-WirelessProfiles {
    
        # Import each wireless profile found in the configuration store.
    
        param (
            [Parameter(Mandatory=$true, HelpMessage = "Path of the folder containing wireless profiles to add to the device")][string] $Path,
            [Parameter(Mandatory=$true, HelpMessage = "Name of the interface with which to associate the wireless profiles")][string] $Interface
        )
    
        if ((Test-Path -Path $Path -PathType Container)) {
            Push-Location -Path $Path
    
            $Profiles = Get-ChildItem -Filter *.xml
    
            $ProfilesCount = ($Profiles | Measure-Object).Count
            Write-Output "Importing ($ProfilesCount) wireless profiles from the configuration store."
    
            $Profiles | ForEach-Object {
                Write-Output "...$_"
    
                netsh.exe wlan add profile filename=$_ interface=$Interface | Tee-Object -Variable Results | Out-Null
                if ($LASTEXITCODE -ne 0) {
                    throw $Results
                }
            }
    
            Pop-Location
            Write-Output "Finished importing wireless profiles from the configuration store."
        }
        else {
            Write-Output "Skipping wireless profiles because path was not found."
        }
    }
    

Settings

Like any recent version of Windows, Windows RT stores system and user settings in the registry. You can export registry settings from the registry to REG files. After editing them so that they contain only the settings you want to deploy, store them in the Settings subfolder of the configuration store.

Note

If you are not familiar with the Windows registry, see About the Registry before adding REG files to the configuration store. You must understand how to create REG files that contain only the settings you want to deploy.

The script in Listing 9 imports each REG file it finds in the configuration store’s Settings folder into the target device’s registry. This is an easy way to configure system and user settings that you cannot configure through local Group Policy.

Listing 9. Apply-RegFiles.ps1

function Apply-RegFiles {

    # Import each registry file found in the configuration store.

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Path of the folder containing registry (REG) files to import on the device")][string] $Path
    )

    if ((Test-Path -Path $Path -PathType Container)) {
        Push-Location -Path $Path
    
        $RegFiles = Get-ChildItem -Filter *.reg
    
        $RegFileCount = ($RegFiles | Measure-Object).Count
        Write-Output "Importing ($RegFileCount) REG files from the configuration store."

        $RegFiles | ForEach-Object {
            Write-Output "...$_"
            reg import $_ | Tee-Object -Variable Results | Out-Null
            if ($LASTEXITCODE -ne 0) {
                throw $Results
            }
        }

        Pop-Location
        Write-Output "Finished importing REG files from the configuration store."
    }
    else {
        Write-Output "Skipping settings because path was not found."
    }
}

Tasks

Schools can use Windows Intune with or without Microsoft System Center 2012 R2 Configuration Manager to manage Windows RT devices by using Mobile Device Management (MDM), but they cannot use Windows Intune to run Windows PowerShell scripts on remote devices. Although Windows Intune does support a subset of local Group Policy settings that it uses to manage compliance (e.g., Windows Update schedule, password policy, and so on), it does not support configuration of arbitrary local Group Policy settings.

A simple workaround is to schedule a task on each device that downloads and runs a script from the school’s network once each day. Then, you can update the script as necessary to at least have some capability to touch Windows RT devices. You can also schedule a task that downloads the local GPO from the configuration store once each day, allowing you to update the local GPOs beyond initial delivery. Listing 10 and Listing 11 are examples. Schedule Listing 10, which is a batch script that runs the Windows PowerShell script in Listing 11, bypassing execution policy.

Listing 10. Update-DeviceConfig.cmd

@echo off

rem Update-DeviceConfig.cmd
rem
rem Start Update-DeviceConfig.ps1, bypassing execution policy.

powershell.exe -ExecutionPolicy Bypass %~dp0Update-DeviceConfig.ps1 -PoliciesPath D:\Store\Policies

Listing 11. Update-DeviceConfig.ps1

Update-DeviceConfig.ps1
# This is a sample script that you can run from a scheduled task on each
# Windows RT device. Use this script to touch remote devices when they phone
# home on schedule. Place it in a network share, which your scheduled task
# can access, so that you can update it in the future. See the guide
# "Deploying Windows RT 8.1 in education" for more information about using and
# customizing this script to configure Windows RT devices in schools.
# DISCLAIMER
# ----------
# This sample script is not supported under any Microsoft standard support program
# or service. This sample script is provided AS IS without warranty of any kind.
# Microsoft further disclaims all implied warranties including, without limitation,
# any implied warranties of merchantability or of fitness for a particular purpose.
# The entire risk arising out of the use or performance of this sample script and
# documentation remains with you. In no event shall Microsoft, its authors, or anyone
# else involved in the creation, production, or delivery of this sample script be
# liable for any damages whatsoever (including, without limitation, damages for loss
# of business profits, business interruption, loss of business information, or other
# pecuniary loss) arising out of the use of or inability to use this sample script or
# documentation, even if Microsoft has been advised of the possibility of such damages.

param (
    [Parameter(Mandatory=$true, HelpMessage = "Path to the folder containing the local Group Policy object to copy")][string] $PoliciesPath
)
$ErrorActionPreference = 'Stop'

# GLOBAL VARIABLES ##############################################

$PolicyTarget    = "C:\Windows\System32\GroupPolicy" # DO NOT CHANGE

# If you change the following folder and file names, you must also change them in
# Apply-SharedConfig.pst, Apply-PersonalConfig.pst, and Update-DeviceConfig.pst.

$SecurityInfFile = Join-Path $PolicyTarget "security.inf"
$SecuritySdbFile = Join-Path $PolicyTarget "secedit.sdb"

# MAIN ##########################################################

# Update Group Policy on the device.

xcopy $PoliciesPath\*.* $PolicyTarget\*.* /s /d /h /r /y | Out-Null
secedit /configure /db $SecuritySdbFile /cfg $SecurityInfFile | Out-Null
gpupdate /force | Out-Null

The process is as follows:

  1. On a reference device, configure each task that you want to schedule. Check the actions, triggers, and settings carefully.

Note

Microsoft recommends that you configure a random delay on any task that accesses the network to prevent all of your devices from hitting it at the same time. Of course, test your tasks to make sure they work as expected.

  1. Export each task definition to an XML file in the Tasks subfolder of the configuration store. In Task Scheduler, click the task you want to export; then, click Export in the Actions pane.

  2. On each target device, import the task definitions from the configuration store.

Listing 12 imports each XML file it finds in the Tasks subfolder of the configuration store on the device. In addition to the path, it requires the user name and password of an account under which to run the task. Listing 12 creates that account on the local device and adds it to the local Administrators group.

Listing 12. Import-ScheduledTasks.ps1

function Import-ScheduledTasks {

    # Install each task found in the configuration store.

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Path of the folder containing task (XML) files to import into scheduled tasks")][string] $Path,
        [Parameter(Mandatory=$true, HelpMessage = "Name of the account under which to run each imported scheduled task")][string] $TaskUser,
        [Parameter(Mandatory=$true, HelpMessage = "Password for the account under which to run each imported scheduled task")][string] $TaskPassword
    )

    if ((Test-Path -Path $Path -PathType Container)) {
        Push-Location -Path $Path
    
        # Create the local administrator account to use for running the tasks.

        Write-Output "Creating the local administrator account for $TaskUser."
        net user $TaskUser $TaskPassword /add /expires:never /passwordchg:no | Tee-Object -Variable Results | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw $Results
        }
        net localgroup "Administrators" $TaskUser /add | Tee-Object -Variable Results | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw $Results
        }

        # Add each task file to the task scheduler, using our local administrator account.

        $TaskFiles = Get-ChildItem -Filter *.xml
    
        $TaskFileCount = ($TaskFiles | Measure-Object).Count
        Write-Output "Importing ($TaskFileCount) scheduled tasks from the configuration store."

        $TaskFiles | ForEach-Object {
            Write-Output "...$_"
            $TaskXML = get-content $_ | Out-String
            Register-ScheduledTask -Xml $TaskXML -TaskName $_ -User $TaskUser -Password $TaskPassword
        }

        Pop-Location
        Write-Output "Finished importing scheduled tasks from the configuration store."
    }
    else {
        Write-Output "Skipping tasks because path was not found."
    }
}

Updates

Windows RT downloads updates over the Internet directly from Microsoft. It does not support Windows Server Update Services (WSUS), and an update catalog is not available for Windows RT. For updates that you absolutely must install during preparation (for example, updates on which your apps have a dependency), contact your account team to see whether they can provide update packages (MSU files). (For more information about update packages, see Description of the Windows Update Standalone Installer in Windows.) Put each MSU file your account team provides in the Updates folder in the configuration store. The script in Listing 13 automatically installs each update package that it finds in the Updates folder by using the command-line tool Wusa.exe.

Listing 13. Apply-UpdateFiles.ps1

function Apply-UpdateFiles {

    # Install each update found in the configuration store. Windows RT does
    # not support WSUS and an update catalog is not available. Contact your
    # account team about acquiring update packages (MSU files).

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Folder containing Microsoft update (MSU) files to install on the device")][string] $Path
    )

    if ((Test-Path -Path $Path -PathType Container)) {
        Push-Location -Path $Path
    
        $UpdatePackages = Get-ChildItem -Filter *.msu
    
        $PackageCount = ($UpdatePackages | Measure-Object).Count
        Write-Output "Installing ($PackageCount) updates from the configuration store."

        $UpdatePackages | ForEach-Object {
            Write-Output "...$_"
            $cmd = "wusa $_ /quiet /norestart"
            Invoke-Expression $cmd

            # Wait until the process finishes before continuing.

            while ((Get-Process | Where { $_.Name -eq 'wusa'}) -ne $null) { 
                Start-Sleep -Seconds 1
            }
        }

        Pop-Location
        Write-Output "Finished installing update packages on the device."
    }
    else {
        Write-Output "Skipping update packages because path was not found."
    }
}

Users

In shared-device scenarios, you must create a local account for students to use, but what do you name this account? You can use the same name for the account on every device, but a common alternative is to base the name of the shared user account on the computer, removing any special characters, like dashes. The script in Listing 14 shows an example that creates a local user account based on the computer name. You can easily modify this script to use any other convention, though.

As a bonus, this script also demonstrates how to configure the device so that it automatically signs in by using the shared user account to help students get to the desktop quicker. Alternatively, you can use the netplwiz.cpl Control Panel applet to configure automatic sign-in.

Note

The script in Listing 14 uses NET USER to add the shared user account, and it uses the command-line option /expires:never to disable password expiration for the account. In shared-device scenarios, this is necessary to prevent mayhem when devices are distributed in classrooms. However, schools using an MDM service must understand that this option breaks password management in these services.

Listing 14. Create-SharedUser.ps1

function Create-SharedUser {

    # Provision a shared local account based on the device's name.

    param (
        [Parameter(Mandatory=$true, HelpMessage = "Password to use for the device's shared user account")][string] $Password
    )

    $LocalUserName = $env:ComputerName -replace "-", ""
    Write-Output "Creating the local user account $LocalUserName."

    # Use NET USER to add the shared account to this device. This script
    # disables password expiration for the shared user account, which is
    # necessary in shared-device scenarios. However, this will break
    # password management in Mobile Device Management.

    net user $LocalUserName $Password /add /expires:never /passwordchg:no | Tee-Object -Variable Results | Out-Null
    if ($LASTEXITCODE -ne 0) {
        throw $Results
    }

    Write-Output "Configuring device to automatically sign in as $LocalUserName."
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultDomainName -Value $env:ComputerName | Out-Null
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName   -Value $LocalUserName    | Out-Null
    New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword   -Value $Password         | Out-Null
    Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon    -Value "1"               | Out-Null

    Write-Output "Finished creating the local user account on the device."
}

See also