Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add how to install from MS Store using PowerShell #2925

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

JuryA
Copy link

@JuryA JuryA commented Feb 6, 2023


Adding new installation option to README.md: PowerShell script for installation from Microsoft Store utilizing CIM MDM.

Rationale

Manual installation of winget-cli from Microsoft Store is easy, fast, and straightforward, but automated installation (unattended) - although possible with a lot of pain, manual dependency hell solving and finally deployed without automatic updates, etc. - is (as described in the documentation) unusable and with a lot of problems.

I propose a solution that de facto mimics manual GUI attended installation from Microsoft Store leveraging CIM MDM + PowerShell. This solution is clean, quick, reusable, and can be used as part of deployment scripts/solutions including Microsoft DSC with fully functional automatic updates, and automatic dependency resolving (by MS Store) and should work no matter what changes (major/minor) will be in next versions.

(Inspired by an awesome script Install-CompanyPortal.ps1 from @adotcoop! Thank you!)

Microsoft Reviewers: Open in CodeFlow
Microsoft Reviewers: Open in CodeFlow

@JuryA JuryA requested a review from a team as a code owner February 6, 2023 02:25
@JuryA
Copy link
Author

JuryA commented Feb 6, 2023

@microsoft-github-policy-service agree

@github-actions
Copy link

github-actions bot commented Feb 6, 2023

@check-spelling-bot Report

🔴 Please review

See the 📂 files view or the 📜action log for details.

Unrecognized words (11)
adotcoop
bspmts
cim
companyportal
dmmap
mdm
NBLGGH
NNS
oma
sku
skuid
Previously acknowledged words that are now absent PWSTR Sku :arrow_right:
To accept ✔️ these unrecognized words as correct and remove the previously acknowledged and now absent words, run the following commands

... in a clone of the git@github.com:JuryA/winget-cli.git repository
on the JuryA-msstore-powershell-script branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.21/apply.pl' |
perl - 'https://github.com/microsoft/winget-cli/actions/runs/4099762440/attempts/1'
Available 📚 dictionaries could cover words not in the 📘 dictionary

This includes both expected items (405) from .github/actions/spelling/expect.txt and unrecognized words (11)

Dictionary Entries Covers
cspell:cpp/src/cpp.txt 30216 26
cspell:win32/src/win32.txt 53509 18
cspell:python/src/python/python-lib.txt 3873 7
cspell:php/php.txt 2597 6
cspell:java/java.txt 7642 5
cspell:python/src/python/python.txt 453 3
cspell:python/src/common/extra.txt 741 3
cspell:django/django.txt 859 3
cspell:typescript/typescript.txt 1211 2
cspell:npm/npm.txt 288 2

Consider adding them using (in .github/workflows/spelling3.yml):

      with:
        extra_dictionaries:
          cspell:cpp/src/cpp.txt
          cspell:win32/src/win32.txt
          cspell:python/src/python/python-lib.txt
          cspell:php/php.txt
          cspell:java/java.txt
          cspell:python/src/python/python.txt
          cspell:python/src/common/extra.txt
          cspell:django/django.txt
          cspell:typescript/typescript.txt
          cspell:npm/npm.txt

To stop checking additional dictionaries, add:

      with:
        check_extra_dictionaries: ''
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

@Trenly
Copy link
Contributor

Trenly commented Feb 6, 2023

I think this would probably be better as a separate file, so that it can be downloaded and run directly or called using Invoke-WebRequest. Including large blocks of script in README is often a deterrent for non-technical people to get the information they are looking for.

Another point to consider is that this relies on an API, which could be deprecated or updated at any point in time. Since documentation in the readme is often one of the lower priority items, it could be a while before anyone notices the install script is broken; If the script is in a separate file then it would be easier for users to create an issue against, since it would be clearer where the exact issue is.

$applicationId = "9NBLGGH4NNS1" # Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
$skuId = 0016

# Obtain packageFamilyName from applicationId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to obtain the packageFamilyName if it is already known?


# Obtain packageFamilyName from applicationId
$webpage = Invoke-WebRequest -UseBasicParsing -Uri "https://bspmts.mp.microsoft.com/v1/public/catalog/Retail/Products/$applicationId/applockerdata"
$packageFamilyName = ($webpage | ConvertFrom-JSON).packageFamilyName
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invoke-RestMethod will automatically parse the JSON rather than requiring an explicit call to ConvertFrom-JSON

$newInstance.CimInstanceProperties.Add($property)

# Set CIM session parameters
$flags = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a variable here when it's only used once later?


```PowerShell
$applicationId = "9NBLGGH4NNS1" # Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
$skuId = 0016
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a variable here when it's only used once later?

# Prepare CIM session object
$namespaceName = "root\cimv2\mdm\dmmap"
$session = New-CimSession
$omaUri = "./Vendor/MSFT/EnterpriseModernAppManagement/AppInstallation"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a variable here when it's only used once later?

@JuryA
Copy link
Author

JuryA commented Feb 6, 2023

I think this would probably be better as a separate file, so that it can be downloaded and run directly or called using Invoke-WebRequest. Including large blocks of script in README is often a deterrent for non-technical people to get the information they are looking for.

I agree - let me prepare a separate installation script. I suggest using simple install.ps1 - is that ok?

Another point to consider is that this relies on an API, which could be deprecated or updated at any point in time. Since documentation in the readme is often one of the lower priority items, it could be a while before anyone notices the install script is broken; If the script is in a separate file then it would be easier for users to create an issue against, since it would be clearer where the exact issue is.

Good point! Installation script in the dedicated file will have better maintainability.

Copy link
Contributor

@yao-msft yao-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Winget team is working on mechanisms to make sure AppInstaller/Winget is installed on a target machine as part of some future features. I'm not sure if using MDM would be our advertised way of doing that. We'll post back after the team has reached a conclusion. Thanks.

$skuId = 0016

# Obtain packageFamilyName from applicationId
$webpage = Invoke-WebRequest -UseBasicParsing -Uri "https://bspmts.mp.microsoft.com/v1/public/catalog/Retail/Products/$applicationId/applockerdata"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if talking directly to this endpoint would be advertised by us.

Copy link
Author

@JuryA JuryA Feb 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed... We can hardcode packageFamilyName directly as:

$packageFamilyName = 'Microsoft.Microsoft.DesktopAppInstaller_8wekyb3d8bbwe'

It's constant for this product across all versions so it's safe and faster. This construct is part of the gist which I simply reused as is... It's definitely better not to depend on any APIs, let alone unofficial ones.

@JuryA
Copy link
Author

JuryA commented Feb 6, 2023

Winget team is working on mechanisms to make sure AppInstaller/Winget is installed on a target machine as part of some future features. I'm not sure if using MDM would be our advertised way of doing that. We'll post back after the team has reached a conclusion. Thanks.

I understand your point but - for now - it's the cleanest way how to deploy AppInstaller/Winget correctly, with all dependencies and without any complicated hacks. This is a really critical thing. Maybe better - highly critical. I don't see anything bad to use MDM for this purpose - it's the turn of Microsoft to prepare a better way to do that. Users don't have time to sit and wait for a "better endpoint" - despite that this one is very well documented and used (in advanced Intune tasks). I'm still wondering why Microsoft Store doesn't have API for application deployment... Is that intended to avoid something? This problem is here for a long time and - to be honest - installation of Winget if - for correct installation - we need Winget ... this is the classic egg-chicken problem... But thank you very much for opening the discussion internally - really appreciate it. Meanwhile, I prepare changes proposed by @Trenly and wait for your statement.

@yao-msft
Copy link
Contributor

yao-msft commented Feb 7, 2023

Thanks for clarification. @denelon, would we add "installing through MDM from MSStore" script and referenced in Readme.md as temporary solution before our "make sure winget is installed" work is done?

@denelon
Copy link
Contributor

denelon commented Aug 31, 2023

@yao-msft,
Is the "Repair-WinGetPackageManager" cmdlet in the "Microsoft.WinGet.Client" PowerShell module fully functioning with the new changes to the UI.Xaml package?

I know we needed to add some clarity about what happens when a user installs the PowerShell module and WinGet isn't present in release notes here at GitHub. I was wondering if the other cmdlets could "check" to see if WinGet is installed and output some kind of error message with instructions to run the repair cmdlet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants