What does ". script" do? - powershell

What will the following lines of PowerShell code do?
Set-Location D:\Utilities\AZ
. D:\Utilities\AZ\Uti.ps1
I am sure about he first line that it changes the path but not really sure about the second.

The . C:\path\to\script.ps1 part is known as "Dot Sourcing".
When you run a script without the dot, the contents of the script is executed in its own scope (the Script scope), and so any variable or functions defined inside the script will not persist after the script is done executing.
When you dot source a script (like in your example), the contents of the script is executed in the the callers scope, and functions defined inside the script will persist even after it has run.
Quote from the about_Scripts help file:
SCRIPT SCOPE AND DOT SOURCING
Each script runs in its own scope. The functions, variables, aliases, and
drives that are created in the script exist only in the script scope. You
cannot access these items or their values in the scope in which the
script runs.
To run a script in a different scope, you can specify a scope, such as
Global or Local, or you can dot source the script.
The dot sourcing feature lets you run a script in the current scope instead
of in the script scope. When you run a script that is dot sourced, the
commands in the script run as though you had typed them at the command
prompt. The functions, variables, aliases, and drives that the script
creates are created in the scope in which you are working. After the script
runs, you can use the created items and access their values in your session.
To dot source a script, type a dot (.) and a space before the script path.
For example:
. C:\scripts\UtilityFunctions.ps1
-or-
. .\UtilityFunctions.ps1

Set-Location changes the working directory of the script to the given folder. The second statement dot-sources the PowerShell script, i.e. it loads the content of the file and runs it in the current context (see section SCRIPT SCOPE AND DOT SOURCING of the help topic I linked to).
Since you said you cannot run the script, you're most likely getting the following error when dot-sourcing it:
File D:\Utilities\AZ\Uti.ps1 cannot be loaded because the execution of scripts is
disabled on this system. Please see "get-help about_signing" for more details.
At line:1 char:2
+ . <<<< D:\Utilities\AZ\Uti.ps1
+ CategoryInfo : NotSpecified: (:) [], PSSecurityException
+ FullyQualifiedErrorId : RuntimeException
This means that the execution policy for PowerShell scripts on your system is set to Restricted. Use Get-ExecutionPolicy to verify that, and use Set-ExecutionPolicy to change it:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope User
or just
Set-ExecutionPolicy RemoteSigned
if you have administrative rights.
If Set-ExecutionPolicy fails, the execution policy is probably defined with a group policy, in which case you need to talk to your admins about it.
See here for a more detailed explanation of execution policy scopes.

Related

Powershell function call causes missing function error using powershell v7 on windows 10

I wrote a script to build all .net projects in a folder.
Issue
The issue is I am getting a missing function error when I call Build-Sollution.
What I tried
I made sure that function was declared before I used it so I am not really sure why it saids that it is not defined.
I am new to powershell but I would think a function calling another functions should work like this?
Thanks in advance!
Please see below for the error message and code.
Error Message
Line |
3 | Build-Sollution $_
| ~~~~~~~~~~~~~~~
The term 'Build-Sollution' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Build-Sollution:
Code
param (
#[Parameter(Mandatory=$true)][string]$plugin_path,
[string]$depth = 5
)
$plugin_path = 'path/to/sollutions/'
function Get-Sollutions {
Get-ChildItem -File -Path $plugin_path -Include *.sln -Recurse
}
function Build-Sollution($solution) {
dotnet build $solution.fullname
}
function Build-Sollutions($solutions) {
$solutions | ForEach-Object -Parallel {
Build-Sollution $_
}
}
$solutions_temp = Get-Sollutions
Build-Sollutions $solutions_temp
From PowerShell ForEach-Object Parallel Feature | PowerShell
Script blocks run in a context called a PowerShell runspace. The runspace context contains all of the defined variables, functions and loaded modules.
...
And each runspace must load whatever module is needed and have any variable be explicitly passed in from the calling script.
So in this case, the easiest solution is to define Build-Sollution inside Build-Sollutions
As for this...
I am new to powershell but I would think a function calling another
functions should work like this?
... you cannot use the functions until you load your code into memory. You need to run the code before the functions are available.
If you are in the ISE or VSCode, if the script is not saved, Select All and hit use the key to run. In the ISE use F8 Selected, F5 run all. In VSCode, F8 run selected, crtl+F5 run all. YOu can just click the menu options as well.
If you are doing this from the consolehost, the run the script using dot sourcing.
. .\UncToYourScript.ps1
It's ok to be new, we all started somewhere, but it's vital that you get ramped up first. so, beyond what I address here, be sure to spend time on Youtube and search for Beginning, Intermediate, Advanced PowerShell for videos to consume. There are tons of free training resources all over the web and using the built-in help files would have given you the answer as well.
about_Scripts
SCRIPT SCOPE AND DOT SOURCING Each script runs in its own scope. The
functions, variables, aliases, and drives that are created in the
script exist only in the script scope. You cannot access these items
or their values in the scope in which the script runs.
To run a script in a different scope, you can specify a scope, such as
Global or Local, or you can dot source the script.
The dot sourcing feature lets you run a script in the current scope
instead of in the script scope. When you run a script that is dot
sourced, the commands in the script run as though you had typed them
at the command prompt. The functions, variables, aliases, and drives
that the script creates are created in the scope in which you are
working. After the script runs, you can use the created items and
access their values in your session.
To dot source a script, type a dot (.) and a space before the script
path.
See also:
'powershell .net projects build run scripts'
'powershell build all .net projects in a folder'
Simple build script using Power Shell
Update
As per your comments below:
Sure the script should be saved, using whatever editor you choose.
The ISE does not use PSv7 by design, it uses WPSv5x and earlier.
The editor for PSv7 is VSCode. If you run a function that contains another function, you have explicitly loaded everything in that call, and as such it's available.
However, you are saying, you are using PSv7, so, you need to run your code in the PSv7 consolehost or VSCode, not the ISE.
Windows PowerShell (powershell.exe and powershell_ise.exe) and PowerShell Core (pwsh.exe) are two different environments, with two different executables, designed to run side-by-side on Windows, but you do have to explicitly choose which to use or write your code to branch to a code segment to execute relative to the host you started.
For example, let's say I wanted to run a console command and I am in the ISE, but I need to run that in Pwsh. I use a function like this that I have in a custom module autoloaded via my PowerShell profiles:
# Call code by console executable
Function Start-ConsoleCommand
{
[CmdletBinding(SupportsShouldProcess)]
[Alias('scc')]
Param
(
[string]$ConsoleCommand,
[switch]$PoSHCore
)
If ($PoSHCore)
{Start-Process pwsh -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -PassThru -Wait}
Else {Start-Process powershell -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -PassThru -Wait}
}
All this code is doing is taking whatever command I send it and if I use the PoSHCore switch...
scc -ConsoleCommand 'SomeCommand' -PoSHCore
... it will shell out to PSCore, run the code, otherwise, it just runs from the ISE>
If you want to use the ISE with PSv7 adn not do the shell out thing, you need to force the ISE to use PSv7 to run code. See:
Using PowerShell Core 6 and 7 in the Windows PowerShell ISE

DPM Commands in Powershell Version 2

I am running a PowerShell script which requires connection to a DPM Server.
When I run run the Connect-DPMServer <DPM Server Name> cmdlet from the DPM Manangement Shell, the command succeeds and I am able to connect to the server.
However, when I enclose the same command in a script and invoke the script through the DPM Management Shell, the following error occurs:
The term 'Connect-DPMServer' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
+ CategoryInfo : ObjectNotFound: (Connect-DPMServer:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Similar is the case with other DPM cmdlets like Get-DPMProtectionGroup.
I am running Powershell version 2.0 on Windows Server 2008 R2.
What is the reason for this peculiar behaviour and how can I get around this?
Edit
There is some observation I made. My script has two parts: A wrapper script and a helper script which is called by the wrapper script as an independent job.
All the DPM Commands are identified in the wrapper script but they are not identified in the helper script when it runs as a job.
Any explanation why this may be and any suggestions to resolve the same?
I figured out the solution and here it is:
What is Happening
The wrapper script runs in the DPM PowerShell and then invokes a helper script as a separate job or thread. The environment in which this helper script runs is windows native powershell and not DPM Powershell. Hence the DPM commands are not identified there.
Solution
The DPM specific modules need to be imported as soon as the helper script is invoked. The steps are as follows :
Right Click on the DPM Management Shell icon and view properties.
Select the value of Target. For me, it looks like this C:\Windows\system32\windowspowershell\v1.0\powershell.exe -noexit -File "D:\DPM\DPM\bin\dpmcliinitscript.ps1"
The value of the parameter -File that is "D:\DPM\DPM\bin\dpmcliinitscript.ps1" is the file which when imported to Windows Powershell converts it to DPM Management Console. That means, it loads the shell with DPM commands.
Include this file in the helper script through dot-sourcing. Which means, the first line of the helper script should look like this : ."D:\DPM\DPM\bin\dpmcliinitscript.ps1"
This will help the invoked shell to identify the DPM specific commands.

Get Powershell top-level script location?

$PSScriptRoot and $PSCommandPath are very useful in locating script files that are consumed by other scripts. Is there an easy, reliable way of getting the top-level script file that was actually executed? My users are right-clicking .ps1 files to execute them from File Explorer.
Example:
C:\Powershell\a.ps1
C:\Powershell\lib\b.ps1
I need a command that I can use from b.ps1 that preferably returns C:\Powershell\a.ps1, or at least C:\Powershell. $PSScriptRoot, $PSCommandPath, and $MyInvocation.MyCommand.Path all return C:\Powershell\lib\b.ps1, though.
Thanks!
This seems to work:
get-variable psscriptroot -scope ((get-pscallstack).count - 2)
That should get you the scriptroot in the fist child scope of the global scope, which would be the script scope of the initial script invocation.
Inside b.ps1, use the cmdlet:
Get-Location
This will return the parent path that you expect.
Why This Works:
This works because you will navigate to the directory C:\Powershell\ and execute the script a.ps1 from there. When you execute . .\lib\b.ps1 you haven't left that execution location, so it will return exactly what you want.
Note: Note that this only works if you first navigate to the directory first, or for scheduled tasks you specify a "Start in" directory. So if you are in a different directory and execute your first script like this: C:\Powershell\a.ps1 it will return your other directory.
If you are really keen, more info on encapsulation is here: Further Down the Rabbit Hole PowerShell Modules and Encapsulation

dot-source failing oin powergui

I'im trying to dot-source a script file in PowerGui 3.0 , but all i get is ;
The term '.\PowerShell.Common.ps1' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spel
ling of the name, or if a path was included, verify that the path is
correct and try again. At
D:\TFS\SharePoint\Dev\Deploy\AutoSPInstaller\SP2010\AutoSPInstaller\AutoSPInstallerFunctionsCustom.ps1:6
char:31
+ .\PowerShell.Common.ps1 <<<<
+ CategoryInfo : ObjectNotFound:
(.\PowerShell.Common.ps1:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
And powerGui subsequently does not offer my script function within said file - in the context sensitive list in the parent script.
the file "PowerShell.Common.ps1" is in the same directory as AutoSPInstallerFunctionsCustom.ps1
Thank you for your assistance
To dot-source the file from PowerGUI's command line, make sure that your current working directory is at the script's directory. You can check this by typing $PWD at PowerGUI's command line.
To reference another script from a script you would do this:
# Get the current script's directory
$MyDir = Split-Path $MyInvocation.MyCommand.Definition
# Dot-source the external script by using the current script's directory
. "$MyDir\ScriptName.ps1"
Getting the script's directory ensures that even if your current working directory is not the same as the script's directory, you will be able to reference files relative to the script's location.
#Rynant is certainly correct in pointing out that the problem is you need to reference the script's directory rather than your current directory. However, it is important to note that his code solution is only partially correct; in fact, whether it works depends on where you call it!
A more robust solution is this:
function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
As it happens, I just wrote a detailed discussion analyzing this very point of correctly getting the script directory in another SO question. Rather than repeat my lengthy answer (complete with test vehicle and results matrix) I will provide this link.
This problem arises when you browse to the script you are working on from within PowerGUI.
Instead of changing the invocation paths to the other scripts you may prefer to run the script in-situ, i.e. with $PWD set to the directory of the script. This is most easily done by opening the script in PowerGUI through the Windows shell by using by the right-click context menu in Windows Explorer.

PowerShell mkdir alias + Set-StrictMode -Version 2. Strange bug. Why?

It's something unbelievable. This is a PowerShell code snippet in test.ps1 file:
Set-StrictMode -Version 2
mkdir c:\tmp\1 # same with 'md c:\tmp\1'
Start cmd.exe, navigate to folder with test.ps1 script and run it:
c:\tmp>powershell ".\test.ps1"
This produces the following error:
The variable '$_' cannot be retrieved because it has not been set.
At line:50 char:38
+ $steppablePipeline.Process($_ <<<< )
+ CategoryInfo : InvalidOperation: (_:Token) [], ParentContainsEr
rorRecordException
+ FullyQualifiedErrorId : VariableIsUndefined
Why?
It works when started from PowerShell console but not cmd.exe. I discovered this bug in much larger script. It was a WTF moment.
What is wrong with this simple script?
Even though a workaround has already been found, I thought people might be interested in an explanation.
As to why it behaves differently in the shell versus cmd.exe, see Powershell 2.0 - Running scripts for the command line call vs. from the ISE
As mentioned in the reference, there is a difference between the following two commands:
powershell ".\test.ps1"
powershell -File ".\test.ps1"
When using the first syntax, it seems to mess with scope, causing the Set-StrictMode command to modify the strict mode for functions defined at the global scope.
This triggers a bug (or an incorrect assumption, perhaps) in the definition of the mkdir function.
The function makes use of the GetSteppablePipeline method to proxy the pipeline for the New-Item cmdlet. However, the author neglected to account for the fact that the PROCESS section is still executed even when there is nothing in the pipeline. Thus, when the PROCESS section is reached, the $_ automatic variable is not defined. If strict mode is enabled, an exception will occur.
One way for Microsoft to account for this would be to replace following line:
$steppablePipeline.Process($_)
with the following:
if (test-path Variable:Local:_) {
$steppablePipeline.Process($_)
}
I admit that this may not be the best way to fix it, but the overhead would be negligible. Another option would be to somehow test if the pipeline is empty in the BEGIN section, and then set $_ to $null.
Either way, if you run your scripts with the "powershell.exe -File filename" syntax, then you won't need to worry about it.
It looks like a bug (in PowerShell).