Poweshell, Namespaces, and Variable Declaration - powershell

I'm making some Powershell routines that automate tasks for Windows 10, Firewall, Services, and Task Scheduler.
Windows 10 Pro x64
Powershell 7.1.2 x64
I declare variables using their object declaration exposed by their namespaces, this works quite well for functions operating on Windows Services. As follows:
using namespace System
using namespace System.ServiceProcess # ServiceController exposure
Calling the System.ServiceProcess objects/library, exposes ServiceController object, so that I can declare variable or function returns as such.
[ServiceController] $WmplayerNetSrv = (Get-Service -Name "WMPNetworkSvc" -ErrorAction Stop)
This allows me to know exactly what a variable is, and enables a better intellisense list when referencing the variable.
All is well until I construct functions for the Windows 10 Firewall.
using namespace System
using namespace Microsoft.Management.Infrastructure #.CimInstance exposure
using namespace Microsoft.Management.Infrastructure.CimInstance#root/standardcimv2 # /MSFT_NetFirewallRule exposure
I know variables in Powershell need not be declared, but I really want to do so.
I've tried:
using namespace Microsoft.Management.Infrastructure.CimInstance#root/standardcimv2
[MSFT_NetFirewallRule] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
Doesn't work, complains it can't find, MSFT_NetFirewallRule, type.
using namespace Microsoft.Management.Infrastructure.CimInstance
[#root/standardcimv2/MSFT_NetFirewallRule] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
Doesn't work, complains about the #, and the /, I'm sure the type wouldn't work too.
[Microsoft.Management.Infrastructure.CimInstance#root/standardcimv2/MSFT_NetFirewallRule] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
Doesn't work, complains it can't find, MSFT_NetFirewallRule, type.
So my questions are, how can I declare a variable as, [MSFT_NetFirewallRule]? Is this possible? I know I can fall back on [object[]], or not declare it.
Thanks and regards,
-njc

Your confusion is probably because you're mixing up CIM classes with dotnet classes (i.e. types in the dotnet type system).
MSFT_NetFirewallRule is a CIM class (see MSFT_NetFirewallRule) and not a type from the dotnet type system, and as far as PowerShell is concerned your firewall rule is just a dotnet object of type CimInstance with a string property called CimClass that happens to have the value Root/StandardCimv2:MSFT_NetFirewallRule.
Your code should work if you do this:
[CimInstance] $WinFwRule = (Get-NetFirewallRule -DisplayName "RULE_NAME" -ErrorAction Stop)
but that's about as specific as you can be with your types.

Related

How can i list namespaces under the root?

I want to list which things are useable under \\root\ .However i don't know how to list or see which things i can use.Because,i am beginner in the powershell.
I am coding this thing:
wmic /namespace:\root\
(But i don't know which things i can use under root.And because of this, i cannot use anything :/)
How can i list which things could be useable under root ?
If someone can help, i will be really happy :D
I tried use "/?" but it didn't help.Also i researched on google BUT again i couldn't find something useful for myself or maybe i couldn't understand their solutions.
There is a WMI class of __namespace you can query:
Get-WmiObject -Namespace Root -Class __Namespace | Select Name
Name
----
subscription
DEFAULT
CIMV2
msdtc
Cli
SECURITY
SecurityCenter2
RSOP
PEH
StandardCimv2
WMI
MSPS
directory
Policy
Interop
Hardware
ServiceModel
SecurityCenter
Microsoft
Appv
I would recommend reading through about WMI. It covers some of the discoverability aspects, which are important because:
In a default installation of Windows 8, there are more than 1,100 WMI classes in Root/Cimv2
Newer versions of powershell use CIM over WMI with commands like Get-CimInstance. It's not worth worrying about for now, but it's good to look into while you're learning
WMIC is a separate exe from powershell, and doesn't return powershell objects. I would avoid it unless you're stuck to command prompt
Cpt.Whale's answer is helpful, but is uses the deprecated WMI cmdlets (from the Get-WmiObject docs page: "Starting in PowerShell 3.0, this cmdlet has been superseded by Get-CimInstance"); similarly, wmic.exe is deprecated (see note at the top of the page). Both are deprecated in favor of the CIM cmdlets, so the standard advice applies:
The CIM cmdlets (e.g., Get-CimInstance) superseded the WMI cmdlets (e.g., Get-WmiObject) in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell (Core) v7+, where all future effort will go, doesn't even have them anymore. Note that WMI still underlies the CIM cmdlets, however.
For more information, including the differences between these two sets of cmdlets, see this answer.
Thus, here are solutions based on the CIM cmdlets:
To get all namespaces nested inside another namespace, such as root:
Get-CimInstance -Class __Namespace -Namespace root | ForEach-Object Name
To get all classes inside a given namespace, such as root, by name:
Get-CimClass -Namespace root | ForEach-Object CimClassName
Note:
Append | Sort-Object to the commands above to get alphabetically sorted output.
The default namespace (also for the WMI cmdlets) is root/cimv2, which applies if you omit a -Namespace argument.

I created a custom powershell .psm1 module but it won't update after an edit

I created a custom powershell module in the
C:\Program Files\WindowsPowerShell\Modules\PennoniAppManagement directory. Whenever I make changes to a function in the module, then import the module into a script, the updated code won't take effect. Any solutions?
Make sure you remove the already-loaded version of the module from the session before re-importing it:
Remove-Module PennoniAppManagement -Force
Import-Module PennoniAppManagement
Normally, Import-Module-Force - by itself - is enough to force reloading of an updated module into the current session.
Import-Module -Force implicitly performs Remove-Module before reloading the module (if the module isn't currently loaded, -Force just loads the module normally).
Also note that force-reloading a module is not an option if you're loading it via a using module statement (at least as of PowerShell 7.1.2). Notably the using module method of importing is required if a module exports custom class definitions that the caller should see - see this answer for details.
Mathias' two-step approach - Remove-Module -Force, followed by Import-Module - is apparently needed in some cases, and seems to be required in yours.
It would be good to understand when the two-step approach is needed. Mathias thinks it is related to cached versions of custom class definitions (used module-internally) lingering instead of getting reloaded and redefined when Import-Module -Force is called. That is, while the module overall may get reloaded, it may be operating on stale classes. At least in the simple scenario below I was not able to reproduce this problem, neither in Windows PowerShell 5.1, nor in PowerShell (Core) 7.2.1, but there may be scenarios where the problem does surface.
The Remove-Module documentation describes the -Force parameter solely as relating to the - rarely used - .AccessMode property available on a loaded module's module-information object (you can inspect it with (Get-Module ...).AccessMode). The default value is ReadWrite, which allows unloading (removal) of the module anytime. If the property value is ReadOnly, Remove-Module -Force is needed to unload; if it is Constant, the module cannot be removed from the session at all, once loaded - at least not with Remove-Module.
Notably, the implicit unloading that happens with Import-Module -Force is not subject to these restrictions and implicitly unloads a module even if its .AccessMode is Constant (as of PowerShell 7.1.2; I am unclear on whether that is by design).
Test code involving reloading a module with a modified class definition, to see if Import-Module -Force is enough:
# Create a template for the content of a sample script module.
# Note: The doubled { and } are needed for use of the string with
# with the -f operator later.
$moduleContent = #'
class MyClass {{
[string] $Foo{0}
}}
function Get-Foo {{
# Print the property names of custom class [MyClass]
[MyClass]::new().psobject.Properties.Name
}}
'#
# Create the module with property name .Foo1 in the [MyClass] class.
$moduleContent -f 1 > .\Foo.psm1
# Import the module and call Get-Foo to echo the property name.
Import-Module .\Foo.psm1; Get-Foo
# Now update the module on disk by changing the property name
# to .Foo2
$moduleContent -f 2 > .\Foo.psm1
# Force-import (reload) the module and
# see if the property name changed.
Import-Module -Force .\Foo.psm1; Get-Foo
# Clean up.
Remove-Item .\Foo.psm1
In both Windows PowerShell (whose latest and last version is v5.1) and PowerShell (Core) 7.2.1 (current as of this writing), the above yields, as expected:
Foo1 # Original import.
Foo2 # After modifying the class and force-reloading

Why COM object methods are not visible/accessible in powershell?

As described here, COM objects can be created using New-Object -ComObject ProgID.
With the same steps we can create COM object with ProgID wscAPI.WSCProductList.1. But we see no methods are available whereas there are few as per docs here. Why these methods are not shown/accessible in powershell?
PS C:\>$Wsc = New-Object -ComObject wscAPI.WSCProductList.1
PS C:\>$Wsc | Get-Member
TypeName: System.__ComObject
Name MemberType Definition
---- ---------- ----------
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
ToString Method string ToString()
PS: There is a sample code to access these methods using C++ here.
OS: Microsoft Windows 10 Enterprise
Well, by design, PowerShell is not a COM Object Browser. COM objects are only loaded when instantiated and called.
PowerShell is .Net based, and .Net is part is the Operating system and loaded when the OS starts. This is the reason what you get the .Net namespaces in IntelliSense In PowerShell without extra work. Though even with that one needs to call it correctly. For example,
[System.Net.Dns]::
… or if you want if you don’t what to type all that, you can put this…
Using Namespace System.Net
at the very top of your profile / your script or the first thing you’d put in an interactive session, then you could just due
[Dns]::
About Using It allows you to indicate which namespaces are used
in the session.
COM would not be available to you in this manner. As for your pointer the C++ code. You can use external code segments/blocks/functions in PowerShell to supplement what you may be trying to do and this is what the Add-type cmdlet is used for.
Examples ---
Using CSharp (C#) code in Powershell scripts
As noted online:
from within PS run help add-type -full and look for the F# example
(example 7 on my system). Assuming you have a C++ compiler that
follows the "CodeDom" model, you can mirror the F# example.
Also, PowerShell can call unmanaged code using P/Invoke (this is a .NET concept), search the web for the use case.
So, based on even what you are showing, you are talking beyond this?
$wscAPI = New-Object -ComObject wscAPI.WSCProductList.1
$wscAPI.CreateObjRef(
... so, you want to be able to say to have IntelliSense/tab completion for when you start typing
New-Object -ComObject #[tab if on the command line or IntelliSense popup in an editor]
Yet there are modules/scripts, etc. via the Microsoft powershellgallery.com, the demonstrate how you could write your own object browser. See the resources below:
PowerShell Object Browser
This is a PowerShell application that displays objects in an explorer
type application written in PowerShell. The PowerShell Object Browser
displays the properties and child objects/collections for any
PowerShell / .Net framework object. For an explanation on how to use
this a
Download: PSObjectBrowser.ps1
There are several WMI, CIM, .Net examples/tools that show how to do
object browsing, but again, that .Net. Just search for them as well.
For Example:
PowerShell Object Browser
This is a PowerShell application that displays objects in an explorer
type application written in PowerShell. The PowerShell Object Browser
displays the properties and child objects/collections for any
PowerShell / .Net framework object. For an explanation on how to use
this a
Download: PSObjectBrowser.ps1
Weekend Scripter: The WMI Explorer Tool
Weekend Scripter: Announcing WMI Explorer 2.0
PowerShell ISE Add-on - CIM Explorer
PowerShell ISE add-on for navigating CIM(WMI) namespaces/definitions
and creating PowerShell script examples through CIM cmdlets
Free WMI Explorer, WMI Tools & Powershell GUI Builder
Coretech WMI and PowerShell Browser
· A powerful WMI tool that allows you to connect to and work with WMI
locally and remotely. With the tool, you can browse WMI namespaces,
classes, methods, properties and class instances. Besides being a WMI
browser the Coretech WMI and PowerShell Explorer allows also to export
query results and namespace methods.
These could give you ideas regarding how you might design one for COM. Yet, you'd have to know all the possible COM classes (registered or not) you'd like to instantiate. So, you could do something like this to find them ...
Get-ChildItem -Path 'HKLM:\Software\Classes' -ErrorAction SilentlyContinue |
Where-Object {$PSItem.PSChildName -match '^\w+\.\w+$' -and
(Get-ItemProperty "$($PSItem.PSPath)\CLSID" -ErrorAction SilentlyContinue)} |
Format-Table PSChildName -AutoSize
# Results
<#
Sample output from the COM lookup.
PSChildName
-----------
...
Access.Application
...
AcroPDF.PDF
...
AudioVBScript.1
...
CDO.Message
...
Cmiv2.CmiFactory
...
Excel.Application
...
InternetExplorer.Application
...
Outlook.Application
...
Paint.Picture
...
#>
One, of course, can modify the regex to look up whatever string(s) they choose. See the Regular Expression resource for detailed guidance on regex use.
RegExLib.com Regular Expression Cheat Sheet (.NET)
Regular-Expressions.Info Tutorials
So, with that block for say this effort...
New-Object -ComObject #[tab if on the command line or IntelliSense popup in an editor]
... as long as you load this at startup, and give them a populated variable name, that would be a quick and dirty way to do this. I've done similar things not only for this but for the .Net classes as well...
Function Get-AllCimClassesAsVariables
{
# Set variables for CimClasses
ForEach($Item in ((Get-CimClass -ClassName *).CimClassName))
{Set-Variable -Name $Item -Value $Item}
}
Point of note here:
I don't use this approach anymore, because it is cumbersome, has a heavy startup load because it would have to populate this list on every PowerShell session.
I have full Visual Studio on my workstation (one can use the paid-for or free version), as well, so I use it for PowerShell development stuff as well to reference and browse COM resources.
Prior to that, I used OleView.exe from the Windows / MSOffice SDKs. There was also in the Windows Resource kits that were free to download. I still have that in a cloud location for my easy access, pinned to my taskbar when I just need that vs spinning up Visual Studio or when I am in a location where I don’t have Visual Studio at hand.
To do what you are after, it would be prudent just to use OleVIew.exe as part of your PowerShell toolset to look up and use the COM info, find the ones you'd regularly use and put those into a module you regularly load via your profile or you'll need to gin up whatever thingy you need for browsing the similar to what tools are shown for WMI/CIM/.Net ones above.

Possible to use PowerShell's Get-AppvClientPackage to list AppV packages on a machine other than my own?

I can use Get-AppvClientPackage -all [| select name] or Get-WmiObject -Namespace root\appv -Class AppvClientPackage [|select name] to list all installed AppV packages installed on my own machine. It doesn't appear to be possible to use this cmdlet to get the AppV packages installed on another machine, without remote execution.
I am asking this question in hopes of finding something that works (see purpose) or get a definitive answer that it's not possible. There may be better options available (other than PS), but my question is simply if it is possible or not, so that if the latter is the case, we can push to develop a script (which could be run by someone with elevated privileges) to gather information needed.
Purpose: Our team doesn't have visibility into SCCM (that's another option is to have that team report on what is installed where, though sometimes we need answers quickly) and remote PS execution is restricted to one security team (which is understandable), but at times (for support or decommission purposes) we need to check to see if a specific client machine has a package installed, check what AppV packages a specific client has installed, as well as check to see which machines have a particular package installed.
If there is another module or cmdlet (or even something other than powershell or WMI) that might be able to yield the same information, suggestions are welcome.
Get-WmiObject utilizes RPC to connect to remote PCs and does not require PSRemoting. In this effort, all you need to do is add the -ComputerName parameter.
#Requires -Version 3
$Target = 'localhost'
$Params=#{
Namespace = 'root\appv'
Class = 'AppvClientPackage'
Property = 'Name'
ComputerName = $Target
}
Get-WmiObject #Params
PS C:\> Get-Help -Name 'Get-WmiObject' -Parameter 'ComputerName'
-ComputerName <String[]>
Specifies the target computer for the management operation. Enter a fully
qualified domain name (FQDN), a NetBIOS name, or an IP address. When the remote
computer is in a different domain than the local computer, the fully qualified
domain name is required.
The default is the local computer. To specify the local computer, such as in a
list of computer names, use "localhost", the local computer name, or a dot (.).
This parameter does not rely on Windows PowerShell remoting, which uses
WS-Management. You can use the ComputerName parameter of Get-WmiObject even if
your computer is not configured to run WS-Management remote commands.
Required? false
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? false

Powershell - Why does $ENV | GET-MEMBER not work?

I come from a C, C++, C#, python background so i'm applying this thought pattern to Powershell which i'm learning from scratch but I'm a little confused so far as at first glance it seems to be inconsistent and does not follow a fixed base class type structure for all objects so that things can be queried in a consistent manner.
The following works fine:
$host | get-member
$env:username
So $env is a valid object but this does not work:
$env | get-member
These also do not work:
$env.gettype()
dir $env
dir $env:
but this type query on $host does so I'm assuming $host is a .net variable but $env is not?
$host.gettype()
I found that env: also works with dir (aka get-childitem) but this colon is yet another type of notation i'm unfamiliar with and things are starting to get very confusing now. This does not seem to be a string format in this case which I have seen some google posts about so what is it? It behaves like a member selection or dictionary key specifier. If it is a member selector or dictionary key then i would expect get-member to work because it is a standard object.
This outputs the variables and values that I wanted but I don't understand why this syntax is used. This is not DOS syntax either so what's going on here?
dir env:
But dir $env seems to equate to dir $env:userprofile???? why?
Therefore $host appears to be a .net object but $env or env: is something else completely different and I've no idea what object type it is in the grand scheme of things and cannot seem to query it's type with by conventional means. Initial thoughts are that it is a list object of sorts because get-childitem works with it but other than that I'm completely lost.
I'm clearly missing something here so can someone steer me in the right direction please?
Get-Help 'about_Providers' -ShowWindow shows that env: is drive in Environment Provider, i.e. one of Windows PowerShell providers.
BUILT-IN PROVIDERS: Windows PowerShell includes a set of built-in
providers that you can use to access the different types of data
stores.
Provider Drive Data store
-------- ----- ----------
Alias Alias: Windows PowerShell aliases
Certificate Cert: x509 certificates for digital signatures
Environment Env: Windows environment variables
FileSystem * File system drives, directories, and files
Function Function: Windows PowerShell functions
Registry HKLM:, HKCU: Windows registry
Variable Variable: Windows PowerShell variables
WSMan WSMan: WS-Management configuration information
* The FileSystem drives vary on each system.
You can also create your own Windows PowerShell providers, and you can
install providers that others develop. To list the providers that are
available in your session, type: get-psprovider.
That's why Get-ChildItem env: works in contrary to dir $env:, dir $env etc.
First thing to note is that $env and $env:username are not related. $env is just a variable and normally it does not exists, because nobody assign anything to it. Using colon in variable name (like $env:username, with exception to some predefined prefixes: global:, script:, local:, private: and variable:) is a special syntax, which allows to access to PowerShell provider item content with variable syntax. It works with any PowerShell provider which implement content cmdlets: ${C:\Windows\System.ini} or $function:prompt. That syntax is equivalent of calling of Get-Content or Set-Content for given PowerShell path.
My 2 cents:
Try get-psdrive and you will get something like:
Name Used (GB) Free (GB) Provider Root
Env Environment
So it seems to be something like a driver in batch.