{"id":26186,"date":"2016-05-16T00:00:00","date_gmt":"2016-05-16T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/persistent-powershell-the-powershell-profile\/"},"modified":"2017-10-03T13:33:44","modified_gmt":"2017-10-03T13:33:44","slug":"persistent-powershell-the-powershell-profile","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/persistent-powershell-the-powershell-profile\/","title":{"rendered":"Persistent PowerShell: The PowerShell Profile"},"content":{"rendered":"<div class=\"article-content\">\n<p class=\"start\">While you work with a PowerShell session, you can modify its state (or environment) in any number of ways: You might import modules, create functions, specify aliases, or define variables, to name a few. But all of those are transitory: their effects disappear as soon as you close the PowerShell session. However, PowerShell provides a mechanism, called the <em>PowerShell profile<\/em>, which allows you to recreate any such environmental constructs and settings each time you start up a new PowerShell session. And if one profile is good, wouldn&#8217;t more be better? It turns out that any single PowerShell session may use any one (or all) of <em>four<\/em> different profiles, and <em>different<\/em> PowerShell hosts provide yet more profile choices: But it is actually much simpler to use than all this might appear, once you appreciate where all the moving parts fit. Let&#8217;s take a look.<\/p>\n<h2>The Shell in PowerShell<\/h2>\n<p>There is a distinction between the <em>shell<\/em> and the <em>host<\/em> in PowerShell. Don Jones, in his aptly titled post <a href=\"http:\/\/powershell.org\/wp\/2013\/10\/19\/the-shell-vs-the-host\/\">The Shell vs. The Host<\/a>, explains that you-as a user typing at the keyboard-do <em>not<\/em> directly interact with PowerShell&#8217;s shell (or engine). Rather more accurately, you interact with a host application (like powershell.exe or powershell_ise.exe) that creates a <em>runspace<\/em> (an instance of the PowerShell engine). You can actually see the distinction by displaying the contents of the Name property of the <code>$Host<\/code> system variable alongside the <code>$ShellId<\/code> variable. You will see that, if you examine the values for the three most common PowerShell hosts, they all use the same engine (shell) while each presents a different user interface (host).<\/p>\n<table class=\"datagrid\">\n<thead>\n<tr>\n<td>Host<\/td>\n<td>$Host.Name<\/td>\n<td>$ShellId<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>PowerShell<\/td>\n<td>ConsoleHost<\/td>\n<td>Microsoft.PowerShell<\/td>\n<\/tr>\n<tr>\n<td>PowerShell ISE<\/td>\n<td>Windows PowerShell ISE Host<\/td>\n<td>Microsoft.PowerShell<\/td>\n<\/tr>\n<tr>\n<td>Visual Studio Package Manager Console<\/td>\n<td>Package Manager Host<\/td>\n<td>Microsoft.PowerShell<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Other PowerShell hosts could potentially have different <code>$ShellId<\/code> values (for example, some of the freely available PowerShell IDEs include PowerGUI, PowerShell Analyzer, and PowerShell Plus, but I&#8217;ve not checked their <code>$ShellId<\/code> values).<\/p>\n<h2>The PowerShell Profile<\/h2>\n<p>A PowerShell profile is nothing more than a fancy name for a script that runs when a PowerShell host starts. Quoting standard PowerShell help on <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/hh847857.aspx\">about_profiles<\/a>, &#8220;You can use the profile as a logon script to customize the environment. You can add commands, aliases, functions, variables, snap-ins, modules, and Windows PowerShell drives. You can also add other session-specific elements to your profile so they are available in every session without having to import or re-create them.&#8221;<\/p>\n<p>Each PowerShell host actually supports <em>two<\/em> profiles, one is <em>user<\/em>-level, distinct for each user, and the other is <em>system<\/em>-level, common to all users. This should be a familiar paradigm that you will have seen with many Windows applications. PowerShell adds its own unique twist, though: it similarly distinguishes between <em>host<\/em>-level (one profile for each host) and <em>system<\/em>-level (one common profile for all hosts).<\/p>\n<p>Thus, taking all the combinations of users and hosts, you could potentially use any (or all) of <em>four<\/em> different profiles:<\/p>\n<ul>\n<li>AllUsersAllHosts<\/li>\n<li>AllUsersCurrentHost<\/li>\n<li>CurrentUserAllHosts<\/li>\n<li>CurrentUserCurrentHost<\/li>\n<\/ul>\n<p>These profiles all peaceably co-exist so you then need to be aware of the precedence-they are listed above <em>in the order <\/em> <em>of execution<\/em>. If you were to define the <em>same<\/em> variable in all four profiles, the variable will, once you launch a PowerShell host and finally receive a prompt, have the value assigned by the <em>last<\/em> profile, <code>CurrentUserCurrentHost<\/code>, because each successively processed profile will overwrite that variable with its value. Another example, showing cooperation between profiles rather than contention, might be to increment a variable. First define and initialize it to a starting value (e.g. <code>$someVar<\/code> = 0) in <code>AllUsersAllHosts<\/code>, then increment it in each of the other profiles (e.g. <code>$someVar++<\/code> or perhaps <code>$someVar += 5<\/code> depending on what you want to do with it).<\/p>\n<p>As to which of the four to use, that largely depends on your own needs: if you use a dedicated computer (i.e. not shared with anyone else) you don&#8217;t need to worry about the &#8220;all users&#8221; profiles. If you do use multiple hosts, you might want to differentiate some things between an &#8220;all hosts&#8221; profile and a specific host profile. I&#8217;ll be giving more details about this below in &#8220;How Many Profiles Do You Need?&#8221;<\/p>\n<h2>The $Profile Variable<\/h2>\n<p>To create or edit any of the profiles, of course, you need to know where to find them. PowerShell itself can easily tell you, but it can also just open one for editing without you having to explicitly bother with the path. To see the path, display the value of the <code>$Profile<\/code> variable. It reveals a single file path, which is the path to the <code>CurrentUserCurrentHost<\/code> profile. In a standard PowerShell host, mine shows this:<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true listing powershell\">PS&gt; $Profile\r\nC:\\Users\\msorens\\Documents\\WindowsPowerShell\\Microsoft.PowerShell_profile.ps1\r\n<\/pre>\n<p>Note that this makes no claim about whether the file <em>exists<\/em>, only that that is the path it must have <em>if<\/em> it exists. To check existence, use <code>Test-Path<\/code>:<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true listing powershell\">PS&gt; Test-Path $Profile\r\nTrue\r\n<\/pre>\n<p>If the profile does not exist, you can easily create it:<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true listing powershell\">PS&gt; New-Item -Type file -Path $Profile -Force\r\n<\/pre>\n<p>And if you want to have that in a script, you can then combine the above, creating the file only if necessary:<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true listing powershell\">PS&gt; if (!(Test-Path $Profile)) {\r\n    New-Item -Type file -Path $Profile -Force }\r\n<\/pre>\n<p>Finally, to edit the profile, just invoke your favorite editor on the file, or you can always use the ubiquitous notepad editor:<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true listing powershell\">PS&gt; notepad $Profile<\/pre>\n<p>All of the above examples are for the &#8220;default&#8221; (<code>CurrentUserCurrentHost<\/code>) profile, as mentioned. But you can apply all of the same commands to any of the four profiles by specific reference; either of these produce the same result:<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true listing powershell\">PS&gt; notepad $Profile \r\nPS&gt; notepad $Profile.CurrentUserCurrentHost\r\n<\/pre>\n<p>Substitute any of the other three profile property names to access one of the non-default profiles.<\/p>\n<div class=\"note\">\n<p class=\"note\">Note that it can be tricky to create or save files in system directories (where the &#8220;all users&#8221; profiles are stored). <\/p>\n<p> You can do so with certain Microsoft tools (e.g. notepad) but you <em>cannot<\/em> with certain non-Microsoft tools (e.g. my favorite editor, vim). Curiously, vim <em>pretended<\/em> to work for me but it actually did not: I could create a file, close the editor completely, then re-open the editor and recall the file-yet the file did not show up in Windows Explorer nor was it seen by PowerShell upon startup! (I do not quite know the root cause of this issue, but it is <em>not<\/em> due to lack of elevated privileges.) <\/p>\n<p> On the other hand, notepad apparently knows the secret incantation, as that works as expected. Another workaround is to create your &#8220;all users&#8221; profile in your own user directory then just copy or move it into the appropriate system directory.<\/p>\n<\/div>\n<h2>Profile File Names &amp; Locations<\/h2>\n<p>The previous section explained how to edit any of your profiles without actually knowing where they are on the file system, but you are a developer; you have an innate compulsion to know where they are hiding! The table shows the path of each. ( <em>HostId<\/em> is a placeholder, explained in just a bit.)<\/p>\n<table class=\"datagrid\">\n<thead>\n<tr>\n<td>Profile<\/td>\n<td>Location<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>AllUsersAllHosts<\/td>\n<td>$PsHome\\profile.ps1<\/td>\n<\/tr>\n<tr>\n<td>AllUsersCurrentHost<\/td>\n<td>$PsHome\\<em>HostId<\/em>_profile.ps1<\/td>\n<\/tr>\n<tr>\n<td>CurrentUserAllHosts<\/td>\n<td>$Home\\Documents\\WindowsPowerShell\\profile.ps1<\/td>\n<\/tr>\n<tr>\n<td>CurrentUserCurrentHost<\/td>\n<td>$Home\\Documents\\WindowsPowerShell\\<em>HostId<\/em>_profile.ps1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"note\">\n<p class=\"note\">An error that I have seen in a number of articles available on the web is that the &#8220;all users&#8221; profiles are under <code>$env:WinDir\\System32<\/code>. <strong>That<\/strong> <strong> is <\/strong> <strong>incorrect<\/strong> <strong>!<\/strong> <code>$PsHome<\/code> may coincidentally resolve to <code>$env:WinDir\\System32<\/code> for some hosts but not for all. As an example, on my system the Visual Studio Package Manager Console stores its &#8220;all users&#8221; profiles under <code>$env:WinDir\\SysWOW64<\/code>. (This error appears even in articles from very reputable sources, like <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/bb613488(v=vs.85).aspx\">this MSDN article<\/a>.)<\/p>\n<\/div>\n<p>Reviewing the locations, it is simple to understand the naming conventions. The system-level profiles-those for all users-are in the system directory pointed to by <code>$PsHome<\/code>. The user-level profiles are under each user&#8217;s home directory. The one point that needs explanation is the <code>HostId<\/code> shown for the host-specific profiles in the table. Unfortunately, this host id bears <em>no<\/em> direct resemblance to the host description nor to the <code>$Host.Name<\/code> property! The way to discover the <code>HostId<\/code> is simply to display the value of the <code>$Profile<\/code> variable, as it is part of the path. For convenience, here are the <code>HostId<\/code> values for the most common hosts:<\/p>\n<table class=\"datagrid\">\n<thead>\n<tr>\n<td>Host<\/td>\n<td>HostId<\/td>\n<td>$Host.Name<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>PowerShell<\/td>\n<td>Microsoft.PowerShell<\/td>\n<td>ConsoleHost<\/td>\n<\/tr>\n<tr>\n<td>PowerShell ISE<\/td>\n<td>Microsoft.PowerShellISE<\/td>\n<td>Windows PowerShell ISE Host<\/td>\n<\/tr>\n<tr>\n<td>Visual Studio Package Manager Console<\/td>\n<td>NuGet<\/td>\n<td>Package Manager Host<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"note\">\n<p class=\"note\">Another error out there in the wild, though less common, is that <code>HostId<\/code> is equivalent to the <code>$ShellId<\/code> variable mentioned earlier. <strong>That<\/strong> <strong> is incorrect!<\/strong> As you have seen, all three of the common hosts shown just above have the same <code>$ShellId<\/code>, and that coincidentally matches the <code>HostId<\/code> <em>only<\/em> for the standard PowerShell host. (This error appears in, for example, the book <a href=\"http:\/\/www.informit.com\/articles\/article.aspx?p=729101&amp;amp;seqNum=6\">Windows PowerShell Unleashed<\/a>.)<\/p>\n<\/div>\n<h2>How Many Profiles Do You Need?<\/h2>\n<p>Any standard Windows system has two profiles for the standard PowerShell host, two for the PowerShell ISE host, and two for all hosts-six in all <em>as a minimum<\/em>. Add in the VS package manager that I have shown, that&#8217;s two more. Add other PowerShell IDEs-two more for each. How do you manage so many profiles?\u00a0<\/p>\n<p>Interpreting official MSDN doctrine ( <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/hh847857.aspx\">about_profiles<\/a>) consider a tripartite solution.\u00a0<\/p>\n<ul>\n<li>First, put truly common things in <code>AllUsersAllHost<\/code>.\u00a0<\/li>\n<li>Second, if there are some peculiarities in particular hosts, use <code>AllUsersCurrentHost<\/code> for those miscreant hosts.\u00a0<\/li>\n<li>Finally, let each user manage his\/her own preferences and settings in user-specific profiles.\u00a0<\/li>\n<\/ul>\n<p>But again, there could be many different profiles for &#8220;current host&#8221;, both at the system level and at the user level. One good reference, as you are mulling over your choices here, is Ed Wilson&#8217;s (The Scripting Guy) post <a href=\"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2012\/05\/22\/deciding-between-one-or-multiple-powershell-profiles\/\">Deciding Between One or Multiple PowerShell Profiles<\/a>. He includes in there a list of advantages and disadvantages for choosing between one profile and multiple profiles. But to me, the advantages of a single profile far outweigh the advantages of multiple profiles. It takes a little more work to set up, but you can still account for host-specific differences while having everything in one place, making it much simpler to maintain over time.\u00a0<\/p>\n<p>Most of your profile stuff will probably be common to any host, so using a single profile means that any changes down the road will be done in exactly one file. For the stuff that differs amongst hosts, just include a section specific to each host you care about. Mine includes something like this:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">if ($Host.Name -eq 'ConsoleHost')\r\n{\r\n    Import-Module PSReadline\r\n    # differentiate verbose from warnings!\r\n    $privData = (Get-Host).PrivateData\r\n    $privData.VerboseForegroundColor = \"cyan\"\r\n}\r\nelseif ($Host.Name -like '*ISE Host')\r\n{\r\n    Start-Steroids\r\n    Import-Module PsIseProjectExplorer\r\n}\r\nif (!$env:github_shell)\r\n{\r\n    # not sure why, but this fails in a git-flavored host\r\n    Add-PSSnapin Microsoft.TeamFoundation.PowerShell\r\n}<\/pre>\n<p>Notice how I identify the current host with <code>$Host.Name<\/code> then selectively execute code. You see examples above for the standard PowerShell host as well as the PowerShell ISE host. For the former, it loads the PSReadline extension which only works in the PowerShell host. For the latter, it loads the PsISEProjectExplorer and the ISE Steroids module, both of which can only function in the ISE environment.\u00a0<\/p>\n<p>Finally, if you use Git and the particular installer you used created a Git PowerShell host for you, note that it <em>cannot<\/em> be distinguished from the standard PowerShell host by the <code>$Host.Name<\/code>. Instead, I identified a uniquely defined variable, <code>$env:github_shell<\/code>, which is only present in the Git-flavored host.<\/p>\n<h2>What to Put in your Profile<\/h2>\n<p>The answer to this question depends on your individual needs so it has to be very abstract: <em>put in your profile what you can use to be more productive.<\/em> There is therefore no single answer to this, but I can certainly provide some suggestions to get your creative juices flowing. You have just seen above a few examples of importing third-party modules to add functionality to the given host. Besides module imports, the following sections provide just a few ideas to get you thinking about what you might want to put in there. Also, take a look at <a href=\"http:\/\/stackoverflow.com\/q\/138144\/115690\">What&#8217;s in your PowerShell `profile.ps1` file?<\/a> (on StackOverflow) for many more examples.<\/p>\n<h3>Aliases<\/h3>\n<p>Many built-in PowerShell cmdlets have aliases; some have multiple aliases. You can, for example use <code>ls<\/code> or <code>dir<\/code> or <code>gci<\/code> instead of typing <code>Get-ChildItem<\/code>. For those that you use regularly that do not provide aliases (or for your own custom functions and cmdlets) you can create your own aliases with <code>Set-Alias<\/code>. <code>Set-Alias<\/code> is appropriate if you just want to abbreviate the name of the cmdlet or function. But sometimes you might want an alias to include, for example, a cmdlet name plus a parameter that you tend to always use. For these, you can emulate an alias with a simple function. To illustrate, consider the cmdlet <code>Import-Module<\/code>. I use it frequently and I prefer all my frequent use cmdlets to just use the first letter of each component. This sets up the alias <code>im<\/code> to do that:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true listing powershell  \">Set-Alias im Import-Module<\/pre>\n<p>But being a developer, I also need to frequently use <code>Import-Module<\/code> with the <code>-Force<\/code> switch. So for that, I need to resort to a function. For my naming convention, I add the first letter of the switch, hence <code>imf<\/code> here:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true listing powershell\">function imf($name)   { Import-Module $name -force }<\/pre>\n<p>I can then use, e.g. <code>im foobar<\/code> to do a vanilla import, or <code>imf foobar<\/code> to import with <code>-Force<\/code> applied.<\/p>\n<h3>Simple Functions<\/h3>\n<p>Functions were just mentioned as a means to creating pseudo-aliases, i.e. essentially to save you typing. But, of course, they are not limited to that purpose. You might want to include in your profile a variety of &#8220;one-liners&#8221; that both save typing and save having to remember details of cmdlets you use less often. Quick, how do you show the last 50 items in your command history? Not certain? The cmdlet to use is <code>Get-History<\/code> (it has a standard alias of just the letter h). Is it easy to remember <code>Get-History -Count 50<\/code> or just h50? Here&#8217;s my definition for h50 (and an h10 thrown in just for good measure):<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true listing powershell\">function h50 { Get-History -Count 50 }\r\nfunction h10 { Get-History -Count 10 }\r\n<\/pre>\n<p>Here&#8217;s a more interesting function. How would you reveal the cmdlet behind an alias, the path to an executable given just the program name, the parameter sets available for a given cmdlet, or the content of a user-defined function? I use this one-liner to do all that (named after the unix\/linux command that performs similarly):<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true listing powershell\">function which($cmd) { (Get-Command $cmd).Definition }<\/pre>\n<p>Here are some results of using it:<\/p>\n<pre class=\"theme:powershell-output lang:ps decode:true listing powershell \">PS&gt; which h\r\nGet-History\r\n\r\nPS&gt; which notepad\r\nC:\\Windows\\system32\\notepad.exe\r\n\r\nPS&gt; which which\r\nparam($cmd)\r\n (Get-Command $cmd).Definition\r\n\r\nPS&gt; which import-module\r\n\r\nImport-Module [-Name] &lt;string[]&gt; [-Global] [-Prefix &lt;string&gt;] [-Function &lt;string[\r\nmObject] [-MinimumVersion &lt;version&gt;] [-RequiredVersion &lt;version&gt;] [-ArgumentList\r\n\r\nImport-Module [-Name] &lt;string[]&gt; -PSSession &lt;PSSession&gt; [-Global] [-Prefix &lt;strin\r\n] [-PassThru] [-AsCustomObject] [-MinimumVersion &lt;version&gt;] [-RequiredVersion &lt;ve\r\narameters&gt;]\r\n\r\nImport-Module [-Name] &lt;string[]&gt; -CimSession &lt;CimSession&gt; [-Global] [-Prefix &lt;str\r\nce] [-PassThru] [-AsCustomObject] [-MinimumVersion &lt;version&gt;] [-RequiredVersion &lt;\r\nsourceUri &lt;uri&gt;] [-CimNamespace &lt;string&gt;] [&lt;CommonParameters&gt;]\r\n<\/pre>\n<p>Finally, another handy one-liner that again works like the unix\/linux command, reporting your domain-qualified user name:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true listing powershell\">function whoami { (get-content env:\\userdomain) + \"\\\" + (get-content env:\\username }<\/pre>\n<h3>Complex Functions<\/h3>\n<p>It is likely that you will create some utility functions in the course of your work that are much more involved than a simple one-liner. You could, of course, just embed them in your profile but that tends to make your profile long and messy. If you have a collection of such functions you could always organize them into a true PowerShell module and then just add an <code>Import-Module<\/code> in your profile to bring them in. For something in-between those two extremes, consider this approach. In my profile I have this command sequence that brings all the scripts in my local profile_scripts directory into scope at startup:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true listing powershell\">Resolve-Path $PSScriptRoot\\profile_scripts\\*.ps1 |\r\n\tWhere-Object { -not ($_.ProviderPath.Contains(\".Tests.\")) } |\r\n\tForeach-Object { . $_.ProviderPath }\r\n<\/pre>\n<p><code>$PSScriptRoot<\/code> is a standard system variable that only exists within the context of a running script. It resolves to <code>$Home<\/code>\\Documents\\WindowsPowerShell\\. Thus my profile_scripts directory is under that path. Any script (sans any test scripts) is dot-sourced, making it visible in your current scope as soon as you have a prompt after startup.<\/p>\n<h3>Perform Actions<\/h3>\n<p>The previous items in this section are all passive; they define things for you to use at some future time after startup. But you can also include active items that execute during startup. I highly recommend you use the &#8220;don&#8217;t let me shoot myself in the foot&#8221; cmdlet:<\/p>\n<pre class=\"theme:powershell-ise lang:ps decode:true listing powershell \">Set-PSDebug -Strict <\/pre>\n<p>Languages that are weakly-typed (e.g. JavaScript, PowerShell) give you the <em>option<\/em> of working safely or not, whereas strongly-typed languages generally force you to work safely. Primarily, <em>safely<\/em> means not allowing you to use a variable before it is declared. That prevents inadvertent mistyping from causing you untold grief trying to figure out why your code is not working. (Presumably, the weakly-typed language folks think some people might find operating safely burdensome, so they make it an option.) Just put the <code>Set-PSDebug<\/code> in your profile. Be safe. Please.<\/p>\n<p>Other types of actions you might put in your profile are things like displaying some statistics, e.g. up time, disk space, or PowerShell execution policy. If you administer multiple machines you might want to see details about the machine you have &#8216;remoted&#8217; into, to make sure you&#8217;re on the box you think you are (domain name, computer name, IP address, etc.).<\/p>\n<h2>Securing Your Profile<\/h2>\n<p>When dealing with computers, security must always be a consideration. You use a firewall and an antivirus to try to keep your system safe. Similarly you need to consider PowerShell scripts that you run- <em>including your own profiles<\/em>. PowerShell has good support for security, starting with its default setting of not letting you run <em>any<\/em> scripts; out of the box you can only use PowerShell commands interactively. You need to open your system just enough to enable you to accomplish whatever work you need to do by setting your computer&#8217;s execution policy (see <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/hh849812(v=wps.630).aspx\">Set-ExecutionPolicy<\/a>).<\/p>\n<p>But as soon as you allow running scripts, there is a chance that you might inadvertently be running a <em>compromised<\/em> script. This is nothing unique to PowerShell scripts per se- <em>anything<\/em> on your computer could be compromised-it is just that PowerShell helps you mitigate the situation. And it does this by allowing you to set the execution policy to different security levels per your own needs. You can require that every script must be authenticated, or that just any scripts that you download must be authenticated, among other options. <em>Authentication<\/em>, in this case, refers to signing scripts with a digital signature (see <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/hh849819(v=wps.630).aspx\">Set-AuthenticodeSignature<\/a>) so that, if a file is modified (maliciously or not), the digital signature would detect the tampering and prevent the script from running.<\/p>\n<p>Managing security for your PowerShell scripts (including your profiles), however, is not a trivial endeavor. (It would more than double the length of this article!) But a lot of good information is already out there to guide you. I would recommend starting with another article here on Simple-Talk, Nicolas Prigent&#8217;s <a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/powershell-day-to-day-sysadmin-tasks-securing-scripts\/\">PowerShell Day-to-Day SysAdmin Tasks: Securing Scripts<\/a>. There are also several good references in PowerShell&#8217;s own documentation: <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/hh847874.aspx\">about_signing<\/a> gives a good introduction to the topic; <a href=\"https:\/\/technet.microsoft.com\/library\/72fcdaea-bb33-40c6-afce-e7c33cf622d0(v=wps.630).aspx\">New-SelfSignedCertificate<\/a> lets you create your own self-signed certificates, and <a href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/microsoft.powershell.security\/providers\/get-childitem-for-certificate?view=powershell-5.1\">Get-ChildItem for Certificate<\/a> reveals the little-known differences in <code>Get-ChildItem<\/code> when referencing your certificate store. Microsoft provides an old but still useful reference on <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/hardware\/dn653556(v=vs.85).aspx\">Code-Signing Best Practices<\/a>. And Geoff Bard&#8217;s <a href=\"http:\/\/www.hanselman.com\/blog\/SigningPowerShellScripts.aspx\">Signing PowerShell Scripts<\/a> is worth a look as well.<\/p>\n<h2>Get That Profile Out of the Way!<\/h2>\n<p>Now you know how to set up your profile, why it is useful, what to do with it, and how to safeguard it. But like any super power, you have to be cognizant of its dark side. Well, not so much a dark side per se, but that there are times you simply do not want your profile(s) to get in your way. Or, more poignantly, other people&#8217;s profiles.<\/p>\n<p>There are a variety of situations where you might want to actually execute powershell.exe either with a command literal or a script file to run. Here is just one example: say you have created a PowerShell script that you want to share with a colleague. Unlike batch files, you cannot just double-click a PowerShell script to execute it; that&#8217;s part of PowerShell&#8217;s security modus operandi to keep your system safe. But that is easy to circumvent (not that I am recommending it!) by creating a standard Windows shortcut targeting powershell.exe with your PowerShell script file as a parameter.<\/p>\n<p>Another, perhaps more legitimate use, would be to run a PowerShell script or command within a build file. Since MSBuild does not innately know how to run PowerShell scripts, you would typically execute a script by supplying it as an argument to powershell.exe.<\/p>\n<p>Anytime you run powershell.exe, though, you are opening a new PowerShell host. And what happens when you open a host? It runs any (or all) of your four profiles! But almost any time you are opening a host by directly invoking powershell.exe you do <em>not<\/em> want your profiles to run, neither for the overhead nor for the possible conflicts that might ensue. Keep in mind, if someone else is running a build where you have introduced a command to run powershell.exe, it is <em>their <\/em>profile that will be run on their machine, and you have no notion of what might be lurking there. Further still, you do not want to depend on something in a profile because the first time someone runs your build who does not know of the dependency, it will (possibly mysteriously) fail. So it is safest all around to simply adopt the best practice of always ignoring profiles when you invoke powershell.exe. (I don&#8217;t mean <em>you<\/em> should ignore them, rather that you should tell PowerShell to ignore them, of course!<\/p>\n<p>So after all that suspenseful buildup, the denouement may be a bit anticlimactic: simply add a <code>-NoProfile<\/code> as a parameter to powershell.exe.<\/p>\n<h2>Conclusion<\/h2>\n<p>The PowerShell profile is your friend. With the roadmap set out in this article, you have seen the types of profiles available to you and can select the ones that will work for you. Or you can choose to use a single profile, distinguishing any host-specific items as needed. The profile is a simple yet powerful tool available to you, not at all complicated to use, and its uses are limited only by your imagination. About the only downside is all the time you are now going to spend searching for handy and clever things to add to your profile. (Did I mention you should look at including the <a href=\"https:\/\/github.com\/cameronharp\/Go-Shell\">Go-Shell<\/a> module&#8230;?)<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>You can mould PowerShell to the way you want to work, with all the settings and modules that you require, by using the profiles. Profiles are PowerShell scripts that run at startup, and once you have understood where they are and when each is used, they have a whole range of uses that make using PowerShell a lot more convenient. &hellip;<\/p>\n","protected":false},"author":221868,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[35],"tags":[],"coauthors":[6802],"class_list":["post-26186","post","type-post","status-publish","format-standard","hentry","category-powershell"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/26186","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221868"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=26186"}],"version-history":[{"count":11,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/26186\/revisions"}],"predecessor-version":[{"id":73801,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/26186\/revisions\/73801"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=26186"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=26186"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=26186"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=26186"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}