AA22-249A: #StopRansomware: Vice Society

This post was originally published on this site

Original release date: September 6, 2022

Summary

Actions to take today to mitigate cyber threats from ransomware:

• Prioritize and remediate known exploited vulnerabilities.
• Train users to recognize and report phishing attempts.
• Enable and enforce multifactor authentication.

Note: This joint Cybersecurity Advisory (CSA) is part of an ongoing #StopRansomware effort to publish advisories for network defenders that detail various ransomware variants and ransomware threat actors. These #StopRansomware advisories include recently and historically observed tactics, techniques, and procedures (TTPs) and indicators of compromise (IOCs) to help organizations protect against ransomware. Visit stopransomware.gov to see all #StopRansomware advisories and to learn more about other ransomware threats and no-cost resources.

The Federal Bureau of Investigation (FBI), the Cybersecurity and Infrastructure Security Agency (CISA), and the Multi-State Information Sharing and Analysis Center (MS-ISAC) are releasing this joint CSA to disseminate IOCs and TTPs associated with Vice Society actors identified through FBI investigations as recently as September 2022. The FBI, CISA, and the MS-ISAC have recently observed Vice Society actors disproportionately targeting the education sector with ransomware attacks.

Over the past several years, the education sector, especially kindergarten through twelfth grade (K-12) institutions, have been a frequent target of ransomware attacks. Impacts from these attacks have ranged from restricted access to networks and data, delayed exams, canceled school days, and unauthorized access to and theft of personal information regarding students and staff. The FBI, CISA, and the MS-ISAC anticipate attacks may increase as the 2022/2023 school year begins and criminal ransomware groups perceive opportunities for successful attacks. School districts with limited cybersecurity capabilities and constrained resources are often the most vulnerable; however, the opportunistic targeting often seen with cyber criminals can still put school districts with robust cybersecurity programs at risk. K-12 institutions may be seen as particularly lucrative targets due to the amount of sensitive student data accessible through school systems or their managed service providers.

The FBI, CISA, and the MS-ISAC encourage organizations to implement the recommendations in the Mitigations section of this CSA to reduce the likelihood and impact of ransomware incidents.

Technical Details

Note: This advisory uses the MITRE ATT&CK® for Enterprise framework, version 11. See MITRE ATT&CK for Enterprise for all referenced tactics and techniques.

Vice Society is an intrusion, exfiltration, and extortion hacking group that first appeared in summer 2021. Vice Society actors do not use a ransomware variant of unique origin. Instead, the actors have deployed versions of Hello Kitty/Five Hands and Zeppelin ransomware, but may deploy other variants in the future.

Vice Society actors likely obtain initial network access through compromised credentials by exploiting internet-facing applications [T1190]. Prior to deploying ransomware, the actors spend time exploring the network, identifying opportunities to increase accesses, and exfiltrating data [TA0010] for double extortion–a tactic whereby actors threaten to publicly release sensitive data unless a victim pays a ransom. Vice Society actors have been observed using a variety of tools, including SystemBC, PowerShell Empire, and Cobalt Strike to move laterally. They have also used “living off the land” techniques targeting the legitimate Windows Management Instrumentation (WMI) service [T1047] and tainting shared content [T1080].

Vice Society actors have been observed exploiting the PrintNightmare vulnerability (CVE-2021-1675 and CVE-2021-34527 ) to escalate privileges [T1068]. To maintain persistence, the criminal actors have been observed leveraging scheduled tasks [T1053], creating undocumented autostart Registry keys [T1547.001], and pointing legitimate services to their custom malicious dynamic link libraries (DLLs) through a tactic known as DLL side-loading [T1574.002]. Vice Society actors attempt to evade detection through masquerading their malware and tools as legitimate files [T1036], using process injection [T1055], and likely use evasion techniques to defeat automated dynamic analysis [T1497]. Vice Society actors have been observed escalating privileges, then gaining access to domain administrator accounts, and running scripts to change the passwords of victims’ network accounts to prevent the victim from remediating. 

Indicators of Compromise (IOCs)

Email Addresses

v-society.official@onionmail[.]org

ViceSociety@onionmail[.]org

OnionMail email accounts in the format of [First Name][Last Name]@onionmail[.]org

 

TOR Address

http://vsociethok6sbprvevl4dlwbqrzyhxcxaqpvcqt5belwvsuxaxsutyad[.]onion

 

IP Addresses for C2

Confidence Level

5.255.99[.]59

High Confidence

5.161.136[.]176

Medium Confidence

198.252.98[.]184

Medium Confidence

194.34.246[.]90

Low Confidence

See Table 1 for file hashes obtained from FBI incident response investigations in September 2022.

Table 1: File Hashes as of September 2022

MD5

SHA1

fb91e471cfa246beb9618e1689f1ae1d

a0ee0761602470e24bcea5f403e8d1e8bfa29832

 

3122ea585623531df2e860e7d0df0f25cce39b21

 

41dc0ba220f30c70aea019de214eccd650bc6f37

 

c9c2b6a5b930392b98f132f5395d54947391cb79

MITRE ATT&CK TECHNIQUES

Vice Society actors have used ATT&CK techniques, similar to Zeppelin techniques, listed in Table 2.

Table 2: Vice Society Actors ATT&CK Techniques for Enterprise

Initial Access

Technique Title

ID

Use

Exploit Public-Facing Application

T1190

Vice Society actors exploit vulnerabilities in an internet-facing systems to gain access to victims’ networks.

Valid Accounts

T1078

Vice Society actors obtain initial network access through compromised valid accounts.

Execution

Technique Title

ID

Use

Windows Management Instrumentation (WMI)

T1047

Vice Society actors leverage WMI as a means of “living off the land” to execute malicious commands. WMI is a native Windows administration feature.

Scheduled Task/Job

T1053

Vice Society have used malicious files that create component task schedule objects, which are often mean to register a specific task to autostart on system boot. This facilitates recurring execution of their code.

Persistence

Technique Title

ID

Use

Modify System Process

T1543.003

Vice Society actors encrypt Windows Operating functions to preserve compromised system functions.

Registry Run Keys/Startup Folder

T1547.001

Vice Society actors have employed malicious files that create an undocumented autostart Registry key to maintain persistence after boot/reboot.

DLL Side-Loading

T1574.002

Vice Society actors may directly side-load their payloads by planting their own DLL then invoking a legitimate application that executes the payload within that DLL. This serves as both a persistence mechanism and a means to masquerade actions under legitimate programs.

Privilege Escalation

Technique Title

ID

Use

Exploitation for Privilege Escalation

T1068

Vice Society actors have been observed exploiting PrintNightmare vulnerability (CVE-2021-1675 and CVE-2021-34527) to escalate privileges.

Defense Evasion

Technique Title

ID

Use

Masquerading

T1036

Vice Society actors may attempt to manipulate features of the files they drop in a victim’s environment to mask the files or make the files appear legitimate.

Process Injection

T1055

Vice Society artifacts have been analyzed to reveal the ability to inject code into legitimate processes for evading process-based defenses. This tactic has other potential impacts, including the ability to escalate privileges or gain additional accesses.

Sandbox Evasion

T1497

Vice Society actors may have included sleep techniques in their files to hinder common reverse engineering or dynamic analysis.

Lateral Movement

Technique Title

ID

Use

Taint Shared Content

T1080

Vice Society actors may deliver payloads to remote systems by adding content to shared storage locations such as network drives.

Exfiltration

Technique Title

ID

Use

Exfiltration

TA0010

Vice Society actors are known for double extortion, which is a second attempt to force a victim to pay by threatening to expose sensitive information if the victim does not pay a ransom.

Impact

Technique Title

ID

Use

Data Encrypted for Impact

T1486

Vice Society actors have encrypted data on target systems or on large numbers of systems in a network to interrupt availability to system and network resources.

Account Access Removal

T1531

Vice Society actors run a script to change passwords of victims’ email accounts.

 

 

Mitigations

The FBI and CISA recommend organizations, particularly the education sector, establish and maintain strong liaison relationships with the FBI Field Office in their region and their regional CISA Cybersecurity Advisor. The location and contact information for FBI Field Offices and CISA Regional Offices can be located at www.fbi.gov/contact-us/field-offices and www.cisa.gov/cisa-regions, respectively. Through these partnerships, the FBI and CISA can assist with identifying vulnerabilities to academia and mitigating potential threat activity. The FBI and CISA further recommend that academic entities review and, if needed, update incident response and communication plans that list actions an organization will take if impacted by a cyber incident.

The FBI, CISA, and the MS-ISAC recommend network defenders apply the following mitigations to limit potential adversarial use of common system and network discovery techniques and to reduce the risk of compromise by Vice Society actors:

Preparing for Cyber Incidents

  • Maintain offline backups of data, and regularly maintain backup and restoration.  By instituting this practice, the organization ensures they will not be severely interrupted, and/or only have irretrievable data.
  • Ensure all backup data is encrypted, immutable (i.e., cannot be altered or deleted), and covers the entire organization’s data infrastructure. Ensure your backup data is not already infected.
  • Review the security posture of third-party vendors and those interconnected with your organization. Ensure all connections between third-party vendors and outside software or hardware are monitored and reviewed for suspicious activity.
  • Implement listing policies for applications and remote access that only allow systems to execute known and permitted programs under an established security policy.
  • Document and monitor external remote connections. Organizations should document approved solutions for remote management and maintenance, and immediately investigate if an unapproved solution is installed on a workstation.
  • Implement a recovery plan to maintain and retain multiple copies of sensitive or proprietary data and servers in a physically separate, segmented, and secure location (i.e., hard drive, storage device, the cloud).

Identity and Access Management

  • Require all accounts with password logins (e.g., service account, admin accounts, and domain admin accounts) to comply with National Institute of Standards and Technology (NIST) standards for developing and managing password policies.
    • Use longer passwords consisting of at least 8 characters and no more than 64 characters in length;
    • Store passwords in hashed format using industry-recognized password managers;
    • Add password user “salts” to shared login credentials;
    • Avoid reusing passwords;
    • Implement multiple failed login attempt account lockouts;
    • Disable password “hints”;
    • Refrain from requiring password changes more frequently than once per year unless a password is known or suspected to be compromised.
      Note: NIST guidance suggests favoring longer passwords instead of requiring regular and frequent password resets. Frequent password resets are more likely to result in users developing password “patterns” cyber criminals can easily decipher.
    • Require administrator credentials to install software.
  • Require phishing-resistant multifactor authentication for all services to the extent possible, particularly for webmail, virtual private networks, and accounts that access critical systems.
  • Review domain controllers, servers, workstations, and active directories for new and/or unrecognized accounts.
  • Audit user accounts with administrative privileges and configure access controls according to the principle of least privilege. 
  • Implement time-based access for accounts set at the admin level and higher. For example, the Just-in-Time (JIT) access method provisions privileged access when needed and can support enforcement of the principle of least privilege (as well as the Zero Trust model). This is a process where a network-wide policy is set in place to automatically disable admin accounts at the Active Directory level when the account is not in direct need. Individual users may submit their requests through an automated process that grants them access to a specified system for a set timeframe when they need to support the completion of a certain task.

Protective Controls and Architecture

  • Segment networks to prevent the spread of ransomware. Network segmentation can help prevent the spread of ransomware by controlling traffic flows between—and access to—various subnetworks and by restricting adversary lateral movement.
  • Identify, detect, and investigate abnormal activity and potential traversal of the indicated ransomware with a networking monitoring tool. To aid in detecting the ransomware, implement a tool that logs and reports all network traffic, including lateral movement activity on a network. Endpoint detection and response (EDR) tools are particularly useful for detecting lateral connections as they have insight into common and uncommon network connections for each host.
  • Install, regularly update, and enable real time detection for antivirus software on all hosts.
  • Secure and closely monitor remote desktop protocol (RDP) use.
    • Limit access to resources over internal networks, especially by restricting RDP and using virtual desktop infrastructure. If RDP is deemed operationally necessary, restrict the originating sources and require MFA to mitigate credential theft and reuse. If RDP must be available externally, use a VPN, virtual desktop infrastructure, or other means to authenticate and secure the connection before allowing RDP to connect to internal devices. Monitor remote access/RDP logs, enforce account lockouts after a specified number of attempts to block brute force campaigns, log RDP login attempts, and disable unused remote access/RDP ports.

Vulnerability and Configuration Management

  • Keep all operating systems, software, and firmware up to date. Timely patching is one of the most efficient and cost-effective steps an organization can take to minimize its exposure to cybersecurity threats. Organizations should prioritize patching of vulnerabilities on CISA’s Known Exploited Vulnerabilities catalog.
  • Disable unused ports.
  • Consider adding an email banner to emails received from outside your organization.
  • Disable hyperlinks in received emails.
  • Disable command-line and scripting activities and permissions. Privilege escalation and lateral movement often depend on software utilities running from the command line. If threat actors are not able to run these tools, they will have difficulty escalating privileges and/or moving laterally.
  • Ensure devices are properly configured and that security features are enabled.
  • Disable ports and protocols that are not being used for a business purpose (e.g., RDP Transmission Control Protocol Port 3389).
  • Restrict Server Message Block (SMB) Protocol within the network to only access servers that are necessary, and remove or disable outdated versions of SMB (i.e., SMB version 1). Threat actors use SMB to propagate malware across organizations.

REFERENCES

REPORTING

The FBI is seeking any information that can be shared, to include boundary logs showing communication to and from foreign IP addresses, a sample ransom note, communications with Vice Society actors, Bitcoin wallet information, decryptor files, and/or a benign sample of an encrypted file.

The FBI, CISA, and the MS-ISAC strongly discourage paying ransom as payment does not guarantee victim files will be recovered. Furthermore, payment may also embolden adversaries to target additional organizations, encourage other criminal actors to engage in the distribution of ransomware, and/or fund illicit activities. Regardless of whether you or your organization have decided to pay the ransom, the FBI and CISA urge you to promptly report ransomware incidents to a local FBI Field Office, or to CISA at report@cisa.gov or (888) 282-0870. SLTT government entities can also report to the MS-ISAC (SOC@cisecurity.org or 866-787-4722).

DISCLAIMER

The information in this report is being provided “as is” for informational purposes only. The FBI, CISA, and the MS-ISAC do not endorse any commercial product or service, including any subjects of analysis. Any reference to specific commercial products, processes, or services by service mark, trademark, manufacturer, or otherwise, does not constitute or imply endorsement, recommendation, or favoring by the FBI, CISA, or the MS-ISAC.

Revisions

  • September 6, 2022: Initial Version

This product is provided subject to this Notification and this Privacy & Use policy.

PowerShell Extension for Visual Studio Code August 2022 Update

This post was originally published on this site

We are excited to announce that the August update to the PowerShell Extension for Visual Studio Code is now available on the extension marketplace.

This release adds a walkthrough experience for getting started with PowerShell in VS Code, more regression tests, a major LSP client library update, and includes a number of bug fixes!

Updates in the August Release

Note that these updates all shipped in our PowerShell Preview Extension for VS Code before shipping in our stable channel.

Some highlights of August releases:

For the full list of changes please refer to our changelog.

Getting Started Walkthrough

As a part of this release we have introduced a getting started experience for PowerShell in VS Code. This experience was designed through a series of customer surveys and interviews conducted by our summer intern. The walkthrough can be accessed on the Getting Started page in VS Code, or through the command pallette.

image

We look forward to getting more feedback on this walkthrough and learning how we can improve it.

LSP Client Library Update

This release also includes a major update to our LSP client library dependency, vscode-languageclient. The extension uses this library to start, connect, and communicate with the LSP server, PowerShell Editor Services.

By incorporating this update in vscode-powershell #4128 we were able to prevent a number of race conditions that could be encountered during startup, as the latest version of this library allows us to register our notification and request handlers before starting the server. The lifecycle management code was also given some much needed attention, and so startup and shut-down is now a more stable experience.

Please note that due to an upstream change, there is now a second notification when the server is stopped. We are working with the upstream team to de-duplicate this popup, and are also contemplating enabling a configurable auto-restart of the server.

Getting Support and Giving Feedback

While we hope the new implementation provides a much better user experience, there are bound to be issues. Please let us know if you run into anything.

If you encounter any issues with the PowerShell Extension in Visual Studio Code or have feature requests, the best place to get support is through our GitHub repository.

Sydney Smith and Andy Jordan PowerShell Team

The post PowerShell Extension for Visual Studio Code August 2022 Update appeared first on PowerShell Team.

Archive Module 2.0 Preview 2

This post was originally published on this site

We are excited to announce that the second preview of a rewrite of Microsoft.PowerShell.Archive, the module that lets you create and extract archives, is now available on the PowerShell Gallery.

This release is the second preview release of a rewrite of the module and is not feature complete. Please note that this release is only compatible with PowerShell 7.3.0-preview5 and up. For more information on what is proposed in this rewrite, or to give feedback on the design please refer to the RFC.

Installing the module

Please note that this release will only work with PowerShell 7.3.0-preview5 and up. Find and install the latest preview version of PowerShell here.

To install the Microsoft.PowerShell.Archive 2.0 preview 2 using PowerShellGet 2.2.5 run the following command

Install-Module Microsoft.PowerShell.Archive -AllowPrerelease

To install the Microsoft.PowerShell.Archive 2.0 preview 2 using PowerShellGet 3.0 previews run the following command

Install-PSResource Microsoft.PowerShell.Archive -Prerelease

Features of the release

This release contains the rewrite of the Expand-Archive cmdlet.

This cmdlet contains two parameter sets

Expand-Archive [-Path] <string> [[-DestinationPath] <string>] [-WriteMode {Create | Update | Overwrite}] [-PassThru] [-Filter <string[]>] [-WhatIf] [-Confirm] [<CommonParameters>]

Expand-Archive [-LiteralPath <string>] [-DestinationPath] <string>]  [-WriteMode {Create | Update | Overwrite}] [-PassThru] [-Filter <string[]>] [-WhatIf] [-Confirm] [<CommonParameters>]

Some features to note of this release

  • Added -WriteMode parameter to Expand-Archive
  • Added support for zip64
  • Fixed a bug where the entry names of files in a directory would not be correct when compressing an archive

Features of the next release

The next release will focus on additional features of the module. To track the progress of this release, look at this project in our GitHub repository.

Giving Feedback and Getting support

While we hope the new implementation provides a much better user experience, there are bound to be issues. Please let us know if you run into anything.

If you encounter any issues with the module or have feature requests, the best place to get support is through our GitHub repository.

Sydney

PowerShell Team

The post Archive Module 2.0 Preview 2 appeared first on PowerShell Team.

Announcing the release of Get-WhatsNew

This post was originally published on this site

We are pleased to announce the release of Get-WhatsNew, a cmdlet that delivers feature
information about new versions of PowerShell to your local terminal experience.

Stay informed with Get-WhatsNew

Between General Availability (GA), Long-Term Servicing (LTS), and Previews release, PowerShell ships
updates several times a year. At this pace, PowerShell users may become unaware of new features and
changes that improve automation, performance and security. Today, this information is provided through
release notes and the PowerShell
GitHub repository. Get-WhatsNew enables you to:

  • Learn about new features that enable new solutions
  • Get version-specific information to make upgrade decisions
  • View the information in disconnected scenarios (data ships with module)
  • View the information in Windows PowerShell 5.1 and higher
  • Get information for a single random feature for a message-of-the-day (MOTD) experience
  • Quickly open the release notes on the web version using the Online option

Installing WhatsNew

The Get-WhatsNew cmdlet ships in the
Microsoft.PowerShell.WhatsNew
module, which can be installed from the PowerShell Gallery.

Requirements:

  • Microsoft.PowerShell.WhatsNew is a cross platform module that requires Windows PowerShell 5.1
    or higher

To install Microsoft.PowerShell.WhatsNew:

Install-Module -Name Microsoft.PowerShell.WhatsNew

To install Microsoft.PowerShell.WhatsNew using the new
PowerShellGet.v3

Install-PSResource -Name Microsoft.PowerShell.WhatsNew

Using Get-WhatsNew

Get-WhatsNew displays PowerShell features by version. The cmdlet includes
complete help with several examples. Here are some highlights to get started:

To get the complete help information for Get-WhatsNew

Get-Help Get-WhatsNew -Full

To display the release notes for the version of PowerShell in which the cmdlet is running.

Get-WhatsNew

To display the release notes for PowerShell 7.2 regardless of which version the cmdlet is running.

Get-WhatsNew -Version 7.2

To display one randomly selected section of the release notes per version of PowerShell selected.
Add this to your profile to receive a Message-Of-The-Day (motd).

Get-WhatsNew -Daily -Version 6.0, 7.0, 7.1, 7.2

Future plans

We value your ideas and feedback and hope you will give WhatsNew a try. Stop by our
GitHub repository and let us know of any issues you find
or features you would like added.

The post Announcing the release of Get-WhatsNew appeared first on PowerShell Team.

PowerShell Extension for Visual Studio Code July 2022 Update

This post was originally published on this site

We are excited to announce that the July update to the PowerShell Extension for Visual Studio Code

is now available on the extension marketplace.

This release renames the “PowerShell Integrated Console” to the “PowerShell Extension Terminal” to align with VS Code’s verbiage, adds many regression tests, and includes a number of bug fixes!

Updates in the July release

Note that these updates all shipped in our PowerShell Preview Extension
for VS Code before shipping in our stable channel.

Some highlights of July release:

For the full list of changes please refer to our changelog.

Improvements to our tests

We are currently building out regression tests for the extension to cover everything we broke and subsequently fixed during the major rewrite earlier this year. We have a strong focus on quality,
and want to ensure we continue to deliver a production-ready, high-quality extension to you, our users.
We have made a large investment in improving the extension so we want to be able to
confidently continue to iterate on this project without inadvertently impacting the performance, stability, or feature set.

We’ve begun this work already, and are tracking in our GitHub repository.

Some highlights of this work so far include:

Getting support and giving feedback

While we hope the new implementation provides a much better user experience, there are bound to be issues. Please let us know if you run into anything.

If you encounter any issues with the PowerShell Extension in Visual Studio Code or have feature requests, the best place to get support is through our GitHub repository.

Sydney
PowerShell Team

The post PowerShell Extension for Visual Studio Code July 2022 Update appeared first on PowerShell Team.

PowerShellGet 3.0 Preview 16

This post was originally published on this site

We are excited to announce that an update to our preview of PowerShellGet 3.0 is now available on the PowerShell Gallery!

This release includes ScriptFileInfo cmdlets, Update-ModuleManifest support, and an AuthenticodeCheck for Install.

How to Install PowerShellGet 3.0 Preview 16

Prerequisites

Please note that this preview release of PowerShellGet 3.0 does not support PowerShell 7.0, 7.1 or 7.2-preview1.

This is a temporary issue due to a dependency and should be resolved in future releases. This release does support Windows PowerShell 5.1, PowerShell 7.2 and 7.3-previews.

Please ensure that you have the latest (non-prerelease) version of PowerShellGet and PackageManagement installed. To check the version you currently have installed run the command Get-InstalledModule PowerShellGet, PackageManagement

The latest version of PowerShellGet is 2.2.5, and the latest version of PackageManagement is 1.4.7. To install the latest versions of these modules run the following: Install-Module PowerShellGet -Force -AllowClobber

Installing the Preview

To install this preview release side-by-side with your existing PowerShellGet version, open any PowerShell console and run: Install-Module PowerShellGet -Force -AllowPrerelease

What to expect in this update

This update adds a number of cmdlets related to package metadata as well as a new -AuthenticodeCheck parameter for Install-PSResourceSave-PSResourceUpdate-PSResource.

Features of this release

  • New-ScriptFileInfo
  • Update-ScriptFileInfo
  • Test-ScriptFileInfo
  • Update-ModuleManifest
  • Implementation of Authenticode validation via -AuthenticodeCheck for Install-PSResource

Bug Fixes

  • Bug fix for installing modules with manifests that contain dynamic script blocks

Using -AuthenticodeCheck

In PowerShellGetv2 we performed what was called a PublisherCheck, for the sake of clarity in PowerShellGet 3.0 we have renamed this check to be specific to the check being made. The parameter is now opt-in and is called -AuthenticodeCheck. This check validates signed files and catalog files on Windows.

Features to Expect in Coming Preview Releases

This module is not yet complete. The focus for our next preview release is improving the performance of find/install. For the full list of issues for our next preview release please refer to our GitHub project.

How to Track the Development of this Module

GitHub is the best place to track the bugs/feature requests related to this module. We have used a combination of projects and labels on our GitHub repo to track issues for this upcoming release. We are using the label Resolved-3.0 to label issues that we plan to release at some point before we release the module as GA (generally available).

To track issues/features for the next release, please refer to this GitHub project.

Timeline/Road Map

Expect to see preview releases as new functionality is added and bug fixes are made. User feedback will help us determine when we can have a Release Candidate version of the module which will be supported to be used in production. Based on user feedback, if we believe the 3.0 release is complete, then we will publish a 3.0 version of the module as Generally Available. Since these milestones are driven by quality, rather than date, we cannot offer an exact timeline at this point.

How to Give feedback and Get Support

We cannot overstate how critical user feedback is at this stage in the development of the module. Feedback from preview releases help inform design decisions without incurring a breaking change once generally available and used in production.

In order to help us to make key decisions around the behavior of the module please give us feedback by opening issues in our GitHub repository.

Sydney Smith

PowerShell Team

The post PowerShellGet 3.0 Preview 16 appeared first on PowerShell Team.

Archive Module 2.0 Preview 1

This post was originally published on this site

We are excited to announce that the first preview of a rewrite of Microsoft.PowerShell.Archive, the module that lets you create and extract archives, is now available on the PowerShell Gallery.

This release is the first preview release of a rewrite of the module and is not feature complete. Please note that this release only contains the Compress-Archive cmdlet and is only compatible with PowerShell 7.3.0-preview5 and up. For more information on what is proposed in this rewrite, or to give feedback on the design please refer to the RFC.

Goals of the 2.0 release

The goal for the Archive module is to build a high-performing and maintainable module that offers high utility and works cross-platform (especially with regard to file paths).

Currently, the archive module has a number of limitations. The module supports the zip32 format only. .NET 7 is planned to support the tar archive format, so there is an opportunity to support an additional archive format by taking advantage of these new APIs. Meanwhile, this opportunity can be leveraged not only to add tar support, but to rewrite the entire module in C# and address existing usability issues.

The module has limited performance compared to other archive software. Although performance is dictated by the .NET APIs, a rewrite of the module in C# can reduce the overhead from script modules and address performance issues to an extent.

The module has limited cross-platform support because archive entries are written in an OS-specific way due to different characters being used as directory separators in different OSs. This makes it difficult for Unix-based OS users to use archives compressed on a Windows computer or vice versa. The rewrite of the module can solve this problem by normalizing all paths to use Unix directory separators (the ‘/’ character) across all platforms.

There are a number of issues in regard to using wildcard characters in paths. For example, in some cases, wildcard characters are not interpreted literally, which makes it difficult to use paths containing wildcard characters to specify the location of the archive.

In some circumstances, error reporting can be improved and more descriptive. Compatibility with other archive software can also be improved as there are cases where an archive program may not recognize an archive produced by this module as valid. Interactions with other parts of PowerShell, such as the job system, advanced functions, and common parameters can be further improved, so that users can have a seamless experience when using the module.

Additionally, .NET has supported creating large archives and compressing large files, but the archive module has lacked support for this. It makes sense to support archive formats that are supported by .NET, such as zip64 and tar, and to provide options that .NET also provides. The next version of the archive module, Microsoft.PowerShell.Archive v2.0.0, plans on resolving these limitations and usability issues.

Installing the module

Please note that this release will only work with PowerShell 7.3.0-preview5 and up. Find and install the latest preview version of PowerShell here.

To install the Microsoft.PowerShell.Archive 2.0 preview 1 run the following command

Install-Module Microsoft.PowerShell.Archive -AllowPrerelease

Features of the release

This release is not feature complete and only contains the rewrite of the Compress-Archive cmdlet.

This cmdlet contains two parameter sets

    Compress-Archive [-Path] <string[]> [-DestinationPath] <string> [-WriteMode {Create | Update | Overwrite}] [-PassThru] [-CompressionLevel {Optimal | Fastest |
    NoCompression | SmallestSize}] [-Format {zip}] [-WhatIf] [-Confirm] [<CommonParameters>]

    Compress-Archive [-LiteralPath] <string[]> [-DestinationPath] <string> [-WriteMode {Create | Update | Overwrite}] [-PassThru] [-CompressionLevel {Optimal |
    Fastest | NoCompression | SmallestSize}] [-Format {zip}] [-WhatIf] [-Confirm] [<CommonParameters>]

Some features to note of this release

  • Support for zip32 & zip64
  • Preserves relative path structure (when relative paths are specified)
  • Improved performance from previous versions of the cmdlet
  • New -WriteMode support in case of destination path collisions

Features of the next release

The next release will focus in the Expand-Archive cmdlet. To track the progress of this release, look at this project in our GitHub repository.

Giving Feedback and Getting support

While we hope the new implementation provides a much better user experience, there are bound to be issues. Please let us know if you run into anything.

If you encounter any issues with the PowerShell Extension in Visual Studio Code or have feature requests, the best place to get support is through our GitHub repository.

Abdullah Yousuf

Sydney Smith

The post Archive Module 2.0 Preview 1 appeared first on PowerShell Team.

PowerShell Extension for Visual Studio Code June 2022 Update

This post was originally published on this site

We are excited to announce that an update to the PowerShell Extension for Visual Studio Code is now available on the extension marketplace.

This release fixes a number of issues related to IntelliSense and enables updates to PowerShell Editor Services (the engine of the VS Code extension) for other clients, such as Emacs and Vim with LSP plug-ins.

Updates from the June releases

Some highlights of June releases:

For the full list of changes please refer to our changelog.

IntelliSense improvements

In this release we have made a number of improvements to IntelliSense (the general term for various code editing features including: code completion, parameter info, tool-tip hovers, etc.).

Mark completion request handler as serial

Previously, if you typed Get- very quickly, you’d get no results at all. This stops completion from being cancelled whenever a DidChangeTextDocument notification is sent. This also lets completion cancel some other expensive requests like code lenses, which means everything is both speedier and less flaky.

Fix tasks never completing if cancelled quickly

We were exiting SynchronousTask.ExecuteSynchronously early if the cancellation was already requested. This was not setting our state correctly, causing the caller to just fall off without warning. This made some things like finally and using disposals never happen, which led to a lot of erratic behavior!

Make alias discovery possible on idle

The use of Runspace.SessionStateProxy while the pipeline thread is busy (even if you are on the pipeline thread) results in an exception. By fixing this we can also cancel GetAliasesAsync (a slow operation) when needed, and do it while idling instead of startup.

Add “space” character to completion trigger characters

Adding the “space” character to completion triggers lets us automatically give completion results for parameter values. This also required building in some support for marking completion results as “incomplete”. This means that the client will not cache results when standard identifier characters are typed.

Mainly, this solves the scenario where you type space, get file completion results, and then type -. You expect to then get parameter names, but without marking “incomplete”, it will instead filter the file results.

Also fixed a scenario where you type $script: and on typing the : it would not update to available script scope variables.

Make completion requests cancellable

Now any completion results from custom argument completers can be cancelled if they take too long and the user keeps typing. In non-remote runspaces, we can also use it as a completion source that we can cancel by way of issuing a pipeline stop request.

What’s next for the project?

We are currently building out regression tests for the extension to cover everything we broke and subsequently fixed during the major rewrite earlier this year. We have a strong focus on quality, and want to ensure we continue to deliver a production-ready, high-quality extension to you, our users. We have made a large investment in improving the extension so we want to be able to confidently continue to iterate on this project without inadvertently impacting the performance, stability, or feature set. We’ve begun this work already, and are tracking in our GitHub repository.

Getting support and giving feedback

While we hope the new implementation provides a much better user experience, there are bound to be issues. Please let us know if you run into anything.

If you encounter any issues with the PowerShell Extension in Visual Studio Code or have feature requests, the best place to get support is through our GitHub repository.

Sydney

PowerShell Team

The post PowerShell Extension for Visual Studio Code June 2022 Update appeared first on PowerShell Team.

Hosting PowerShell in a Python script

This post was originally published on this site

Yes Virginia, languages other than PowerShell do exist.

I was working with a partner group here at Microsoft and they explained that they wanted to parse PowerShell scripts from Python.
Their natural approach was to invoke the PowerShell executable and construct a command-line that did what they needed.
I thought there might be a better way as creating a new PowerShell process each time is expensive, so I started doing a bit of research to see something could be done.
I’ve been aware of IronPython (Python that tightly integrates .NET) for a long time, and
we met with Jim Hugunin shortly after he arrived at Microsoft and PowerShell was just getting underway,
but the group is using cPython so I went hunting for Python modules that host .NET and found the pythonnet module.

The pythonnet package gives Python developers extremely easy access to the dotnet runtime from Python.
I thought this package might be the key for accessing PowerShell,
after some investigation I found that it has exactly what I needed to host PowerShell in a Python script.

The guts

I needed to figure out a way to load the PowerShell engine.
First, there are a couple of requirements to make this all work.
Dotnet has to be available, as does PowerShell and pythonnet provides a way to specify where to look for dotnet.
Setting the environment variable DOTNET_ROOT to the install location,
enables pythonnet a way find the assemblies and other support files to host .NET.

import os
os.environ["DOTNET_ROOT"] = "/root/.dotnet"

Now that we know where dotnet is, we need to load up the CLR and set up the runtime configuration.
The runtime configuration describes various aspects of how we’ll run.
We can create a very simple pspython.runtimeconfig.json

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    }
  }
}

The combination of the DOTNET_ROOT and the runtime configuration enables
loading the CLR with the get_coreclr and set_runtime functions.

# load up the clr
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("/root/pspython.runtimeconfig.json")
set_runtime(rt)

Now that we have the CLR loaded, we need to load the PowerShell engine.
This was a little non-obvious.
Initially, I just attempted to load System.Management.Automation.dll but that failed
due to a strong name validation error.
However, If I loaded Microsoft.Management.Infrastructure.dll first, I can avoid that error.
I’m not yet sure about why I need to load this assembly first, that’s still something
I need to determine.

import clr
import sys
import System
from System import Environment
from System import Reflection

psHome = r'/opt/microsoft/powershell/7/'

mmi = psHome + r'Microsoft.Management.Infrastructure.dll'
clr.AddReference(mmi)
from Microsoft.Management.Infrastructure import *

full_filename = psHome + r'System.Management.Automation.dll'
clr.AddReference(full_filename)
from System.Management.Automation import *
from System.Management.Automation.Language import Parser

Eventually I would like to make the locations of dotnet and PSHOME configurable,
but for the moment, I have what I need.

Now that the PowerShell engine is available to me,
I created a couple of helper functions to make handling the results easier from Python.
I also created a PowerShell object (PowerShell.Create()) that I will use in some of my functions.

ps = PowerShell.Create()
def PsRunScript(script):
    ps.Commands.Clear()
    ps.Commands.AddScript(script)
    result = ps.Invoke()
    rlist = []
    for r in result:
        rlist.append(r)
    return rlist

class ParseResult:
    def __init__(self, scriptDefinition, tupleResult):
        self.ScriptDefinition = scriptDefinition
        self.Ast = tupleResult[0]
        self.Tokens = tupleResult[1]
        self.Errors = tupleResult[2]

    def PrintAst(self):
        print(self.ast.Extent.Text)

    def PrintErrors(self):
        for e in self.Errors:
            print(str(e))

    def PrintTokens(self):
        for t in self.Tokens:
            print(str(t))

    def FindAst(self, astname):
        Func = getattr(System, "Func`2")
        func = Func[System.Management.Automation.Language.Ast, bool](lambda a : type(a).__name__ == astname)
        asts = self.Ast.FindAll(func, True)
        return asts

def ParseScript(scriptDefinition):
    token = None
    error = None
    # this returns a tuple of ast, tokens, and errors rather than the c# out parameter
    ast = Parser.ParseInput(scriptDefinition, token, error)
    # ParseResult will bundle the 3 parts into something more easily consumed.
    pr = ParseResult(scriptDefinition, ast)
    return pr

def ParseFile(filePath):
    token = None
    error = None
    # this returns a tuple of ast, tokens, and errors rather than the c# out parameter
    ast = Parser.ParseFile(filePath, token, error)
    # ParseResult will bundle the 3 parts into something more easily consumed.
    pr = ParseResult(filePath, ast)
    return pr

def PrintResults(result):
    for r in result:
        print(r)

I really wanted to mimic the PowerShell AST methods with some more friendly Python functions.
To create the FindAst() function, I needed to combine the delegate in c# with the lambda feature in Python.
Normally, in PowerShell, this would look like:

$ast.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)

But I thought from a Python script, it would easier to use the name of the type.
You still need to know the name of the type,
but bing is great for that sort of thing.
As I said, I don’t really know the Python language,
so I expect there are better ways to handle the Collection[PSObject] that Invoke() returns.
I found that I had to iterate over the result no matter what, so I built it into the convenience function.
Anyone with suggestions is more than welcome to improve this.

The glory

Now that we have the base module together, we can write some pretty simple Python to
execute our PowerShell scripts.
Invoking a PowerShell script is now as easy as:

#!/usr/bin/python3

from pspython import *

scriptDefinition = 'Get-ChildItem'
print(f"Run the script: '{scriptDefinition}")
result = PsRunScript(scriptDefinition)
PrintResults(result)
/root/__pycache__
/root/dotnet-install.sh
/root/get-pip.py
/root/grr.py
/root/hosted.runtimeconfig.json
/root/pspar.py
/root/pspython.py
/root/psrun.py

You’ll notice that the output is not formatted by PowerShell.
This is because Python is just taking the .NET objects and (essentially) calling ToString() on them.

It’s also possible to retrieve objects and then manage formatting via PowerShell.
This example retrieves objects via Get-ChildItem,
selects those files that start with “ps” in Python,
and then creates a string result in table format.

scriptDefinition = 'Get-ChildItem'
result = list(filter(lambda r: r.BaseObject.Name.startswith('ps'), PsRunScript(scriptDefinition)))
ps.Commands.Clear()
ps.Commands.AddCommand("Out-String").AddParameter("Stream", True).AddParameter("InputObject", result)
strResult = ps.Invoke()
# print results
PrintResults(strResult)
    Directory: /root

UnixMode   User             Group                 LastWriteTime           Size Name
--------   ----             -----                 -------------           ---- ----
-rwxr-xr-x root             dialout             6/17/2022 01:30           1117 pspar.py
-rwxr-xr-x root             dialout             6/16/2022 18:55           2474 pspython.py
-rwxr-xr-x root             dialout             6/16/2022 21:43            684 psrun.py

But that’s not all

We can also call static methods on PowerShell types.
Those of you that noticed in my module there are a couple of language related functions.
The ParseScript and ParseFile functions allow us to call the PowerShell language parser
enabling some very interesting scenarios.

Imagine I wanted to determine what commands a script is calling.
The PowerShell AST makes that a breeze, but first we have to use the parser.
In PowerShell, that would be done like this:

$tokens = $errors = $null
$AST = [System.Management.Automation.Language.Parser]::ParseFile("myscript.ps1", [ref]$tokens, [ref]$errors)

The resulting AST is stored in $AST, the tokens in $tokens, and the errors in $errors.
With this Python module, I encapsulate that into the Python function ParseFile,
which returns an object containing all three of those results in a single element.
I also created a couple of helper functions to print the tokens and errors more easily.
Additionally, I created a function that allows me to look for any type of AST (or sub AST)
in any arbitrary AST.

parseResult = ParseFile(scriptFile)
commandAst = parseResult.FindAst("CommandAst")
commands = set()
for c in commandAst:
    commandName = c.GetCommandName()
    # sometimes CommandName is null, don't include those
    if commandName != None:
       commands.add(c.GetCommandName().lower())
PrintResults(sorted(commands))

Note that there is a check for commandName not being null.
This is because when & $commandName is used, the command name cannot be
determined via static analysis since the command name is determined at run-time.

…a few, uh, provisos, uh, a couple of quid pro quo

First, you have to have dotnet installed (via the install-dotnet),
as well as a full installation of PowerShell.
pythonnet doesn’t run on all versions of Python,
I’ve tested it only on Python 3.8 and Python 3.9 on Ubuntu20.04.
As of the time I wrote this, I couldn’t get it to run on Python 3.10.
There’s more info on pythonnet at the pythonnet web-site.
Also, this is a hosted instance of PowerShell.
Some things, like progress, and verbose, and errors may act a bit differently than you
would see from pwsh.exe.
Over time, I will probably add additional helper functions to retrieve more runtime information
from the engine instance.
If you would like to pitch in, I’m happy to take Pull Requests or to simply understand your use cases integrating PowerShell and Python.

Take it out for a spin

I’ve wrapped all of this up and added a Dockerfile (running on Ubuntu 20.04) on
github.
To create the docker image, just run
Docker build --tag pspython:demo .
from the root of the repository.

The post Hosting PowerShell in a Python script appeared first on PowerShell Team.

PowerShellGet 3.0 Preview 14

This post was originally published on this site

We are excited to announce that an update to our preview of PowerShellGet 3.0 is now available on the PowerShell Gallery!

This release includes support for PSResourceRepository credential persistence, as well as support for RequiredResourceFiles for Install.

How to Install PowerShellGet 3.0 Preview 14

Prerequisites

Please ensure that you have the latest (non-prerelease) version of PowerShellGet and PackageManagement installed. To check the version you currently have installed run the command Get-InstalledModule PowerShellGet, PackageManagement

The latest version of PowerShellGet is 2.2.5, and the latest version of PackageManagement is 1.4.7. To install the latest versions of these modules run the following: Install-Module PowerShellGet -Force -AllowClobber

Installing the Preview

To install this preview release side-by-side with your existing PowerShellGet version, open any PowerShell console and run: Install-Module PowerShellGet -Force -AllowPrerelease

What to expect in this update

The key features and bug fixes are listed below, for the full list of changes please refer to the Changelog.

Features of this release

  • -Scope parameter for Get-PSResource and Uninstall-PSResource
  •  -CredentialInfo parameter for Register-PSResourceRepository
  • Clt+C support for Find-PSResource
  • -RequiredResource parameter for Install-PSResource
  • Publish-PSResource Script publishing support
  • -Prerelease support for Uninstall-PSResource
  • Install-PSResource -RequiredResourceFile parameter for psd1 and json
  • -RequiredResource parameter Install-PSResource

Bug Fixes

  • RequiredModules support for publishing
  • Register-PSResourceRepository -Repositories parameter renamed to -Repository
  • Rename PSResourceInfo’s PrereleaseLabel property to match Prerelease column displayed
  • Parameter consistency: change url to uri

Using Credential Persistence

This community contributed feature allows you to provide information about where a credential is stored when registering the PSResourceRepository using a -CredentialInfo parameter. This parameter takes a PSCredentialInfo object which takes a vault name and secret name. The secret will then be pulled to authenticate to the Repository on Find and Install calls. This feature takes advantage of SecretManagement, for more information on SecretManagement refer the documentation.

Requirements

  • Microsoft.PowerShell.SecretManagement
  • Any vault extension registered (for this example I will use Microsoft.PowerShell.SecretStore)
  • A repository to connect to (for this example I will use Artifactory)

Getting started

For this example I am going to be using SecretStore to store my credentials for an Artifactory repository. This can be repeated with any SecretManagement extension vault, and with any repository. First, I need to make sure I have a SecretManagement vault set up, with my credential stored. Currently this feature only supports the PSCredential secret type, however, we have an issue to track support for other types.

PS C:Users > Get-SecretInfo
Name          Type         VaultName
----          ----         ---------
jFrogCred     PSCredential SecretStore

Then I can create a PSCredentialInfo object to pass into my PSResourceRepository registration, to create this I pass in the name of the vault and the name of the secret.

$credentialInfo = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("SecretStore", "jfrogCred")

Now I am ready to register the PSResourceRepository, I will pass in a friendly name, a URI for the repository, and the CredentialInfo. I also use the -Trusted switch because I will be controlling what gets published to the repository.

Register-PSResourceRepository -Name artifactory `
-URI " https://<name>.jfrog.io/artifactory/api/nuget/v3/<Repository-Name>
-Trusted `
-CredentialInfo $credentialInfo 

Now I can run Get-PSResourceRepository to see my registered repositories

PS C:Users > Get-PSResourceRepository

Name        Uri                                                                                     Trusted Priority
----        ---                                                                                     ------- --------
artifactory https://<name>.jfrog.io/artifactory/api/nuget/v3/<Repository-Name>                      True    50      
PSGallery   https://www.powershellgallery.com/api/v2                                                False   50  

If the repository is empty you are going to want to begin by publishing to it. I have saved my API key for publishing to the repository in my SecretStore.

Publish-PSResource -Path .Get-Hello -Repository artifactory -ApiKey (Get-Secret jfrogPublish)

Now I can find and install PSResources from my repository without needing to provide a credential at each call.

PS C:Users> Find-PSResource -Name Get-Hello -Repository artifactory

Name      Version Prerelease Repository  Description
----      ------- ---------- ----------  -----------
Get-Hello 0.0.2.0            artifactory test module

PS C:Users> Install-PSResource Get-Hello

Using Required Resources

Install-PSResource can accept a path to a psd1 or json file (using -RequiredResourceFile), or a hashtable or json (using -RequiredResource).

Getting Started

The -RequiredResource parameter can take either a hashtable or a json format string. The following example shows how to format these inputs.

Install-PSResource -RequiredResource @{
  'Configuration' = @{
    version = '[1.3.1,2.0]'
    repository = 'PSGallery'                        
   }
  'Pester'        = @{
    version = '[4.4.2,4.7.0]'
    repository = 'PSGallery'
    allowPrerelease = $true
  }
}

In this case the modules named “Configuration”, and “Pester” will be installed. The json format will be the same as if this hashtable is passed to ConvertTo-Json:

"{
  'Pester': {
    'allowPrerelease': true,
    'version': '[4.4.2,4.7.0]',
    'repository': 'PSGallery'
  },
  'Configuration': {
    'version': '[1.3.1,2.0]',
    'repository': 'PSGallery'
  }
}"

 Declared dependencies are searched and installed using the same trust algorithm as for Install-PSResource.

The -RequiredResourceFile parameter can accept either a psd1 or a json file. The psd1 should use the same format as the hashtable above, the json file should use the same format as the json sting above.

Features to Expect in Coming Preview Releases

This module is not yet feature complete. Below is a list of features which you can expect to see in the next preview release.

  • New-ScriptFileInfo cmdlet
  • Test-ScriptFileInfo cmdlet
  • Update-ScriptFileInfo cmdlet
  • Update-ModuleManifest cmdlet
  • -AuthenticodeCheck parameter switch for Save, Install, Update

Using the CompatPowerShellGet module

CompatPowerShellGet is a compatibility module that allows use of PowerShellGet 2.x (and below) cmdlet syntax with PowerShellGet 3.0 (and above) functionality by making a best effort mapping between the cmdlet interfaces of both versions of the module. New PowerShell scripts that only leverage PowerShellGet v3 cmdlets do not need to use this compatibility module. For example, if a user has the CompatPowerShellGet module installed and runs the command:

Install-Module PowerShellGet -MinimumVersion 1 -MaximumVersion 2 -AllowPrerelease

the CompatPowerShellGet module will get autoloaded into the PowerShell Session and will map the command to PowerShellGet 3.0 syntax:

Install-PSResource PowerShellGet -Version "[1,2]" -Prerelease" 

The command will then be executed by the PowerShellGet 3.0 implementation. The user will also get a warning to update their script to the new cmdlet interface:

WARNING: The cmdlet 'Install-Module' is deprecated, please use 'Install-PSResource.

This module is designed so that users will not need to immediately update their scripts in order to update to the latest version of PowerShell or to begin taking advantage of the performance improvements already available in PowerShellGet 3.0. We still do recommend that authors begin making the minimal changes required to update their scripts to the new cmdlet interface.

This compatibility module is designed so that it takes precedence over legacy versions of PowerShellGet. If you have this compatibility module installed and would not like it to be used, you can remove it from the PowerShell session using the Remove-Module command.

Please note that this flow is only possible if the user has both the CompatPowerShellGet module installed and the PowerShellGet 3.0 preview module installed. Once PowerShellGet 3.0 is generally available it will be a dependency of CompatPowerShellGet.

Please also note that this compatibility module will not be called if you use fully qualified cmdlets. For example, if you use PowerShellGetInstall-Module this will call a legacy version of PowerShellGet. If this is a common scenario for you, and will be a barrier to migrating to PowerShellGet 3.0 we would appreciate that feedback in our GitHub repository.

How to Track the Development of this Module

GitHub is the best place to track the bugs/feature requests related to this module. We have used a combination of projects and labels on our GitHub repo to track issues for this upcoming release. We are using the label Resolved-3.0 to label issues that we plan to release at some point before we release the module as GA (generally available).

To track issues/features for the next release, please refer to this GitHub project.

How to Give feedback and Get Support

We cannot overstate how critical user feedback is at this stage in the development of the module. Feedback from preview releases help inform design decisions without incurring a breaking change once generally available and used in production.

In order to help us to make key decisions around the behavior of the module please give us feedback by opening issues in our GitHub repository.

Sydney Smith

PowerShell Team

The post PowerShellGet 3.0 Preview 14 appeared first on PowerShell Team.