Automate installations with WinGet + YAML

  • WinGet allows you to manage applications in Windows from the command line and, together with DSC, apply declarative configurations using YAML files.
  • WinGet configuration files are structured into assertions and resources, making it easy to check prerequisites and automate installations, system settings, and scripts.
  • The combination of winget configure, DSC modules, and repositories (public or private) enables an Infrastructure as Code approach for Windows machines.
  • Organizations can control sources, security policies, and the use of WinGet configurations through group policies and integration with Intune.

Automate installations with WinGet + YAML

Setting up a new Windows PC from scratch Every time you switch computers or join a new project, it can be a real ordeal: downloading installers, clicking next-next-next, accepting licenses, restarting, tweaking system settings… and repeating the same process on every machine. With WinGet and YAML files, you can transform this entire manual ritual into a single command that gets your environment ready to work seamlessly. reliable, repeatable and automated.

The idea is very simple but incredibly powerful: you define in a configuration file which applications you want, which version of Windows you need, which settings should be applied, and which scripts should be run. Then you let it run. Windows Package Manager (winget)Supported by PowerShell Desired State Configuration (DSC), it takes care of installing, configuring and verifying that your computer is exactly in the desired state, like a cooking recipe but for your work environment.

What is WinGet and why is it key to automating installations?

WinGet (Windows Package Manager) It is Microsoft's official package manager for Windows 10 and Windows 11. It works from the command line and allows you to install, update, configure and uninstall applications similar to what is done in GNU/Linux with managers like apt or dnf, but integrated into the Windows ecosystem.

Instead of searching the web for installers, downloading EXE or MSI files and running them one by one, with WinGet you simply run a command like install install so that the system downloads the package from its authorized sources and completes the installation without you having to click anything. This reduces human error, saves time, and opens the door to... mass automation of equipment.

The main sources of software used by WinGet are Microsoft Store and the Community RepositoryIn addition, you can add extra repositories, for example a private one for your organization, which allows you to control which packages are distributed internally without relying solely on the public catalog.

WinGet's solution is not limited to the console tool. The complete ecosystem includes the CLI winget, the services for send and store packages (manifestos) and the mechanism of declarative configuration files in YAML, which are the main players when we talk about automating Windows installations and configuration.

WinGet and YAML files to automate configuration

Basic WinGet commands for managing applications

Before we delve into the YAML filesIt's helpful to be familiar with WinGet's essential commands for everyday use. Everything can be done from Windows Terminal, PowerShell, or the classic Command Prompt.

To see general help and check that winget is available, simply type winget in a terminal. You'll see the version, the main commands, and the available options.

Install an app It's as simple as running:

Installation example: winget install Microsoft.VisualStudioCode

In this example, Microsoft Visual Studio Code This is the ID of the Visual Studio Code package in the WinGet repository. If the package is available in the Store, it can also be installed using its name, enclosing spaces in quotation marks.

To update all apps which WinGet recognizes in the system is used:

Update command: winget upgrade --all

If you only want to update a specific program, simply:

Update specific package: winget upgrade Microsoft.VisualStudioCode

Initially, WinGet only updated applications installed with it, but in recent versions it detects more installed software and can update it as long as there is a valid manifest in its origins.

In case you need uninstall a program, the command is:

Uninstallation command: winget uninstall Microsoft.VisualStudioCode

Uninstallation works with packages installed by WinGet as well as those that the system itself recognizes and has mapped in the WinGet catalog, although not all scenarios are supported.

To locate available software, you can use Winget searchFor example, to search for packages related to “notepad”:

Package Search: winget search notepad

Package ID: This command returns a list of matches showing the name, package ID, and source (Store, community repository, private repository, etc.). You will use this ID later in installation or update commands to ensure you install the exact package you want.

Finally, if what you need is to see Which applications does WinGet recognize as installed? In the system, you can use:

List of applications: winget list

Automate mass installations with YAML files

Mass automation of installations with WinGet

The true magic of WinGet This appears when you stop issuing commands one by one and start describing an entire working environment in a single configuration file. Instead of having a list of loose commands in a notepad or a makeshift script, you define your "ideal state" in YAML format and apply it with a single command.

These WinGet configuration files (often called configuration.winget or similar) follow a declarative structure: you don't write a sequence of imperative steps, but rather you indicate How do you want the machine to look?WinGet, relying on PowerShell Desired State Configuration (DSC)It is responsible for installing packages, activating Windows features, changing registry values, running scripts, and generally leaving the computer as described.

To use these types of configurations, you need version WinGet 1.6.2631 or later, since it is from that point onwards that support for integration with DSC 3.0 and the functionality of winget configure.

The great advantage is that the whole process can be launched in a neglectedYou run the corresponding command, accept the necessary configuration agreements, and let the system do its work. Meanwhile, you can grab a coffee while Windows installs all your tools, enables developer mode, configures policies, and applies scripts.

Furthermore, these configuration files can version and share easily in a GitHub repository, GitLab, OneDrive, or other secure medium. That makes preparing the development environment a real case of Infrastructure as Code (IAC) applied to the Windows desktop.

How does winget configure work and how does it integrate with DSC?

The winget configure command It is the entry point to the entire declarative part. It is responsible for reading the YAML file, validating its structure, downloading the necessary PowerShell modules, executing the DSC resources, and checking if the system enters the desired state.

Enable DSC: Before applying a configuration, you can explicitly enable DSC support in WinGet with:

winget configure --enable

The following is highly recommended validate the YAML file to ensure that the syntax is correct and matches the official scheme:

YAML validation: winget configure validate ruta\a\tu_configuracion.yaml

Keep in mind that YAML is very sensitive to the indentation and uses spaces (not tabs) to define nested blocks. An extra or missing space can break the configuration, which is why it's important to use an editor like Visual Studio Code with the Red Hat YAML extension and the WinGet JSON schema bound.

Apply settings: winget configure --file ruta\tu_configuracion.yaml --accept-configuration-agreements

At that moment the process comes into play ConfigurationRemotingServer.exeThe process interprets the YAML, downloads DSC modules from the PowerShell Gallery when needed, and begins evaluating assertions and resources. Some tasks run in parallel, others require elevated privileges (UAC), and still others depend on certain preconditions being met.

Dry test: winget configure test -f ruta\tu_configuracion.yaml --accept-configuration-agreements

In this mode, WinGet scans the system, compares it to the described state, and tells you whether or not the machine is in the expected configuration. When something doesn't match, you'll see messages like "System is not in the described configuration state." for those elements that differ.

By reapplying the YAML, WinGet only implements the changes necessary to correct the differences, which guarantees idempotencyYou can run the same configuration as many times as you want without fear of "breaking" anything by duplicating actions.

Remote: winget configure --accept-configuration-agreements --disable-interactivity -f https://ruta/a/tu/archivo.yaml

Centralized configuration It allows IT teams or administrators to publish a centralized configuration and for customers to simply run a command to apply it, which is very useful in mass deployments or on remote teams.

Structure and sections of a WinGet configuration file

WinGet configuration files They use YAML syntax, but also reference a public JSON schema that defines the valid structure. This facilitates automatic validation and autocompletion in compatible editors.

The naming convention for these files recommends using the extension. .winget, for example configuration.wingetIn projects with version control (Git, for example) it is good practice to place them under a directory .configso that the route is something like ./.config/configuration.wingetIf you need more than one file for different environment variants, they can all coexist in the same folder.

The first line of the file is usually a special comment indicating the scheme:

Schema header: # yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2

At that URL you'll find the latest version of the supported schema. It's a good idea to check for updates from time to time, as Microsoft may release new versions with additional capabilities.

Based on that comment, the root node is propertiesWithin it, a declaration is made configurationVersion (for example, 0.2.0) which you should keep updating as you change the file, and the two key sections appear: assertions y Resources.

The section assertions This section outlines the prerequisites or requirements that must be met for the configuration to be considered applicable to the current system. Resources List all the software, settings, scripts, and system elements that you want to install or configure.

Assertions: Pre-checks and dependencies

Automate installations with WinGet + YAML

Assertions are like “health tests” of the system before applying changes. Each assertion is a DSC resource that checks a condition; for example, that the machine is running a minimum version of Windows compatible with the configuration.

A classic example is the assertion of operating system versionYou can specify that the configuration requires, for example, Windows 10 19041 or higher, or a specific version of Windows 11. If the machine does not comply, the assertion returns false and any resources that depend on it are unavailable. dependsOn no se ejecutará.

Parallel evaluation: The assertions They do not have a mandatory sequential orderWinGet can evaluate them in parallel. This greatly simplifies large files, as you don't have to worry about the position of a new assertion as long as you declare the dependencies correctly.

If an assertion fails, Normally, the configuration will continue trying to execute other tasks that don't depend on it, taking the machine as far as possible in the desired direction. At the end of the process, you'll need to review the execution output to see what went well and what didn't.

Example of an error: Assert::OsVersion The configuration unit could not be found. Apply::DeveloperMode This configuration unit was not run because an assert failed or was false. Apply::WinGetPackage [vsPackage] This configuration unit was not run because an assert failed or was false.

In this scenario, the operating system version verification is not fulfilled, so resources such as activating developer mode or installing certain packages are not executed because they explicitly depend on that assertion.

Resources: packages, Windows settings, and scripts

The resources section This is where you declare everything you want WinGet and DSC to do on the machine: install applications, configure the system, launch PowerShell scripts, manage services, modify the registry, enable Windows roles, etc.

Each entry in the resource list indicates a resource format {NombreModulo}/{NombreRecursoDSC}, for example Microsoft.Windows.Settings/WindowsSettings o Microsoft.WinGet.DSC/WinGetPackageWinGet will handle installing the module from the PowerShell Gallery if you don't already have it available and to run that DSC resource.

In addition to the resource type, you can define a id unique, very useful when another resource needs to declare it in the field dependsOnThis way you build clear dependency chains: first I install Visual Studio, then I apply a workload configuration, etc.

Each resource includes a block directives with metadata such as the task description, if the use of preliminary versions of the module is allowed (allowPrerelease) And the securityContextIf the latter is established in elevatedWinGet will request elevated privileges at the start of the configuration and will run that resource with administrator permissions.

The block settings It contains the specific parameters that are passed to the DSC resource: for example, the ID of the package to install, the source (winget, msstore, private repository), the path to a .vsconfig file, a registry value to set, or a flag to enable Windows developer mode.

Through the field dependsOn Dependencies on other assertions or resources are indicated. If one of these elements fails, the dependent resource will automatically be marked as failed and will not be executed. This allows you to build complex configurations without having to manually order each step.

Practical example of a YAML file and use of ${WinGetConfigRoot}

A typical configuration file This can include everything from checking the Windows version to installing complete development environments and running custom scripts. Although the detailed syntax may vary, the general structure follows the pattern we've discussed.

A simple example of resource usage would be something like this (simplified): first, a minimal version of the operating system is checked, then packages such as Visual Studio Code or Google Chrome are installed, and finally, a PowerShell script is run to adjust the configuration or install additional modules.

In development environments, the combination of resources such as Microsoft.VisualStudio.DSC/VSComponents along with a file .vsconfigYou can specify in the YAML that, once Visual Studio is installed, the workloads, optional components, and recommendations you want to add to the IDE should be read from that file.

To avoid encoding absolute paths, WinGet offers the variable ${WinGetConfigRoot}, which represents the directory from which you are running winget configureFrom there you can build relative paths, for example to point to a configuration file within your project's repository, without depending on the drive or folder where it is cloned.

The logic would be something like this: the configuration specifies that VS Components reads its configuration file from '${WinGetConfigRoot}\..\.vsconfig'This means that the user must have previously placed that file in the appropriate relative path with respect to their working folder.

In addition to Visual Studio packages and components, you can define resources that manage windows services, environment variables, registry keys (for example, to enable periodic registry backups using EnablePeriodicBackup) or even specific processes that must be started or stopped as part of the configuration.

Logical organization of the configuration file

In large projectsConfiguration files can grow quite large, so it's worth thinking about how to organize them so they remain readable and easy to maintain, both for you and the rest of the team.

One way to organize them is by logical order of executionFirst, basic assertions and checks, then installation of base tools, later IDE configuration, and finally system settings and optional scripts. Although the engine doesn't depend on the physical order, it helps humans to see the sequence clearly.

Another approach is to group resources by level of risk or probability of errorThose steps that tend to fail (for example, installations that are heavily dependent on the network or specific credentials) can be placed at the beginning to detect problems early, before the user wastes time waiting for the rest of the process.

It is also very common to group by type of resourceWinGet packages, Windows settings, scripts, system roles and features, IDE configurations, etc. This pattern resembles how many software projects are structured and often feels quite natural to development teams.

Whatever the criteria, it's a good idea to accompany the file with a README in the open source or internal source repository, explaining the structure, design decisions and how the configuration is expected to be used (e.g., what parameters need to be adjusted before running it).

This additional documentation facilitates contributions from other developers and reduces the risk of someone breaking the configuration by trying to add a new resource without being clear about the existing dependencies.

Where to find DSC modules and configuration examples

For the resources to workWinGet and DSC require PowerShell modules to implement the corresponding logic. Microsoft offers a range of "inbox" resources for common tasks such as managing the registry, installing MSI files, starting services, or modifying environment variables.

Among these standard resources we find, for example: Environment (manages environment variables), msiPackage (install or uninstall MSI), registry (register keys and values), Script (executes PowerShell blocks), Service (Windows services control), Windows Feature (roles and characteristics), and WindowsProcess (start or stop processes).

Beyond Microsoft modules, in the PowerShell Gallery There are hundreds of DSC modules contributed by the community. You can apply the "DSC Resource" filter in the search category to focus on those containing resources for configuring desired states.

Caution with the Gallery: The PowerShell Gallery It is not audited by MicrosoftTherefore, it is crucial to review the origin, code, and security of any module before trusting it. Any script can contain dangerous actions if not carefully reviewed, especially when executed with elevated privileges.

Microsoft also maintains a repository with sample WinGet configuration files, accessible via the short link https://aka.ms/dsc.yamlwhere you can see reference configurations for different scenarios. There is also specific documentation on how to check reliability of a configuration before running it and general good security practices.

On the other hand, collaborative repositories are beginning to emerge in the community that collect silent installation patterns and confirmed commands for complex software (Autodesk, VPN solutions, Citrix clients, etc.), which can serve as inspiration when you have to deal with particularly "heavy" packages.

Package sources, private repositories, and enterprise control

By default, WinGet obtains the packages While these resources originate from the community and the Microsoft Store, organizations can go much further. It's possible to add additional REST repositories, such as a private catalog hosted on Azure or a solution like WinGetty deployed on-premises with Docker.

To add a new source, simply open PowerShell as administrator and run:

Add source: winget source add --name <nombre_repo> --arg <URL_repo>

You can complement this command with options such as –type (type of source, usually REST), –trust-level (none or trusted) and –accept-source-agreements to accept license agreements without manual interaction, something very common in automated scripts.

To verify the sources configured on the machine, use:

List sources: winget source list

In the business environment, WinGet plays an important role not only in software installation, but also in the security and complianceIt integrates with Microsoft Intune and the group policy infrastructure, allowing control over which sources are allowed, how the proxy is handled, whether specific certificates are accepted, etc.

For example, there is a directive called BypassCertificatePinningForMicrosoftStore This controls whether WinGet verifies that the Microsoft Store certificate matches one of the known certificates before establishing the connection. Disabling this pinning may be necessary in some environments with SSL inspection, but it also opens the door to attacks of the type Man-in-the-middleTherefore, it should only be done with full knowledge of the risks.

In addition, there are specific directives for Block the use of WinGet configuration files in an organization (for example, the EnableWindowsPackageManagerConfiguration group policy objects and their associated explanation) or to define which sources are allowed, which experimental features can be enabled, and how local manifest installations are handled.

WinGet-specific group policy templates (.admx and .adml) are included starting with Windows 11 and can also be downloaded from the project's GitHub repository, within the package DesktopAppInstallerPolicies.zipCopying those files to C:\Windows\PolicyDefinitions and its corresponding language subfolder, you can configure advanced policies from the group policy management console.

WinGet, the YAML and DSC files They transform the configuration of Windows systems into a declarative, repeatable, and controllable process, for both individual developers and large organizations. With a single Git versioned file and a couple of commands, you can set up a working environment in minutes that previously required hours of manual installation, and do so securely, traceably, and consistently across all your devices.