How to mock PowerShell V3 attributes in V2 - powershell

Being one who likes to document thoroughly, I was glad to discover the SupportsWildcards attribute, among others, added in PowerShell V3. I have decorated parameters in my library with that attribute as appropriate. In the long run there is no issue, but in the short term there are still plenty of folks using V2 for various reasons (including me in one environment).
It seems silly that just because of one attribute some of my functions can no longer run in PowerShell V2. So I am looking for a way to mock the attribute in V2 to essentially turn it into a "no-op".
The solution, as I see it, needs two parts:
create an essentially empty custom attribute.
make this take effect in V2 but be ignored in V3 (and hence allow the true V3 attribute to work properly).
I am looking for guidance on both parts, having not played with custom attributes before.

Perhaps you can try this.
Add-Type #"
public class CustomAttribute : System.Attribute
{
public bool SupportSomething { get; set; }
}
"#
function Do-Something {
param(
[CustomAttribute(SupportSomething=$true)]
$Command
)
}
$parameters = Get-Command -Name Do-Something | Select-Object -ExpandProperty Parameters
$parameters["Command"].Attributes
Then the output:
SupportSomething : True
TypeId : CustomAttribute
We first define the attribute in C#, which you can also do in PowerShell.
Add the attribute to the parameter. Then get the list of attributes. See here for more attribute examples

Related

Invoke-Pester to only run a single Assert/It block

I am writing unit tests for my Powershell Modules, with a file for each module, and Describe blocks for each function. Context blocks organize the tests along the behavior I am trying to test for with some arrange code, and my It blocks contain minimal arrange/act code and a assert.
I can limit my tests to only run a single test file by using Invoke-Pester "Path/To/Module"
I can also limit based on the Describe blocks by using Invoke-Pester "Path/To/Module" -TestName #("RunThisDescribe","AndRunThisDescribe")
As I am adding a new assertion (via a new It block) to an existing file/Describe/Context, I want to test/debug my new assertion alone, without the rest of the assertions of a given describe/context running (but with any mocks/variables that I set at the describe/context scope still available.
I have been commenting out my existing assertions while I am developing the new one, then remove the block comments and run them all after I am finished with the new test. This works, but is clunky.
Is there a way to run Invoke-Pester to only execute a given list of Its?
Is there a better workflow for developing/debuging new tests other than either letting all of them run or commenting them out?
I know, this question is pretty old, but it deserves an update:
Starting with Pester version 5, you can have a -Tag on everything: Describe, Context, It
This makes it really easy to run specific assertions and nothing else.
You can even exclude specific code with -ExcludeTag.
Please see https://github.com/pester/Pester#tags for details.
Also check out the braking changes, if you plan to migrate from version 4 to 5!
It doesn't look like there's any way to specify tests to run by the name of the It block.
You could split your new test in to a new Describe block and then run it via the -TestName parameter as you described, or give it a -Tag and then specify that tag via Invoke-Pester, however that doesn't seem to work for a nested Describe, it has to be at the top level.
I assume this wouldn't work for you because your Mocks and Variables would be in the other describe.
VSCode with the PowerShell extension installed allows you to run individual Describe blocks via a "Run Tests" link at the top of the Describe and this does work for nested blocks. However i'm not sure if this would result in the Mocks/Variables from the parent Describe block being invoked (my guess would be not).
Example of nested Describe, which can be individually run within VSCode:
Describe 'My-Tests' {
It 'Does something' {
$true | Should -Be $true
}
Describe 'NewTest' {
It 'Does something new' {
$true | Should -Be $true
}
}
}
It's a shame you can't currently put Tags on Context blocks for filtering in/out certain sets of tests. That was requested as a feature 2 years ago but it seems is not simple to implement.
To add to Tofuburger's answer, and based on Pester 5.3.1, you can also manipulate tests programmatically, in your test scripts, based on tags.
Describe 'Colour' -Tag 'Epistemology' {
BeforeAll {
$ParentBlockTags = $____Pester.CurrentBlock.Tag
if ($ParentBlockTags -eq 'Epistemology')
{
Set-ItResult -Inconclusive
}
}
BeforeEach {
$ItTags = $____Pester.CurrentTest.Tag
if ($ItTags -eq 'HSL')
{
Set-ItResult -Skipped -Because 'Not implemented'
}
}
It 'Saturates' -Tag 'HSL' {
1 | Should -Be 2
}
It 'Greens' -Tag 'RGB' {
1 | Should -Be 3
}
}

Getting target framework attribute in PowerShell Core

I'm looking for a way to retrieve the target framework attribute (e.g. .NETCoreApp,Version=v2.1) from a DLL when using PowerShell Core, ideally without loading the DLL directly into the main session.
I can do this in Windows PowerShell 5, as it has access to the ReflectionOnlyLoadFrom method...
$dllPath = 'C:\Temp\ADALV3\microsoft.identitymodel.clients.activedirectory.2.28.4\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll'
[Reflection.Assembly]::ReflectionOnlyLoadFrom($dllPath).CustomAttributes |
Where-Object {$_.AttributeType.Name -eq 'TargetFrameworkAttribute'} |
Select -ExpandProperty ConstructorArguments |
Select -ExpandProperty value
However, I realise that this approach isn't available in .NET Core.
Editor's note: Even though the documentation (as of this writing) misleadingly suggests that the ReflectionOnlyLoadFrom method is available in .NET Core, it is not, as explained here.
From what I've seen, it looks likely that I should be able to access the custom attributes that hold the target framework attribute by using an instance of the System.Reflection.Metadata.MetadataReader class that's available in .NET Core (a couple of examples of this in use can be found here: https://csharp.hotexamples.com/examples/System.Reflection.Metadata/MetadataReader/GetCustomAttribute/php-metadatareader-getcustomattribute-method-examples.html ). However, all the constructors for this type seem to use a Byte* type, as the following shows when running from PowerShell Core:
([type] 'System.Reflection.Metadata.MetadataReader').GetConstructors() | % {$_.GetParameters() | ft}
I have no idea how to create a Byte* type in any version of PowerShell. Perhaps there's a method in System.Reflection.Metadata that I should be using before creating the MetadataReader object, but I haven't found it yet.
Apologies for the length of this question, but I'm hoping by sharing my notes I'll help in tracking down the solution. Any advice on how this target framework information can be obtained using PowerShell Core?
After quite a bit of work, I managed to put together a PowerShell script that works (without external dependencies) in PowerShell Core that pulls in the target framework from a DLL:
$dllPath = 'C:\Temp\ADALV3\microsoft.identitymodel.clients.activedirectory.2.28.4\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll'
$stream = [System.IO.File]::OpenRead($dllPath)
$peReader = [System.Reflection.PortableExecutable.PEReader]::new($stream, [System.Reflection.PortableExecutable.PEStreamOptions]::LeaveOpen -bor [System.Reflection.PortableExecutable.PEStreamOptions]::PrefetchMetadata)
$metadataReader = [System.Reflection.Metadata.PEReaderExtensions]::GetMetadataReader($peReader)
$assemblyDefinition = $metadataReader.GetAssemblyDefinition()
$assemblyCustomAttributes = $assemblyDefinition.GetCustomAttributes()
$metadataCustomAttributes = $assemblyCustomAttributes | % {$metadataReader.GetCustomAttribute($_)}
foreach ($attribute in $metadataCustomAttributes) {
$ctor = $metadataReader.GetMemberReference([System.Reflection.Metadata.MemberReferenceHandle]$attribute.Constructor)
$attrType = $metadataReader.GetTypeReference([System.Reflection.Metadata.TypeReferenceHandle]$ctor.Parent)
$attrName = $metadataReader.GetString($attrType.Name)
$attrValBytes = $metadataReader.GetBlobContent($attribute.Value)
$attrVal = [System.Text.Encoding]::UTF8.GetString($attrValBytes)
if($attrName -eq 'TargetFrameworkAttribute') {Write-Output "AttributeName: $attrName, AttributeValue: $attrVal"}
}
$peReader.Dispose()
I'm mostly happy with it, the only issue I'd still like to sort out is that I'm getting some unhandled characters in the string output. I'll try to get rid of them.

Exposing the Connection token from Connect-AzureAd

I am using the AzureAd Powershell module for user management. However it does not have all the functionality that I need, specifically, I can't assign Application Extension values to objects, (although I can create delete and remove application extensions themselves via [Get/New/Remove]-AzureADApplicationExtensionProperty).
I know from watching the API calls with Fiddler that the graph calls are using bearer tokens, and I've called the graph API directly from Postman manually so I know how to use the Bearer token if I could get it. How do I get it?
To get the token simply use:
$token = [Microsoft.Open.Azure.AD.CommonLibrary.AzureSession]::AccessTokens['AccessToken']
But how could one come to this conclusion?
First look for where the module is located:
(Get-Module AzureAd).Path
C:\Program Files\WindowsPowerShell\Modules\AzureAD\2.0.1.3\Microsoft.Open.AzureAD16.Graph.PowerShell.dll
Now lets just make 2 assumptions. First that the token is stored in a static member of a static class, and second that it might not be stored in that dll, but any of the DLLs in the folder.
$fileInfo = New-Object 'IO.FileInfo' (Get-Module AzureAd).Path
$moduleFolder = $fileInfo.Directory.FullName
$assemblies = [AppDomain]::CurrentDomain.GetAssemblies() | where { $_.Location -ne $null -and $_.Location.StartsWith($moduleFolder)}
$assemblies | select -expandproperty ExportedTypes | Where { $_.IsSealed -and $_.IsAbstract } | Select Name, FullName
That last line btw is because of the weird way static types are noted in IL.
Which outputs a very small list:
Name FullName
---- --------
RestSharpExtensionMethods Microsoft.Open.Azure.AD.CommonLibrary.RestSharpExtensionMethods
AzureSession Microsoft.Open.Azure.AD.CommonLibrary.AzureSession
DictionaryExtensions Microsoft.Open.Azure.AD.CommonLibrary.DictionaryExtensions
Logger Microsoft.Open.Azure.AD.CommonLibrary.Logger
ImageUtils Microsoft.Open.Azure.AD.CommonLibrary.Utilities.ImageUtils
SecureStringExtension Microsoft.Open.Azure.AD.CommonLibrary.Extensions.SecureStringExtension
AzureEnvironmentConstants Microsoft.Open.Azure.AD.CommonLibrary.AzureEnvironment+AzureEnvironmentConstants
TypeToOdataTypeMapping Microsoft.Open.AzureAD16.Client.TypeToOdataTypeMapping
JsonConvert Newtonsoft.Json.JsonConvert
Extensions Newtonsoft.Json.Linq.Extensions
Extensions Newtonsoft.Json.Schema.Extensions
TypeToOdataTypeMapping Microsoft.Open.MSGraphV10.Client.TypeToOdataTypeMapping
AdalError Microsoft.IdentityModel.Clients.ActiveDirectory.AdalError
AuthenticationContextIntegratedAuthExtensions Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions
AdalOption Microsoft.IdentityModel.Clients.ActiveDirectory.AdalOption
MiscExtensions RestSharp.Extensions.MiscExtensions
ReflectionExtensions RestSharp.Extensions.ReflectionExtensions
ResponseExtensions RestSharp.Extensions.ResponseExtensions
ResponseStatusExtensions RestSharp.Extensions.ResponseStatusExtensions
StringExtensions RestSharp.Extensions.StringExtensions
XmlExtensions RestSharp.Extensions.XmlExtensions
RestClientExtensions RestSharp.RestClientExtensions
SimpleJson RestSharp.SimpleJson
We could pipe through Out-Gridview if the list was longer, but my attention was immediatly drawn to AzureSession. After that a little PowerShell autocomplete, and I found my way to [Microsoft.Open.Azure.AD.CommonLibrary.AzureSession]::AccessTokens['AccessToken']

how to create a cmdlet?

I have written my program in c# .net. I want to convert it in to a powershell cmdlet. I was instructed to use pssnapin and getproc programs. Can anyone plz help me out..
Regards
Arun
To create a PowerShell cmdlet I would recommend you read Easy Windows PowerShell cmdlet development and debugging by Bart De Smet (B#) it's a great walk through for creating and debugging cmdlet's (Does what it says on the tin!)
Also I've found Professional Windows PowerShell Programming, ISBN 978-0470173930, (ISBN-10) 0470173939 very good for creating cmdlets and providers.
So, here's the PSCmdlet-Class[from medata], that you can inherit from.
namespace System.Management.Automation
{
public abstract class PSCmdlet : Cmdlet
{
protected PSCmdlet();
public PSHost Host { get; }
public CommandInvocationIntrinsics InvokeCommand { get; }
public ProviderIntrinsics InvokeProvider { get; }
public InvocationInfo MyInvocation { get; }
public string ParameterSetName { get; }
public SessionState SessionState { get; }
public PathInfo CurrentProviderLocation(string providerId);
public Collection<string> GetResolvedProviderPathFromPSPath(string path, out ProviderInfo provider);
public string GetUnresolvedProviderPathFromPSPath(string path);
public object GetVariableValue(string name);
public object GetVariableValue(string name, object defaultValue);
}
}
In order to get your cmdlets loaded, you need to sign them additionally, because Powershell does not execute not signed code.
Install windows powershell template thereby u will get the pssnapin program, using that you can convert your .cs file into a dll. Then search for getproc program in msdn. I don't remember exactly but there will be a method which will be executed at the first. you call your dll file in that method. I don't remeber the code, but this is the procedure to do.
Take a look at this article, Creating PowerShell Cmdlets in VB 2005. It uses VB 2005, but the process is the same for C#.
Full disclosure, I wrote the article, but I do not get paid by you looking at it. :)
Check also http://blogs.msdn.com/daiken/. In particular all the months from February 2007 to June 2007. You'll find the Visual Studio template link (for 2005, also works in Express), and several examples/labs.
The PowerTime project (http://code.google.com/p/powertime/) is open source and it implements a number of cmdlets. Good for a demo to get you going.

Suggestions for implementation of a command line interface

I am redesigning a command line application and am looking for a way to make its use more intuitive. Are there any conventions for the format of parameters passed into a command line application? Or any other method that people have found useful?
I see a lot of Windows command line specifics, but if your program is intended for Linux, I find the GNU command line standard to be the most intuitive. Basically, it uses double hyphens for the long form of a command (e.g., --help) and a single hyphen for the short version (e.g., -h). You can also "stack" the short versions together (e.g., tar -zxvf filename) and mix 'n match long and short to your heart's content.
The GNU site also lists standard option names.
The getopt library greatly simplifies parsing these commands. If C's not your bag, Python has a similar library, as does Perl.
If you are using C# try Mono.GetOptions, it's a very powerful and simple-to-use command-line argument parser. It works in Mono environments and with Microsoft .NET Framework.
EDIT: Here are a few features
Each param has 2 CLI representations (1 character and string, e.g. -a or --add)
Default values
Strongly typed
Automagically produces an help screen with instructions
Automagically produces a version and copyright screen
One thing I like about certain CLI is the usage of shortcuts.
I.e, all the following lines are doing the same thing
myCli.exe describe someThing
myCli.exe descr someThing
myCli.exe desc someThing
That way, the user may not have to type the all command every time.
A good and helpful reference:
https://commandline.codeplex.com/
Library available via NuGet:
Latest stable: Install-Package CommandLineParser.
Latest release: Install-Package CommandLineParser -pre.
One line parsing using default singleton: CommandLine.Parser.Default.ParseArguments(...).
One line help screen generator: HelpText.AutoBuild(...).
Map command line arguments to IList<string>, arrays, enum or standard scalar types.
Plug-In friendly architecture as explained here.
Define verb commands as git commit -a.
Create parser instance using lambda expressions.
QuickStart: https://commandline.codeplex.com/wikipage?title=Quickstart&referringTitle=Documentation
// Define a class to receive parsed values
class Options {
[Option('r', "read", Required = true,
HelpText = "Input file to be processed.")]
public string InputFile { get; set; }
[Option('v', "verbose", DefaultValue = true,
HelpText = "Prints all messages to standard output.")]
public bool Verbose { get; set; }
[ParserState]
public IParserState LastParserState { get; set; }
[HelpOption]
public string GetUsage() {
return HelpText.AutoBuild(this,
(HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
}
}
// Consume them
static void Main(string[] args) {
var options = new Options();
if (CommandLine.Parser.Default.ParseArguments(args, options)) {
// Values are available here
if (options.Verbose) Console.WriteLine("Filename: {0}", options.InputFile);
}
}
Best thing to do is don't assume anything if you can. When the operator types in your application name for execution and does not have any parameters either hit them with a USAGE block or in the alternative open a Windows Form and allow them to enter everything you need.
c:\>FOO
FOO
USAGE FOO -{Option}{Value}
-A Do A stuff
-B Do B stuff
c:\>
Parameter delimiting I place under the heading of a religious topic: hyphens(dashes), double hyphens, slashes, nothing, positional, etc.
You didn't indicate your platform, but for the next comment I will assume Windows and .net
You can create a console based application in .net and allow it to interact with the Desktop using Forms just by choosing the console based project then adding the Windows.Forms, System.Drawing, etc DLLs.
We do this all the time. This assures that no one takes a turn down a dark alley.
Command line conventions vary from OS to OS, but the convention that's probably gotten both the most use, and the most public scrutiny is the one supported by the GNU getopt package. See http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html for more info.
It allows you to mix single letter commands, such as -nr, with longer, self-documenting options, such as --numeric --reverse. Be nice, and implement a --help (-?) option and then your users will be able to figure out all they need to know.
Here's a CodeProject article that might help you out...
C#/.NET Command Line Arguments Parser
IF VB is your flavor, here's a separate article (with a bit more guidance related content) to check out...
Parse and Validate Command Line Parameters with VB.NET
Complementing #vonc's answer, don't accept ambiguous abbreviations. Eg:
myCli.exe describe someThing
myCli.exe destroy someThing
myCli.exe des someThing ???
In fact, in that case, I probably wouldn't accept an abbreviation for "destroy"...
I always add a /? parameter to get help and I always try to have a default (i.e. most common scenario) implementation.
Otherwise I tend to use the "/x" for switches and "/x:value" for switches that require values to be passed. Makes it pretty easy to parse the parameters using regular expressions.
I developed this framework, maybe it helps:
The SysCommand is a powerful cross-platform framework, to develop Console Applications in .NET. Is simple, type-safe, and with great influences of the MVC pattern.
https://github.com/juniorgasparotto/SysCommand
namespace Example.Initialization.Simple
{
using SysCommand.ConsoleApp;
public class Program
{
public static int Main(string[] args)
{
return App.RunApplication();
}
}
// Classes inheriting from `Command` will be automatically found by the system
// and its public properties and methods will be available for use.
public class MyCommand : Command
{
public void Main(string arg1, int? arg2 = null)
{
if (arg1 != null)
this.App.Console.Write(string.Format("Main arg1='{0}'", arg1));
if (arg2 != null)
this.App.Console.Write(string.Format("Main arg2='{0}'", arg2));
}
public void MyAction(bool a)
{
this.App.Console.Write(string.Format("MyAction a='{0}'", a));
}
}
}
Tests:
// auto-generate help
$ my-app.exe help
// method "Main" typed
$ my-app.exe --arg1 value --arg2 1000
// or without "--arg2"
$ my-app.exe --arg1 value
// actions support
$ my-app.exe my-action -a
-operation [parameters] -command [your command] -anotherthings [otherparams]....
For example,
YourApp.exe -file %YourProject.prj% -Secure true
If you use one of the standard tools for generating command line interfaces, like getopts, then you'll conform automatically.
The conventions that you use for you application would depend on
1) What type of application it is.
2) What operating system you are using.
This is definitely true. I'm not certain about dos-prompt conventions, but on unix-like systems the general conventions are roughly:
1) Formatting is
appName parameters
2) Single character parameters (such as 'x') are passed as -x
3) Multi character parameters (such as 'add-keys') are passed as --add-keys
The conventions that you use for you application would depend on
1) What type of application it is.
2) What operating system you are using. Linux? Windows? They both have different conventions.
What I would suggest is look at other command line interfaces for other commands on your system, paying special attention to the parameters passed. Having incorrect parameters should give the user solution directed error message. An easy to find help screen can aid in usability as well.
Without know what exactly your application will do, it's hard to give specific examples.
If you're using Perl, my CLI::Application framework might be just what you need. It lets you build applications with a SVN/CVS/GIT like user interface easily ("your-command -o --long-opt some-action-to-execute some parameters").
I've created a .Net C# library that includes a command-line parser. You just need to create a class that inherits from the CmdLineObject class, call Initialize, and it will automatically populate the properties. It can handle conversions to different types (uses an advanced conversion library also included in the project), arrays, command-line aliases, click-once arguments, etc. It even automatically creates command-line help (/?).
If you are interested, the URL to the project is http://bizark.codeplex.com. It is currently only available as source code.
I've just released an even better command line parser.
https://github.com/gene-l-thomas/coptions
It's on nuget Install-Package coptions
using System;
using System.Collections.Generic;
using coptions;
[ApplicationInfo(Help = "This program does something useful.")]
public class Options
{
[Flag('s', "silent", Help = "Produce no output.")]
public bool Silent;
[Option('n', "name", "NAME", Help = "Name of user.")]
public string Name
{
get { return _name; }
set { if (String.IsNullOrWhiteSpace(value))
throw new InvalidOptionValueException("Name must not be blank");
_name = value;
}
}
private string _name;
[Option("size", Help = "Size to output.")]
public int Size = 3;
[Option('i', "ignore", "FILENAME", Help = "Files to ignore.")]
public List<string> Ignore;
[Flag('v', "verbose", Help = "Increase the amount of output.")]
public int Verbose = 1;
[Value("OUT", Help = "Output file.")]
public string OutputFile;
[Value("INPUT", Help = "Input files.")]
public List<string> InputFiles;
}
namespace coptions.ReadmeExample
{
class Program
{
static int Main(string[] args)
{
try
{
Options opt = CliParser.Parse<Options>(args);
Console.WriteLine(opt.Silent);
Console.WriteLine(opt.OutputFile);
return 0;
}
catch (CliParserExit)
{
// --help
return 0;
} catch (Exception e)
{
// unknown options etc...
Console.Error.WriteLine("Fatal Error: " + e.Message);
return 1;
}
}
}
}
Supports automatic --help generation, verbs, e.g. commmand.exe
Enjoy.