mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Add appx.absent state, remove duplicated code
This commit is contained in:
parent
d9b5ee5429
commit
608f7acfa2
4 changed files with 148 additions and 84 deletions
|
@ -4,7 +4,7 @@ Manage provisioned apps
|
|||
|
||||
Provisioned apps are part of the image and are installed for every user the
|
||||
first time the user logs on. Provisioned apps are also updated and sometimes
|
||||
reinstalled when the system is updated.
|
||||
reinstalled when the system is updated.
|
||||
|
||||
Apps removed with this module will remove the app for all users and deprovision
|
||||
the app. Deprovisioned apps will neither be installed for new users nor will
|
||||
|
@ -44,6 +44,7 @@ import fnmatch
|
|||
import logging
|
||||
|
||||
import salt.utils.platform
|
||||
import salt.utils.win_pwsh
|
||||
import salt.utils.win_reg
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -60,7 +61,11 @@ def __virtual__():
|
|||
Load only on Windows
|
||||
"""
|
||||
if not salt.utils.platform.is_windows():
|
||||
return False, "Module appx: module only works on Windows systems."
|
||||
return False, "Appx module: Only available on Windows systems"
|
||||
|
||||
pwsh_info = __salt__["cmd.shell_info"](shell="powershell", list_modules=False)
|
||||
if not pwsh_info["installed"]:
|
||||
return False, "Appx module: PowerShell not available"
|
||||
|
||||
return __virtualname__
|
||||
|
||||
|
@ -164,10 +169,10 @@ def list_(query=None, field="Name", include_store=False, frameworks=False, bundl
|
|||
if not field:
|
||||
cmd.append("Sort-Object Name")
|
||||
cmd.append("Select Name, Version, PackageFullName, PackageFamilyName, IsBundle, IsFramework")
|
||||
return __utils__["win_pwsh.run_dict"](" | ".join(cmd))
|
||||
return salt.utils.win_pwsh.run_dict(" | ".join(cmd))
|
||||
else:
|
||||
cmd.append(f"Sort-Object {field}")
|
||||
return _pkg_list(__utils__["win_pwsh.run_dict"](" | ".join(cmd)), field)
|
||||
return _pkg_list(salt.utils.win_pwsh.run_dict(" | ".join(cmd)), field)
|
||||
|
||||
|
||||
def remove(query=None, include_store=False, frameworks=False, deprovision_only=False):
|
||||
|
@ -176,8 +181,8 @@ def remove(query=None, include_store=False, frameworks=False, deprovision_only=F
|
|||
a bundle, the entire bundle will be removed.
|
||||
|
||||
This function removes the package for all users on the system. It also
|
||||
deprovisions the packages so that it isn't re-installed by later system
|
||||
updates. To only deprovision a package and not remove for all users, set
|
||||
deprovisions the package so that it isn't re-installed by later system
|
||||
updates. To only deprovision a package and not remove it for all users, set
|
||||
``deprovision_only=True``.
|
||||
|
||||
Args:
|
||||
|
@ -195,13 +200,13 @@ def remove(query=None, include_store=False, frameworks=False, deprovision_only=F
|
|||
``include_store=True``
|
||||
|
||||
.. note::
|
||||
Use the ``appx.get`` function to make sure your query is
|
||||
Use the ``appx.list`` function to make sure your query is
|
||||
returning what you expect. Then use the same query to remove
|
||||
those packages
|
||||
|
||||
include_store (bool):
|
||||
Include the Microsoft Store in the results of the query to be
|
||||
removed. Use this with caution. It difficult to reinstall the
|
||||
removed. Use this with caution. It is difficult to reinstall the
|
||||
Microsoft Store once it has been removed with this function. Default
|
||||
is ``False``
|
||||
|
||||
|
@ -210,7 +215,7 @@ def remove(query=None, include_store=False, frameworks=False, deprovision_only=F
|
|||
Default is ``False``
|
||||
|
||||
deprovision_only (bool):
|
||||
Deprovision the package. The package will be removed from the
|
||||
Only deprovision the package. The package will be removed from the
|
||||
current user and added to the list of deprovisioned packages. The
|
||||
package will not be re-installed in future system updates. New users
|
||||
of the system will not have the package installed. However, the
|
||||
|
@ -260,7 +265,7 @@ def remove(query=None, include_store=False, frameworks=False, deprovision_only=F
|
|||
else:
|
||||
log.debug("Removing package: %s", remove_name)
|
||||
remove_cmd = f"Remove-AppxPackage -AllUsers -Package {remove_name}"
|
||||
__utils__["win_pwsh.run_dict"](remove_cmd)
|
||||
salt.utils.win_pwsh.run_dict(remove_cmd)
|
||||
|
||||
if isinstance(packages, list):
|
||||
log.debug("Removing %s packages", len(packages))
|
||||
|
@ -314,6 +319,10 @@ def install(package):
|
|||
If a package installed using this function has been deprovisioned
|
||||
previously, the registry entry marking it as deprovisioned will be removed.
|
||||
|
||||
.. NOTE::
|
||||
There is no ``appx.present`` state. Instead, use the
|
||||
``dism.provisioned_package_installed`` state.
|
||||
|
||||
Args:
|
||||
|
||||
package (str):
|
||||
|
|
|
@ -15,6 +15,7 @@ import salt.utils.json
|
|||
import salt.utils.platform
|
||||
import salt.utils.powershell
|
||||
import salt.utils.versions
|
||||
import salt.utils.win_pwsh
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -51,41 +52,6 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def _pshell_json(cmd, cwd=None):
|
||||
"""
|
||||
Execute the desired powershell command and ensure that it returns data
|
||||
in JSON format and load that into python
|
||||
"""
|
||||
cmd = "Import-Module ServerManager; {}".format(cmd)
|
||||
if "convertto-json" not in cmd.lower():
|
||||
cmd = "{} | ConvertTo-Json".format(cmd)
|
||||
log.debug("PowerShell: %s", cmd)
|
||||
ret = __salt__["cmd.run_all"](cmd, shell="powershell", cwd=cwd)
|
||||
|
||||
if "pid" in ret:
|
||||
del ret["pid"]
|
||||
|
||||
if ret.get("stderr", ""):
|
||||
error = ret["stderr"].splitlines()[0]
|
||||
raise CommandExecutionError(error, info=ret)
|
||||
|
||||
if "retcode" not in ret or ret["retcode"] != 0:
|
||||
# run_all logs an error to log.error, fail hard back to the user
|
||||
raise CommandExecutionError(
|
||||
"Issue executing PowerShell {}".format(cmd), info=ret
|
||||
)
|
||||
|
||||
# Sometimes Powershell returns an empty string, which isn't valid JSON
|
||||
if ret["stdout"] == "":
|
||||
ret["stdout"] = "{}"
|
||||
|
||||
try:
|
||||
ret = salt.utils.json.loads(ret["stdout"], strict=False)
|
||||
except ValueError:
|
||||
raise CommandExecutionError("No JSON results from PowerShell", info=ret)
|
||||
return ret
|
||||
|
||||
|
||||
def list_available():
|
||||
"""
|
||||
List available features to install
|
||||
|
@ -129,7 +95,7 @@ def list_installed():
|
|||
"-WarningAction SilentlyContinue "
|
||||
"| Select DisplayName,Name,Installed"
|
||||
)
|
||||
features = _pshell_json(cmd)
|
||||
features = salt.utils.win_pwsh.run_dict(cmd)
|
||||
|
||||
ret = {}
|
||||
for entry in features:
|
||||
|
@ -230,7 +196,7 @@ def install(feature, recurse=False, restart=False, source=None, exclude=None):
|
|||
"-IncludeAllSubFeature" if recurse else "",
|
||||
"" if source is None else "-Source {}".format(source),
|
||||
)
|
||||
out = _pshell_json(cmd)
|
||||
out = salt.utils.win_pwsh.run_dict(cmd)
|
||||
|
||||
# Uninstall items in the exclude list
|
||||
# The Install-WindowsFeature command doesn't have the concept of an exclude
|
||||
|
@ -375,7 +341,7 @@ def remove(feature, remove_payload=False, restart=False):
|
|||
"-Restart" if restart else "",
|
||||
)
|
||||
try:
|
||||
out = _pshell_json(cmd)
|
||||
out = salt.utils.win_pwsh.run_dict(cmd)
|
||||
except CommandExecutionError as exc:
|
||||
if "ArgumentNotValid" in exc.message:
|
||||
raise CommandExecutionError("Invalid Feature Name", info=exc.info)
|
||||
|
|
|
@ -13,6 +13,7 @@ import logging
|
|||
import os
|
||||
|
||||
import salt.utils.platform
|
||||
import salt.utils.win_pwsh
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -35,41 +36,6 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def _pshell_json(cmd, cwd=None):
|
||||
"""
|
||||
Execute the desired powershell command and ensure that it returns data
|
||||
in JSON format and load that into python
|
||||
"""
|
||||
if "convertto-json" not in cmd.lower():
|
||||
cmd = "{} | ConvertTo-Json".format(cmd)
|
||||
log.debug("PowerShell: %s", cmd)
|
||||
ret = __salt__["cmd.run_all"](cmd, shell="powershell", cwd=cwd)
|
||||
|
||||
if "pid" in ret:
|
||||
del ret["pid"]
|
||||
|
||||
if ret.get("stderr", ""):
|
||||
error = ret["stderr"].splitlines()[0]
|
||||
raise CommandExecutionError(error, info=ret)
|
||||
|
||||
if "retcode" not in ret or ret["retcode"] != 0:
|
||||
# run_all logs an error to log.error, fail hard back to the user
|
||||
raise CommandExecutionError(
|
||||
"Issue executing PowerShell {}".format(cmd), info=ret
|
||||
)
|
||||
|
||||
# Sometimes Powershell returns an empty string, which isn't valid JSON
|
||||
if ret["stdout"] == "":
|
||||
ret["stdout"] = "{}"
|
||||
|
||||
try:
|
||||
ret = salt.utils.json.loads(ret["stdout"], strict=False)
|
||||
except ValueError:
|
||||
raise CommandExecutionError("No JSON results from PowerShell", info=ret)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def is_installed(name):
|
||||
"""
|
||||
Check if a specific KB is installed.
|
||||
|
@ -229,7 +195,7 @@ def list():
|
|||
salt '*' wusa.list
|
||||
"""
|
||||
kbs = []
|
||||
ret = _pshell_json("Get-HotFix | Select HotFixID")
|
||||
ret = salt.utils.win_pwsh.run_dict("Get-HotFix | Select HotFixID")
|
||||
for item in ret:
|
||||
kbs.append(item["HotFixID"])
|
||||
return kbs
|
||||
|
|
123
salt/states/win_appx.py
Normal file
123
salt/states/win_appx.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
"""
|
||||
Manage Microsoft Store apps on Windows. Removing an app with this modules will
|
||||
deprovision the app from the online Windows image.
|
||||
"""
|
||||
import fnmatch
|
||||
import logging
|
||||
|
||||
import salt.utils.data
|
||||
import salt.utils.platform
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
__virtualname__ = "appx"
|
||||
|
||||
|
||||
def __virtual__():
|
||||
"""
|
||||
Only work on Windows where the DISM module is available
|
||||
"""
|
||||
if not salt.utils.platform.is_windows():
|
||||
return False, "Appx state: Only available on Windows"
|
||||
|
||||
pwsh_info = __salt__["cmd.shell_info"](shell="powershell", list_modules=False)
|
||||
if not pwsh_info["installed"]:
|
||||
return False, "Appx state: PowerShell not available"
|
||||
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def absent(
|
||||
name, query, include_store=False, frameworks=False, deprovision_only=False
|
||||
):
|
||||
"""
|
||||
Removes Microsoft Store packages from the system. If the package is part of
|
||||
a bundle, the entire bundle will be removed.
|
||||
|
||||
This function removes the package for all users on the system. It also
|
||||
deprovisions the package so that it isn't re-installed by later system
|
||||
updates. To only deprovision a package and not remove it for all users, set
|
||||
``deprovision_only=True``.
|
||||
|
||||
Args:
|
||||
|
||||
query (str):
|
||||
The query string to use to select the packages to be removed. If the
|
||||
string matches multiple packages, they will all be removed. Here are
|
||||
some example strings:
|
||||
|
||||
- ``*teams*`` - Remove Microsoft Teams
|
||||
- ``*zune*`` - Remove Windows Media Player and ZuneVideo
|
||||
- ``*zuneMusic*`` - Only remove Windows Media Player
|
||||
- ``*xbox*`` - Remove all xbox packages, there are 5 by default
|
||||
- ``*`` - Remove everything but the Microsoft Store, unless
|
||||
``include_store=True``
|
||||
|
||||
.. note::
|
||||
Use the ``appx.list`` function to make sure your query is
|
||||
returning what you expect. Then use the same query to remove
|
||||
those packages
|
||||
|
||||
include_store (bool):
|
||||
Include the Microsoft Store in the results of the query to be
|
||||
removed. Use this with caution. It is difficult to reinstall the
|
||||
Microsoft Store once it has been removed with this function. Default
|
||||
is ``False``
|
||||
|
||||
frameworks (bool):
|
||||
Include frameworks in the results of the query to be removed.
|
||||
Default is ``False``
|
||||
|
||||
deprovision_only (bool):
|
||||
Only deprovision the package. The package will be removed from the
|
||||
current user and added to the list of deprovisioned packages. The
|
||||
package will not be re-installed in future system updates. New users
|
||||
of the system will not have the package installed. However, the
|
||||
package will still be installed for existing users. Default is
|
||||
``False``
|
||||
|
||||
Returns:
|
||||
bool: ``True`` if successful, ``None`` if no packages found
|
||||
|
||||
Raises:
|
||||
CommandExecutionError: On errors encountered removing the package
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt
|
||||
"""
|
||||
ret = {"name": name, "result": True, "comment": "", "changes": {}}
|
||||
|
||||
old = __salt__["appx.list"](include_store=include_store, frameworks=frameworks)
|
||||
matches = fnmatch.filter(old, query)
|
||||
if not matches:
|
||||
ret["comment"] = f"No apps found matching query: {query}"
|
||||
return ret
|
||||
|
||||
if __opts__["test"]:
|
||||
ret["changes"]["removed"] = matches
|
||||
ret["comment"] = "Matching apps will be removed"
|
||||
ret["result"] = None
|
||||
return ret
|
||||
|
||||
# Install the capability
|
||||
status = __salt__["appx.remove"](
|
||||
query,
|
||||
include_store=include_store,
|
||||
frameworks=frameworks,
|
||||
deprovision_only=deprovision_only
|
||||
)
|
||||
|
||||
if status is None:
|
||||
ret["comment"] = f"No apps found matching query: {query}"
|
||||
ret["result"] = False
|
||||
|
||||
new = __salt__["appx.list"](include_store=include_store, frameworks=frameworks)
|
||||
changes = salt.utils.data.compare_lists(old, new)
|
||||
|
||||
if changes:
|
||||
ret["comment"] = f"Removed apps matching query: {query}"
|
||||
ret["changes"] = changes
|
||||
|
||||
return ret
|
Loading…
Add table
Reference in a new issue