{"id":12565,"date":"2016-11-29T14:00:11","date_gmt":"2016-11-29T22:00:11","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/?p=12565"},"modified":"2021-09-15T12:12:57","modified_gmt":"2021-09-15T20:12:57","slug":"tips-and-tricks-from-powershell-core-validation","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/tips-and-tricks-from-powershell-core-validation\/","title":{"rendered":"Tips and Tricks from PowerShell Core Validation"},"content":{"rendered":"<p>It has been a privilege for the CAT team to work with customers and the PowerShell team to validate early builds and experiences with PowerShell Core. Some of the customers involved were key influences on the whitepaper, <a href=\"http:\/\/aka.ms\/trpm\">The Release Pipeline Model Applied to Windows Server and Microsoft Cloud<\/a>. As a result, validation has included many experiences from outside the traditional Microsoft tool chain, such as <a href=\"http:\/\/www.vagrantup.com\">Vagrant<\/a> and <a href=\"http:\/\/www.jenkins.io\">Jenkins<\/a>.<\/p>\n<p>For this blog post, I wanted to share some of the learnings that I gained during the validation experience. This is not meant to be a complete picture of PowerShell Core. Rather, a glimpse at some exciting new possibilities.<\/p>\n<h2>Voyage of Discovery<\/h2>\n<p>In my mind the best thing about PowerShell as a scripting language is the never ending journey of exploration. When I have the need to accomplish a task and I&#8217;m unfamiliar with the area of technology, I rely on <strong>Get-Command<\/strong>. If I need to gather information I start the learning process by looking for commands that begin with the verb <strong>Get<\/strong>, so I open the console and type <strong>Get-Command Get-<\/strong>*. When I need to create something new, I&#8217;m going to be looking for commands that start with the verb <strong>New<\/strong>. When I need to change a value, I am probably looking for commands that start with <strong>Set<\/strong>. If I plan to run something, the commands probably start with <strong>Invoke<\/strong>, and so forth.<\/p>\n<p>I view myself as a novice with Linux based servers. I am certainly not an expert but I am by no means a rookie either. Still, it is a muscle I don&#8217;t regularly exercise, so to accomplish even the most routine tasks I require a search engine. My testing began by connecting to an Ubuntu 14.04 server running on Azure using SSH. I followed the documented steps from the <a href=\"https:\/\/github.com\/powershell\/powershell\">PowerShell repo<\/a> to install .NET core and PowerShell Core. Then just as I would when remotely connected to Windows Server Core or Nano, I executed <strong>Get-Command<\/strong> to start looking at which cmdlets are available and how I could combine them to perform operations wizardry.<\/p>\n<p>The &#8220;ah ha!&#8221; moment for me has been combining PowerShell Core with the existing Linux tools and scripting languages. Now the same work patterns that I describe above are applicable on platforms where my domain knowledge has less depth, but I am exploring topics using a familiar approach.<\/p>\n<h2>A simple proof of concept<\/h2>\n<p>I wanted to experiment with simple &#8220;operations tasks&#8221; that I handle daily on Windows based machines. The bare bones list that came to mind was:<\/p>\n<ul>\n<li>getting the IPv4 address of the machine<\/li>\n<li>finding out which DNS name servers it is pointing to<\/li>\n<li>checking the drive configuration<\/li>\n<\/ul>\n<p>I purposefully chose these areas because (at the time of this writing) there are not yet cmdlets available. The challenge would be understanding how to weave together the cmdlets at my disposal until the tasks become simple.<\/p>\n<h3>Getting text using Select-String<\/h3>\n<p>Of the three challenges, the most simple solution was checking which DNS name servers the machine is pointing to. This also allows me to demonstrate a core concept that will be an important skill, getting text.<\/p>\n<p>The existing tools and scripting languages on Linux are GREAT at working with text output. That is a fundamental skill. These tools, such as the <em>cat<\/em> command, could simply be &#8216;wrapped&#8217; by PowerShell functions if needed. I wanted to be stubborn and try working with the native PowerShell cmdlets for pulling information out of text. The <strong>Select-String<\/strong> cmdlet has helped a lot in this pursuit.<\/p>\n<p>If you are not familiar with <strong>Select-String<\/strong>, try the command <strong>Get-Help Select-String -detailed<\/strong> from the console. This will provide you with more information. <strong>Get-Help<\/strong> is a handy way to learn more about cmdlets as you are experimenting.<\/p>\n<pre><code>get-content \/etc\/resolv.conf | select-string nameserver\r\n\r\nnameserver 10.0.0.5\r\n<\/code><\/pre>\n<p>In this example, the one-line command is just capturing the contents of the file resolv.conf, and then returning each line that includes the work &#8216;nameserver&#8217;. Of course it will not always be so simple. You might need to capture multiple lines of text and extract specific values. A good trick to know is the <strong>-Context<\/strong> parameter for <strong>Select-String<\/strong>. The value will be the number of lines before and after the text you are selecting that you want to have returned. Seperate the values with a comma. So to return any line with the word &#8216;nameserver&#8217; and the line after it, see the example below.<\/p>\n<pre><code>get-content \/etc\/resolv.conf | select-string nameserver -context 0,1\r\n\r\n&gt; nameserver 10.0.0.5\r\n  search dns.example.com\r\n<\/code><\/pre>\n<h3>Getting text using Regular Expressions<\/h3>\n<p>As I mentioned above, there are Linux tools to handle this. Great tools, in fact, such as <em>grep<\/em>, <em>awk<\/em>, and <em>sed<\/em>. The challenge I gave myself was to use the style and approach to working with regular expressions that I am most familiar with from PowerShell script examples. Including both the <strong>-match<\/strong> operator and the .NET method <strong>Regex.Matches<\/strong>.<\/p>\n<p>See also:<\/p>\n<ul>\n<li><a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/hh847880.aspx\">about_Regular_Expressions<\/a><\/li>\n<li><a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/hs600312(v=vs.110).aspx\">.NET Framework Regular Expressions<\/a><\/li>\n<\/ul>\n<p>The task of getting the IPv4 address of a server provides a nice example. It is also an opportunity to work with text that is output from a command.<\/p>\n<pre><code>$ipInfo = ifconfig | select-string 'inet'\r\n$ipInfo = [regex]::matches($ipInfo,\"addr:\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b\") | ForEach-Object value\r\n$ipInfo\r\n\r\naddr:10.0.0.2\r\naddr:127.0.0.1\r\n<\/code><\/pre>\n<p>Although that is the output I was looking for, it would be nice to clean up the &#8220;addr:&#8221; text in front of each address. If you are unfamiliar with manipulating strings using PowerShell, pass the result of <strong>Select-String<\/strong> to <strong>Get-Member<\/strong> using a pipeline operator <strong>|<\/strong>.<\/p>\n<pre><code>$ipInfo | Get-Member\r\n<\/code><\/pre>\n<p>Among the available options are <strong>split<\/strong> and <strong>replace<\/strong>.<\/p>\n<pre><code>$ipInfo.replace('addr:','')\r\n\r\n10.0.0.2\r\n127.0.0.1\r\n<\/code><\/pre>\n<p>Finally, I don&#8217;t really need to see the localhost address in every output, so I can apply a filter using the <strong>Where-Object<\/strong> cmdlet.<\/p>\n<pre><code>$ipInfo.replace('addr:','') | Where-Object {$_ -ne '127.0.0.1'}\r\n\r\n10.0.0.2\r\n<\/code><\/pre>\n<p>To take this work and create a nice function that we can re-use in the future.<\/p>\n<pre><code>function Get-IPAddress {\r\n    $ipInfo = ifconfig | select-string 'inet'\r\n    $ipInfo = [regex]::matches($ipInfo,\"addr:\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b\") | ForEach-Object value\r\n    $ipInfo.replace('addr:','') | Where-Object {$_ -ne '127.0.0.1'}\r\n}\r\n\r\nGet-IPAddress\r\n\r\n10.0.0.2\r\n<\/code><\/pre>\n<h2>Objects<\/h2>\n<p>All this working with text is unfamiliar to experienced PowerShell users. A core concept in PowerShell is working with Objects. The last example, getting a list of available disks, provided an opportunity to try this. Also, since getting disk information is a privileged operation, I got a chance to try the <em>sudo<\/em> command within a function in PowerShell Core.<\/p>\n<p>Piling on the ideas we have explored so far, I need to find the command that will provide text output with disk information, and I will need to manipulate that text. The new concept this example introduces is taking that text and putting it in an object using the <strong>New-Object<\/strong> cmdlet. The text string values are simply passed in to the <strong>-Property<\/strong> parameter as a hashtable.<\/p>\n<p>Note the use of square brackets to index in to an array. For example, $array[0] returns just the first value.<\/p>\n<pre><code>Function Get-DiskInfo {\r\n    $disks = sudo parted -l | Select-String \"Disk \/dev\/sd*\" -Context 1,0\r\n    $diskinfo = @()\r\n    foreach ($disk in $disks) {\r\n        $diskline1 = $disk.ToString().Split(\"`n\")[0].ToString().Replace('  Model: ','')\r\n        $diskline2 = $disk.ToString().Split(\"`n\")[1].ToString().Replace('&gt; Disk ','')\r\n        $i = New-Object psobject -Property @{'Friendly Name' = $diskline1; Device=$diskline2.Split(': ')[0]; 'Total Size'=$diskline2.Split(':')[1]}\r\n        $diskinfo += $i\r\n    }\r\n    $diskinfo\r\n}\r\n\r\nGet-DiskInfo\r\n[sudo] password for psuser:\r\n\r\nFriendly Name            Total Size Device\r\n-------------            ---------- ------\r\nMsft Virtual Disk (scsi)  31.5GB    \/dev\/sda\r\nMsft Virtual Disk (scsi)  145GB     \/dev\/sdb\r\n<\/code><\/pre>\n<p>Now you can work with specific properties of the object. Recall you can always pipe the object to <strong>Get-Member<\/strong> to see which properties and methods are available.<\/p>\n<pre><code>(Get-DiskInfo).Device\r\n\/dev\/sda\r\n\/dev\/sdb\r\n\r\n<\/code><\/pre>\n<h2>Performing the same work across platforms<\/h2>\n<p>For the final challenge, is it now possible to author a function in PowerShell that can be run across Windows, Linux, and macOS? There is a simple solution that makes this very straightforward.<\/p>\n<p>PowerShell Core includes variables:<\/p>\n<ul>\n<li>$IsLinux<\/li>\n<li>$IsOSX<\/li>\n<li>$IsWindows<\/li>\n<\/ul>\n<p>So in your function you can perform Linux type work in one <strong>IF<\/strong> block and Windows type work in another, and as long as you return an object with the same properties you can work with the results using the same cmdlets. In testing I found it handy to check if I was in Linux or OSX and if not, use cmdlets from modules I expected to find in Windows. Since the $IsWindows variable is not available (at the time of this writing) in the version of PowerShell provided with Windows.<\/p>\n<pre><code>function Get-Something {\r\n    if ($IsLinux -or $IsOSX) {\r\n        # Get this information using approaches for Linux and\/or OSX\r\n        $something = get-content \/file | select-string text\r\n        }\r\n    else {\r\n        # Get this information using cmdlets already available in Windows\r\n        $something = get-whatever -parameter value\r\n    }\r\n\r\n    $something | Where-Object {$_ -ne 'excluded value'}\r\n}\r\n<\/code><\/pre>\n<h2>For more information<\/h2>\n<p>For more information on this topic and for expansion on the examples used above, see &#8216;Learning PowerShell&#8217; in the docs folder of the <a href=\"https:\/\/github.com\/powershell\/powershell\">PowerShell repo<\/a>. While you are there, you might also visit the &#8216;demos&#8217; folder at the root of the repository for the latest examples of using PowerShell in cross-platform environments.<\/p>\n<p>Thank you!\nMichael Greene\nPrincipal Program Manager\nECG CAT<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It has been a privilege for the CAT team to work with customers and the PowerShell team to validate early builds and experiences with PowerShell Core. Some of the customers involved were key influences on the whitepaper, The Release Pipeline Model Applied to Windows Server and Microsoft Cloud. As a result, validation has included many [&hellip;]<\/p>\n","protected":false},"author":658,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[134,248,335],"class_list":["post-12565","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-core","tag-powershell","tag-validation"],"acf":[],"blog_post_summary":"<p>It has been a privilege for the CAT team to work with customers and the PowerShell team to validate early builds and experiences with PowerShell Core. Some of the customers involved were key influences on the whitepaper, The Release Pipeline Model Applied to Windows Server and Microsoft Cloud. As a result, validation has included many [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/12565","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/658"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=12565"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/12565\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=12565"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=12565"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=12565"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}