├── .vscode └── launch.json ├── LICENSE ├── README.md ├── best-practices.md ├── ch02 └── hello-world.ps1 ├── ch04 └── Initialize-PacktPs6CoreLinuxLab.ps1 ├── ch05 ├── 6.2-dotnetcore-obj.ps1 └── 6.6-CustomTypes.ps1xml ├── ch06 ├── 03-input-file-expanding.txt ├── 03-input-file.txt ├── 05-Get-TravelArticles.ps1 ├── 05-sitelist.csv ├── 06-puregfm.md ├── 06-rawcontent.txt ├── 07-input.csv ├── 07-split-input.ps1 ├── 08-details.txt └── 08-mailto.txt ├── ch07 ├── 02.1-Get-WeekDay-if-else.ps1 ├── 02.2-Get-Weekday-switch-case.ps1 ├── 03-Wait-BeforeDressCode.ps1 ├── 04-Write-Greetings.ps1 ├── 05-Write-GuestSeatDetails.csv ├── 05-Write-GuestSeatDetails.ps1 ├── 06-Write-GuestSeatDetails.ps1 ├── 07.1-Write-GuestSeatDetails.ps1 ├── 07.2-Find-MothersDay.ps1 ├── 08-Remove-EmptyDirectories.ps1 ├── 08-input-file.txt └── 09-Remove-EmptyDirectories.ps1 ├── ch08 ├── 01-Clear-LogFiles.ps1 ├── 01-Set-LastWriteTime.ps1 ├── 02-Clear-LogFiles.ps1 ├── 05-ConvertTo-Rgb.ps1 └── 06-ConvertTo-OtherBases.ps1 ├── ch09 ├── 01-New-MultiDimensionalArray.ps1 ├── 02-Convert-Name.ps1 ├── 02-names.txt ├── 03-Convert-SortedNames.ps1 ├── 04-Find-Name.ps1 ├── 05-Combine-Arrays.ps1 ├── 05-list-one.txt ├── 05-list-two.txt ├── 06-Search-ByLastname01.ps1 ├── 06-Search-ByLastname02.ps1 ├── 07-Clear-Paths01.ps1 ├── 07-Clear-Paths02.ps1 ├── 08-Compare-ServerName.ps1 ├── 08-server-names-01.txt ├── 08-server-names-02.txt ├── 09-New-MemoryList.ps1 └── 10-New-ProcessReport.ps1 ├── ch10 ├── 01-Get-FileDetails.ps1 ├── 01-random-text.txt ├── 02-Write-ContentOne.ps1 ├── 02-Write-ContentTwo.ps1 ├── 03-Modify-FileContent.ps1 ├── 04-Search-FileForPattern.ps1 ├── 05-Get-InvocationInformation.ps1 ├── 06-New-FilesAndDirectories.ps1 ├── 07-Get-FilteredFiles.ps1 ├── 08-Read-MdmLog.ps1 └── 08-mdm-reinstall-log.log ├── ch11 ├── 04-Start-Count.ps1 ├── 05-Start-Count.ps1 ├── 06-Start-Count.ps1 └── 07-Start-Count.ps1 ├── ch12 ├── 01-Start-Count.ps1 ├── 01-Start-CountFlexible.ps1 ├── 02-Start-Count.ps1 ├── 03-New-PersonalMessage.ps1 ├── 04-Start-Count.ps1 ├── 05-Start-Count.ps1 ├── 06-New-File.ps1 ├── 07-New-File.ps1 ├── 08-Start-Count.ps1 ├── 09-New-File.ps1 ├── 10-New-File.ps1 ├── 11-New-File.psm1 └── 12-New-File.psm1 ├── ch13 ├── 01-New-File.ps1 ├── 02-New-File.ps1 ├── 03-Set-Name.ps1 ├── 04-New-File.ps1 ├── 05-Set-Name.ps1 ├── 06-Find-Item.ps1 ├── 07-Find-Item.ps1 ├── 08-New-LogFile.ps1 └── 09-New-LogFile.ps1 ├── ch14 ├── 01-PoSH-Remote-Query.ps1 ├── 02-PoSH-Remote-Query-HTML.ps1 ├── 03-PoSH-Remote-Query-HTML-Mail.ps1 └── Input.CSV ├── ch15 └── SampleAzureRmVM.ps1 ├── ch16 ├── Install-SqlServer.ps1 ├── New-DataFormatExample.ps1 ├── New-GetProcessRepo.ps1 ├── New-MultiServerTsql.ps1 ├── New-TsqlQuery.ps1 └── program.cs ├── ch17 ├── PoSH-Find-DockerTags.ps1 └── PoSH-Setup-Docker.ps1 ├── cheatsheets ├── chapter-01.md ├── chapter-02.md ├── chapter-03.md ├── chapter-04.md ├── chapter-05.md ├── chapter-06.md ├── chapter-07.md ├── chapter-08.md ├── chapter-09.md ├── chapter-10.md ├── chapter-11.md ├── chapter-12.md └── chapter-13.md └── miscellaneous └── Microsoft.VSCode_profile.ps1 /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "PowerShell", 9 | "request": "launch", 10 | "name": "PowerShell Launch Current File", 11 | "script": "${file}", 12 | "args": [], 13 | "cwd": "${file}" 14 | }, 15 | { 16 | "type": "PowerShell", 17 | "request": "launch", 18 | "name": "PowerShell Launch Current File in Temporary Console", 19 | "script": "${file}", 20 | "args": [], 21 | "cwd": "${file}", 22 | "createTemporaryIntegratedConsole": true 23 | }, 24 | { 25 | "type": "PowerShell", 26 | "request": "launch", 27 | "name": "PowerShell Launch Current File w/Args Prompt", 28 | "script": "${file}", 29 | "args": [ 30 | "${command:SpecifyScriptArgs}" 31 | ], 32 | "cwd": "${file}" 33 | }, 34 | { 35 | "type": "PowerShell", 36 | "request": "attach", 37 | "name": "PowerShell Attach to Host Process", 38 | "processId": "${command:PickPSHostProcess}", 39 | "runspaceId": 1 40 | }, 41 | { 42 | "type": "PowerShell", 43 | "request": "launch", 44 | "name": "PowerShell Interactive Session", 45 | "cwd": "" 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # PowerShell Core for Linux Administrators Cookbook 5 | 6 | PowerShell Core for Linux Administrators Cookbook 7 | 8 | This is the code repository for [PowerShell Core for Linux Administrators Cookbook](https://www.packtpub.com/networking-and-servers/powershell-60-linux-administration-cookbook?utm_source=github&utm_medium=repository&utm_campaign=9781789137231 ), published by Packt. 9 | 10 | **Use PowerShell Core 6.x on Linux to automate complex, repetitive, and time-consuming tasks** 11 | 12 | ## What is this book about? 13 | PowerShell is an implementation of .NET Core. .NET Core is a cross-platform open source management framework, which adheres to POSIX standards and makes available API calls that work well with all of the major operating systems: Windows, Linux and macOS. .NET Core for Linux has been a success, because of its adherence to standards, as well as for its lightweight implementation. PowerShell extends the capabilities towards management of Linux servers as well as using containerizers such as Docker. 14 | 15 | This book covers the following exciting features: 16 | Understand the fundamentals of .NET Core and PowerShell 17 | Understand the advanced concepts of .NET Core and PowerShell 18 | Learn to write PowerShell scripts and functions with the best practices in mind 19 | Take a deep dive into administering computers locally as well as remotely using PowerShell 20 | Use PowerShell for advanced administration such as on the Cloud, Docker containers, VMware and SQL Server 21 | 22 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1789137233) today! 23 | 24 | https://www.packtpub.com/ 26 | 27 | ## Instructions and Navigations 28 | All of the code is organized into folders. For example, ch02. 29 | 30 | The code will look like the following: 31 | ``` 32 | Get-Date 33 | hostname 34 | Write-Output "Hello, $env:username!" 35 | ``` 36 | 37 | **Following is what you need for this book:** 38 | PowerShell Core for Linux Administrators Cookbook is for you if you are a system administrator who wants to learn to control and automate a Linux environment with PowerShell Core 6.1. Basic knowledge of PowerShell scripting is necessary. It is assumed that you already understand how an operating system is structured and how to use the command-line interface to work with the operating system. 39 | 40 | With the following software and hardware list you can run all code files present in the book (Chapter 1-17). 41 | ### Software and Hardware List 42 | | Chapter | Software required | OS required | 43 | | -------- | ------------------------------------ | ----------------------------------- | 44 | | 1-17 | PowerShell Core 6.0/6.1 | Windows, Mac OS X, and Linux (Any) | 45 | | 1 | curl | Windows, Mac OS X, and Linux (Any) | 46 | | 2, 5, 6, 8-13, 15 | Visual Studio Code | Windows, Mac OS X, and Linux (Any) | 47 | | 3 | Visual Studio Code, PNG viewer, such as feh | Windows, Mac OS X, and Linux (Any) | 48 | | 14 | OpenSSH, WinSCP, SMTP server (Postfix) | Windows, Mac OS X, and Linux (Any) | 49 | | 16 | Visual Studio Code, .NET Core, Python, Microsoft SQL Server 2017 | Windows, Mac OS X, and Linux (Any) | 50 | 51 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://www.packtpub.com/sites/default/files/downloads/9781789137231_ColorImages.pdf). 52 | 53 | ### Errata 54 | * Chapter 11, recipe name: _Measuring running duration_, section name: _See also_ 55 | 56 | **Software disenchantment (http://tonsky.me/blog/disenchantment/) by Nikita Prokopov, the author of the beautiful font Fira Sans** 57 | 58 | _should be_ 59 | 60 | **Software disenchantment (http://tonsky.me/blog/disenchantment/) by Nikita Prokopov, the creator of the beautiful font, Fira Code** 61 | 62 | 63 | ### Related products 64 | * Learn PowerShell Core 6.0 [[Packt]](https://www.packtpub.com/networking-and-servers/learn-powershell-core-60?utm_source=github&utm_medium=repository&utm_campaign=9781788838986 ) [[Amazon]](https://www.amazon.com/dp/178883898X) 65 | 66 | * Mastering Windows PowerShell Scripting - Second Edition [[Packt]](https://www.packtpub.com/networking-and-servers/mastering-windows-powershell-scripting-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781787126305 ) [[Amazon]](https://www.amazon.com/dp/1787126307) 67 | 68 | ## Get to Know the Author 69 | **Prashanth Jayaram** 70 | is a product design and automation expert in database technology with 12 years of rich, extensive, hands-on experience in designing database solutions with next-gen database technologies. He was awarded as the second-best SQL author of 2017 for his contribution to the SQL Server technology space. He has written over 200 articles about SQL, NoSQL, PowerShell, Python, SQL on Linux, SQL on Azure, and SQL on AWS. 71 | 72 | **Ram Iyer** 73 | is an automation and application performance management specialist with about eight years of experience in enterprise IT. While ensuring that the environment in his enterprise performs optimally is his primary role, he is passionate about automation using PowerShell, and has contributed to over 60 enterprise-grade automation solutions in Windows, Microsoft Exchange, Microsoft Active Directory, Microsoft System Center, Citrix XenApp, VMware PowerCLI, and Microsoft Azure, using PowerShell. He gives back to the community by conducting training sessions in PowerShell and blogging about administration using PowerShell. 74 | 75 | ### Suggestions and Feedback 76 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 77 | 78 | 79 | ### Download a free PDF 80 | 81 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
82 |

https://packt.link/free-ebook/9781789137231

-------------------------------------------------------------------------------- /best-practices.md: -------------------------------------------------------------------------------- 1 | # Best Practices Roundup 2 | 3 | Efficient administration is all about following practices which are in line with the philosophy of the tool being used. In this chapter, the best practices are re-iterated so as to ensure that the reader remembers them, along with explanations to why they’re considered best practices. 4 | 5 | ## Using cmdlets 6 | 7 | Learn all the best practices pertaining to using PowerShell cmdlets 8 | 9 | ## Using parameter aliases 10 | 11 | Learn the best practices in using parameter aliases, and how to declare and use them 12 | 13 | ## Key names for calculated properties 14 | 15 | Best practices pertaining to using calculated properties while querying information using one-liners 16 | 17 | ## Using parameters 18 | 19 | The best practices with parameters such as usage of named parameters and not using parameter aliases are covered. So is the use of parameter sets when creating functions. 20 | 21 | ## Using comments in scripts and functions 22 | 23 | This section talks about striking the right balance when it comes to using comments in scripts. 24 | 25 | ## Naming cmdlets 26 | 27 | This section is all about choosing names using approved verbs and balancing readability and length. 28 | 29 | ## Prototyping cmdlets 30 | 31 | When creating custom cmdlets, it is important that we add safety switches to system-altering capabilities. 32 | 33 | ## Using regions 34 | 35 | Use regions to enable custom code folding and create boundaries around different blocks of code 36 | 37 | ## Error handling 38 | 39 | Learn the best practices with respect to error handling 40 | 41 | ## Types of string declarations 42 | 43 | The expanding and literal strings 44 | 45 | ## Running scripts without profile 46 | 47 | Call scripts without profiles to avoid configuration conflicts 48 | 49 | ## Using objects 50 | 51 | This section aims to iterate the fact that PowerShell outputs objects and that’s where its power lies, as opposed to text. 52 | 53 | ## Declaring variables 54 | 55 | This section also talks about how to strike a balance between readability and length, but with respect to variables. 56 | 57 | ## Creating parameters 58 | 59 | Best practices pertaining to creating and declaring parameters in functions 60 | 61 | ## Writing readable scripts 62 | 63 | Best practices in code indentation, braces and flow blocks such as begin, process, end, etc. 64 | 65 | ## Using paths 66 | 67 | Sometimes using paths in scripts render them tightly coupled. Also, some usages may render the scripts unusable in some scenarios. This section gives administrators some of the right ways to use paths. 68 | 69 | ## Exporting output 70 | 71 | Remember that output from PowerShell is an object, and export the data accordingly 72 | -------------------------------------------------------------------------------- /ch02/hello-world.ps1: -------------------------------------------------------------------------------- 1 | Write-Host "Hello, World!" 2 | $Message = "I was dot-sourced!" -------------------------------------------------------------------------------- /ch04/Initialize-PacktPs6CoreLinuxLab.ps1: -------------------------------------------------------------------------------- 1 | function Initialize-PacktPs6CoreLinuxLab { 2 | param( 3 | # The location where the files need to be created 4 | [Parameter(Position=1)] 5 | [string] 6 | $Path = (Join-Path -Path $HOME -ChildPath random) 7 | ) 8 | 9 | process { 10 | Clear-Host 11 | 12 | $CitiesPath = Join-Path -Path $Path -ChildPath cities 13 | 14 | Write-Warning "This is a quick-and-dirty function. Use this only to create your lab objects; not to learn PowerShell scripting." 15 | 16 | New-Item $CitiesPath -ItemType Directory -Force | Out-Null 17 | 'random-text.txt', 'himalayas.jpg', 'crunched-numbers.csv', 'screenshot-001.png', 'screenshot-002.png', 'screenshot-003.png', 'demo.doc', 'my-plugin.rb' | ForEach-Object { New-Item -Path (Join-Path -Path $Path -ChildPath $PSItem) -ItemType File } 18 | 19 | Write-Host 'Downloading city pages from Wikipedia' 20 | Invoke-WebRequest -Uri 'https://en.wikipedia.org/wiki/Mumbai' -OutFile (Join-Path -Path $CitiesPath -ChildPath mumbai.html) 21 | Invoke-WebRequest -Uri 'https://en.wikipedia.org/wiki/Cairo' -OutFile (Join-Path -Path $CitiesPath -ChildPath cairo.html) 22 | Invoke-WebRequest -Uri 'https://en.wikipedia.org/wiki/Dubai' -OutFile (Join-Path -Path $CitiesPath -ChildPath dubai.html) 23 | Invoke-WebRequest -Uri 'https://en.wikipedia.org/wiki/New_York_City' -OutFile (Join-Path -Path $CitiesPath -ChildPath nyc.html) 24 | Invoke-WebRequest -Uri 'https://en.wikipedia.org/wiki/Paris' -OutFile (Join-Path -Path $CitiesPath -ChildPath paris.html) 25 | 26 | Write-Host 'Downloading some multimedia content' 27 | Invoke-WebRequest -Uri 'https://farm3.staticflickr.com/2912/33114379033_5bdee305fa_h.jpg?fdl=1' -OutFile (Join-Path -Path $Path -ChildPath volcano.jpg) 28 | Invoke-WebRequest -Uri 'https://farm8.staticflickr.com/7561/28010104522_f9b54051da_h.jpg?fdl=1' -OutFile (Join-Path -Path $Path -ChildPath coffee.jpg) 29 | Invoke-WebRequest -Uri 'https://player.vimeo.com/external/121142413.hd.mp4?s=55cd0e066b1b588b4121a6280da4a9d84bc97947&profile_id=119&oauth2_token_id=57447761&download=1' -OutFile (Join-Path -Path $Path -ChildPath matrix-like-wm.mp4) 30 | Invoke-WebRequest -Uri 'http://feeds.soundcloud.com/stream/265848849-wowamusik-piratosbeta.mp3' -OutFile (Join-Path -Path $Path -ChildPath piratos-wowa.me.mp3) 31 | Invoke-WebRequest -Uri 'https://unsplash.com/photos/NgtK0TdGT0Y/download?force=true' -OutFile (Join-Path -Path $Path -ChildPath bangalore.jpg) 32 | 33 | Write-Warning "If you encountered errors during the download, chances are that the content is missing from the original sources. Any content would do for the lab; download your own if you want to." 34 | 35 | Write-Host "`n`nCredits:`nCristian Ungureanu (http://mystock.photos/author/cristi/) for volcano.jpg and coffee.jpg`nSean Do (https://www.videezy.com/members/everywheresean) for matrix-like-wm.mp4`nWOWA (http://www.wowa.me/) for piratos-wowa.me.mp3`nAatur Harsh (https://unsplash.com/@aaturharsh) for bangalore.jpg`n" 36 | } 37 | } 38 | 39 | Initialize-PacktPs6CoreLinuxLab -------------------------------------------------------------------------------- /ch05/6.2-dotnetcore-obj.ps1: -------------------------------------------------------------------------------- 1 | class Person { 2 | [string]$Name 3 | [Int32]$Age 4 | [int32]$Salary 5 | 6 | Person () {} 7 | 8 | Person ([string]$Name, [int32]$Age) { 9 | $this.Name = $Name 10 | $this.Age = $Age 11 | } 12 | 13 | [int32] sal ([int32]$Salary, [int32]$Comm) { 14 | return $Salary * $Comm 15 | } 16 | } -------------------------------------------------------------------------------- /ch05/6.6-CustomTypes.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | System.IO.FileInfo 5 | 6 | 7 | Modified 8 | LastWriteTime 9 | 10 | 11 | Age 12 | [math]::Round(((Get-Date) - $this.LastWriteTime).TotalDays) 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ch06/03-input-file-expanding.txt: -------------------------------------------------------------------------------- 1 | /home/ram/dir-01/dir-07/dir-09/dir-13/ 2 | /home/ram/dir-02/dir-05/dir-06/ 3 | /home/ram/dir-03/dir-08/dir-10/ 4 | /home/ram/dir-04/dir-11/dir-12/dir-14/ 5 | /home/ram/dir-04/dir-11/dir-12/dir-15/ 6 | /home/ram/dir-04/dir-11/dir-12/dir-16/ 7 | /home/ram/dir-04/dir-11/dir-12/dir-17/ 8 | /home/ram/dir-04/dir-11/dir-12/dir-18/dir-19/ 9 | /home/ram/dir-04/dir-11/dir-12/dir-18/dir-20/ 10 | -------------------------------------------------------------------------------- /ch06/03-input-file.txt: -------------------------------------------------------------------------------- 1 | ./dir-01/dir-07/dir-09/dir-13/ 2 | ./dir-02/dir-05/dir-06/ 3 | ./dir-03/dir-08/dir-10/ 4 | ./dir-04/dir-11/dir-12/dir-14/ 5 | ./dir-04/dir-11/dir-12/dir-15/ 6 | ./dir-04/dir-11/dir-12/dir-16/ 7 | ./dir-04/dir-11/dir-12/dir-17/ 8 | ./dir-04/dir-11/dir-12/dir-18/dir-19/ 9 | ./dir-04/dir-11/dir-12/dir-18/dir-20/ 10 | -------------------------------------------------------------------------------- /ch06/05-Get-TravelArticles.ps1: -------------------------------------------------------------------------------- 1 | $CsvImport = Import-Csv ./05-sitelist.csv 2 | if ($CsvImport.Name -contains 'BBC') { 3 | $Uri = ($CsvImport | Where-Object Name -EQ 'BBC').HomeURL 4 | (Invoke-WebRequest -Uri $Uri).Links | 5 | Where-Object href -match '/travel/.+' | 6 | Select-Object -ExpandProperty href -Unique 7 | } 8 | Remove-Item ./05-sitelist.csv -------------------------------------------------------------------------------- /ch06/05-sitelist.csv: -------------------------------------------------------------------------------- 1 | Name,HomeURL 2 | BBC,http://www.bbc.com/ 3 | Reuters,https://in.reuters.com/ 4 | Associated Press,https://www.ap.org/en-gb/ 5 | Philippine News,http://www.pna.gov.ph/ 6 | Al Jazeera,http://www.aljazeera.net/portal 7 | Bloomberg,https://www.bloomberg.com/ 8 | The Hindu,https://www.thehindu.com/ -------------------------------------------------------------------------------- /ch06/06-puregfm.md: -------------------------------------------------------------------------------- 1 | # Command line, anybody? 2 | 3 | We're Windows people. We love the GUI. We don't use the boring plain window with grey text. All that is so yesterday, aye? We are more used to moving the mouse, clicking at places, and then touch the keyboard only when we have something to type. We love that experience, be it Aero™ effect, or some other sassy visuals. Command line, again, is so yesterday. 4 | 5 | Fair? Perhaps. Efficient? 6 | 7 | > The console is your friend. 8 | 9 | We're IT folks. We're engineers. We care about efficiency. I do, and I'm sure most of you do. I mean, think about it: the time taken by you to copy-paste the file names of files in a certain directory can be used to get a cup of coffee, if you could offload it to something more efficient, don't you think? (No \*nix jabs, please!) Literally speaking, PowerShell can do the task of listing out all the file names in a certain directory and put the content in your clipboard in a matter of three seconds (including the time taken to write the command). Compare that with `Right-click`, look for `Rename`, click on `Rename` to get the file name selected, `Right-click` again and select `Copy`. Take the pointer to the task bar, click on the icon for the file... OK, I'm tired already. A better way? Let's say you do a `cd C:\Users\AwesomeMan` in your PowerShell console, and you want to list out all the contents in that folder. 10 | 11 | ```powershell 12 | Get-ChildItem | select Name | clip 13 | ``` 14 | 15 | Less than three seconds. Seriously, try it! All you have to do now is go to where you wanted the file names to be pasted (email, perhaps), and press `Ctrl+V`. 16 | 17 | My point is, the console is our friend. And friends, we embrace. Embrace the console, give it a shot. You'll get addicted to the efficiency and the speed. 18 | 19 | It's now time to download Windows PowerShell. 20 | 21 | It will ask you for a restart, so be prepared. Once it's done, you're all set to follow this blog. If you'd like to get updates very time a post is added, follow me on Facebook. 22 | 23 | # Basics of Windows 24 | 25 | Among all the jazz of ClearType fonts and graphics, we often take for granted what is actually some serious task—running the hardware of a computer. Ever wondered how different the actual working of a computer is, compared to what we see on the screen? In reality, it's overwhelmingly difficult to imagine how computers work, especially today. It's hard to believe that two voltage-based states of a bunch of transistors and gates are able to show to us what we see on the screen. These things finish off some tasks in seconds, that humans would usually take anywhere from a few minutes to several days to accomplish. 26 | 27 | To try to fathom what happens inside a computer (and this is of some importance), let's go back to the days of DOS. As we all know from common sense today, a computer is nothing but a bunch of hardware which runs some software to get work done. But how the software talks to the hardware is something that we need to know. 28 | 29 | Imagine the operating system (the piece of software which runs a computer) to be made of two primary layers: 30 | 31 | - The kernel 32 | - The shell 33 | 34 | In simple terms, the kernel talks to the hardware, and is responsible for all the interactions the operating system has with the hardware. The shell, on the other hand, is responsible for interactions with the user. So this is how a computer should look like, in general: 35 | 36 | - User 37 | - Shell 38 | - OS libraries 39 | - Kernel 40 | - Hardware 41 | 42 | So there's the hardware. Then, there's the kernel that is the mediator between the hardware and the shell. And then there's the shell, which is the mediator between the kernel and the user. 43 | 44 | In the interest of simplicity of the interface, Microsoft, for one, made things in such a way that applications sat on top of the shell, and the applications interacted with the users. A few examples for these applications would be simple things like `ping` and `telnet`, and more complex things such as `Adobe Photoshop`. So the shell was not really exposed to the user. There was the shell, the command line interface (or CLI) tools. When the GUI was made, the GUI sat along with the CLI. 45 | 46 | If my memory is right, DOS booted up this way: 47 | 48 | - The hardware was powered on, and the Basic Input Output System (BIOS) ran Power-On Self-Test (POST) 49 | - Once POST was complete, the BIOS looked for an operating system at the first sector of the first track of the first disk 50 | - If an OS entry was found, BIOS started loading the OS 51 | - First, a file called `io.sys` was loaded onto the RAM 52 | - Then, `msdos.sys` was loaded 53 | - The file, `command.com` was loaded onto the RAM 54 | - BIOS relinquished control to MS-DOS, and the computer was ready for operation. 55 | 56 | While I'm not entirely sure of this, this does seem like the same sequence of kernel, shell, and the CLI. 57 | 58 | The problem was that the applications that were built to run on top of the CLI had access only to the CLI. They had to talk to the CLI, the CLI then spoke to the shell, which in turn spoke to the kernel, which then spoke to the hardware. Whew! 59 | 60 | Now think of it as a game of Chinese Whispers. Funny, right? 61 | 62 | So there were problems with this approach, in that the output was not always usable for programming. The efficiency dropped significantly, and calling the classes within the internal framework was more than challenging. 63 | 64 | The post is quite long for a read. Let's give ourselves a break here, and resume in the next post. 65 | 66 | # Enter PowerShell 67 | 68 | In the last post, we spoke about how it was a challenge to get data using the CLI, for programmatic use. It was a challenge, but the concept was not bad. After all, computers started off being commanded using the text interface. The CLI, though, got some job done, of course. There are more than a boatload of applications that can run purely from the command line. But there were challenges: 69 | 70 | - Too much to remember 71 | - Not exactly systematic or uniform 72 | - Getting help seemed like juvenile text chat (use of `/?`) 73 | - Not-very-useful plain text output 74 | 75 | Overall, the Windows CLI had started to seem like a big mess people could not handle anymore. Aside from the security issues that were a weakness with Windows, Microsoft now had more reasons to go for a paradigm shift. In 2006, they finally unveiled the first version of what is today, one of the best things Microsoft ever created: Windows PowerShell. They made it available for Windows XP starting Service Pack 3, and Windows Vista. Windows 7 and all subsequent versions of Windows shipped with PowerShell. 76 | 77 | PowerShell managed to address all of the aforementioned problems. Also, PowerShell sat a level closer to the hardware. PowerShell is an extension of the Windows shell itself; it sits right on top of the .NET libraries. More on that later. 78 | 79 | Apart from the proximity to the libraries, here's why PowerShell is awesome on Windows: 80 | 81 | ### Remember less, use logic 82 | 83 | PowerShell sounds like English. When we need water, we say, "Could you please *get* me some *water*?" Notice the verb, the noun, and the sequence in which they're placed. If PowerShell ever got the capability of getting water, the command (or cmdlet—pronounce: command-let) would be, `Get-Water`. This way, we need to *remember less*. So if I want a list of processes running on my PC, I would just have to say, `Get-Process`. Can it get any simpler? 84 | 85 | ### Systematic commands 86 | 87 | With PowerShell, Microsoft (and the community) introduced the concept of *Approved Verbs* (type in `Get-Verb` in the PowerShell window), wherein, you have to choose from a predefined set of verbs to create your cmdlets in PowerShell. So if I wanted PowerShell to get me some water, I could only say, `Get-Water`, and never `Bring-Water`. It's just about a little foresight: this way, the first point would be reinforced. 88 | 89 | ### Finding commands simplified 90 | 91 | That's right. Now if you want to get a command that kind of, say, sets the date, I can find my command either using the verb or the noun: 92 | 93 | ```powershell 94 | Get-Command -Noun 'Date' 95 | ``` 96 | 97 | Or you could write this to be more specific 98 | 99 | ```powershell 100 | Get-Command -Verb 'Set' -Noun 'Date' 101 | ``` 102 | 103 | Or you could simply take a guess and say, 104 | 105 | ```powershell 106 | Get-Command -Name 'Get-Date' 107 | ``` 108 | 109 | Try it out! Experiment. Since the article is about 500 words, let's call it a day. We'll meet tomorrow to know more. 110 | 111 | # Sense, sensibility and flexibility 112 | 113 | Yesterday. The reason I'm asking for the upgrade is that some of the things I'm going to post in the future will depend on PowerShell 3.0. The link given is for version 5.0. If you feel that you may already be on a version higher than 3.0, then run the following command to ensure you are. 114 | 115 | ```powershell 116 | $PsVersionTable 117 | ``` 118 | 119 | Look for the value for `PSVersion`. If it is higher than 3.0, it's not *necessary* that you upgrade PowerShell on your computer, although, I would recommend the upgrade. What's there to lose, it's free! 120 | 121 | Coming back to sensibility, the output that PowerShell gives you is more sensible, and... *computable*. 122 | 123 | What makes it better is that if something isn't sensible right away, you can *make it sensible*. For example, run the following command: 124 | 125 | ```powershell 126 | Get-ChildItem 127 | ``` 128 | 129 | You see a column called `Length`. It wouldn't make sense to some people right off the bat. While it just means "size", people who are not very familiar with the technical terminology would not understand it at the first shot. The fix? Just use *Calculated Properties*! 130 | 131 | Here: 132 | 133 | ```powershell 134 | Get-ChildItem | Select-Object Name, @{Name="Size(MB)";Expression={$_.Length / 1MB}} 135 | ``` 136 | 137 | Did you run that? No? Please open the console and run it. 138 | 139 | Cool, right? Calculated properties are nothing but values that are _manipulated_. You can simply tell PowerShell that you want the column renamed as "Size(MB)". As long as the name is in quotes, you can have spaces if you want, but I'd recommend against it. You'll understand when we start referring to specific properties from the output. We'll talk about it in more detail then. 140 | 141 | ### Easier help 142 | 143 | Before we wind up for the day, let's look at how getting help is no more juvenile talk, but a pleasant experience. When you want to get some help, all you say is... That's right, `Get-Help`! When you need help with the command to fetch the services running on your PC, you would simply say, 144 | 145 | ```powershell 146 | Get-Command -Verb 'Get' -Noun '*service*' 147 | 148 | Get-Help Get-Service 149 | 150 | Get-Help Get-Service -Full 151 | 152 | Get-Help Get-Service -Examples 153 | ``` 154 | 155 | 156 | Try out those commands. Get your hands dirty in PowerShell. There's something called as muscle memory; it's what helps you type, without having to constantly look at the keyboard, also, how we type `Get-Help` instead of `Get Help`. Type the commands. Go on. Let's meet up again tomorrow! 157 | 158 | # Familiarising the windows 159 | 160 | We saw some cool stuff in the past couple of posts on PowerShell. Some of us probably even found it ironic that there were certain things in the commands given in the post, which did not make much sense, while everything in PowerShell *should* have made sense. 161 | 162 | Worry not. We're on the next step of our PowerShell journey. 163 | 164 | Before we proceed, we need to know a few things about PowerShell, and how to use it. So let's simply talk about the different PowerShell consoles we have, and which one to use when. 165 | 166 | In general, I'd say, there's no hard and fast rule that only a certain type of console can be used in a certain situation. It's a matter of convenience, personal preferences and popular choices. And popular choices, when it comes to tech stuff, are usually good choices in general. No, please, no, this is not the time to talk about how Androids are highly popular but not necessarily a good choice. There are various factors there. And it's a never-ending debate. 167 | 168 | Back to using PowerShell. Technically speaking, if you're running a 64-bit (x64) computer, there are eight different kinds of PowerShell windows. If you're running a 32-bit (x86) computer, there are four. 169 | 170 | * The Console 171 | * Windows PowerShell 172 | * Windows PowerShell (x86) 173 | * The administrator] window for both 174 | * The Integrated Scripting Environment (ISE) 175 | * Windows PowerShell ISE 176 | * Windows PowerShell ISE (x86) 177 | * The administrator] window for both 178 | 179 | Please note that on x86 computers, there would be no Windows PowerShell (x86) because their PowerShell would anyway be x86, eliminating the need for another x86-type window. x64 computers, on the other hand, have x64 windows by default, and can launch an x86 window if needed, although, I can't think of a practical reason to do it. 180 | 181 | ### The console 182 | 183 | The console seems very similar to the command prompt window. This does not have much options. This window is a lightweight one, and is used when you have to run commands line-by-line, just to fetch some basic information (or, like, restart your computer through command). When you hit the `Enter` key after a command, the command executes (duh, right?), and if you want to go to the next line without running the command, you have to do a `Shift+Enter`. So, as I said, this is used primarily to run commands, and not write scripts. In this window, you enter the command, and the output appears right below the command. 184 | 185 | The window colours are a little different from the command prompt to give us a visual cue that this is a PowerShell window. Although, it's worth noting that most `cmd` commands run on the PowerShell console. Some options and the way the arguments are passed may differ in cases where the command itself is an alias to an underlying PowerShell cmdlet. 186 | 187 | Whoa, whoa, hold on! What did you say? What alias? 188 | 189 | Don't worry about it. I'll remember to talk about this in the very next post, if possible. For now, just remember that most of CMD commands can run on PowerShell. 190 | 191 | ### The Integrated Scripting Environment 192 | 193 | The PowerShell ISE, as its name suggests, is a scripting-and-debugging environment. This is heavier compared to the PowerShell command console. The ISE is feature-rich, with Intellisense™, command pane showing you all available commands, add-ons and things like that. 194 | 195 | The older versions of PowerShell ISE had three sections where action happened: 196 | 197 | * The script pane, where we wrote scripts 198 | * The output pane, where the output was shown 199 | * The command pane, which was the same as the command console 200 | 201 | Today's PowerShell ISE combines the second and the third panes into one. So we have only the script pane and the console. Again, the console is used to run commands by hitting the `Enter` key, and viewing output. The scripting pane is to write PowerShell scripts. Here, hitting `Enter` just takes you to the next line. You have to run the script by hitting the `Run` key, and you can then break into the debugger to debug your scripts and all. 202 | 203 | As you can see, this is a more advanced way of using PowerShell—in fact, I'd daresay that this is *the* way to use PowerShell! But of course, if I just want to run `Restart-Computer`, I wouldn't launch a session of ISE. By the time it loads, the computer restart could begin, if done through the console. 204 | 205 | > Did you know that you can convert a CMD window to a PowerShell command window? Open CMD, type in `PowerShell` and hit `Enter`. Notice the prompt change! 206 | 207 | That's all for today! Play around with the console and get used to how it looks and all. As a bonus, if you're into code highlight themes and all, check out this repository of PowerShell ISE themes. Enjoy! -------------------------------------------------------------------------------- /ch06/06-rawcontent.txt: -------------------------------------------------------------------------------- 1 | --- 2 | title: Command line, anybody? 3 | date: '2017-04-01 21:17' 4 | tags: 5 | - windows 6 | category: tyro 7 | --- 8 | 9 | We're Windows people. We love the GUI. We don't use the boring plain window with grey text. All that is so yesterday, aye? We are more used to moving the mouse, clicking at places, and then touch the keyboard only when we have something to type. We love that experience, be it Aero™ effect, or some other sassy visuals. Command line, again, is so yesterday. 10 | 11 | Fair? Perhaps. Efficient? 12 | 13 | > The console is your friend. 14 | 15 | We're IT folks. We're engineers. We care about efficiency. I do, and I'm sure most of you do. I mean, think about it: the time taken by you to copy-paste the file names of files in a certain directory can be used to get a cup of coffee, if you could offload it to something more efficient, don't you think? (No \*nix jabs, please!) Literally speaking, PowerShell can do the task of listing out all the file names in a certain directory and put the content in your clipboard in a matter of three seconds (including the time taken to write the command). Compare that with `Right-click`, look for `Rename`, click on `Rename` to get the file name selected, `Right-click` again and select `Copy`. Take the pointer to the task bar, click on the icon for the file... OK, I'm tired already. A better way? Let's say you do a `cd C:\Users\AwesomeMan` in your PowerShell console, and you want to list out all the contents in that folder. 16 | 17 | ```powershell 18 | Get-ChildItem | select Name | clip 19 | ``` 20 | 21 | Less than three seconds. Seriously, try it! All you have to do now is go to where you wanted the file names to be pasted (email, perhaps), and press `Ctrl+V`. 22 | 23 | My point is, the console is our friend. And friends, we embrace. Embrace the console, give it a shot. You'll get addicted to the efficiency and the speed. 24 | 25 | It's now time to download Windows PowerShell. 26 | 27 | It will ask you for a restart, so be prepared. Once it's done, you're all set to follow this blog. If you'd like to get updates very time a post is added, follow me on Facebook. 28 | 29 | --- 30 | title: Basics of Windows 31 | date: '2017-04-03 12:00' 32 | tags: 33 | - windows 34 | category: tyro 35 | --- 36 | 37 | Among all the jazz of ClearType fonts and graphics, we often take for granted what is actually some serious task—running the hardware of a computer. Ever wondered how different the actual working of a computer is, compared to what we see on the screen? In reality, it's overwhelmingly difficult to imagine how computers work, especially today. It's hard to believe that two voltage-based states of a bunch of transistors and gates are able to show to us what we see on the screen. These things finish off some tasks in seconds, that humans would usually take anywhere from a few minutes to several days to accomplish. 38 | 39 | To try to fathom what happens inside a computer (and this is of some importance), let's go back to the days of DOS. As we all know from common sense today, a computer is nothing but a bunch of hardware which runs some software to get work done. But how the software talks to the hardware is something that we need to know. 40 | 41 | Imagine the operating system (the piece of software which runs a computer) to be made of two primary layers: 42 | 43 | - The kernel 44 | - The shell 45 | 46 | In simple terms, the kernel talks to the hardware, and is responsible for all the interactions the operating system has with the hardware. The shell, on the other hand, is responsible for interactions with the user. So this is how a computer should look like, in general: 47 | 48 | - User 49 | - Shell 50 | - OS libraries 51 | - Kernel 52 | - Hardware 53 | 54 | So there's the hardware. Then, there's the kernel that is the mediator between the hardware and the shell. And then there's the shell, which is the mediator between the kernel and the user. 55 | 56 | In the interest of simplicity of the interface, Microsoft, for one, made things in such a way that applications sat on top of the shell, and the applications interacted with the users. A few examples for these applications would be simple things like `ping` and `telnet`, and more complex things such as `Adobe Photoshop`. So the shell was not really exposed to the user. There was the shell, the command line interface (or CLI) tools. When the GUI was made, the GUI sat along with the CLI. 57 | 58 | If my memory is right, DOS booted up this way: 59 | 60 | - The hardware was powered on, and the Basic Input Output System (BIOS) ran Power-On Self-Test (POST) 61 | - Once POST was complete, the BIOS looked for an operating system at the first sector of the first track of the first disk 62 | - If an OS entry was found, BIOS started loading the OS 63 | - First, a file called `io.sys` was loaded onto the RAM 64 | - Then, `msdos.sys` was loaded 65 | - The file, `command.com` was loaded onto the RAM 66 | - BIOS relinquished control to MS-DOS, and the computer was ready for operation. 67 | 68 | While I'm not entirely sure of this, this does seem like the same sequence of kernel, shell, and the CLI. 69 | 70 | The problem was that the applications that were built to run on top of the CLI had access only to the CLI. They had to talk to the CLI, the CLI then spoke to the shell, which in turn spoke to the kernel, which then spoke to the hardware. Whew! 71 | 72 | Now think of it as a game of Chinese Whispers. Funny, right? 73 | 74 | So there were problems with this approach, in that the output was not always usable for programming. The efficiency dropped significantly, and calling the classes within the internal framework was more than challenging. 75 | 76 | The post is quite long for a read. Let's give ourselves a break here, and resume in the next post. 77 | 78 | --- 79 | title: Enter PowerShell 80 | date: '2017-04-04 12:00' 81 | tags: 82 | - windows 83 | - powershell 84 | category: tyro 85 | --- 86 | 87 | In the last post, we spoke about how it was a challenge to get data using the CLI, for programmatic use. It was a challenge, but the concept was not bad. After all, computers started off being commanded using the text interface. The CLI, though, got some job done, of course. There are more than a boatload of applications that can run purely from the command line. But there were challenges: 88 | 89 | - Too much to remember 90 | - Not exactly systematic or uniform 91 | - Getting help seemed like juvenile text chat (use of `/?`) 92 | - Not-very-useful plain text output 93 | 94 | Overall, the Windows CLI had started to seem like a big mess people could not handle anymore. Aside from the security issues that were a weakness with Windows, Microsoft now had more reasons to go for a paradigm shift. In 2006, they finally unveiled the first version of what is today, one of the best things Microsoft ever created: Windows PowerShell. They made it available for Windows XP starting Service Pack 3, and Windows Vista. Windows 7 and all subsequent versions of Windows shipped with PowerShell. 95 | 96 | PowerShell managed to address all of the aforementioned problems. Also, PowerShell sat a level closer to the hardware. PowerShell is an extension of the Windows shell itself; it sits right on top of the .NET libraries. More on that later. 97 | 98 | Apart from the proximity to the libraries, here's why PowerShell is awesome on Windows: 99 | 100 | ### Remember less, use logic 101 | 102 | PowerShell sounds like English. When we need water, we say, "Could you please *get* me some *water*?" Notice the verb, the noun, and the sequence in which they're placed. If PowerShell ever got the capability of getting water, the command (or cmdlet—pronounce: command-let) would be, `Get-Water`. This way, we need to *remember less*. So if I want a list of processes running on my PC, I would just have to say, `Get-Process`. Can it get any simpler? 103 | 104 | ### Systematic commands 105 | 106 | With PowerShell, Microsoft (and the community) introduced the concept of *Approved Verbs* (type in `Get-Verb` in the PowerShell window), wherein, you have to choose from a predefined set of verbs to create your cmdlets in PowerShell. So if I wanted PowerShell to get me some water, I could only say, `Get-Water`, and never `Bring-Water`. It's just about a little foresight: this way, the first point would be reinforced. 107 | 108 | ### Finding commands simplified 109 | 110 | That's right. Now if you want to get a command that kind of, say, sets the date, I can find my command either using the verb or the noun: 111 | 112 | ```powershell 113 | Get-Command -Noun 'Date' 114 | ``` 115 | 116 | Or you could write this to be more specific 117 | 118 | ```powershell 119 | Get-Command -Verb 'Set' -Noun 'Date' 120 | ``` 121 | 122 | Or you could simply take a guess and say, 123 | 124 | ```powershell 125 | Get-Command -Name 'Get-Date' 126 | ``` 127 | 128 | Try it out! Experiment. Since the article is about 500 words, let's call it a day. We'll meet tomorrow to know more. 129 | 130 | --- 131 | title: Sense, sensibility and flexibility 132 | date: '2017-04-05 12:00' 133 | tags: 134 | - windows 135 | - powershell 136 | category: tyro 137 | --- 138 | 139 | Yesterday. The reason I'm asking for the upgrade is that some of the things I'm going to post in the future will depend on PowerShell 3.0. The link given is for version 5.0. If you feel that you may already be on a version higher than 3.0, then run the following command to ensure you are. 140 | 141 | ```powershell 142 | $PsVersionTable 143 | ``` 144 | 145 | Look for the value for `PSVersion`. If it is higher than 3.0, it's not *necessary* that you upgrade PowerShell on your computer, although, I would recommend the upgrade. What's there to lose, it's free! 146 | 147 | Coming back to sensibility, the output that PowerShell gives you is more sensible, and... *computable*. 148 | 149 | What makes it better is that if something isn't sensible right away, you can *make it sensible*. For example, run the following command: 150 | 151 | ```powershell 152 | Get-ChildItem 153 | ``` 154 | 155 | You see a column called `Length`. It wouldn't make sense to some people right off the bat. While it just means "size", people who are not very familiar with the technical terminology would not understand it at the first shot. The fix? Just use *Calculated Properties*! 156 | 157 | Here: 158 | 159 | ```powershell 160 | Get-ChildItem | Select-Object Name, @{Name="Size(MB)";Expression={$_.Length / 1MB}} 161 | ``` 162 | 163 | Did you run that? No? Please open the console and run it. 164 | 165 | Cool, right? Calculated properties are nothing but values that are _manipulated_. You can simply tell PowerShell that you want the column renamed as "Size(MB)". As long as the name is in quotes, you can have spaces if you want, but I'd recommend against it. You'll understand when we start referring to specific properties from the output. We'll talk about it in more detail then. 166 | 167 | ### Easier help 168 | 169 | Before we wind up for the day, let's look at how getting help is no more juvenile talk, but a pleasant experience. When you want to get some help, all you say is... That's right, `Get-Help`! When you need help with the command to fetch the services running on your PC, you would simply say, 170 | 171 | ```powershell 172 | Get-Command -Verb 'Get' -Noun '*service*' 173 | 174 | Get-Help Get-Service 175 | 176 | Get-Help Get-Service -Full 177 | 178 | Get-Help Get-Service -Examples 179 | ``` 180 | 181 | 182 | Try out those commands. Get your hands dirty in PowerShell. There's something called as muscle memory; it's what helps you type, without having to constantly look at the keyboard, also, how we type `Get-Help` instead of `Get Help`. Type the commands. Go on. Let's meet up again tomorrow! 183 | 184 | --- 185 | title: Familiarising the windows 186 | date: '2017-04-06 12:00' 187 | tags: powershell 188 | category: tyro 189 | --- 190 | 191 | We saw some cool stuff in the past couple of posts on PowerShell. Some of us probably even found it ironic that there were certain things in the commands given in the post, which did not make much sense, while everything in PowerShell *should* have made sense. 192 | 193 | Worry not. We're on the next step of our PowerShell journey. 194 | 195 | Before we proceed, we need to know a few things about PowerShell, and how to use it. So let's simply talk about the different PowerShell consoles we have, and which one to use when. 196 | 197 | In general, I'd say, there's no hard and fast rule that only a certain type of console can be used in a certain situation. It's a matter of convenience, personal preferences and popular choices. And popular choices, when it comes to tech stuff, are usually good choices in general. No, please, no, this is not the time to talk about how Androids are highly popular but not necessarily a good choice. There are various factors there. And it's a never-ending debate. 198 | 199 | Back to using PowerShell. Technically speaking, if you're running a 64-bit (x64) computer, there are eight different kinds of PowerShell windows. If you're running a 32-bit (x86) computer, there are four. 200 | 201 | * The Console 202 | * Windows PowerShell 203 | * Windows PowerShell (x86) 204 | * The administrator] window for both 205 | * The Integrated Scripting Environment (ISE) 206 | * Windows PowerShell ISE 207 | * Windows PowerShell ISE (x86) 208 | * The administrator] window for both 209 | 210 | Please note that on x86 computers, there would be no Windows PowerShell (x86) because their PowerShell would anyway be x86, eliminating the need for another x86-type window. x64 computers, on the other hand, have x64 windows by default, and can launch an x86 window if needed, although, I can't think of a practical reason to do it. 211 | 212 | ### The console 213 | 214 | The console seems very similar to the command prompt window. This does not have much options. This window is a lightweight one, and is used when you have to run commands line-by-line, just to fetch some basic information (or, like, restart your computer through command). When you hit the `Enter` key after a command, the command executes (duh, right?), and if you want to go to the next line without running the command, you have to do a `Shift+Enter`. So, as I said, this is used primarily to run commands, and not write scripts. In this window, you enter the command, and the output appears right below the command. 215 | 216 | The window colours are a little different from the command prompt to give us a visual cue that this is a PowerShell window. Although, it's worth noting that most `cmd` commands run on the PowerShell console. Some options and the way the arguments are passed may differ in cases where the command itself is an alias to an underlying PowerShell cmdlet. 217 | 218 | Whoa, whoa, hold on! What did you say? What alias? 219 | 220 | Don't worry about it. I'll remember to talk about this in the very next post, if possible. For now, just remember that most of CMD commands can run on PowerShell. 221 | 222 | ### The Integrated Scripting Environment 223 | 224 | The PowerShell ISE, as its name suggests, is a scripting-and-debugging environment. This is heavier compared to the PowerShell command console. The ISE is feature-rich, with Intellisense™, command pane showing you all available commands, add-ons and things like that. 225 | 226 | The older versions of PowerShell ISE had three sections where action happened: 227 | 228 | * The script pane, where we wrote scripts 229 | * The output pane, where the output was shown 230 | * The command pane, which was the same as the command console 231 | 232 | Today's PowerShell ISE combines the second and the third panes into one. So we have only the script pane and the console. Again, the console is used to run commands by hitting the `Enter` key, and viewing output. The scripting pane is to write PowerShell scripts. Here, hitting `Enter` just takes you to the next line. You have to run the script by hitting the `Run` key, and you can then break into the debugger to debug your scripts and all. 233 | 234 | As you can see, this is a more advanced way of using PowerShell—in fact, I'd daresay that this is *the* way to use PowerShell! But of course, if I just want to run `Restart-Computer`, I wouldn't launch a session of ISE. By the time it loads, the computer restart could begin, if done through the console. 235 | 236 | > Did you know that you can convert a CMD window to a PowerShell command window? Open CMD, type in `PowerShell` and hit `Enter`. Notice the prompt change! 237 | 238 | That's all for today! Play around with the console and get used to how it looks and all. As a bonus, if you're into code highlight themes and all, check out this repository of PowerShell ISE themes. Enjoy! -------------------------------------------------------------------------------- /ch06/07-input.csv: -------------------------------------------------------------------------------- 1 | Path,Retention,Extension,Exclusion 2 | /home/coolapp/logs,7,.log,/home/coolapp/logs/devicelogs;/home/coolapp/logs/install -------------------------------------------------------------------------------- /ch06/07-split-input.ps1: -------------------------------------------------------------------------------- 1 | $PathInfo = Import-Csv './ch06/07-input.csv' 2 | 3 | # Looping construct here 4 | $Path = $PathInfo.Path 5 | $RetentionDays = $PathInfo.Retention 6 | $Extension = $PathInfo.Extension 7 | $Exclusion = $PathInfo.Exclusion 8 | # Eng loop 9 | 10 | $ExclusionPaths = $Exclusion -split ';' 11 | 12 | # Some code block to handle deletion and other things 13 | 14 | Write-Host "These $($ExclusionPaths.Count) paths will be excluded from deletion: $($ExclusionPaths -join ', ')" -------------------------------------------------------------------------------- /ch06/08-details.txt: -------------------------------------------------------------------------------- 1 | Mr 2 | Bilbo Baggins 3 | 9149-4554-2127-8685 4 | Bag End, The Shire, Hobbiton, Middle Earth 5 | bilbo.baggins@middlearthmail.me 6 | 0069928789312 7 | 8472 -------------------------------------------------------------------------------- /ch06/08-mailto.txt: -------------------------------------------------------------------------------- 1 | Send email -------------------------------------------------------------------------------- /ch07/02.1-Get-WeekDay-if-else.ps1: -------------------------------------------------------------------------------- 1 | $Date = Get-Date 2 | 3 | if ($Date.DayOfWeek -in 'Saturday', 'Sunday') { 4 | Write-Host 'We party on weekends!' -BackgroundColor Yellow -ForegroundColor Black 5 | } 6 | elseif ($Date.DayOfWeek -eq 'Wednesday') { 7 | Write-Host 'Half the week is over, and I want to do so much more!' 8 | } 9 | else { 10 | Write-Host 'Work is worship. Ahem!' 11 | } -------------------------------------------------------------------------------- /ch07/02.2-Get-Weekday-switch-case.ps1: -------------------------------------------------------------------------------- 1 | $Date = Get-Date 2 | 3 | switch ($Date.DayOfWeek) { 4 | 'Monday' { Write-Output 'Red' } 5 | 'Tuesday' { Write-Output 'Violet' } 6 | 'Wednesday' { Write-Output 'Indigo' } 7 | 'Thursday' { Write-Output 'Blue' } 8 | 'Friday' { Write-Output 'Green' } 9 | Default { Write-Output 'Orange' } 10 | } -------------------------------------------------------------------------------- /ch07/03-Wait-BeforeDressCode.ps1: -------------------------------------------------------------------------------- 1 | $Date = Get-Date 2 | 3 | if ($Date.DayOfWeek -in 'Saturday', 'Sunday') { 4 | Write-Host 'We party on weekends!' -BackgroundColor Yellow -ForegroundColor Black 5 | } 6 | elseif ($Date.DayOfWeek -eq 'Wednesday') { 7 | Write-Host 'Half the week is over, and I want to do so much more!' 8 | } 9 | else { 10 | Write-Host 'Work is worship. Ahem!' 11 | } 12 | 13 | Start-Sleep -Seconds 5 14 | 15 | switch ($Date.DayOfWeek) { 16 | 'Monday' { Write-Output 'Wear red.'; break } 17 | 'Tuesday' { Write-Output 'Wear violet.'; break } 18 | 'Wednesday' { Write-Output 'Wear indigo.'; break } 19 | 'Thursday' { Write-Output 'Wear blue.'; break } 20 | 'Friday' { Write-Output 'Wear green.'; break } 21 | Default { Write-Output 'Poor you, working today. Wear orange.'; break } 22 | } -------------------------------------------------------------------------------- /ch07/04-Write-Greetings.ps1: -------------------------------------------------------------------------------- 1 | $GuestsRaw = Read-Host "Enter the guest names, separated by commas" 2 | $Guests = $GuestsRaw -split ",$([regex]'[\s]*')" 3 | 4 | $Guests | ForEach-Object { Write-Output "Welcome, $PSItem!" } -------------------------------------------------------------------------------- /ch07/05-Write-GuestSeatDetails.csv: -------------------------------------------------------------------------------- 1 | Name,Seat 2 | Mr Jain,A-12 3 | Mr Jacobs,C-28 4 | Ms Sanders,B-17 5 | Mr Shah,M-22 6 | Mr Hugo,E-08 -------------------------------------------------------------------------------- /ch07/05-Write-GuestSeatDetails.ps1: -------------------------------------------------------------------------------- 1 | $Guests = Import-Csv './ch07/05-Write-GuestSeatDetails.csv' 2 | 3 | foreach ($Guest in $Guests) { 4 | $RowIdentifier = [byte][char](($Guest.Seat -split '-')[0].ToUpper()) 5 | # Split the seat identifier at '-' : $Guest.Seat -split '-' 6 | # Pick the first element from the resultant array : ($Guest.Seat -split '-')[0] 7 | # Enforce uppercase : ($Guest.Seat -split '-')[0].ToUpper() 8 | # Convert it to char : [char](($Guest.Seat -split '-')[0].ToUpper()) 9 | # Find the ASCII identifier : [byte][char](($Guest.Seat -split '-')[0].ToUpper()) 10 | 11 | $RowNumber = ($RowIdentifier - 64).ToString() 12 | # A should be 1, B should be 2, C should be 3... 13 | 14 | switch -Regex ($RowNumber) { 15 | '1(1|2|3)$' { $RowNumber += 'th'; break } # Generate 11th, 12th, 13th 16 | '.?1$' { $RowNumber += 'st'; break } # 1st, 21st 17 | '.?2$' { $RowNumber += 'nd'; break } # 2nd, 22nd 18 | '.?3$' { $RowNumber += 'rd'; break } # 3rd, 23rd 19 | Default { $RowNumber += 'th'; break } # everything else from 1 to 26 should have 'th'. 20 | } 21 | 22 | $SeatNumber = ($Guest.Seat -split "-")[1] 23 | # Pick the numeric element 24 | 25 | if ($SeatNumber -gt 20) { 26 | $Side = 'right' 27 | } 28 | else { 29 | $Side = 'left' 30 | } 31 | 32 | Start-Sleep -Seconds 1 33 | Write-Host "Welcome, $($Guest.Name)! " -NoNewline # Subexpression `$Guest.Name` to be computed first. 34 | Start-Sleep -Seconds 1 35 | Write-Host "Your seat is in the $RowNumber row, to the $Side the aisle." 36 | } -------------------------------------------------------------------------------- /ch07/06-Write-GuestSeatDetails.ps1: -------------------------------------------------------------------------------- 1 | $Guests = Import-Csv './ch07/05-Write-GuestSeatDetails.csv' 2 | 3 | for ($CurrentGuest = 0; $CurrentGuest -lt $Guests.Length; $CurrentGuest++) { 4 | $Guest = $Guests[$CurrentGuest] 5 | 6 | $RowIdentifier = [byte][char](($Guest.Seat -split '-')[0].ToUpper()) 7 | # Split the seat identifier at '-' : $Guest.Seat -split '-' 8 | # Pick the first element from the resultant array : ($Guest.Seat -split '-')[0] 9 | # Enforce uppercase : ($Guest.Seat -split '-')[0].ToUpper() 10 | # Convert it to char : [char](($Guest.Seat -split '-')[0].ToUpper()) 11 | # Find the ASCII identifier : [byte][char](($Guest.Seat -split '-')[0].ToUpper()) 12 | 13 | $RowNumber = ($RowIdentifier - 64).ToString() 14 | # A should be 1, B should be 2, C should be 3... 15 | 16 | switch -Regex ($RowNumber) { 17 | '1(1|2|3)$' { $RowNumber += 'th'; break } # Generate 11th, 12th, 13th 18 | '.?1$' { $RowNumber += 'st'; break } # 1st, 21st 19 | '.?2$' { $RowNumber += 'nd'; break } # 2nd, 22nd 20 | '.?3$' { $RowNumber += 'rd'; break } # 3rd, 23rd 21 | Default { $RowNumber += 'th'; break } # everything else from 1 to 26 should have 'th'. 22 | } 23 | 24 | $SeatNumber = ($Guest.Seat -split "-")[1] 25 | # Pick the numeric element 26 | 27 | if ($SeatNumber -gt 20) { 28 | $Side = 'right' 29 | } 30 | else { 31 | $Side = 'left' 32 | } 33 | 34 | Start-Sleep -Seconds 1 35 | Write-Host "Welcome, $($Guest.Name)! " -NoNewline # Subexpression `$Guest.Name` to be computed first. 36 | Start-Sleep -Seconds 1 37 | Write-Host "Your seat is in the $RowNumber row, to the $Side the aisle." 38 | } -------------------------------------------------------------------------------- /ch07/07.1-Write-GuestSeatDetails.ps1: -------------------------------------------------------------------------------- 1 | $Guests = Import-Csv './ch07/05-Write-GuestSeatDetails.csv' 2 | $CurrentGuest = 0 3 | 4 | while ($CurrentGuest -lt $Guests.Length) { 5 | $Guest = $Guests[$CurrentGuest] 6 | 7 | $RowIdentifier = [byte][char](($Guest.Seat -split '-')[0].ToUpper()) 8 | $RowNumber = ($RowIdentifier - 64).ToString() 9 | 10 | switch -Regex ($RowNumber) { 11 | '1(1|2|3)$' { $RowNumber += 'th'; break } 12 | '.?1$' { $RowNumber += 'st'; break } 13 | '.?2$' { $RowNumber += 'nd'; break } 14 | '.?3$' { $RowNumber += 'rd'; break } 15 | Default { $RowNumber += 'th'; break } 16 | } 17 | 18 | $SeatNumber = ($Guest.Seat -split "-")[1] 19 | 20 | if ($SeatNumber -gt 20) { $Side = 'right' } 21 | else { $Side = 'left' } 22 | 23 | Start-Sleep -Seconds 1 24 | Write-Host "Welcome, $($Guest.Name)! " -NoNewline 25 | Start-Sleep -Seconds 1 26 | Write-Host "Your seat is in the $RowNumber row, to the $Side the aisle." 27 | 28 | $CurrentGuest++ 29 | } -------------------------------------------------------------------------------- /ch07/07.2-Find-MothersDay.ps1: -------------------------------------------------------------------------------- 1 | $Year = Read-Host "Enter the year (YYYY) you would like to find Mothers’ Day for" 2 | 3 | $CurrentDay = Get-Date "01 May $Year" 4 | 5 | while ($CurrentDay.DayOfWeek -ne 'Sunday') { 6 | $CurrentDay = $CurrentDay.AddDays(1) 7 | } 8 | $MothersDay = $CurrentDay.AddDays(7) 9 | 10 | Write-Output "Mothers’ Day falls on $($MothersDay.ToLongDateString())." -------------------------------------------------------------------------------- /ch07/08-Remove-EmptyDirectories.ps1: -------------------------------------------------------------------------------- 1 | $Iteration = 0 2 | do { 3 | $AllDirectories = (Get-ChildItem -Path $HOME/random -Recurse -Directory).FullName 4 | $EmptyDirectories = $AllDirectories | Where-Object {(Get-ChildItem $PSItem).Count -eq 0} 5 | $EmptyDirectories | Remove-Item 6 | 7 | Write-Output "Iteration $Iteration. Removed the following $($EmptyDirectories.Count) directories." 8 | $EmptyDirectories 9 | $Iteration++ 10 | } while ($EmptyDirectories.Count -gt 0) -------------------------------------------------------------------------------- /ch07/08-input-file.txt: -------------------------------------------------------------------------------- 1 | ./dir-01/dir-07/dir-09/dir-13 2 | ./dir-02/dir-05/dir-06 3 | ./dir-03/dir-08/dir-10 4 | ./dir-04/dir-11/dir-12/dir-14 5 | ./dir-04/dir-11/dir-12/dir-15 6 | ./dir-04/dir-11/dir-12/dir-16 7 | ./dir-04/dir-11/dir-12/dir-17 8 | ./dir-04/dir-11/dir-12/dir-18/dir-19 9 | ./dir-04/dir-11/dir-12/dir-18/dir-20 -------------------------------------------------------------------------------- /ch07/09-Remove-EmptyDirectories.ps1: -------------------------------------------------------------------------------- 1 | $Iteration = 0 2 | do { 3 | $AllDirectories = (Get-ChildItem -Path $HOME/random -Recurse -Directory).FullName 4 | $EmptyDirectories = $AllDirectories | Where-Object {(Get-ChildItem $PSItem).Count -eq 0} 5 | $EmptyDirectories | Remove-Item 6 | $Count = $EmptyDirectories.Count 7 | 8 | Write-Output "Iteration $Iteration`nRemoved the following $Count directories. '$Count = 0' is $($Count -eq 0)" 9 | $EmptyDirectories 10 | $Iteration++ 11 | } until ($Count -eq 0) -------------------------------------------------------------------------------- /ch08/01-Clear-LogFiles.ps1: -------------------------------------------------------------------------------- 1 | # Change this to match ./path/of/your/choice/ from the aforementioned scripts 2 | $LabPath = "$HOME/random" 3 | 4 | $Today = Get-Date 5 | $TotalFileSize = 0 6 | 7 | $FilesToDelete = Get-ChildItem $LabPath -Recurse -File | Where-Object {[math]::Floor(($Today - $_.LastWriteTime).TotalDays) -eq 30} 8 | 9 | 10 | Write-Host "The following files will be deleted:" 11 | Write-Host $FilesToDelete.FullName 12 | 13 | foreach ($File in $FilesToDelete) { 14 | $TotalFileSize += $File.Length 15 | Remove-Item -Path $File -WhatIf 16 | } 17 | 18 | Write-Host "Total space cleared: $([math]::Round($TotalFileSize/[math]::Pow(1024, 2))) MB" 19 | -------------------------------------------------------------------------------- /ch08/01-Set-LastWriteTime.ps1: -------------------------------------------------------------------------------- 1 | function Set-LastWriteTime { 2 | param ( 3 | # Path to the random directory 4 | [Parameter(Mandatory=$false)] 5 | [string] 6 | $Path=(Join-Path $HOME -ChildPath random) 7 | ) 8 | begin { 9 | $DateToSet = (Get-Date).AddDays(-25) 10 | $Files = Get-ChildItem -Path $Path -Recurse -File 11 | $NewerFiles = $Files | Select-Object -First 12 12 | $OlderFiles = $Files | Select-Object -Last 12 13 | } 14 | process { 15 | foreach ($File in $NewerFiles) { 16 | (Get-Item $File).LastWriteTime = $DateToSet 17 | } 18 | $DateToSet = $DateToSet.AddDays(-5) 19 | foreach ($File in $OlderFiles) { 20 | (Get-Item $File).LastWriteTime = $DateToSet 21 | } 22 | } 23 | } 24 | Set-LastWriteTime -------------------------------------------------------------------------------- /ch08/02-Clear-LogFiles.ps1: -------------------------------------------------------------------------------- 1 | # Change this to match ./path/of/your/choice/ from the aforementioned scripts 2 | $LabPath = "$HOME/random" 3 | 4 | $Today = Get-Date 5 | $TotalFileSize = 0 6 | 7 | $AllFiles = Get-ChildItem $LabPath -Recurse -File 8 | $FilesToDelete = $AllFiles | Where-Object {[math]::Floor(($Today - $_.LastWriteTime).TotalDays) -eq 30} 9 | 10 | 11 | Write-Host "The following files will be deleted:" 12 | Write-Host $FilesToDelete.FullName 13 | 14 | foreach ($File in $FilesToDelete) { 15 | $TotalFileSize += $File.Length 16 | Remove-Item -Path $File -WhatIf 17 | } 18 | 19 | New-Object -TypeName psobject -Property @{ 20 | TotalFiles = $AllFiles.Count 21 | FilesToDelete = $FilesToDelete.Count 22 | SpaceCleared = $TotalFileSize 23 | } -------------------------------------------------------------------------------- /ch08/05-ConvertTo-Rgb.ps1: -------------------------------------------------------------------------------- 1 | $Rgb = Read-Host "Enter the hexadecimal RGB value" 2 | $TrimmedRgb = $Rgb.Substring($Rgb.Length - 6) 3 | 4 | $R = $TrimmedRgb.Substring(0, 2) 5 | $G = $TrimmedRgb.Substring(2, 2) 6 | $B = $TrimmedRgb.Substring(4, 2) 7 | 8 | "Here are the R, G and B levels for the supplied hex value:" 9 | $R, $G, $B | ForEach-Object { [int]("0x" + $PSItem) } -------------------------------------------------------------------------------- /ch08/06-ConvertTo-OtherBases.ps1: -------------------------------------------------------------------------------- 1 | $InputString = Read-Host "Enter an integer" 2 | 3 | Write-Host "Octal representation: " -NoNewline 4 | [Convert]::ToString($InputString, 8) 5 | 6 | Write-Host "Hexadecimal representation: " -NoNewline 7 | [Convert]::ToString($InputString, 16) 8 | 9 | Write-Host "Binary representation: " -NoNewline 10 | [Convert]::ToString($InputString, 2) -------------------------------------------------------------------------------- /ch09/01-New-MultiDimensionalArray.ps1: -------------------------------------------------------------------------------- 1 | $MultiDimensionalArray = New-Object -TypeName "int[,]" 4, 5 2 | $Count = 1 3 | 4 | for ([int]$i = 0; $i -lt 4; $i++) { 5 | for ([int]$j = 0; $j -lt 5; $j++) { 6 | $MultiDimensionalArray[$i,$j] = $Count 7 | $Count++ 8 | } 9 | } -------------------------------------------------------------------------------- /ch09/02-Convert-Name.ps1: -------------------------------------------------------------------------------- 1 | $Names = Get-Content .\ch09\02-names.txt 2 | $NewNames = @() 3 | 4 | foreach ($Name in $Names) { 5 | $Name = $Name -split ' ' 6 | $NewName = $Name[1], $Name[0] -join ', ' 7 | 8 | $NewNames += $NewName 9 | } 10 | $NewNames -------------------------------------------------------------------------------- /ch09/02-names.txt: -------------------------------------------------------------------------------- 1 | Lila Day 2 | Wade Reed 3 | Kenny Todd 4 | Cindy Goodman 5 | Rick Howell 6 | Amber Mclaughlin 7 | Bernice Flowers 8 | Boyd Stanley 9 | Claire Greene 10 | Woodrow Mcdaniel 11 | Marcus Ward 12 | Colleen Roberts 13 | Brian Walton 14 | Grant Tyler 15 | Kara Leonard 16 | Dave Chapman 17 | Dwayne Pena 18 | Isaac Schultz 19 | Jacqueline Duncan 20 | Rudy Townsend 21 | Wilbert Gutierrez 22 | Wilma Hudson 23 | Geraldine James 24 | Diana Henderson 25 | Rosalie Terry 26 | Ellis Willis 27 | Bonnie Craig 28 | Alejandro Coleman 29 | Lorene Cohen 30 | Rex Summers 31 | Elvira Bell 32 | Rodney Drake 33 | Byron Washington 34 | Olga Bowen 35 | Steven Gonzalez 36 | Allen Barton 37 | Ignacio Lowe 38 | Billy Carroll 39 | Jeremy Erickson 40 | Owen Wade 41 | Karla Hunter 42 | Linda Bush 43 | Ervin Berry 44 | Laverne Gray 45 | Mack Turner 46 | Jaime Lawrence 47 | Zachary Massey 48 | Rosie Gilbert 49 | Frederick Blair 50 | Alfred Marshall 51 | Gary Matthews 52 | Johnnie Knight 53 | David Jenkins 54 | Willis Reyes 55 | Regina Edwards 56 | Dexter Santos 57 | Felix Miles 58 | Rachael Cummings 59 | Terry Smith 60 | Julie Sharp 61 | Maggie Hardy 62 | Jack Fuller 63 | Cameron Pearson 64 | Henry Baker 65 | Bryan Vega 66 | Leon Torres 67 | Darlene Conner 68 | Dana Davidson 69 | Steve Gibson 70 | Connie Shaw 71 | Jerry Chambers 72 | Kim Mills 73 | Bertha Burton 74 | Fernando Clayton 75 | Tony Greer 76 | Benny Roberson 77 | Nina Hawkins 78 | Francis Scott 79 | Hattie Grant 80 | Miranda Rodgers 81 | Malcolm Mack 82 | Susie Collins 83 | Verna Porter 84 | Jo Mcbride 85 | Ralph Cook 86 | Gwen Reynolds 87 | Lewis Francis 88 | Carol Mccoy 89 | Ismael Lucas 90 | Edwin Bowman 91 | Manuel Valdez 92 | Hope Guzman 93 | Shannon Nash 94 | Jenna Benson 95 | Timothy Rhodes 96 | Leland Figueroa 97 | Tyler Becker 98 | Tanya Mccarthy 99 | Clifton Gardner 100 | Gail Parks -------------------------------------------------------------------------------- /ch09/03-Convert-SortedNames.ps1: -------------------------------------------------------------------------------- 1 | $Names = Get-Content .\ch09\02-names.txt | Sort-Object 2 | $NewNames = @() 3 | 4 | foreach ($Name in $Names) { 5 | $Name = $Name -split ' ' 6 | $NewName = $Name[1], $Name[0] -join ', ' 7 | 8 | $NewNames += $NewName 9 | } 10 | $NewNames -------------------------------------------------------------------------------- /ch09/04-Find-Name.ps1: -------------------------------------------------------------------------------- 1 | $Names = Get-Content .\ch09\02-names.txt 2 | $NewNames = @() 3 | 4 | foreach ($Name in $Names) { 5 | $Name = $Name -split ' ' 6 | $NewName = $Name[1], $Name[0] -join ', ' 7 | 8 | $NewNames += $NewName 9 | } 10 | if ($NewNames -contains 'Torres,*') { 11 | Write-Host "Found Leon Torres in the list!" 12 | } -------------------------------------------------------------------------------- /ch09/05-Combine-Arrays.ps1: -------------------------------------------------------------------------------- 1 | $ListOne = Get-Content ./05-list-one.txt 2 | $ListTwo = Get-Content ./05-list-two.txt 3 | 4 | "List one contains $($ListOne.Count) items." 5 | "List two contains $($ListTwo.Count) items." 6 | 7 | $CombinedList = $ListOne + $ListTwo 8 | 9 | $CombinedList -------------------------------------------------------------------------------- /ch09/05-list-one.txt: -------------------------------------------------------------------------------- 1 | Wilbert Gutierrez 2 | Wilma Hudson 3 | Geraldine James 4 | Diana Henderson 5 | Rosalie Terry 6 | Ellis Willis 7 | Bonnie Craig 8 | Alejandro Coleman 9 | Lorene Cohen 10 | Rex Summers -------------------------------------------------------------------------------- /ch09/05-list-two.txt: -------------------------------------------------------------------------------- 1 | Jerry Chambers 2 | Kim Mills 3 | Bertha Burton 4 | Fernando Clayton 5 | Tony Greer 6 | Benny Roberson 7 | Nina Hawkins 8 | Francis Scott 9 | Hattie Grant 10 | Miranda Rodgers -------------------------------------------------------------------------------- /ch09/06-Search-ByLastname01.ps1: -------------------------------------------------------------------------------- 1 | $Names = Get-Content .\ch09\02-names.txt 2 | $Names = $Names -match 'son$' 3 | 4 | # or $Names = $Names -like '*son' 5 | 6 | $NewNames = @() 7 | 8 | foreach ($Name in $Names) { 9 | $Name = $Name -split ' ' 10 | $NewName = $Name[1], $Name[0] -join ', ' 11 | 12 | $NewNames += $NewName 13 | } 14 | $NewNames -------------------------------------------------------------------------------- /ch09/06-Search-ByLastname02.ps1: -------------------------------------------------------------------------------- 1 | $Names = Get-Content .\ch09\02-names.txt 2 | $NewNames = @() 3 | 4 | foreach ($Name in $Names) { 5 | $Name = $Name -split ' ' 6 | $NewName = $Name[1], $Name[0] -join ', ' 7 | 8 | $NewNames += $NewName 9 | } 10 | "Using the match operator:" 11 | $NewNames -match 'son,' 12 | 13 | "Using the like operator:" 14 | $NewNames -like '*son,*' -------------------------------------------------------------------------------- /ch09/07-Clear-Paths01.ps1: -------------------------------------------------------------------------------- 1 | $Paths = (Get-ChildItem $HOME/random -Recurse).FullName 2 | $Paths = $Paths | Where-Object {$PSItem -notmatch "^$HOME/random/dir-04"} 3 | Write-Output "Here is the list of paths within the lab directory, without those within dir-04:`n`n" 4 | $Paths -------------------------------------------------------------------------------- /ch09/07-Clear-Paths02.ps1: -------------------------------------------------------------------------------- 1 | $AllPaths = New-Object -TypeName System.Collections.ArrayList 2 | foreach ($Path in (Get-ChildItem $HOME/random -Recurse)) { 3 | [void]$AllPaths.Add($Path.FullName) 4 | } 5 | 6 | $PathsToExclude = $AllPaths -match "^$HOME/random/dir-04" 7 | 8 | foreach ($Dir4Path in $PathsToExclude) { 9 | $AllPaths.Remove($Dir4Path) 10 | } 11 | 12 | Write-Output "Here is the list of paths within the lab directory, without those within dir-04:`n`n" 13 | $AllPaths -------------------------------------------------------------------------------- /ch09/08-Compare-ServerName.ps1: -------------------------------------------------------------------------------- 1 | $MyServers = Get-Content ./ch09/08-server-names-01.txt 2 | $ModServers = Get-Content ./ch09/08-server-names-02.txt 3 | 4 | Compare-Object -ReferenceObject $MyServers -DifferenceObject $ModServers -------------------------------------------------------------------------------- /ch09/08-server-names-01.txt: -------------------------------------------------------------------------------- 1 | undida 2 | hisomi 3 | staura 4 | kemers 5 | iaterv 6 | bysher 7 | kisthm 8 | indaho 9 | ecoena 10 | bolais 11 | arsure 12 | droerb 13 | uphawa 14 | anight 15 | quardi 16 | chlelo 17 | tichil 18 | aitapy 19 | zioldy 20 | dublan 21 | teanco 22 | dirlec 23 | aoradu 24 | spouir 25 | usealu 26 | honclu 27 | ounort 28 | odesco 29 | piteif 30 | onicia -------------------------------------------------------------------------------- /ch09/08-server-names-02.txt: -------------------------------------------------------------------------------- 1 | undida 2 | hisomi 3 | staura 4 | heinap 5 | iaterv 6 | kisthm 7 | indaho 8 | ecoena 9 | bolais 10 | aptoor 11 | shaeco 12 | droerb 13 | modusp 14 | anight 15 | quardi 16 | hulont 17 | jublam 18 | urilip 19 | tichil 20 | aitapy 21 | exeini 22 | dublan 23 | teanco 24 | eullib 25 | spouir 26 | usealu 27 | ounort 28 | odesco 29 | piteif 30 | onicia -------------------------------------------------------------------------------- /ch09/09-New-MemoryList.ps1: -------------------------------------------------------------------------------- 1 | $Processes = @{} 2 | Get-Process | ForEach-Object { $Processes[$PSItem.Name] = $PSItem.WS/1MB } 3 | $Processes['pwsh'] -------------------------------------------------------------------------------- /ch09/10-New-ProcessReport.ps1: -------------------------------------------------------------------------------- 1 | $Processes = @{} 2 | Get-Process | ForEach-Object { $Processes[$PSItem.Name] = $PSItem.WS/1MB } 3 | 4 | $Report = "The computer is currently running $($Processes.Count) processes." 5 | 6 | if ($Processes.Contains('pwsh') -or $Processes.Contains('pwsh-preview' 7 | )) { 8 | $Report += "`n`nPowerShell is also running at the moment." 9 | $Processes.Remove('pwsh') 10 | $Processes.Remove('pwsh-preview') 11 | } 12 | else { 13 | $Report += "`n`nPowerShell is not running at the moment." 14 | } 15 | 16 | $Report += "`n`nFollowing is the list of processes currently running, sorted by name:" 17 | 18 | $Report += "`n`n$(($Processes.GetEnumerator() | Sort-Object Name).Name -join "`n")" 19 | 20 | $Report += "`n`nThe average working set used is $(($Processes.Values | Measure-Object -Average).Average) MB. There are $($Processes.Count) processes running, apart from PowerShell." 21 | 22 | $Processes.Clear() 23 | 24 | $Report += "`n`n$($Processes.Count) processes left after clearing the list." 25 | 26 | $Report -------------------------------------------------------------------------------- /ch10/01-Get-FileDetails.ps1: -------------------------------------------------------------------------------- 1 | Write-Host "File details:" 2 | 3 | Get-Item ./ch10/01-random-text.txt 4 | 5 | "Here are the first seven lines from the file." 6 | 7 | Get-Content ./ch10/01-random-text.txt -ReadCount 7 | Select-Object -First 1 8 | 9 | "Here are the last five lines from the file." 10 | 11 | Get-Content ./ch10/01-random-text.txt -Tail 5 12 | 13 | "Here are the eleventh to the thirteenth lines." 14 | 15 | Get-Content ./ch10/01-random-text.txt | Select-Object -First 3 -Skip 10 16 | 17 | "And here are some details about the content in the file." 18 | 19 | Get-Content ./ch10/01-random-text.txt | Measure-Object -Character -Word -Line 20 | 21 | "Finally, the content will be imported into a variable. Let us see if the content is a single block of text or not." 22 | 23 | $Content = Get-Content ./ch10/01-random-text.txt 24 | 25 | "There are $($Content.Count) elements in the variable." 26 | 27 | "When read as raw content, the number of elements is $((Get-Content ./ch10/01-random-text.txt -Raw).Count)" -------------------------------------------------------------------------------- /ch10/01-random-text.txt: -------------------------------------------------------------------------------- 1 | qbpkSnAC NHwkZdgF kgVJhwRG OVWtNmSw xfHpbajI UAWBcfOq KpZtVzqJ AmoJRrIy 2 | GURaYbXl dlPtyzfZ dvjeSLHK GwkYQNcz qJfoWRuX UAeMozfE GVtLYMgu vUBZMuKR 3 | VUcXeSWH YDdwMPNn JBQHEAKk VlyKPdtR vpBUCZYO jZKbYUlV wsubJPBH wbitYrDO 4 | bNFIRxqU JhOdnmXP rbUghvFT yjDYdgzL knzPYjED opCYnMgb jZCGslaU XRrJOYtl 5 | CBqxivTy dgDJCvQx IWbBLRzM rJqoTkbt vpgrEJym qHVmhjbk NPghmZkq eoQkKLDB 6 | EpHWgoQG YbKwmEgL SIQRzOuf FyTQscpf HpGaIRTJ HNKakFTg JqSKeoRT uPgpJihO 7 | zyHCqNlV TczfsmhE ykKWgUJZ SeIavNli dfQbkoVy yStWCsTP oGUFAMPx TCYokKPe 8 | hxYsAcMU MPKmyHSI vWHMPfNO rTOJSFWd aPoNfJbu IxafOydt QXEpHBqo pjQxEKTk 9 | xAUHvoNK INDszMPW hjbupFKO gpDiRMJd YjUykXEN JsymwnGK bQFjslgr dykUieEI 10 | fNniqUxQ ObrfRWBJ abLkNzgh mPyqxXgk VsMWjNTA SLcDWsPr sSpaJfdI BYiPHscb 11 | yjPqdTxo VyRXWeah EMzgCFVf abFdzpXU RbYTqFAm aeUdnlty OVwMYjJT hwMzbsGX 12 | MWYBajuf lBaKjATO OURWsqeN OFEzZwsd QkZGlxrB URWtbXLD ybUXzxpW TdVFEsWK 13 | wZrOEaBL sEPBGNFw lIHBFuOW POiclsaN ZsUSuObo PjqVJrKX FyfWcPoK bzAHTmCK 14 | zpkyUlSE lWyVarkL ZGUMlJOQ fSMJCKLm WidjuVNG LiAjqElD hxKlOkUX GrhKjlcO 15 | GtqYfPJA bwkJKucG jcTZpGvC tblsqjZK wzDyknCj rbfvVCGX rlDEYqox vEAXkqxN 16 | XPEbGZgH bAGJMsax HVKaynYm xEVcBRGa SUBuLvXP gWvnDcIt bSrRusAT UAvYmWIg 17 | dLNxljYW yiRtzdZJ qxsidcCO MSEkclmH qPWNZejx RNsKgYoh pXsmlWfi EHozXvwO 18 | TPslkxjN pGVywuCM eugnTWBt dOwYemXo axwVIGZU mvuHjFqt bJTCPMDg qRwbMBNT 19 | IBQKpvYS PeEXcCdx xkXcZSUv UDsWnjzB BLbKQqwa lGUMqVfk OfLxEtDN BYXWyObE 20 | QhdbmgoX vxTRYMCA eoDLMYgF jVQDIfhA JRwToxnD rwYvMHZt OvbsWXya aecLRzuv 21 | UDOKAoRZ yauZDUkr QPFVdkDS mMCsZRUK ABecgFOf pivsnHwe NRKUahvj NZukAQyG 22 | IAUbOdsp asLezPqv OyKglaSA tnTLRjhk tuWSJirl OxFhteoU pbnzhutv pVtjFdPB 23 | cUCmyEXB OCNouBmz fBuVmykE fuHOarFx wSCIoRjX LnDOTZQS MjFLTkHW SGvZLhUF 24 | SaPUQAvI wcEyRIzq bhWexyXZ JfgPpMRK KQzcgnbS nPXUaVsl ipPIbVmJ wUlYHeGF 25 | aKleQVhZ WXRYmkON rzHJkVAt YxNuWUPm qNaYDytm tGZFcLeP PIOZfNkR ThgclvHP 26 | fZFlULdg ZyQIPlTv ClRbmodw VsnKpUQW SWAtnveR xtqLflsQ yHwCmQns zunMEsVG 27 | hqNGZVEk IKCLbRMQ XIfoZBMF BZGIxHSz WSTAYFmJ HzNfwWKu aTzORvmr bKLsqeUZ 28 | MAKhFszO HlBhKeFr bDoBhVqy lJjdVcHG zQegJLbk KasuIvix VgdfctGl BqlcLYbS 29 | luGCLoUA yvMimhcG vDQPzpeb LvqOZkdD yUDpoSZl BJOQXbIN AmvgiHYS yaBsoJqF 30 | ojLsUBSw OjsnLtfU OwQgoGZC GRVyOeiH TGLtxkJh HelCyENR jbZJUahO mOZPThFA 31 | pHFmrZXP cXDYxoIB myGxZOuk vkHQSjVy oanqFSel WdliLvMS ZrdHshtg bWwdSlgs 32 | eygmVkxL iAnZDfRN LvRBMEsw ewAPhnRb MZFuiOKD hLRkBKfY NxuvdnkU HwGaDjOF 33 | DZaqfklW ekinaxKL KsBPmhzA ixLYMKFy spEQIVAx rgkAevCZ QbJtlUhd GROeQZFn 34 | nGzwyPCN ULsQNbmS OhHQrJwN SJYRIZUi LpqXRlyA pidVxEUF YhtnakbE eoxKEDFZ 35 | oCATkiYS SvZBUsgp zZApLkTF whUqxzEX zmRLVyrG fwznvskM pSFzlaRD VskeKJYv 36 | RhfUWbiz PaLzGTpD SCcwkzpV qdBFSRKm OedSDmWv pzaTOwFl NnBeZSKy YwuyTASp 37 | FMzJUpsd vAyiRHGg ONPuSBiE TctzOgjL phQlxNXd amZlVNnC tEkjMClm cdQwZeHN 38 | dPmjuklT MdhBmWqA glNbVuxI QvtNiVGu IXKQoTOB XiLvqtfU BSCfRgLG XiercLFA 39 | UbDHVpGA ZnTeSKwp hDNiwJKf WiHRrMsI hopLICvH mhRsxIwg TgnmzWlU JuedolNT 40 | HXZKmtAu ZTGNnbrA pztExQXP FnGgTQOb EyHwFlft MSVjhRNG zNnPfHLA gfuSTaOP 41 | UnGtFyxz kRYcLsAF UFXMVNiW qFfMlQpS dkfESigp yDYGoHfU XjLrKHsa JSNprkoI 42 | qbecgEkI jbZMOVwo maGURXPt IMwtTZXU athZsQHv kwPWcqNY PTSuykmO smdcZBNl 43 | spCHkVWl fbLwrHMc WDuGZyTU wqnMyWVA IJLDNyVC AEcqbStQ hmCWflLR ACdqVyei 44 | auPKFpyS EdWpbvNa AtVwcoST BDkeGNJW IoFNzHvj ZOlauEQd MwVcsqCr DGnpwoEm 45 | RruUWYBV KIDZEARS tmGnlreb dHjTcUPV rEeUscPh sohfkzXR NtLTgkJj cBOwbxYe 46 | TgmhPSEZ BrnzhJPw tCmMsKfD YbFRzyoN pELrxPtB EelPhUJX LTZNzHrI SqwVzKWb 47 | iwqoOusz ABTQdeXZ JFpGWPzO gfkHJhuy zMhySEcY zwWjgnOr BwSFXhvd GIouJpfL 48 | iNxlvGmu vDopucVI MbgVSiNo UsvKlxLJ kWOKhYzA CGVwLvFK vQFgzsRi RncAPflj 49 | JdMKNjCa JkXbOifs rAUsOXdY pgqQLGyb nWKVcqIg BEtvyMiU TwGUkHYg ZazsMyjm 50 | KzIemHgf CibGkmPs txgozuYM cCmopxjT wxbSvtkl JBWouZsn NeQEYUbg XyKcMEYC 51 | kpgNwIRq PpoUWdRk AOIzJsoS qsAklxnM qnLOesvK BTeopWGH trQZudzx GMmvlyYu 52 | CkXfDoKc DcCiuKzr CuoUDrfp GfAFEdNo vYEHLbXu CHsSBRGk DTzFoLaE hvkfzFVg 53 | KbhpGeOP NQusyHUc VtdYbMHw Oixgosvf jdXntNLg rsuCHYLj AYbQLXWk BPtwUIgX 54 | ZqRGklNo QfIlUuSV aiCBQvzN oLxjDSbk JsWhicIP IjuaPsHK ftgpXJrT hilmyftw 55 | EGzyAlRa pUGXsxfJ RUiVepOB YjDEFOtC DrHbCSBl AoQEgLdT hXpsjnig IKhEraVO 56 | qWYuViNo SmepvEuh cLsWdzKm wSgZHijk KfUNARWG wQaUdYbK wVBJpjGn kOQNbBGE 57 | kVtFQEwD OZcGgPMQ PDwnAXjN sXJVnkti suBYUbCi fiEsZPwS BwRdYVQx NWfhlIUb 58 | vQjCgaUh eoXyzkIx fwuUhvPO oqHAkKuM qUCftxER MczWYOZL sqiwoTmr ZtynhJzW 59 | VZTrueyX mAuJwZOa HYqTSPve TRDzMrvK bkdhfcjQ vkugDEmR lJZmqNxV lnYduaft 60 | fXLCwrKU BGxvEYNS MtySBwRb xVzHLPoK eTMHBglf TMzUEplH sVQktMAC mresCvzq 61 | zvhZeitf lFUfIEPu uRoPqZGw iqSQhfTE EdnITrBZ uThMaNSD vhQabTle ihZJTeaH 62 | KEtVxIrN QSsFJDAU sSaEogHJ aeNbvPUC EoxUQTcW IbliTqCt TAIQJdRp giJxDNHp 63 | pKJfUSrE ersLjNoH KnVFrPEW cLnETsJW zABmoHkw FSxhcTlG QxcnOzsi PBDZgFhr 64 | YJFuMEyT EoxhDQGc skPpNqEB AhXmBfZL Hbftpzyq LEyPfDxe SKzTOWZP RwakAhtr 65 | BlAeGJcL jcAZIxGe lQxjqiGn OpXJnbCZ PSBfAsKN nKLXHwsc CpbfqKnm gpnlTNdL 66 | ExDPobCX fSnblKAU BPQrvlXF NTyJkZod tHKzvMuo BLHfsogY WFBAJHYe fjGtqZTw 67 | pEuICXZk LmcDqZzF zjZNVORl AGbLczOI esMXBLnt sAXCINpe lfTWNodP VjrIbcmQ 68 | YKUtgyZa gQkOwUdV CoPypSYH BRSNCqUL mFloIDMY PCZzUigJ cWTuhBAb KwVyFZND 69 | DsUFISOR EWVBiZJz cmPsRuez ardXDNgz GOLMQquD dTIAQFuh ioTesDbm HnfAKvGx 70 | HKRlijGZ wvGmkYKl qSfxaVTw HGeyUEoj ZISnARjY zfSbuprY aSEpNOtQ drmMOFJQ 71 | VtPcnmGg embtOjNJ EAeWGLRZ ZWGqTXxc hGvwpsZS FmoWOZzG YwtCGjBR swYOlfEM 72 | VHzUjceI sMOJvFrh gsuvbNWY iwNAIBQV kAuBVIPa WoNgaAzc Druacbhs ihKtHNDe 73 | jqfoDGMz cBoRuSwm IYqPdlZS abyAOgnF QSsdGyze cLgfReZk bJHuYsPR FPoOfUQY 74 | bOdTsBIX vnIGmCZP JoipnhRu JzkbheKU rRYNnSdQ UtzejPvB MBdQefVI PLESktcC 75 | vbPaOGYo TWjUqsoX ovlCpOrD cVIjkUmg BtKRXhjv rAOxkNGb gWUQqPKb uhSQlfwp 76 | HuLbOdzM AJxosFyr mwvltjVU MZTDCgHl AWvxZCMu SlLCGbcJ rAxPpFIU CLoVPsbe 77 | HMLSdtzi gqEiOmyG pBajfPkr fRMtUeVP SQomnUvw FEGnytVg kjldcoAe DlLMxGsy 78 | GATmESco bndwopkT osDMpCmw bMyQDFSz WmdRnsrP VRQpXqvc CwgTrQXY EwNgAicU 79 | OLGnpwbq afIRymtd ejVRBHhF yhmecqHp LztfgBbU hcUlLfYn OqKvaRmA oUzgIurm 80 | ZsmTXISy GNqBtaxi xfgJQwdz DFwdlhCV LdctjqUk BWhmdpDk EGfHVMiP KuVzPOYC 81 | gsWLNUtd xaFdqpDe dUCHcSQJ AjQNpIGT ufPFDVzS EuPtFBjZ XwCKvqUb EJOjtlZd 82 | HivVJqYn wRTmEDou YGaTNUVk WTfIvVLZ ADjXhlec CvwhMTPB DRBivmpy aTQIMxnA 83 | wjgvlFyc zpnKPgoa qRudeFAB CjydORuv TbwKegsu LoNthVdE qjBQPIad ahuncRVl 84 | YDzqVClQ pUnuIMlF goJFzHqV bjHKrTXA KCjZpcMY KNdrBcGe xVpgoOWa cTjFzRCn 85 | wKvCukpS yxtdnKQD tqxenUSX hEfXsiWN tIEDqnNR SoTkKIrB cIxNnSLj RWptzMZf 86 | RnExHQTw NfDhpyos podiALmZ zWtxkFUh RJcXaOko wBAcHLzU tSkhCQUZ pJqHsviD 87 | HhVDQlWF PbHTtRVu fEDVJZRc xswICYQD rsRtQvAz zdNQfuEA CrWxivuS igVFEdJy 88 | wiFmyDdK woLfKNaq DxlCWoKt zvybecMA mIsDzhBQ eIinYGow HMJCFQqf VTFrLbtj 89 | rqGvDATp qPUndizH TGNfvCKk lNQmhSVe byfqVnwc UFhDegZE DoLFVAbw FJQRGXaW 90 | JwLZzXoO jAWSRvOI YJEUmsSF ERcYJATp MokXPmpr XRYyOuPC JvRfOqka TKABCqgd 91 | RHPIlEku QzsYketZ tfiWTSUb AfNwdDhy icTmDxaX dRopNALy gXzsOlGJ KvytzbBT 92 | bpfdDwKe knalNirQ tFmRhUvP ZrewqyPV NzSIeKCJ ZEnXsOqI qBnOjozt JDIoLQyw 93 | pwJWCAeB RZDpgXMS jdORXoJL PkylEnMm nwhNKmCe DqjbrkCS FVOekauX OkFCwdfq 94 | xKCycRtL PXaxfDKE aDgJuhvR LwrXiAgN uvSXVOYn JEsuWObp OJbFcXLW KOzsVTSl 95 | bgSfEMYK tJIhDivU lguoYkip hbqiAlMT uajXODUh vdVPnhLp RiAaUskC jxBzJrki 96 | VFgXIzQk HZGcDMYt gqFbPACU mHgGxjAM LUgGKXSc smbtPYRU elihSvrV BRCYdXyb 97 | qcngRWZe chIiuCQo NXhAQyrx szJVCWXY HMQAScil PDRNUzcx jzndkBvP dvxZsRcw 98 | AYpoKzNL HagEtTAJ SAsxhlpo MSwtNxfl bcJlNAma YpstwNmA SnkTNPtj EcMdRqHP 99 | NOSYnKmA DOnNEsvT HpMOIZUC iZYslRFp RUovKZcO MUQrocqB AVtWeExD wzvXEmji 100 | rdDEfyOA OHVJXDCw knIaeuWT OsinbJXm vYxZOJTu riATSBJm sWUIjVXY PWEysKwq -------------------------------------------------------------------------------- /ch10/02-Write-ContentOne.ps1: -------------------------------------------------------------------------------- 1 | Get-ChildItem $HOME/random/dir-04 > $HOME/random/file-list.txt 2 | 3 | Get-ChildItem $HOME/random/dir-05 > $HOME/random/error.txt 4 | 5 | Write-Host "Listing the contents of ~/random/dir-03 and appending the list to file-list.txt." > $HOME/random/message.txt 6 | 7 | Get-ChildItem $HOME/random/dir-03 | Tee-Object $HOME/random/file-list.txt -Append -------------------------------------------------------------------------------- /ch10/02-Write-ContentTwo.ps1: -------------------------------------------------------------------------------- 1 | Get-ChildItem $HOME/random/dir-04 | Out-File $HOME/random/file-list.txt 2 | 3 | Get-ChildItem $HOME/random/dir-05 2>&1 | Out-File $HOME/random/error.txt 4 | 5 | Write-Host "Listing the contents of ~/random/dir-03 and appending the list to file-list.txt." 6>&1 | Out-File $HOME/random/message.txt 6 | 7 | Get-ChildItem $HOME/random/dir-03 | Tee-Object $HOME/random/file-list.txt -Append 8 | 9 | <# Alternatives, using only operators: 10 | Get-ChildItem $HOME/random/dir-04 > $HOME/random/file-list.txt 11 | 12 | Get-ChildItem $HOME/random/dir-05 2>&1 > $HOME/random/error.txt 13 | 14 | Write-Host "Listing the contents of ~/random/dir-03 and appending the list to file-list.txt." 6>&1 > $HOME/random/message.txt 15 | 16 | Get-ChildItem $HOME/random/dir-03 | Tee-Object $HOME/random/file-list.txt -Append 17 | #> -------------------------------------------------------------------------------- /ch10/03-Modify-FileContent.ps1: -------------------------------------------------------------------------------- 1 | #region Copy files to a backup directory before proceeding: 2 | New-Item $HOME/random/backup/ -ItemType Directory 3 | Copy-Item $HOME/random/message.txt, $HOME/random/file-list.txt, $HOME/random/error.txt $HOME/random/backup/ 4 | 5 | # Verbose version: Copy-Item -Path $HOME/random/message.txt, $HOME/random/file-list.txt, $HOME/random/error.txt -Destination $HOME/random/backup/ 6 | #endregion 7 | 8 | #region Commence operations 9 | Set-Content $HOME/random/message.txt "Successfully sent the contents of ~/random/dir-03 and appending the list to file-list.txt" 10 | 11 | # Verbose version: Set-Content -Path $HOME/random/message.txt -Value "Successfully sent the contents of ~/random/dir-03 and appending the list to file-list.txt" 12 | 13 | Add-Content $HOME/random/message.txt, $HOME/random/file-list.txt (Get-Date) 14 | 15 | # Vervose version: Add-Content -Path $HOME/random/message.txt, $HOME/random/file-list.txt -Value (Get-Date) 16 | 17 | Clear-Content $HOME/random/error.txt 18 | 19 | # Verbose version: Clear-Content -Path $HOME/random/error.txt 20 | #endregion 21 | 22 | "Here are the contents before and after the modifications:" 23 | 24 | "Message (before):" 25 | Get-Content $HOME/random/backup/message.txt 26 | 27 | "Message (now):" 28 | Get-Content $HOME/random/message.txt 29 | 30 | Read-Host -Prompt "Press ENTER to continue" 31 | 32 | "File list (before):" 33 | Get-Content $HOME/random/backup/file-list.txt 34 | 35 | "File list (now):" 36 | Get-Content $HOME/random/file-list.txt 37 | 38 | Read-Host -Prompt "Press ENTER to continue" 39 | 40 | "Error (before):" 41 | Get-Content $HOME/random/backup/error.txt 42 | 43 | "Error (now):" 44 | Get-Content $HOME/random/error.txt -------------------------------------------------------------------------------- /ch10/04-Search-FileForPattern.ps1: -------------------------------------------------------------------------------- 1 | $ReportPath = "$HOME/random/FileSearchReport.txt" 2 | $CheatsheetPath = "$HOME/Documents/code/github/powershell/cheatsheets" 3 | 4 | if (!(Test-Path $ReportPath)) { 5 | New-Item $ReportPath -ItemType File -Force 6 | } 7 | 8 | "Here are the PowerShell code blocks present within the cheatsheets." | Out-File $ReportPath 9 | Select-String '```powershell' $CheatsheetPath/*.md | 10 | ForEach-Object { 11 | $PSItem = $PSItem -split ':' 12 | Write-Output "File Name: $($PSItem[0])" 13 | Write-Output "Line number: $($PSItem[1])" 14 | Write-Output "Pattern: $($PSItem[2])`n" 15 | } | Out-File $ReportPath -Append 16 | 17 | <# Verbose version: 18 | Select-String -Pattern '```powershell' -Path $CheatsheetPath/*.md 19 | #> 20 | 21 | "Here is a contextual report of all the PowerShell code blocks:" | Out-File $ReportPath -Append 22 | Select-String '```powershell' $CheatsheetPath/*.md -Context 2, 2 | 23 | Select-Object Path, LineNumber, ` 24 | @{ Name = "Before"; Expression = { $PsItem.Context.PreContext -join "`n" } }, ` 25 | @{ Name = "After"; Expression = { $PsItem.Context.PostContext -join "`n" } } | 26 | Format-List | Out-File $ReportPath -Append 27 | 28 | "Here are the files that contain the word, 'command' in them, except the cheatsheet for the first chapter:" | Out-File $ReportPath -Append 29 | Select-String 'command' $CheatsheetPath/*.md -Exclude '*chapter-01.md' | 30 | Group-Object Path | 31 | Select-Object Name, Count | 32 | Format-Table -AutoSize | Out-File $ReportPath -Append 33 | 34 | "Here are the files that contain the word, 'PowerShell' in them." | Out-File $ReportPath -Append 35 | Select-String -CaseSensitive 'PowerShell' $CheatsheetPath/*.md -Exclude '*chapter-01.md' | 36 | Select-Object Path -Unique | 37 | Format-Table -AutoSize | Out-File $ReportPath -Append -------------------------------------------------------------------------------- /ch10/05-Get-InvocationInformation.ps1: -------------------------------------------------------------------------------- 1 | "The script is located within:" 2 | $PSScriptRoot 3 | 4 | Read-Host "Press Enter to continue" 5 | 6 | "Here is the complete path to the script that was run:" 7 | $PSCommandPath 8 | 9 | Read-Host "Press Enter to continue" 10 | 11 | "Creating a new folder, demo-lab in the home directory, and adding files to it." 12 | 13 | $Path = Join-Path $HOME 'demo-lab' 14 | # Verbose: Join-Path -Path $HOME -ChildPath 'demo-lab' 15 | 16 | "Creating files at the demo lab location." 17 | New-Item $Path -ItemType Directory 18 | 19 | 'random-text.txt', 'himalayas.jpg', 'crunched-numbers.csv', 'screenshot-001.png', 'screenshot-002.png', 'screenshot-003.png', 'demo.doc', 'my-plugin.rb' | ForEach-Object { New-Item -Path (Join-Path -Path $Path -ChildPath $PSItem) -ItemType File } -------------------------------------------------------------------------------- /ch10/06-New-FilesAndDirectories.ps1: -------------------------------------------------------------------------------- 1 | Set-Location (Join-Path $HOME random) 2 | 3 | Join-Path dir-01, dir-02, dir-03, dir-04 -ChildPath demo.txt | ForEach-Object { New-Item $PSItem -ItemType File } 4 | 5 | 'random-text.txt', 'himalayas.jpg', 'crunched-numbers.csv', 'screenshot-001.png', 'screenshot-002.png', 'screenshot-003.png', 'demo.doc', 'my-plugin.rb' | ForEach-Object { New-Item (Join-Path dir-01 -ChildPath $PSItem) -ItemType File } 6 | 7 | Rename-Item screenshot-001.png myscreenshot.png 8 | # Verbose version: Rename-Item -Path screenshot-001.png -NewName myscreenshot.png 9 | 10 | Copy-Item myscreenshot.png dir-02 11 | # Verbose version: Copy-Item -Path myscreenshot.png -Destination ./dir-02 12 | 13 | Remove-Item dir-04 -Recurse 14 | # Verbose version: Remove-Item -Path ./dir-04 -Recurse -------------------------------------------------------------------------------- /ch10/07-Get-FilteredFiles.ps1: -------------------------------------------------------------------------------- 1 | $ThresholdDate = (Get-Date).AddDays(-7) 2 | 3 | Get-ChildItem $HOME/random -rec -na *.mp4 | Where-Object { $_.CreationTime -lt $ThresholdDate -and $_.Length -gt 100KB } 4 | 5 | # Verbose version: Get-ChildItem $HOME/random -Recurse -Name *.mp4 | Where-Object -FilterScript { $PSItem.CreationTime -lt $ThresholdDate -and $PSItem.Length -gt 100KB } 6 | 7 | Get-ChildItem $HOME/random -Recurse | Sort-Object Length -Descending | Select-Object -First 5 8 | # Verbose version: Get-ChildItem -Path $HOME/random -Recurse | Sort-Object -Property Length -Descending | Select-Object -First 5 -------------------------------------------------------------------------------- /ch10/08-Read-MdmLog.ps1: -------------------------------------------------------------------------------- 1 | $AllLogLines = Get-Content ./ch10/08-mdm-reinstall-log.log 2 | $UserTable = @() 3 | 4 | foreach ($Line in $AllLogLines) { 5 | $Timestamp = ($Line.Split(' '))[0] 6 | 7 | $Line = $Line -replace ".+(DeviceActionEvent)\ \{", '{' 8 | $Line = $Line -replace '\]$' 9 | 10 | <# Alternatively: 11 | $Line = $Line | Select-String -Pattern '(\{.*})' | 12 | ForEach-Object { 13 | $PSItem.Matches | ForEach-Object { 14 | $PSItem.Value 15 | } 16 | } 17 | #> 18 | 19 | $JsonData = ConvertFrom-Json $Line 20 | $UserInfo = $JsonData.userDeviceInfo.userInfo 21 | 22 | $Record = [ordered]@{ 23 | Timestamp = Get-Date $Timestamp -Format d 24 | Username = $UserInfo.userName 25 | EmailAddress = $UserInfo.emailAddress 26 | } 27 | 28 | $UserTable += New-Object -TypeName psobject -Property $Record 29 | } 30 | $UserTable = $UserTable | Sort-Object Username -Unique | Sort-Object Timestamp 31 | $UserTable -------------------------------------------------------------------------------- /ch10/08-mdm-reinstall-log.log: -------------------------------------------------------------------------------- 1 | 2018-09-06T00:09:45.351-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536210585273,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-1ef6fb492c3b","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-3f76-7e74-98d8-ab725136a663","userInfo":{"userGuid":"af78905c-806f-49a8-b491-3f3fc26564e5","userName":"NBFA091","emailAddress":"Joyce.Rose@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1332,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-19d5-b4bc-b05c-2e3c63190a11"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 2 | 2018-09-06T04:22:49.394-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536225769347,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-24c74ac96804","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-1c6d-7e74-889a-2111504a181d","userInfo":{"userGuid":"af78905c-aad9-475e-b491-7287c31c00b5","userName":"NBPA581","emailAddress":"Al.Lindsey@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1196,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-6af8-b4bc-a00a-3e4439d40662"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 3 | 2018-09-06T04:48:14.128-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536227294082,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-0debbac0e68e","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-8067-7e74-88fa-2573f3e735a5","userInfo":{"userGuid":"af78905c-efdb-4810-b491-880e0746b757","userName":"NBFA117","emailAddress":"Jody.Collins@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1397,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-2844-b4bc-877b-5ea155afadd5"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 4 | 2018-09-06T05:00:51.368-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536228051337,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-73a6b6f825ec","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-1c6d-7e74-889a-2111504a181d","userInfo":{"userGuid":"af78905c-aad9-475e-b491-7287c31c00b5","userName":"NBPA581","emailAddress":"Al.Lindsey@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1196,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-6af8-b4bc-a00a-3e4439d40662"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 5 | 2018-09-06T05:57:08.728-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536231428697,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-1c9ac25ad19b","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-8067-7e74-88fa-2573f3e735a5","userInfo":{"userGuid":"af78905c-efdb-4810-b491-880e0746b757","userName":"NBFA117","emailAddress":"Jody.Collins@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1397,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-2844-b4bc-877b-5ea155afadd5"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 6 | 2018-09-06T06:11:34.245-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536232294183,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-c2bc24106d60","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-a8d8-7e74-98d7-a07c075a38bd","userInfo":{"userGuid":"af78905c-b049-4cf4-b491-d4b3bb81183c","userName":"NBOB376","emailAddress":"Jean.Blair@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1618,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-fac9-b4bc-9a78-b81cf41f0633"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 7 | 2018-09-06T06:28:25.665-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536233305618,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-16100b4cd18a","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-1c6d-7e74-889a-2111504a181d","userInfo":{"userGuid":"af78905c-aad9-475e-b491-7287c31c00b5","userName":"NBPA581","emailAddress":"Al.Lindsey@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1196,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-6af8-b4bc-a00a-3e4439d40662"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 8 | 2018-09-06T07:11:15.156-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536235875109,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-ced4b1096412","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-1c6d-7e74-889a-2111504a181d","userInfo":{"userGuid":"af78905c-aad9-475e-b491-7287c31c00b5","userName":"NBPA581","emailAddress":"Al.Lindsey@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1196,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-6af8-b4bc-a00a-3e4439d40662"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 9 | 2018-09-06T07:33:39.693-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536237219584,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-46712ab46dfa","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-886a-7e74-a811-0730d2bb13c8","userInfo":{"userGuid":"af78905c-04df-4c0e-b491-6ea0ce6bed16","userName":"NBob003","emailAddress":"Marion.Cohen@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1254,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-55c9-b4bc-9223-54723922a997"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 10 | 2018-09-06T07:38:31.621-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536237511589,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-fae891dae7e2","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-71d4-7e74-b87d-95c42e6edece","userInfo":{"userGuid":"af78905c-e729-49c5-b491-8b9f11c25055","userName":"NBpa562","emailAddress":"Ruby.Chapman@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1500,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-da34-b4bc-8687-1b80cc81f5fb"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 11 | 2018-09-06T07:49:40.639-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536238180592,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-d4dee1d3b48a","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-8067-7e74-88fa-2573f3e735a5","userInfo":{"userGuid":"af78905c-efdb-4810-b491-880e0746b757","userName":"NBFA117","emailAddress":"Jody.Collins@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1397,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-2844-b4bc-877b-5ea155afadd5"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 12 | 2018-09-06T08:38:41.618-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536241121524,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-c31243e536c3","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-71d4-7e74-b87d-95c42e6edece","userInfo":{"userGuid":"af78905c-e729-49c5-b491-8b9f11c25055","userName":"NBpa562","emailAddress":"Ruby.Chapman@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1500,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-da34-b4bc-8687-1b80cc81f5fb"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 13 | 2018-09-06T09:10:02.513-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536243002466,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-7be244daa12a","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-1c6d-7e74-889a-2111504a181d","userInfo":{"userGuid":"af78905c-aad9-475e-b491-7287c31c00b5","userName":"NBPA581","emailAddress":"Al.Lindsey@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1196,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-6af8-b4bc-a00a-3e4439d40662"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 14 | 2018-09-06T09:49:02.366-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536245342319,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-fa85f40bab90","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-8067-7e74-88fa-2573f3e735a5","userInfo":{"userGuid":"af78905c-efdb-4810-b491-880e0746b757","userName":"NBFA117","emailAddress":"Jody.Collins@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1397,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-2844-b4bc-877b-5ea155afadd5"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 15 | 2018-09-06T11:00:47.174-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536249647127,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-a5001569c5f0","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-fe35-7e74-a823-2635d591079a","userInfo":{"userGuid":"af78905c-f687-42fd-b491-fe2791e42a40","userName":"NBob106","emailAddress":"Essie.Douglas@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1436,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-eae0-b4bc-be57-abefee274bee"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 16 | 2018-09-06T11:02:26.613-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536249746582,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-da3d688a5bbe","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-1c6d-7e74-889a-2111504a181d","userInfo":{"userGuid":"af78905c-aad9-475e-b491-7287c31c00b5","userName":"NBPA581","emailAddress":"Al.Lindsey@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1196,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-6af8-b4bc-a00a-3e4439d40662"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 17 | 2018-09-06T12:20:28.084-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536254428037,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-248cf993a722","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-1c6d-7e74-889a-2111504a181d","userInfo":{"userGuid":"af78905c-aad9-475e-b491-7287c31c00b5","userName":"NBPA581","emailAddress":"Al.Lindsey@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1196,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-6af8-b4bc-a00a-3e4439d40662"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 18 | 2018-09-06T12:40:26.220-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536255626173,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-c2ced769a120","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-71d4-7e74-b87d-95c42e6edece","userInfo":{"userGuid":"af78905c-e729-49c5-b491-8b9f11c25055","userName":"NBpa562","emailAddress":"Ruby.Chapman@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1500,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-da34-b4bc-8687-1b80cc81f5fb"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 19 | 2018-09-06T13:24:53.042-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536258292980,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-670f97b54b1f","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-a8d8-7e74-98d7-a07c075a38bd","userInfo":{"userGuid":"af78905c-b049-4cf4-b491-d4b3bb81183c","userName":"NBOB376","emailAddress":"Jean.Blair@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1618,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-fac9-b4bc-9a78-b81cf41f0633"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 20 | 2018-09-06T15:34:26.053-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536266065959,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-a6033312e34a","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-886a-7e74-a811-0730d2bb13c8","userInfo":{"userGuid":"af78905c-04df-4c0e-b491-6ea0ce6bed16","userName":"NBob003","emailAddress":"Marion.Cohen@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1254,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-55c9-b4bc-9223-54723922a997"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 21 | 2018-09-06T16:27:41.420-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536269261342,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-2fb7b6479712","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-a8d8-7e74-98d7-a07c075a38bd","userInfo":{"userGuid":"af78905c-b049-4cf4-b491-d4b3bb81183c","userName":"NBOB376","emailAddress":"Jean.Blair@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1618,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-fac9-b4bc-9a78-b81cf41f0633"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 22 | 2018-09-06T16:40:39.256-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536270039209,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-fb11e2160ee1","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-fe35-7e74-a823-2635d591079a","userInfo":{"userGuid":"af78905c-f687-42fd-b491-fe2791e42a40","userName":"NBob106","emailAddress":"Essie.Douglas@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1436,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-eae0-b4bc-be57-abefee274bee"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 23 | 2018-09-06T21:29:14.549-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536287354517,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-8ec615c839dc","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-8067-7e74-88fa-2573f3e735a5","userInfo":{"userGuid":"af78905c-efdb-4810-b491-880e0746b757","userName":"NBFA117","emailAddress":"Jody.Collins@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":1397,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-2844-b4bc-877b-5ea155afadd5"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 24 | 2018-09-06T21:32:15.112-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536287535066,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-757ca5511383","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-061d-7e74-a885-4f9be29c4486","userInfo":{"userGuid":"af78905c-83e8-452d-b491-e82ca0d7f649","userName":"NBpac22","emailAddress":"Andre.Parker@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":881,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-53f8-b4bc-b581-85ef6ab20e0f"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] 25 | 2018-09-06T21:32:15.112-0500 - CORE Exchange[Body: DeviceActionEvent {"eventTypeId":"979dba5297b4c8c8a9fb59d25f33fd2a","timestamp":1536287535081,"tenantId":1,"tenantGuid":"5b73c758-57af-47b2-a6d4-d7b6c52cb5e5","externalTenantId":"S650643799","correlationId":"7876c65b-2ba4-7648-9a94-757ca5511383","hostName":"svrbbp.mydomain.com","version":"5.63.87-SNAPSHOT","severity":"CLEARED","tags":["device_action","user","userdevice"],"userDeviceInfo":{"enrollmentType":"MDM_CONTROLS","perimeterUuid":"afa57869b-061d-7e74-a885-4f9be29c4486","userInfo":{"userGuid":"af78905c-83e8-452d-b491-e82ca0d7f649","userName":"NBpac22","emailAddress":"Andre.Parker@mydomain.com"},"deviceInfo":{"deviceOSFamily":"ios","id":881,"udid":"4a7783999478577b7746dc774657b992","guid":"6657bc4e-53f8-b4bc-b581-85ef6ab20e0f"},"perimeterState":"ENROLLED"},"deviceActionType":"INSTALL_APP"}] -------------------------------------------------------------------------------- /ch11/04-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | $TotalTime = 25 2 | $CurrentTime = 0 3 | 4 | while ($CurrentTime -le $TotalTime) { 5 | Write-Progress -Activity "Counting to $TotalTime" -Status "Elapsed time: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 6 | Start-Sleep 1 7 | $CurrentTime++ 8 | } -------------------------------------------------------------------------------- /ch11/05-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | param ($TotalTime = 25) 2 | 3 | $CurrentTime = 0 4 | 5 | while ($CurrentTime -le $TotalTime) { 6 | Write-Progress -Activity "Counting to $TotalTime" -Status "Elapsed time: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 7 | Start-Sleep 1 8 | $CurrentTime++ 9 | } -------------------------------------------------------------------------------- /ch11/06-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | function Start-Count { 2 | param ($TotalTime = 25) 3 | 4 | $CurrentTime = 0 5 | 6 | while ($CurrentTime -le $TotalTime) { 7 | Write-Progress -Activity "Counting to $TotalTime" -Status "Elapsed time: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 8 | Start-Sleep 1 9 | $CurrentTime++ 10 | } 11 | } -------------------------------------------------------------------------------- /ch11/07-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | $MyScriptBlock = { 2 | param ($TotalTime = 25) 3 | 4 | $CurrentTime = 0 5 | 6 | while ($CurrentTime -le $TotalTime) { 7 | Write-Progress -Activity "Counting to $TotalTime" -Status "Elapsed time: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 8 | Start-Sleep 1 9 | $CurrentTime++ 10 | } 11 | } -------------------------------------------------------------------------------- /ch12/01-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | function Start-Count { 2 | param ( 3 | # The current time, or the starting point 4 | [Parameter(Mandatory=$true, Position=0)] 5 | [int] 6 | $CurrentTime, 7 | 8 | # The total number of seconds to count 9 | [Parameter(Mandatory=$true, Position=1)] 10 | [int] 11 | $TotalTime 12 | ) 13 | 14 | while ($CurrentTime -le $TotalTime) { 15 | Write-Progress -Activity "Counting to $TotalTime" -Status "Counting: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 16 | Start-Sleep 1 17 | $CurrentTime++ 18 | } 19 | } -------------------------------------------------------------------------------- /ch12/01-Start-CountFlexible.ps1: -------------------------------------------------------------------------------- 1 | function Start-Count { 2 | param ( 3 | # The current time, or the starting point 4 | [Parameter(Mandatory=$false)] 5 | [int] 6 | $CurrentTime=0, 7 | 8 | # The total number of seconds to count 9 | [Parameter(Mandatory=$true, Position=1)] 10 | [int] 11 | $TotalTime 12 | ) 13 | 14 | while ($CurrentTime -le $TotalTime) { 15 | Write-Progress -Activity "Counting to $TotalTime" -Status "Counting: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 16 | Start-Sleep 1 17 | $CurrentTime++ 18 | } 19 | } -------------------------------------------------------------------------------- /ch12/02-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | function Start-Count { 2 | param ( 3 | # The current time, or the starting point 4 | [Parameter(Mandatory=$false)] 5 | [Alias("CT", "From")] 6 | [int] 7 | $CurrentTime=0, 8 | 9 | # The total number of seconds to count 10 | [Parameter(Mandatory=$true, Position=1)] 11 | [Alias("TT", "To")] 12 | [int] 13 | $TotalTime 14 | ) 15 | 16 | while ($CurrentTime -le $TotalTime) { 17 | Write-Progress -Activity "Counting to $TotalTime" -Status "Counting: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 18 | Start-Sleep 1 19 | $CurrentTime++ 20 | } 21 | } -------------------------------------------------------------------------------- /ch12/03-New-PersonalMessage.ps1: -------------------------------------------------------------------------------- 1 | function New-PersonalMessage { 2 | [CmdletBinding(DefaultParameterSetName='Audio')] 3 | param ( 4 | # The phone number 5 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='Audio')] 6 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='Text')] 7 | [string] 8 | $PhoneNumber, 9 | 10 | # The path to the WAV file 11 | [Parameter(Mandatory=$true, Position=1, ParameterSetName='Audio')] 12 | [string] 13 | $WavPath, 14 | 15 | # The message to be sent 16 | [Parameter(Mandatory=$true, Position=1, ParameterSetName='Text')] 17 | [string] 18 | $Message 19 | ) 20 | 21 | if ($WavPath) { 22 | New-VoiceCall -PhoneNumber $PhoneNumber -FilePath $WavPath 23 | } 24 | else { 25 | Send-TextMessage -PhoneNumber $PhoneNumber -Message $Message 26 | } 27 | } -------------------------------------------------------------------------------- /ch12/04-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | function Start-Count { 2 | param ( 3 | # The current time, or the starting point 4 | [Parameter(Mandatory=$false)] 5 | [Alias("CT", "From")] 6 | [int] 7 | $CurrentTime=0, 8 | 9 | # The total number of seconds to count 10 | [Parameter(Mandatory=$false, Position=1)] 11 | [Alias("TT", "To")] 12 | [int] 13 | $TotalTime=10 14 | ) 15 | 16 | while ($CurrentTime -le $TotalTime) { 17 | Write-Progress -Activity "Counting to $TotalTime" -Status "Counting: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 18 | Start-Sleep 1 19 | $CurrentTime++ 20 | } 21 | } -------------------------------------------------------------------------------- /ch12/05-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | function Start-Count { 2 | param ( 3 | # The current time, or the starting point 4 | [Parameter(Mandatory=$false)] 5 | [Alias("CT", "From")] 6 | [int] 7 | $CurrentTime=0, 8 | 9 | # The total number of seconds to count 10 | [Parameter(Mandatory=$false, Position=1)] 11 | [Alias("TT", "To")] 12 | [ValidateSet(5, 10, 15, 20)] 13 | [int] 14 | $TotalTime=10 15 | ) 16 | 17 | while ($CurrentTime -le $TotalTime) { 18 | Write-Progress -Activity "Counting to $TotalTime" -Status "Counting: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 19 | Start-Sleep 1 20 | $CurrentTime++ 21 | } 22 | } -------------------------------------------------------------------------------- /ch12/06-New-File.ps1: -------------------------------------------------------------------------------- 1 | function New-File { 2 | param ( 3 | # The path to the file (or the name) 4 | [Parameter(Mandatory=$true, Position=0)] 5 | [string[]] 6 | $Path 7 | ) 8 | begin { 9 | Write-Host "$(Get-Date)" 10 | } 11 | process { 12 | foreach ($Item in $Path) { 13 | New-Item -Path $Item -ItemType File 14 | } 15 | } 16 | end {} 17 | } -------------------------------------------------------------------------------- /ch12/07-New-File.ps1: -------------------------------------------------------------------------------- 1 | function New-File { 2 | [CmdletBinding( 3 | ConfirmImpact='High', 4 | SupportsShouldProcess=$true 5 | )] 6 | param ( 7 | # The path to the file (or the name) 8 | [Parameter(Mandatory=$true, Position=0)] 9 | [string[]] 10 | $Path 11 | ) 12 | begin { 13 | Write-Host "$(Get-Date)" 14 | } 15 | process { 16 | foreach ($Item in $Path) { 17 | if ($PSCmdlet.ShouldProcess("$PsScriptRoot", "Create $Path")) { 18 | New-Item -Path $Item -ItemType File 19 | } 20 | } 21 | } 22 | end {} 23 | } -------------------------------------------------------------------------------- /ch12/08-Start-Count.ps1: -------------------------------------------------------------------------------- 1 | function Start-Count { 2 | <# 3 | .SYNOPSIS 4 | This cmdlet counts seconds by accepting a from and a to value. The from value is optional. 5 | 6 | .DESCRIPTION 7 | This cmdlet is a counting function. It accepts two parameters: the TotalTime being the "to" and CurrentTime being the "from". CurrentTime defaults to 0 if not specified. 8 | 9 | .PARAMETER CurrentTime 10 | The starting point, in seconds. 11 | 12 | .PARAMETER TotalTime 13 | The stopping point, in seconds. 14 | 15 | .EXAMPLE 16 | Start-Count 10 15 17 | 18 | .NOTES 19 | Created as a demo for PowerShell 6.0 Linux Administration Cookbook 20 | #> 21 | param ( 22 | # The current time, or the starting point 23 | [Parameter(Mandatory=$false)] 24 | [Alias("CT", "From")] 25 | [int] 26 | $CurrentTime=0, 27 | 28 | # The total number of seconds to count 29 | [Parameter(Mandatory=$false, Position=1)] 30 | [Alias("TT", "To")] 31 | [ValidateSet(5, 10, 15, 20)] 32 | [int] 33 | $TotalTime=10 34 | ) 35 | 36 | while ($CurrentTime -le $TotalTime) { 37 | Write-Progress -Activity "Counting to $TotalTime" -Status "Counting: $CurrentTime seconds" -PercentComplete ($CurrentTime / $TotalTime * 100) 38 | Start-Sleep 1 39 | $CurrentTime++ 40 | } 41 | } -------------------------------------------------------------------------------- /ch12/09-New-File.ps1: -------------------------------------------------------------------------------- 1 | function New-File { 2 | [CmdletBinding( 3 | ConfirmImpact='High', 4 | SupportsShouldProcess=$true 5 | )] 6 | param ( 7 | # The path to the file (or the name) 8 | [Parameter(Mandatory=$true, Position=0, ValueFromPipeline)] 9 | [string[]] 10 | $Path 11 | ) 12 | begin { 13 | Write-Host "$(Get-Date)" 14 | } 15 | process { 16 | foreach ($Item in $Path) { 17 | if ($PSCmdlet.ShouldProcess("$PsScriptRoot", "Create $Item")) { 18 | New-Item -Path $Item -ItemType File 19 | } 20 | } 21 | } 22 | end {} 23 | } -------------------------------------------------------------------------------- /ch12/10-New-File.ps1: -------------------------------------------------------------------------------- 1 | function Main { 2 | New-FileName -Count 10 | New-File 3 | } 4 | 5 | function New-FileName { 6 | param ( 7 | # The name of the file 8 | [Parameter(Mandatory=$false)] 9 | [string] 10 | $Prefix='File', 11 | 12 | # The number of files to be generated 13 | [Parameter(Mandatory=$false, Position=1)] 14 | [int] 15 | $Count=1 16 | ) 17 | 18 | begin { 19 | $InitCount = 1 20 | } 21 | 22 | process { 23 | while ($InitCount -le $Count) { 24 | Write-Output $Prefix$InitCount 25 | $InitCount++ 26 | } 27 | } 28 | } 29 | 30 | function New-File { 31 | [CmdletBinding( 32 | ConfirmImpact='High', 33 | SupportsShouldProcess=$true 34 | )] 35 | param ( 36 | # The path to the file (or the name) 37 | [Parameter(Mandatory=$true, Position=0, ValueFromPipeline)] 38 | [string[]] 39 | $Path 40 | ) 41 | begin { 42 | Write-Host "$(Get-Date)" 43 | } 44 | process { 45 | foreach ($Item in $Path) { 46 | if ($PSCmdlet.ShouldProcess("$PsScriptRoot", "Create $Item")) { 47 | New-Item -Path $Item -ItemType File 48 | } 49 | } 50 | } 51 | end {} 52 | } 53 | 54 | . Main -------------------------------------------------------------------------------- /ch12/11-New-File.psm1: -------------------------------------------------------------------------------- 1 | function New-FileName { 2 | param ( 3 | # The name of the file 4 | [Parameter(Mandatory=$false)] 5 | [string] 6 | $Prefix='File', 7 | 8 | # The number of files to be generated 9 | [Parameter(Mandatory=$false, Position=1)] 10 | [int] 11 | $Count=1 12 | ) 13 | 14 | begin { 15 | $InitCount = 1 16 | } 17 | 18 | process { 19 | while ($InitCount -le $Count) { 20 | Write-Output $Prefix$InitCount 21 | $InitCount++ 22 | } 23 | } 24 | } 25 | 26 | function New-File { 27 | [CmdletBinding( 28 | SupportsShouldProcess=$true, 29 | DefaultParameterSetName='File' 30 | )] 31 | param ( 32 | # The path to the file (or the name) 33 | [Parameter(Mandatory=$true, Position=0, ValueFromPipeline, ParameterSetName='File')] 34 | [string[]] 35 | $Path, 36 | 37 | # Whether files need to be generated 38 | [Parameter(Mandatory=$true, ParameterSetName='Generate')] 39 | [switch] 40 | $Generate, 41 | 42 | # Number of files to be generated 43 | [Parameter(Mandatory=$false, ParameterSetName='Generate')] 44 | [int] 45 | $Count=1 46 | ) 47 | begin { 48 | Write-Host "$(Get-Date)" 49 | } 50 | process { 51 | if ($Generate) { 52 | $Path = New-FileName -Count $Count 53 | } 54 | foreach ($Item in $Path) { 55 | if ($PSCmdlet.ShouldProcess("$PsScriptRoot", "Create $Item")) { 56 | New-Item -Path $Item -ItemType File 57 | } 58 | } 59 | } 60 | end {} 61 | } 62 | 63 | Export-ModuleMember New-File -------------------------------------------------------------------------------- /ch12/12-New-File.psm1: -------------------------------------------------------------------------------- 1 | function New-FileName { 2 | param ( 3 | # The name of the file 4 | [Parameter(Mandatory=$false)] 5 | [string] 6 | $Prefix='File', 7 | 8 | # The number of files to be generated 9 | [Parameter(Mandatory=$false, Position=1)] 10 | [int] 11 | $Count=1 12 | ) 13 | 14 | begin { 15 | $InitCount = 1 16 | } 17 | 18 | process { 19 | while ($InitCount -le $Count) { 20 | Write-Output $Prefix$InitCount 21 | $InitCount++ 22 | } 23 | } 24 | } 25 | 26 | function New-File { 27 | [CmdletBinding( 28 | SupportsShouldProcess=$true, 29 | DefaultParameterSetName='File' 30 | )] 31 | param ( 32 | # The path to the file (or the name) 33 | [Parameter(Mandatory=$true, Position=0, ValueFromPipeline, ParameterSetName='File')] 34 | [string[]] 35 | $Path, 36 | 37 | # Whether files need to be generated 38 | [Parameter(Mandatory=$true, ParameterSetName='Generate')] 39 | [switch] 40 | $Generate, 41 | 42 | # Number of files to be generated 43 | [Parameter(Mandatory=$false, ParameterSetName='Generate')] 44 | [int] 45 | $Count=1 46 | ) 47 | begin { 48 | Write-Host "$(Get-Date)" 49 | } 50 | process { 51 | if ($Generate) { 52 | $Path = New-FileName -Count $Count 53 | } 54 | foreach ($Item in $Path) { 55 | if ($PSCmdlet.ShouldProcess("$PsScriptRoot", "Create $Item")) { 56 | New-Item -Path $Item -ItemType File 57 | } 58 | } 59 | } 60 | end {} 61 | } 62 | 63 | Export-ModuleMember New-File 64 | 65 | $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { 66 | Write-Output "Module cleaned up." 67 | } 68 | 69 | Register-EngineEvent PowerShell.Exiting { 70 | Remove-Module '12-New-File' 71 | } -------------------------------------------------------------------------------- /ch13/01-New-File.ps1: -------------------------------------------------------------------------------- 1 | function New-File { 2 | [CmdletBinding()] 3 | param ( 4 | # The path to the file (or the name) 5 | [Parameter(Mandatory=$true, Position=0)] 6 | [string[]] 7 | $Path 8 | ) 9 | Write-Debug "Entered the process block." 10 | foreach ($Item in $Path) { 11 | Write-Debug "Iterating for item, $Item." 12 | New-Item -Path $Item -ItemType File 13 | } 14 | } -------------------------------------------------------------------------------- /ch13/02-New-File.ps1: -------------------------------------------------------------------------------- 1 | function New-File { 2 | [CmdletBinding()] 3 | param ( 4 | # The path to the file (or the name) 5 | [Parameter(Mandatory=$true, Position=0)] 6 | [string[]] 7 | $Path 8 | ) 9 | begin { 10 | Write-Debug "Entered the begin block." 11 | Write-Host "$(Get-Date)" 12 | } 13 | process { 14 | Write-Debug "Entered the process block." 15 | foreach ($Item in $Path) { 16 | Write-Debug "Iterating for item, $Item." 17 | New-Item -Path $Item -ItemType File 18 | } 19 | } 20 | end { 21 | Write-Debug "Entered the end block." 22 | } 23 | } -------------------------------------------------------------------------------- /ch13/03-Set-Name.ps1: -------------------------------------------------------------------------------- 1 | function Set-Name { 2 | $Name = @() 3 | $Name = Read-Host "Enter first name" 4 | $Name += Read-Host "Enter middle name (press Enter for blank)" 5 | $Name += Read-Host "Enter surname" 6 | 7 | $Count = $Name.Count 8 | $Converter = (Get-Culture).TextInfo 9 | 10 | switch ($Count) { 11 | 2 { 12 | "$($Converter.ToTitleCase($Name[1])), $($Converter.ToTitleCase($Name[0]))" 13 | } 14 | 3 { 15 | "$($Converter.ToTitleCase($Name[2])), $($Converter.ToTitleCase($Name[0])) $($Converter.ToTitleCase($Name[1]))" 16 | } 17 | Default { 18 | Write-Host "Invalid input." 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /ch13/04-New-File.ps1: -------------------------------------------------------------------------------- 1 | function New-File { 2 | [CmdletBinding()] 3 | param ( 4 | # The path to the file (or the name) 5 | [Parameter(Mandatory=$true, Position=0)] 6 | [string[]] 7 | $Path 8 | ) 9 | begin { 10 | Write-Debug "Entered the begin block." 11 | Write-Host "$(Get-Date)" 12 | } 13 | process { 14 | Write-Debug "Entered the process block." 15 | foreach ($Item in $Path) { 16 | Write-Debug "Iterating for item, $Item." 17 | New-Item -Path $Item -ItemType File 18 | } 19 | } 20 | end { 21 | Write-Debug "Entered the end block." 22 | } 23 | } 24 | 25 | New-File One.txt -------------------------------------------------------------------------------- /ch13/05-Set-Name.ps1: -------------------------------------------------------------------------------- 1 | function Set-Name { 2 | $Name = @() 3 | $Name = Read-Host "Enter first name" 4 | $Name += Read-Host "Enter middle name (press Enter for blank)" 5 | $Name += Read-Host "Enter surname" 6 | 7 | $Count = $Name.Count 8 | $Converter = (Get-Culture).TextInfo 9 | 10 | switch ($Count) { 11 | 2 { 12 | "$($Converter.ToTitleCase($Name[1])), $($Converter.ToTitleCase($Name[0]))" 13 | } 14 | 3 { 15 | "$($Converter.ToTitleCase($Name[2])), $($Converter.ToTitleCase($Name[0])) $($Converter.ToTitleCase($Name[1]))" 16 | } 17 | Default { 18 | Write-Host "Invalid input." 19 | } 20 | } 21 | } 22 | Set-Name -------------------------------------------------------------------------------- /ch13/06-Find-Item.ps1: -------------------------------------------------------------------------------- 1 | ping ghostname 2 | 3 | if (!$?) { 4 | Write-Host "PowerShell seems to have encountered an error while running the last command." 5 | } 6 | 7 | Write-Host "The exit code for the last operation is $LASTEXITCODE." 8 | 9 | Remove-Variable LASTEXITCODE 10 | 11 | Get-Item $HOME/random/randompackage.zip -ErrorAction SilentlyContinue 12 | 13 | if (!$?) { 14 | Write-Host "PowerShell seems to have encountered an error while running the last command." 15 | } 16 | 17 | Write-Host "The exit code for the last operation is $LASTEXITCODE." -------------------------------------------------------------------------------- /ch13/07-Find-Item.ps1: -------------------------------------------------------------------------------- 1 | Get-Item $HOME/random/randompackage.zip -ErrorAction SilentlyContinue 2 | 3 | if (!$?) { 4 | Write-Host "PowerShell seems to have encountered an error while running the last command." 5 | } 6 | 7 | $Error | Select-Object -Property * -------------------------------------------------------------------------------- /ch13/08-New-LogFile.ps1: -------------------------------------------------------------------------------- 1 | function New-LogFile { 2 | param ( 3 | # The path to the log file 4 | [Parameter(Mandatory=$false)] 5 | [string] 6 | $Path = "$HOME/random/LogDir", 7 | 8 | # The name of the log file 9 | [Parameter(Mandatory=$false)] 10 | [string] 11 | $Name = 'MyLog.log' 12 | ) 13 | 14 | try { 15 | New-Item "$Path/$Name" -ItemType File -ErrorAction Stop 16 | } 17 | catch [System.IO.DirectoryNotFoundException] { 18 | New-Item $Path -ItemType Directory -Force 19 | New-LogFile 20 | } 21 | catch { 22 | "Some error other than DirectoryNotFound" 23 | } 24 | } -------------------------------------------------------------------------------- /ch13/09-New-LogFile.ps1: -------------------------------------------------------------------------------- 1 | function New-LogFile { 2 | param ( 3 | # The path to the log file 4 | [Parameter(Mandatory=$false)] 5 | [string] 6 | $Path = "$HOME/random/LogDir", 7 | 8 | # The name of the log file 9 | [Parameter(Mandatory=$false)] 10 | [string] 11 | $Name = 'MyLog.log' 12 | ) 13 | 14 | try { 15 | Write-Verbose "Creating the file, $Name at $Path." 16 | New-Item "$Path/$Name" -ItemType File -ErrorAction Stop 17 | } 18 | catch [System.IO.DirectoryNotFoundException] { 19 | Write-Verbose "System.IO.DirectoryNotFoundException encountered." 20 | Write-Verbose "Creating the directory, $Path." 21 | New-Item $Path -ItemType Directory -Force 22 | Write-Verbose "Calling the function." 23 | New-LogFile 24 | } 25 | catch { 26 | Write-Verbose "Catch-all. There was an error." 27 | Write-Error $_ 28 | } 29 | } 30 | New-LogFile -Verbose 4>> verbose.log 2>> error.log -------------------------------------------------------------------------------- /ch14/01-PoSH-Remote-Query.ps1: -------------------------------------------------------------------------------- 1 | 2 | Import-Csv ./input.csv|% { Invoke-Command -HostName $_.Server -UserName $_.User -ScriptBlock { Param ([string]$Server) 3 | Get-Process | Sort-Object CPU-Descending |` 4 | Select-Object Handles, CPU, ` 5 | @{name = "NPM"; Expression = {[int]($_.NPM / 1024)}}, ` 6 | @{name = "PM"; Expression = {[int]($_.PM / 1024)}}, ` 7 | @{name = "WS"; Expression = {[int]($_.WS / 1024)}}, ` 8 | @{name = "VM"; Expression = {[int]($_.VM / 1MB)}},` 9 | @{name = "ServerName"; Expression = {($Server)}},` 10 | Id, ProcessName -First 5 11 | }-Args $_.Server 12 | } 13 | -------------------------------------------------------------------------------- /ch14/02-PoSH-Remote-Query-HTML.ps1: -------------------------------------------------------------------------------- 1 | $Top = @() 2 | 3 | $process =Import-Csv ./input.csv|% { Invoke-Command -HostName $_.Server -UserName $_.User -ScriptBlock { Param($Server) 4 | Get-Process | Sort-Object CPU-Descending |` 5 | Select-Object Handles, CPU, ` 6 | @{name = "NPM"; Expression = {[int]($_.NPM / 1024)}}, ` 7 | @{name = "PM"; Expression = {[int]($_.PM / 1024)}}, ` 8 | @{name = "WS"; Expression = {[int]($_.WS / 1024)}}, ` 9 | @{name = "VM"; Expression = {[int]($_.VM / 1MB)}},` 10 | @{name = "ServerName"; Expression = {($server)}},` 11 | Id, ProcessName -First 5 12 | }-Args $_.Server 13 | } 14 | 15 | foreach ($proc in $process) { 16 | 17 | $row = New-Object -Type PSObject -Property @{ 18 | ServerName = $proc.Servername 19 | ID = $proc.ID 20 | ProcessName = $proc.ProcessName 21 | NPM_KB = $proc.NPM 22 | PM_KB = $proc.PM 23 | WS_KB = $proc.WS 24 | VM_MB = $proc.VM 25 | CPU_S = $proc.CPU 26 | } 27 | $Top += $row 28 | } 29 | 30 | $Top | ConvertTo-Html |Out-File /tmp/out.html 31 | -------------------------------------------------------------------------------- /ch14/03-PoSH-Remote-Query-HTML-Mail.ps1: -------------------------------------------------------------------------------- 1 | #inputfile - filelocation of the CSV file 2 | $inputfile='/tmp/input.CSV' 3 | 4 | #Ouput file 5 | $outfile='/tmp/out.html' 6 | #Email variable declaration 7 | $from = 'donotreply@packtpublinux.com' 8 | $to = 'pjayaram@gmail.com' 9 | $cc = 'pjayaram@gmail.com' 10 | $sub = 'Process Info-PowerShell Remoting' 11 | $body = 'PowerShell script automation email to pull process related information from Windows and Linux machines' 12 | $attachment = $outfile 13 | $smtpserver = 'packpublinux@smtp.com' 14 | 15 | #declare the array to hold multiple values 16 | $Top = @() 17 | #the output of import-csv is assigned to a variable 18 | $process =Import-Csv $inputfile|% { Invoke-Command -HostName $_.Server -UserName $_.User -ScriptBlock { Param($Server) 19 | Get-Process | Sort-Object CPU-Descending |` 20 | Select-Object Handles, CPU, ` 21 | @{name = "NPM"; Expression = {[int]($_.NPM / 1024)}}, ` 22 | @{name = "PM"; Expression = {[int]($_.PM / 1024)}}, ` 23 | @{name = "WS"; Expression = {[int]($_.WS / 1024)}}, ` 24 | @{name = "VM"; Expression = {[int]($_.VM / 1MB)}},` 25 | @{name = "ServerName"; Expression = {($server)}},` 26 | Id, ProcessName -First 5 }-Args $_.Server 27 | } 28 | foreach ($proc in $process) 29 | { 30 | $row = New-Object -Type PSObject -Property @{ 31 | ServerName = $proc.Servername 32 | ID = $proc.ID 33 | ProcessName = $proc.ProcessName 34 | NPM_KB = $proc.NPM 35 | PM_KB = $proc.PM 36 | WS_KB = $proc.WS 37 | VM_MB = $proc.VM 38 | CPU_S = $proc.CPU 39 | } 40 | $Top += $row 41 | } 42 | $Top | ConvertTo-Html |Out-File $outfile 43 | 44 | #Send Email outout to intended receipients 45 | 46 | $message = new-object System.Net.Mail.MailMessage 47 | $message.From = $from 48 | $message.To.Add($to) 49 | $message.CC.Add($cc) 50 | $message.IsBodyHtml = $True 51 | $message.Subject = $sub 52 | $attach = new-object Net.Mail.Attachment($attachment) 53 | $message.Attachments.Add($attach) 54 | $message.body = $body 55 | $smtp = new-object Net.Mail.SmtpClient($smtpserver) 56 | $smtp.Send($message) -------------------------------------------------------------------------------- /ch14/Input.CSV: -------------------------------------------------------------------------------- 1 | Server,user 2 | hfd01,ssh-test 3 | 10.2.6.68,prashant 4 | -------------------------------------------------------------------------------- /ch15/SampleAzureRmVM.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | # Variables for common values 3 | [string]$resourceGroup = "packtpub-rg", 4 | [string]$storageAcctName ="packtstorage", 5 | [string]$location = "East US", 6 | [string]$vnetName="packtpub-vnet", 7 | [string]$NicName="packtpub-vm-nic", 8 | [string]$diskname="packtUbuntu", 9 | [string]$vmName = "packtpub-ubuntu" 10 | ) 11 | 12 | 13 | #Check for open session 14 | Connect-AzureRmAccount 15 | 16 | # Create user object 17 | $cred = Get-Credential -Message "Enter username and password of administrator crendentials" 18 | 19 | # Create a resource group 20 | New-AzureRmResourceGroup -Name $resourceGroup -Location $location 21 | 22 | #Create a storage account 23 | New-AzureRmStorageAccount -Name $storageAcctName -ResourceGroupName $resourceGroup -Type Standard_LRS -Location $location 24 | 25 | 26 | # Create a subnet configuration 27 | $subnetCfg = New-AzureRmVirtualNetworkSubnetConfig -Name mySubnet -AddressPrefix 10.0.1.0/24 28 | 29 | # Create a virtual network 30 | $vnet=New-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $resourceGroup -Location $location -AddressPrefix 10.0.0.0/16 -Subnet $subnetCfg 31 | 32 | # Create a public IP address and specify a DNS name 33 | 34 | 35 | # Create a virtual network card and associate with public IP address and NSG 36 | $nic=New-AzureRmNetworkInterface -Name $nicname -ResourceGroupName $resourceGroup -Location $location -SubnetId $vnet.Subnets[0].Id -PublicIpAddressId $IP.Id 37 | 38 | 39 | #Create a blob storage 40 | $storageAccount=Get-AzureRmStorageAccount -ResourceGroupName $resourceGroup -Name $storageAcctName 41 | $osdiskuri=$storageAccount.PrimaryEndpoints.Blob.ToString()+'vhds/'+$diskname+".vhd" 42 | 43 | # Create a virtual machine configuration 44 | $vm =New-AzureRmVMConfig -VMName $vmname -VMSize "Basic_A1" 45 | $vm=Set-AzureRmVMOperatingSystem -VM $vm -Linux -ComputerName $vmname -Credential $cred 46 | $vm=Set-AzureRmVMSourceImage -VM $vm -PublisherName 'Canonical' -Offer 'UbuntuServer' -Skus '18.04-LTS' -Version 'latest' 47 | $vm=Add-AzureRmVMNetworkInterface -VM $vm -id $nic.Id 48 | $vm=Set-AzureRmVMOSDisk -VM $vm -Name $diskname -VhdUri $osdiskuri -CreateOption fromImage 49 | 50 | 51 | 52 | # Create a virtual machine 53 | New-AzureRmVM -ResourceGroupName $resourceGroup -Location $location -VM $vm -------------------------------------------------------------------------------- /ch16/Install-SqlServer.ps1: -------------------------------------------------------------------------------- 1 | # Run this as an administrator. 2 | 3 | Set-PSRepository -Name PsGallery -InstallationPolicy Trusted 4 | 5 | Install-Module SqlServer 6 | 7 | if ((Get-Module -ListAvailable).Name -contains 'SqlServer') { 8 | Get-Command -CommandType Cmdlet | Where-Object Source -Match 'SQL' | Group-Object Source 9 | } -------------------------------------------------------------------------------- /ch16/New-DataFormatExample.ps1: -------------------------------------------------------------------------------- 1 | #Define Input variables 2 | $dataSource = "10.2.6.50" 3 | #Define the login credentials for SQL authentication 4 | $user = "SA" 5 | $pwd = "thanVitha@2015" 6 | #Define the database name from which the data is going to be read 7 | $database = "PacktPub" 8 | #Build the connection string 9 | $connStr = "Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;" 10 | #Prepare the query 11 | $query = "SELECT * FROM TaskManagerDump" 12 | #Instantiate the sqlConnection namespace 13 | $Conn = New-Object System.Data.SqlClient.SqlConnection 14 | $Conn.ConnectionString = $connStr 15 | #Open the connection 16 | $Conn.Open() 17 | #Create sub-objects to the Main object $Conn 18 | $cmd = $Conn.CreateCommand() 19 | #Assign the Query text to a $command object 20 | $cmd.CommandText = $query 21 | #Execute the adapter 22 | $resultset = $cmd.ExecuteReader() 23 | #Create an object DataTable namespace 24 | $table = new-object "System.Data.DataTable" 25 | #Load the data to the table 26 | $table.Load($resultset) 27 | $table |Where-Object {$_.ProcessName -match "pwsh"} 28 | #$table |Where-Object {$_.ProcessName -match "pwsh"} | format-table -autosize 29 | #$table |Where-Object {$_.ProcessName -match "pwsh"} | format-list 30 | #$table |Where-Object {$_.ProcessName -match "pwsh"} | format-table $format 31 | #$table | Where-Object {$_.ProcessName -match "pwsh"}| format-table $format | Out-File /tmp/output.txt 32 | $Conn.Close() -------------------------------------------------------------------------------- /ch16/New-GetProcessRepo.ps1: -------------------------------------------------------------------------------- 1 | #Import the SqlServer Module 2 | Import-Module -Name Sqlserver 3 | #The Linux SQL Instance IP Address 4 | $SQLServer='10.2.6.50' 5 | #Define credential details 6 | $User='SA' 7 | #Convert password text to a secure string 8 | $Pass=ConvertTo-SecureString 'thanVitha@2015' -AsPlainText -Force 9 | #Build the credetial using securing string 10 | $cred=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User,$Pass 11 | (Get-Process | Select-Object -Property ` Id,ProcessName,StartTime,UserProcessorTime,WorkingSet,Description) | ` 12 | Write-SqlTableData -Credential $cred -ServerInstance "localhost" -DatabaseName ` 13 | "PacktPub" -SchemaName "dbo" -TableName "TaskManagerDump" -Force -------------------------------------------------------------------------------- /ch16/New-MultiServerTsql.ps1: -------------------------------------------------------------------------------- 1 | #Import the SqlServer module 2 | Import-Module –Name SqlServer 3 | 4 | #Read the CSV file content using Import-CSV cmdlet 5 | Import-Csv -Path $filepath|ForEach-Object { 6 | 7 | #The Linux SQL Instance IP Address 8 | $SQLServer=$_.InstanceName 9 | 10 | #Define credential details 11 | $User=$_.Username 12 | 13 | #Convert password text to a secure string 14 | $Pass=ConvertTo-SecureString $_.Password -AsPlainText -Force 15 | 16 | #Build the credential using securing string 17 | $cred=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User,$Pass 18 | $SQLServerObject=New-Object Microsoft.SqlServer.Management.Smo.Server $SQLServer 19 | $SQLServerObject.ConnectionContext.LoginSecure=$false 20 | $SQLServerObject.ConnectionContext.set_login($cred.Username) 21 | $SQLServerObject.ConnectionContext.set_SecurePassword($cred.Password) 22 | 23 | #Handle the exceptions using Try and Catch method 24 | try 25 | { #Build connection to the Local SQL Instance 26 | $SQLServerObject.ConnectionContext.connect() 27 | $db=New-Object Microsoft.SqlServer.Management.Smo.Database $SQLServerObject,'PacktPub' 28 | $db.Create() 29 | } 30 | catch 31 | { 32 | #Write the exception message 33 | write-Error $_.Exception.message 34 | } 35 | finally 36 | { 37 | #Close the connection 38 | $SQLServerObject.ConnectionContext.Disconnect() 39 | } 40 | } -------------------------------------------------------------------------------- /ch16/New-TsqlQuery.ps1: -------------------------------------------------------------------------------- 1 | $SQLServer='hqdbt01' 2 | $SQLServerObject=New-Object Microsoft.SqlServer.Management.Smo.Server $SQLServer 3 | $SQLServerObject.ConnectionContext.LoginSecure=$false 4 | $SQLServerObject.ConnectionContext.set_login("SA") 5 | $SQLServerObject.ConnectionContext.set_Password("PackPub@2018") 6 | $SQLServerObject.Information | Select-Object FullyQualifiedNetName, Parent, Version, Edition | Format-Table -AutoSize -------------------------------------------------------------------------------- /ch16/program.cs: -------------------------------------------------------------------------------- 1 | // Replace the Program.cs that got created when you ran `dotnet new console` 2 | 3 | using System; 4 | namespace PacktPub { 5 | class Program { 6 | static void Main(string[] args) { 7 | Console.WriteLine("Introduction to .Net Core on Linux!"); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /ch17/PoSH-Find-DockerTags.ps1: -------------------------------------------------------------------------------- 1 | function Get-AllImages { 2 | param( 3 | # Get the base image name 4 | [Parameter(Mandatory=$true)] 5 | [string] 6 | $Repo 7 | ) 8 | begin { 9 | $UriTest = Invoke-WebRequest "https://registry.hub.docker.com/v1/repositories/$Repo/tags" -DisableKeepAlive -UseBasicParsing 10 | if ($UriTest.StatusCode -eq 200) { 11 | # Just proceed 12 | } 13 | else { 14 | break 15 | } 16 | } 17 | process { 18 | (Invoke-WebRequest "https://registry.hub.docker.com/v1/repositories/$Repo/tags" | ConvertFrom-Json).SyncRoot.Name 19 | } 20 | } 21 | 22 | # Call the function as `Get-AllImages -Repo centos` -------------------------------------------------------------------------------- /ch17/PoSH-Setup-Docker.ps1: -------------------------------------------------------------------------------- 1 | #Define the array of elements [1,2,3] 2 | 1,2,3|foreach-object { 3 | #Build the name of the servers. Its going to be like PackPubDemo1,PackPubDemo2,PackPubDemo3 4 | $name='PackPubDemo'+$_ 5 | #print the name of docker containers 6 | write-host $name 7 | try 8 | { 9 | #integrate the powershell variable with the docker cli command 10 | docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=PacktPubDemo$2018' --name $name -d microsoft/mssql-server-linux:2017-latest 11 | } 12 | catch 13 | { 14 | #Handle exception 15 | write-host $_.Exception.Message 16 | } 17 | } -------------------------------------------------------------------------------- /cheatsheets/chapter-01.md: -------------------------------------------------------------------------------- 1 | # Introducing PowerShell Core 2 | 3 | Get help on any command: 4 | 5 | ```powershell 6 | # Get-Help 7 | Get-Help Get-Command 8 | ``` 9 | 10 | Get more help on a certain cmdlet (e.g. `Get-Command`): 11 | 12 | ```powershell 13 | # Full help text 14 | Get-Help Get-Command -Full 15 | 16 | # Show examples 17 | Get-Help Get-Command -Examples 18 | 19 | # Read the help in a browser 20 | Get-Help Get-Command -Online 21 | ``` 22 | 23 | Update the help information: 24 | 25 | ```powershell 26 | Update-Help 27 | ``` 28 | 29 | Select specific string from the output: 30 | 31 | ```powershell 32 | Get-Help Get-Command | Out-String -Stream | Select-String 'common' 33 | ``` 34 | 35 | Get help on a specific parameter: 36 | 37 | ```powershell 38 | Get-Help Get-Command -Parameter Noun 39 | ``` 40 | 41 | Work with `about_` topics: 42 | 43 | ```powershell 44 | # List out all the `about_` topics 45 | Get-Help about_* 46 | 47 | # Pick the topic you would like to read about and then 48 | Get-Help about_Modules 49 | ``` 50 | 51 | Search for cmdlets: 52 | 53 | ```powershell 54 | # Discover cmdlets based on the noun 55 | Get-Command -Noun Process 56 | 57 | # Dicsover cmdlets based on the verb as well 58 | Get-Command -Verb Get -Noun Process 59 | 60 | # Dicsover cmdlets based on the module 61 | Get-Command -Noun Process -Module Microsoft.PowerShell.Management 62 | ``` 63 | 64 | Search for a module in the PowerShell Gallery 65 | 66 | ```powershell 67 | Find-Module 'Docker' 68 | ``` 69 | 70 | Install a module: 71 | 72 | ```powershell 73 | # Install directly from the PowerShell Gallery 74 | Install-Module 'Docker' 75 | 76 | # To save the module and import it instead 77 | # Create a folder where you would like to store the modules: 78 | New-Item -ItemType Directory -Path ~/PSModules 79 | 80 | # Save the module in the directory you just created 81 | Save-Module Docker ~/PsModules 82 | 83 | # Import the module 84 | Import-Module ~/PsModules/Docker/1.3.2/Docker.psm1 85 | ``` 86 | 87 | Update a module: 88 | 89 | ```powershell 90 | Update-Module Docker 91 | ``` 92 | 93 | Remove a module from the system: 94 | 95 | ```powershell 96 | # Would work on the modules installed with `Install-Module` 97 | Uninstall-Module Docker 98 | 99 | # For modules you saved, simply delete the directory that contains the module files 100 | Remove-Item ~/PsModules/Docker -Recurse 101 | ``` 102 | 103 | List out all the providers in PowerShell: 104 | 105 | ```powershell 106 | Get-PsProvider 107 | ``` 108 | 109 | Navigate to one of the drives in one of the providers: 110 | 111 | ```powershell 112 | Set-Location Alias: 113 | ``` 114 | 115 | Get information on a certain directory: 116 | 117 | ```powershell 118 | # Using the .NET class 119 | New-Object -TypeName System.IO.DirectoryInfo -ArgumentList '/home/ram' 120 | 121 | # Using PowerShell 122 | Get-Item '/home/ram' 123 | ``` 124 | 125 | Get the members that are part of the returned object: 126 | 127 | ```powershell 128 | Get-Item '/home/ram' | Get-Member 129 | ``` 130 | 131 | Get one of the members from the output: 132 | 133 | ```powershell 134 | # Find the time the directory was written to, last 135 | (Get-Item /home/ram).LastWriteTime 136 | 137 | # Find the parent of the directory 138 | (Get-Item /home/ram).Parent 139 | 140 | # Find the time the parent was created 141 | (Get-Item /home/ram).Parent.CreationTime 142 | 143 | # Pick just the year property 144 | (Get-Item /home/ram).Parent.CreationTime.Year 145 | ``` 146 | 147 | Create a subdirectory within a directory using one of the methods available in the output object: 148 | 149 | ```powershell 150 | (Get-Item /home/ram).CreateSubdirectory('test-directory') 151 | ``` 152 | 153 | Use a .NET type accelerator to convert text to an object: 154 | 155 | ```powershell 156 | [datetime]'21 June 2018' 157 | 158 | # Verify if the text was indeed converted into an object 159 | [DateTime]'21 June 2018' | Get-Member 160 | ``` 161 | 162 | Use PowerShell to convert the same string into a date object: 163 | 164 | ```powershell 165 | Get-Date '21 June 2018' 166 | 167 | # Pick just the Year property to verify the output is indeed an object 168 | (Get-Date '21 June 2018').Year 169 | ``` 170 | 171 | Convert text in the form of comma-separated values, to a PowerShell custom object: 172 | 173 | ```powershell 174 | # Create a file from text 175 | @' 176 | WS,CPU,Id,SI,ProcessName 177 | 161226752,23.42,1914,1566,io.elementary.a 178 | 199598080,77.84,1050,1040,gnome-shell 179 | 216113152,0.67,19250,1566,atom 180 | 474685440,619.05,1568,1566,Xorg 181 | 1387864064,1890.29,15720,1566,firefox 182 | '@ | Out-File sample.csv 183 | 184 | # Read the contents of the CSV file 185 | Get-Content ./sample.csv 186 | 187 | # Import the file into PowerShell instead of just reading the content 188 | Import-Csv ./sample.csv 189 | ``` 190 | 191 | Compare output of Bash and PowerShell; work with files within a directory: 192 | 193 | ```powershell 194 | # Use Linux commandline tools 195 | ls -l | awk '{print $6, $7, $8, $9}' 196 | 197 | # Identify the output object type 198 | ls -l | awk '{print $6, $7, $8, $9}' | Get-Member 199 | 200 | # Use PowerShell 201 | Get-ChildItem | select LastWriteTime, Name 202 | 203 | # Identify the output object type 204 | Get-ChildItem | select LastWriteTime, Name | Get-Member 205 | ``` 206 | 207 | Work with aliases in PowerShell: 208 | 209 | ```powershell 210 | # List out all the available aliases 211 | Get-Alias 212 | 213 | # Get information on a specific alias 214 | Get-Alias gbp 215 | 216 | # Find all aliases for a certain cmdlet 217 | Get-Alias -Definition Get-ChildItem 218 | ``` 219 | 220 | Create a custom alias: 221 | 222 | ```powershell 223 | New-Alias listdir Get-ChildItem 224 | ``` 225 | 226 | Export aliases to a file for future use: 227 | 228 | ```powershell 229 | # As a CSV file to use with Import-Alias 230 | Export-Alias aliases.csv 231 | 232 | # As a script, if you do not want to use CSV 233 | Export-Alias aliases.ps1 -As Script 234 | ``` 235 | 236 | Working with execution policies (Windows only for now): 237 | 238 | ```powershell 239 | # Find the execution policy in effect 240 | Get-ExecutionPolicy 241 | 242 | # List out all the execution policies set at different levels 243 | Get-ExecutionPolicy -List 244 | 245 | # Set an execution policy at the machine level 246 | Set-ExecutionPolicy Undefined -Scope LocalMachine 247 | 248 | # Set an execution policy at the user level 249 | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser 250 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-02.md: -------------------------------------------------------------------------------- 1 | # Preparing for Administration using PowerShell 2 | 3 | Install Visual Studio Code from the repository (Ubuntu): 4 | 5 | ```bash 6 | sudo apt install code 7 | ``` 8 | 9 | To install Visual Studio Code using the `deb` package: 10 | 11 | ```bash 12 | sudo apt install install code_version_arch.deb 13 | ``` 14 | 15 | List the variables in a new terminal session: 16 | 17 | ```powershell 18 | # When using a cmdlet 19 | Get-Variable 20 | 21 | # When using a PowerShell provider 22 | Set-Location Variable: 23 | Get-ChildItem . 24 | ``` 25 | 26 | To silence error output in case of errors: 27 | 28 | ```powershell 29 | Set-Variable ErrorActionPreference SilentlyContinue 30 | ``` 31 | 32 | Working with a PowerShell profile: 33 | 34 | ```powershell 35 | # Create the new profile 36 | New-Item $PROFILE -ItemType File -Force 37 | 38 | # Edit the new profile in Visual Studio Code 39 | code $PROFILE 40 | 41 | # Delete the profile 42 | Remove-Item $PROFILE 43 | ``` 44 | 45 | Sample long-running line of PowerShell code, broken for readability: 46 | 47 | ```powershell 48 | Get-ChildItem /home/$env:USERNAME/Downloads | 49 | Where-Object {$_.LastWriteTime.Year -eq 2018} | Select-Object ` 50 | Name, LastWriteTime 51 | ``` 52 | 53 | Send command output to a file: 54 | 55 | ```powershell 56 | # The Linux way (which works on PowerShell) 57 | Get-Process > processes.txt 58 | 59 | # The PowerShell way 60 | Get-Process | Out-File processes.txt 61 | ``` 62 | 63 | Append output to a file: 64 | 65 | ```powershell 66 | # The Linux way (which works in PowerShell) 67 | Get-Process >> processes.txt 68 | 69 | # The PowerShell way 70 | Get-Process | Out-File processes.txt -Append 71 | ``` 72 | 73 | To display the output on the terminal emulator as well as send it to a file: 74 | 75 | ```powershell 76 | # Only the PowerShell way 77 | Get-Process | Tee-Object ./processes.txt 78 | ``` 79 | 80 | Reading content from a file: 81 | 82 | ```bash 83 | # The Linux way (which DOES NOT work on PowerShell) 84 | command < input.txt 85 | ``` 86 | 87 | ```powershell 88 | # The PowerShell way 89 | Get-Content input.txt | Remove-Item -WhatIf 90 | ``` 91 | 92 | Create new files/directories: 93 | 94 | ```powershell 95 | # Create a new directory 96 | New-Item -Path test-dir -ItemType Directory 97 | 98 | # Create a complete path instead (directory inside a directory) 99 | New-Item test-dir/child-dir -ItemType Directory -Force 100 | 101 | # Create multiple files 102 | New-Item ./test-dir/file1, ./test-dir/file2, ./test-dir/child-dir/file3 103 | ``` 104 | 105 | Find parameter aliases: 106 | 107 | ```PowerShell 108 | (Get-Command Invoke-Command).Parameters.Values | select Name, Aliases 109 | ``` 110 | 111 | Call PowerShell scripts from outside of PowerShell 112 | 113 | ```PowerShell 114 | # When the path to the script does not contain spaces 115 | /path/to/the-script.ps1 116 | 117 | # When the path to the script file contains a space 118 | & '/path/to/directory with spaces/script.ps1' 119 | 120 | # When we want the variables, aliases and functions retained in the session 121 | . /path/to/the-script.ps1 122 | 123 | # If there are spaces in the path, enclose it in quotes 124 | . '/path/to/directory with spaces/script.ps1' 125 | ``` 126 | 127 | Call a PowerShell cmdlet from outside of PowerShell 128 | 129 | ```bash 130 | # When calling a cmdlet 131 | pwsh -c Get-ChildItem 132 | 133 | # When calling a script 134 | pwsh -f ./Documents/code/github/powershell/chapter-3/hello-world.ps1 135 | ``` 136 | 137 | Record actions performed at the PowerShell console 138 | 139 | ```powershell 140 | # Start the recording 141 | Start-Transcript -Path ./command-transcript.txt 142 | 143 | # Perform actions on the terminal 144 | Get-Date 145 | 146 | Get-ChildItem . 147 | 148 | New-Item test-transcript -ItemType Directory 149 | 150 | New-Item -Path test-transcript/testing-transcript.txt -ItemType File 151 | 152 | # Add contents to the newly-created file 153 | @' 154 | In publishing and graphic design, lorem ipsum is a placeholder text 155 | commonly used to demonstrate the visual form of a document without relying 156 | on meaningful content (also called greeking). 157 | Replacing the actual content with placeholder text allows designers to 158 | design the form of the content before the content itself has been produced. 159 | —Wikipedia 160 | '@ | Out-File ./test-transcript/testing-transcript.txt -Append 161 | 162 | Remove-Item -Path ./test-transcript -Recurse 163 | 164 | # Stop the recording 165 | Stop-Transcript 166 | 167 | # Read the contents of the recording 168 | Get-Content -Path ./command-transcript.txt 169 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-03.md: -------------------------------------------------------------------------------- 1 | # First steps to administration using PowerShell 2 | 3 | ## Working with date and time 4 | 5 | Custom formatting dates: 6 | 7 | ```PowerShell 8 | $Date = Get-Date 9 | "$($Date.Day)/$($Date.Month)/$($Date.Year)" 10 | ``` 11 | 12 | Pre-formatted dates (some examples): 13 | 14 | ```PowerShell 15 | Get-Date -Format d 16 | Get-Date -Format g 17 | Get-Date -Format U 18 | Get-Date -Format yyyy/MM/dd 19 | Get-Date -Format yyyyMMddhhmmss 20 | ``` 21 | 22 | UNIX-like date formatting: 23 | 24 | ```PowerShell 25 | Get-Date -Uformat %d/%m/%Y 26 | ``` 27 | 28 | Find what day of week a certain date is: 29 | 30 | ```PowerShell 31 | # Avoiding all confusion 32 | (Get-Date -Day 31 -Month 10 -Year 2018).DayOfWeek 33 | 34 | # If your locale is the only context your scripts would run in 35 | (Get-Date 31/10/2018).DayOfWeek 36 | 37 | # Using a type accelerator 38 | ([datetime]'10/31/2018').DayOfWeek 39 | 40 | # Using a type accelerator, avoiding ambiguity 41 | ([datetime]'31 October 2018').DayOfWeek 42 | ``` 43 | 44 | Convert time into UTC: 45 | 46 | ```PowerShell 47 | (Get-Date).ToUniversalTime() 48 | ``` 49 | 50 | Calculating time: 51 | 52 | ```PowerShell 53 | # Add days to the current date 54 | (Get-Date).AddDays(35) 55 | 56 | # Subtract days from the current date 57 | (Get-Date).AddDays(-21) 58 | 59 | # Add hours and minutes 60 | (Get-Date).AddHours(3).AddMinutes(18) 61 | 62 | # Time between two dates 63 | (Get-Date).Subtract((Get-Date '5 June 2016')) 64 | ``` 65 | 66 | ## Working with processes 67 | 68 | Working with currently running processes: 69 | 70 | ```PowerShell 71 | # Listing out the processes 72 | Get-Process 73 | 74 | # Counting the processes 75 | (Get-Process).Count 76 | ``` 77 | 78 | Getting the average of memory pages used: 79 | 80 | ```PowerShell 81 | Get-Process | Measure-Object -Property WS -Average 82 | ``` 83 | 84 | Getting more working set information: 85 | 86 | ```PowerShell 87 | Get-Process | Measure-Object -Property WS -Average -Sum -Minimum - 88 | Maximum 89 | ``` 90 | 91 | Getting more than one such properties from the process table: 92 | 93 | ```PowerShell 94 | Get-Process | Measure-Object -Property WS, CPU -Average -Sum -Minimum - 95 | Maximum 96 | ``` 97 | 98 | Getting the owner of processes: 99 | 100 | ```PowerShell 101 | Get-Process -IncludeUserName 102 | ``` 103 | 104 | Listing the processes owned by you: 105 | 106 | ```PowerShell 107 | # Listing the processes 108 | Get-Process -IncludeUserName | grep $env:USERNAME 109 | 110 | # Counting the processes 111 | (Get-Process -IncludeUserName | grep $env:USERNAME).Count 112 | 113 | # Alternatively, 114 | Get-Process -IncludeUserName | grep $env:USERNAME | Measure-Object 115 | ``` 116 | 117 | Bonus: Measuring the working set consumed by the processes owned by you (combination of PowerShell and Linux commands): 118 | 119 | ```PowerShell 120 | Get-Process -IncludeUserName | grep ram | awk '{print $1}' | ForEachObject 121 | {[Double]$_} | Measure-Object -Sum 122 | ``` 123 | 124 | ## Scheduling jobs 125 | 126 | Creating a job that runs every fifteen minutes: 127 | 128 | ```PowerShell 129 | # Explicit configuration 130 | New-CronJob -Command 'pwsh -f "/tmp/DataLoading.PS1;"' -Minute 131 | 0,15,30,45 | Out-Host 132 | 133 | # Using interval notation 134 | New-CronJob -Command 'pwsh -f "/tmp/DataLoading.PS1;"' -Minute */15 | 135 | Out-Host 136 | 137 | # Specifying a time range 138 | New-CronJob -Command 'pwsh -f "/tmp/DataLoading.PS1;"' -Minute */15 -Hour 10-12 | Out-Host 139 | 140 | # With days of week and months of the year 141 | New-CronJob -Command 'pwsh -f "/tmp/DataLoading.PS1;"' -Minute */15 - 142 | Hour 10-12 -DayOfWeek sun, tue, fri -Month Jan, Mar, Jun, Sep, Dec | Out-Host 143 | ``` 144 | 145 | Listing out scheduled jobs: 146 | 147 | ```PowerShell 148 | Get-CronJob 149 | 150 | # If you want better formatting: 151 | Get-CronJob | Format-Table -Autosize 152 | ``` 153 | 154 | Get the contents of the `crontab` file: 155 | 156 | ```PowerShell 157 | Get-CronTab 158 | ``` 159 | 160 | Removing jobs: 161 | 162 | ```PowerShell 163 | Get-CronJob | Where-Object {$_.Month -match 'Jan'} | Remove-CronJob 164 | ``` 165 | -------------------------------------------------------------------------------- /cheatsheets/chapter-04.md: -------------------------------------------------------------------------------- 1 | # Passing Data through the Pipeline 2 | 3 | Clone the [repository from GitHub](https://github.com/PacktPublishing/PowerShell-6.0-Linux-Administration-Cookbook) or download the [lab setup script](https://github.com/PacktPublishing/PowerShell-6.0-Linux-Administration-Cookbook/blob/master/ch04/Initialize-PacktPs6CoreLinuxLab.ps1) from the repository. 4 | 5 | Run the script to create the files for the lab. 6 | 7 | ```powershell 8 | & ~/Downloads/Initialize-PacktPs6CoreLinuxLab.ps1 9 | ``` 10 | 11 | List out the contents of a directory: 12 | 13 | ```powershell 14 | Get-ChildItem -Path . 15 | ``` 16 | 17 | Omit the `Mode` column from the output: 18 | 19 | ```powershell 20 | Get-ChildItem -Path . | Select-Object LastWriteTime, Length, Name 21 | ``` 22 | 23 | Change the name of a column: 24 | 25 | ```powershell 26 | Get-ChildItem -Path . | Select-Object Name, Length, @{Name='Modified'; Expression={$_.LastWriteTime}} 27 | ``` 28 | 29 | Pick only the year of modification using thecalculated property notation: 30 | 31 | ```powershell 32 | Get-ChildItem -Path . | select Name, Length, @{Name='DaysSinceModification'; Expression={[math]::Round(((Get-Date) - $_.LastWriteTime).TotalDays)}} 33 | ``` 34 | 35 | Pick a certain number of ourput items: 36 | 37 | ```powershell 38 | # First five items 39 | Get-ChildItem . | Select-Object -First 5 40 | 41 | # Last five items 42 | Get-ChildItem . | Select-Object -Last 5 43 | 44 | # Skip the first three items 45 | Get-ChildItem . | Select-Object -Skip 3 46 | 47 | # Pick the fourth item from the output 48 | Get-ChildItem . | Select-Object -Index 3 49 | ``` 50 | 51 | Expand the elements within a property: 52 | 53 | ```powershell 54 | # The pwsh process 55 | Get-Process pwsh | Select-Object -ExpandProperty Threads 56 | 57 | # Select specific properties from within the property 58 | Get-Process pwsh | Select-Object -ExpandProperty Threads | Select-Object -Property Id, PriorityLevel, StartTime 59 | ``` 60 | 61 | Filter out files that are over 0 bytes in size: 62 | 63 | ```powershell 64 | Get-ChildItem -Path . | Where-Object -Property Length -GT -Value 0 65 | ``` 66 | 67 | Filter based on two (or more) properties: 68 | 69 | ```powershell 70 | # Pick all the JPG files larger than 0 bytes 71 | Get-ChildItem -Path . | Where-Object -FilterScript {$_.Length -GT 0 -and $_.Extension -EQ '.jpg'} 72 | 73 | # Pick files as above, whose names start with c 74 | Get-ChildItem -Path . | Where-Object -FilterScript {$_.Length -GT 0 -and $_.Extension -EQ '.jpg' -and $_.Name -CMatch '^c'} 75 | ``` 76 | 77 | Group output based on a property: 78 | 79 | ```powershell 80 | Get-ChildItem . -File | Group-Object Extension 81 | 82 | # Do the same, but do not show the file names 83 | Get-ChildItem -Path . -File | Group-Object -Property Extension -NoElement 84 | 85 | # Show only the JPG files from the lot 86 | Get-ChildItem -Path . -File | Group-Object -Property Extension | Where-Object Name -EQ .jpg | Select-Object -ExpandProperty Group 87 | ``` 88 | 89 | Sort the output based on a property: 90 | 91 | ```powershell 92 | Get-ChildItem -Path . -File | Sort-Object -Property Length 93 | 94 | # Sort in the descending order 95 | Get-ChildItem -Path . -File | Sort-Object -Property Length -Descending 96 | 97 | # Pick the largest three files in the lot 98 | Get-ChildItem -Path . -File | Sort-Object -Property Length -Descending -Top 3 99 | ``` 100 | 101 | (Dummy-)delete top two largest files of each type from a directory (if there is only one file of each type, that would be left alone): 102 | 103 | ```powershell 104 | Get-ChildItem -Path . -File | 105 | Group-Object -Property Extension | 106 | Where-Object Count -GT 1 | 107 | ForEach-Object {$_.Group | 108 | Sort-Object Length -Bottom 2} | 109 | Remove-Item -WhatIf 110 | ``` 111 | 112 | The long way to get directory contents, which demonstrates accepting values through the pipeline, `ByPropertyName`: 113 | 114 | ```powershell 115 | Get-Item . | Select-Object @{Name = 'LiteralPath'; Expression = {$_.FullName}} | Get-ChildItem 116 | ``` 117 | 118 | Import content into PowerShell and work with the object: 119 | 120 | ```powershell 121 | # First export the content so we have some real data 122 | Get-ChildItem -Path . | Select-Object Name, FullName, CreationTime, LastWriteTime, Extension, Length | Export-Csv ./file-list.csv 123 | 124 | # Import the content and get the year when the files were created; what type of object is it? 125 | (Import-Csv ./file-list.csv).CreationTime.Year | Get-Member 126 | 127 | # Export the content with the object type intact 128 | Get-ChildItem -Path . | Export-Clixml ./file-list.xml 129 | 130 | # Import the content and get the year when the files were created; what type of object is it? 131 | (Import-Clixml ./file-list.xml).CreationTime.Year | Get-Member 132 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-05.md: -------------------------------------------------------------------------------- 1 | # Using Variables and Objects 2 | 3 | Save an entire object into a variable: 4 | 5 | ```powershell 6 | $Process = Get-Process 7 | ``` 8 | 9 | Work with environment variables: 10 | 11 | ```powershell 12 | # List out all the environment variables 13 | Get-ChildItem env: 14 | 15 | # Get information on a specific variable 16 | Get-ChildItem Env:/PATH 17 | ``` 18 | 19 | Quickly create a custom object: 20 | 21 | ```powershell 22 | $MyCustomObject = [pscustomobject]@{ 23 | Name = 'Prashanth Jayaram' 24 | Title = 'PowerShell' 25 | Publisher = 'Packt' 26 | } 27 | ``` 28 | 29 | Add a member to an existing object: 30 | 31 | ```powershell 32 | $MyCustomObject | Add-Member -MemberType NoteProperty -Name 'Location' -Value 'United States' 33 | ``` 34 | 35 | Access a single property from the object: 36 | 37 | ```powershell 38 | $MyCustomObject.Name 39 | ``` 40 | 41 | Remove a member from an object: 42 | 43 | ```powershell 44 | $MyCustomObject.PsObject.Properties.Remove('Location') 45 | ``` 46 | 47 | Create a custom object from an output object: 48 | 49 | ```powershell 50 | # Assign an object to a variable 51 | $Process = (Get-Process | Select-Object Name, Id, WS, StartTime)[4] 52 | 53 | # Create a new object 54 | $CustomProcess = New-Object -TypeName PSObject -Property @{ 55 | ProcessName = $Process.Name 56 | ProcessId = $Process.Id 57 | WorkingSet = $Process.WS 58 | StartedAt = $Process.StartTime 59 | } 60 | 61 | # To have the object properties in a certain sequence 62 | $CustomProcess = [ordered]@{ 63 | ProcessName = $Process.Name 64 | ProcessId = $Process.Id 65 | WorkingSet = $Process.WS 66 | StartedAt = $Process.StartTime 67 | } 68 | New-Object -TypeName PSObject -Property $CustomProcess 69 | 70 | # Modify the properties and their values to suit your needs 71 | $CustomProcess = [ordered]@{ 72 | ProcessName = $Process.Name 73 | ProcessId = $Process.Id 74 | WorkingSet = $Process.WS 75 | RunningMins = [math]::Floor(((Get-Date) - $Process.StartTime).TotalMinutes) 76 | } 77 | New-Object -TypeName PsObject -Property $CustomProcess 78 | ``` 79 | 80 | Add members to objects without recreating the entire object: 81 | 82 | ```powershell 83 | # Get the base object 84 | $FilesWithAge = Get-ChildItem . | Select-Object Name, Length, LastWriteTime 85 | 86 | # Add a member to the object 87 | $FilesWithAge | Add-Member -MemberType ScriptProperty -Name Age -Value { [math]::Round(((Get-Date) - $this.LastWriteTime).TotalDays) } 88 | ``` 89 | 90 | Update the type data using cmdlets: 91 | 92 | ```powershell 93 | Update-TypeData -TypeName System.IO.FileInfo -MemberType ScriptProperty -MemberName Age -Value { [math]::Round(((Get-Date) - $this.LastWriteTime).TotalDays) } 94 | ``` 95 | 96 | Basic structure of the XML used to extend the type data: 97 | 98 | ```xml 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | ``` 112 | 113 | Break down the XML to understand the different kinds of properties: 114 | 115 | ```xml 116 | 117 | 118 | Modified 119 | LastWriteTime 120 | 121 | 122 | 123 | 124 | Age 125 | 126 | [math]::Round(((Get-Date) - $this.LastWriteTime).TotalDays) 127 | 128 | 129 | 130 | 131 | 132 | ItemType 133 | File 134 | 135 | ``` 136 | 137 | Extend the type data using an XML file: 138 | 139 | ```powershell 140 | Update-TypeData -PrependPath '~/Documents/code/github/powershell/ch05/CustomTypes.ps1xml' 141 | ``` 142 | 143 | Remove custom type data: 144 | 145 | ```powershell 146 | # Start with the command that gives you the object whose type data you extended 147 | $TypeData = Get-ChildItem -File | Get-Member | Select-Object -ExpandProperty TypeName -Unique 148 | 149 | # Ensure there has been a type data extension; notice the members 150 | Get-TypeData -TypeName $TypeData | Select-Object -ExpandProperty Members 151 | 152 | # Remove the custom type data 153 | Remove-TypeData -TypeName $TypeData 154 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-06.md: -------------------------------------------------------------------------------- 1 | # Working with Strings 2 | 3 | This cheatcheet is divided into two parts: 4 | 5 | 1. *Operators in PowerShell*, which is directly referred to in the chatpter. 6 | 2. *String operations in PowerShell*, which is, well, the usual cheatsheet. 7 | 8 | ## Operators in PowerShell 9 | 10 | Enter the following at the prompt. 11 | 12 | ```powershell 13 | (((2 * -5) / 10 + 16 ) % 4) - (-7) 14 | ``` 15 | 16 | Assign this to a variable that already contains a value. First, assign $Number a value of 25. 17 | 18 | ```powershell 19 | $Number = 25 20 | 21 | $Number += (((2 * -5) / 10 + 16 ) % 4) - (-7) 22 | 23 | $Number 24 | ``` 25 | 26 | Increment $Number and show its value. 27 | 28 | ```powershell 29 | $Number++ 30 | 31 | $Number 32 | ``` 33 | 34 | Perform a few comparisons. 35 | 36 | ```powershell 37 | $Number -gt -5 38 | 39 | $Number -ge 36 40 | 41 | $Number -ne 36 42 | 43 | 'c' -gt 'k' 44 | ``` 45 | 46 | See if a certain string pattern matches another. 47 | 48 | ```powershell 49 | 'Frodo' -clike '?rod?' 50 | 51 | 'Frodo' -cmatch '^[^A-Z]' 52 | 53 | 'Frodo' -match 'frodo' 54 | ``` 55 | 56 | Try a few logical operators. 57 | 58 | ```powershell 59 | $true -or $false 60 | 61 | 1 -and 0 62 | 63 | 1 -and 1 64 | ``` 65 | 66 | Split and join a few strings. 67 | 68 | ```powershell 69 | 'The quick brown fox jumps over the lazy dog' -split ' ' 70 | ``` 71 | 72 | Join the resulting set of words using a comma and a space. 73 | 74 | ```powershell 75 | ('The quick brown fox jumps over the lazy dog' -split ' ') -join ', ' 76 | ``` 77 | 78 | See if the resulting series of characters is a string. 79 | 80 | ```powershell 81 | ('The quick brown fox jumps over the lazy dog' -split ' ') -join ', ' -is [string] 82 | ``` 83 | 84 | Convert a double into an integer. 85 | 86 | ```powershell 87 | 2.21 -as [int] 88 | ``` 89 | 90 | Go back to the pangram we split into separate words. Assign the values to a variable, one after another. 91 | 92 | ```powershell 93 | 'The quick brown fox jumps over the lazy dog' -split ' ' | ForEach-Object { $Pangram += $PSItem } 94 | 95 | $Pangram 96 | ``` 97 | 98 | That was not what we wanted. We want this to be an array of strings. 99 | 100 | ```powershell 101 | Remove-Variable Pangram 102 | 103 | $Pangram = @() 104 | 105 | 'The quick brown fox jumps over the lazy dog' -split ' ' | ForEach-Object { $Pangram += $PSItem } 106 | 107 | $Pangram 108 | ``` 109 | 110 | Now, try the same thing with the comma operator. 111 | 112 | ```powershell 113 | Remove-Variable $Pangram 114 | 115 | 'The quick brown fox jumps over the lazy dog' -split ' ' | ForEach-Object { $Pangram += , $PSItem } 116 | 117 | $Pangram 118 | ``` 119 | 120 | Pick the eighth word from the array because it describes me. 121 | 122 | ```powershell 123 | $Pangram[7] 124 | ``` 125 | 126 | Also: 127 | 128 | ```powershell 129 | $Pangram -contains 'fox' 130 | ``` 131 | 132 | Recall Recipe 4.1: Working with date properties, wherein we showed the date in a proper format. 133 | 134 | ```powershell 135 | "Six hours from now would be $((Get-Date).AddHours(6))." 136 | ``` 137 | 138 | Cast a date as a DateTime object and use the member access operator to call the ToShortDateString method. 139 | 140 | ```powershell 141 | ([datetime]'13 July 2018').ToShortDateString() 142 | ``` 143 | 144 | Finally, set the variable, $Numbers with values ranging from 91 to 100. 145 | 146 | ```powershell 147 | $Numbers = 91..100 148 | 149 | $Numbers 150 | ``` 151 | 152 | Oh, you thought that initialising a variable, splitting a pangram, and then using a loop to add the elements to the empty variable was an overkill, too? PowerShell isn't complicated; it is friendly. That longer path was to help you understand how strings and string arrays work. Here is an easier way to add the split elements to the array: 153 | 154 | ```powershell 155 | Remove-Variable Pangram 156 | $Pangram = 'The quick brown fox jumps over the lazy dog' -split ' ' 157 | ``` 158 | 159 | ## Different types of strings in PowerShell 160 | 161 | To create a literal string: 162 | 163 | ```powershell 164 | # Use single quotes 165 | 'This is a literal string' 166 | 167 | # If the string contains a single quote 168 | 'I''m a literal string' 169 | 170 | # Try a line break 171 | 'I''m a literal string`nI like showing up on the screen.' 172 | ``` 173 | 174 | To create an expanding string: 175 | 176 | ```powershell 177 | # Expanding strings expand the variables within 178 | "My home directory is $HOME" 179 | 180 | # When a double quote appears within an expanding string 181 | "Here is an ""expanding string"" with a double quote" 182 | 183 | # Alternatively 184 | "Here is an `"expanding string`" with a double quote" 185 | 186 | # Try a line break; we know that escape characters work 187 | "I'm an expanding string`nI like showing up on the screen." 188 | ``` 189 | 190 | Here Strings are strings that respect formatting. 191 | 192 | ```powershell 193 | # A literal Here String 194 | @' 195 | Hello, there! 196 | 197 | I am a Here String. PowerShell respects the way I look. 198 | 199 | This Here String is the literal type. Therefore, $HOME appears as it is. 200 | '@ 201 | 202 | # An expanding Here String 203 | @" 204 | Hello, there! 205 | 206 | I am a Here String. PowerShell respects the way I look. 207 | 208 | This Here String is the expanding type. Therefore, $HOME is expanded to its value. 209 | "@ 210 | ``` 211 | 212 | ## String methods 213 | 214 | These are methods within the `System.String` object. 215 | 216 | Let us first look at trimming 217 | 218 | ```powershell 219 | # Let us say we have a string assigned to a variable 220 | $String = '.This is a string with a period on both the ends.' 221 | 222 | # To trim a certain character in the beginning of a string 223 | $String.TrimStart('.') 224 | 225 | # To trim a certain character at the end of a string 226 | $String.TrimEnd('.') 227 | 228 | # To trim a certain character from the beginning and the end of the string 229 | $String.Trim('.') 230 | ``` 231 | 232 | Next, work with string arrays. First, let us split the current string for use as an example. 233 | 234 | ```powershell 235 | $String = $String.TrimStart('.') 236 | 237 | # Split the string into an array 238 | $String = $String.Split(' ') 239 | 240 | # Now to get the fourth word from the sentence 241 | $String[3] 242 | 243 | # To get the index of a certain character 244 | $String[3].IndexOf('i') 245 | 246 | # To get a substring starting at 'i' 247 | $String[3].Substring($String[3].IndexOf('i')) 248 | ``` 249 | 250 | There are some string functions to change the case of strings as well 251 | 252 | ```powershell 253 | # To convert the case of the entire string to uppercase 254 | $String[4].ToUpper() 255 | ``` 256 | 257 | String replacement works using string methods as well 258 | 259 | ```powershell 260 | $String[4].Replace('i', 'o') 261 | ``` 262 | 263 | ## String operations using operators 264 | 265 | To see whether an array contains a certain element, use the `contains` operator. 266 | 267 | ```powershell 268 | $String -contains 'string' 269 | ``` 270 | 271 | To see if a certain string contains a pattern: 272 | 273 | ```powershell 274 | $String[6] -like '*io*' 275 | 276 | # Alternatively 277 | $String[6] -match 'io' 278 | ``` 279 | 280 | To replace a string using an operator instead of a string method: 281 | 282 | ```powershell 283 | $String[4] -replace 'i', 'o' 284 | ``` 285 | 286 | To learn to leverage regex for more complex string matching operations, refer to the recipe, _Replacing substrings within strings_. 287 | 288 | To combine a string, use the `join` operator: 289 | 290 | ```powershell 291 | # Join all the words in the $String array using a space 292 | $String = $String -join ' ' 293 | ``` 294 | 295 | To split the string, use the `split` operator: 296 | 297 | ```powershell 298 | # Split the sentence again at spaces 299 | $String = $String -split ' ' 300 | ``` 301 | 302 | ## Formatting strings 303 | 304 | You can use the formatting operator to format strings. Let us continue using the `$String` array. 305 | 306 | ```powershell 307 | # To say 'This string contains a period', string and period being elements of the array 308 | "This {0} contains a {1}." -f $String[3], $String[6] 309 | ``` 310 | 311 | To add a currency to the formatted output 312 | 313 | ```powershell 314 | "{0:c}" -f 56 315 | ``` 316 | 317 | To format a number a certain way 318 | 319 | ```powershell 320 | "{0:####-####-####-####}" -f 9089878728592950 321 | ``` 322 | 323 | To right-align an entire string array: 324 | 325 | ```powershell 326 | "{0,6}`n{1,6}`n{2,6}`n{3,6}" -f $String 327 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-07.md: -------------------------------------------------------------------------------- 1 | # Flow Control using Branches and Loops 2 | 3 | Branching and looping are perhaps at the heart of any kind of logical flow. 4 | 5 | ## Branching 6 | 7 | The most basic kind of branching is the `if` statement: 8 | 9 | ```powershell 10 | $Today = Get-Date 11 | 12 | if ($Today.DayOfWeek -eq 'Sunday') { 13 | "Don't disturb me." 14 | } 15 | ``` 16 | 17 | If you have two conditions and they are mutually exclusive: 18 | 19 | ```powershell 20 | $Today = Get-Date 21 | 22 | if ($Today.DayOfWeek -eq 'Sunday') { 23 | "Don't disturb me." 24 | } 25 | else { 26 | "I'm available." 27 | } 28 | ``` 29 | 30 | If you have more conditions: 31 | 32 | ```powershell 33 | $Today = Get-Date 34 | 35 | if ($Today.DayOfWeek -eq 'Sunday') { 36 | "Don't disturb me." 37 | } 38 | elseif ($Today.DayOfWeek -eq 'Saturday') { 39 | "I'm available for parties." 40 | } 41 | else { 42 | "I'm available to work." 43 | } 44 | ``` 45 | 46 | If you have more (and specific conditions), use the switch–case statement: 47 | 48 | ```powershell 49 | $Today = (Get-Date).DayOfWeek 50 | 51 | switch ($Today) { 52 | 'Saturday' { 53 | "I'm available for parties." 54 | } 55 | 'Sunday' { 56 | "Don't disturb me." 57 | } 58 | } 59 | ``` 60 | 61 | If you need a catch-all: 62 | 63 | ```powershell 64 | $Today = (Get-Date).DayOfWeek 65 | 66 | switch ($Today) { 67 | 'Saturday' { 68 | "I'm available for parties." 69 | } 70 | 'Sunday' { 71 | "Don't disturb me." 72 | } 73 | Default { 74 | "I'm available to work." 75 | } 76 | } 77 | ``` 78 | 79 | If you would like wildcard matching: 80 | 81 | ```powershell 82 | $Today = (Get-Date).DayOfWeek 83 | 84 | switch -wildcard ($Today) { 85 | 'S*day' { 86 | "Weekend!" 87 | } 88 | Default { 89 | "Weekday." 90 | } 91 | } 92 | ``` 93 | 94 | ## Looping 95 | 96 | There are six looping constructs in PowerShell. 97 | 98 | The first one is a cmdlet, that works through the pipeline: 99 | 100 | ```powershell 101 | $String = 'This is a string with a period.' -split ' ' 102 | 103 | $String | Foreach-Object {$PSItem.ToUpper()} # Of course, you can do $String.ToUpper(), but play along. 104 | ``` 105 | 106 | Next, when you don't want to use the pipeline, but have a finite set of objects as an array: 107 | 108 | ```powershell 109 | $String = 'This is a string with a period.' -split ' ' 110 | 111 | foreach ($Element in $String) { 112 | $Element.ToUpper() 113 | } 114 | ``` 115 | 116 | Now to the looping constructs most of us are familiar with from other languages: 117 | 118 | First is the For loop. 119 | 120 | ```powershell 121 | for ($i = 0; $i -lt 10; $i++) { 122 | $i 123 | } 124 | ``` 125 | 126 | If you want a while construct: 127 | 128 | ```powershell 129 | $i = 0 130 | while ($i -lt 10) { 131 | $i 132 | $i++ 133 | } 134 | ``` 135 | 136 | If you want the loop to execute once regardless of whether the condition is met: 137 | 138 | ```powershell 139 | $i = 11 140 | do { 141 | $i 142 | $i++ 143 | } while ($i -lt 10) 144 | "The current value of i after the loop is $i" 145 | ``` 146 | 147 | That loop exited when the condition became false. If you would like the loop to exit when the condition became true (and execute it once regardless of whether the outcome of the condition is true or false): 148 | 149 | ```powershell 150 | $i = 11 151 | do { 152 | $i 153 | $i++ 154 | } until ($i -gt 10) 155 | "The current value of i after the loop is $i" 156 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-08.md: -------------------------------------------------------------------------------- 1 | # Performing Calculations 2 | 3 | Calculations in PowerShell are rather simple. 4 | 5 | ## Arithmetic operations 6 | 7 | The basic math: 8 | 9 | ```powershell 10 | 5 + 25 / 5 * 21 % (10 - 5) 11 | ``` 12 | 13 | Using math accelerators: 14 | 15 | ```powershell 16 | # To get the square of a number 17 | [math]::Pow(5, 2) 18 | 19 | # To get the square root of a number 20 | [math]::Sqrt(25) 21 | 22 | # The floor and the ceiling functions 23 | [math]::Ceiling(32.223) 24 | [math]::Floor(32.223) 25 | 26 | # To round off a number to the nearest hundredth 27 | [math]::Round(32.223, 2) 28 | ``` 29 | 30 | There are administrative constants in PowerShell that help you convert numbers: 31 | 32 | ```powershell 33 | # To convert bytes into MB 34 | 1099511627776/1MB 35 | 36 | # To convert bytes into GB 37 | 1099511627776/1GB 38 | 39 | # To convert bytes into TB 40 | 1099511627776/1TB 41 | 42 | # To get multiples of 10 43 | 8 * 1e3 44 | ``` 45 | 46 | Type conversion is also simple in PowerShell: 47 | 48 | ```powershell 49 | # To convert hexadecimal into integer 50 | [int]("0x222") 51 | 52 | # To convert an integer into an Octal number 53 | [Convert]::ToString('565', 8) 54 | 55 | # To convert an integer into a hexadecimal number 56 | [Convert]::ToString('565', 16) 57 | 58 | # To convert an integer into a binary number 59 | [Convert]::ToString('565', 2) 60 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-09.md: -------------------------------------------------------------------------------- 1 | # Using Arrays and Hashtables 2 | 3 | Remember them like this: Arrays are collections of elements; use indices to address them. Hashtables are arrays that use names to address the elements. 4 | 5 | ## Arrays 6 | 7 | There are two ways to create an array: 8 | 9 | ```powershell 10 | # First, by initializing an array and adding contents 11 | $Array = @() 12 | 13 | $Array += 'First element' 14 | $Array += 'Second element' 15 | 16 | # Second, using the comma operator 17 | $Array = , 'First element' + 'Second element' 18 | ``` 19 | 20 | To combine arrays, simply add them: 21 | 22 | ```powershell 23 | $ArrayOne = , 'First element' + 'Second element' 24 | $ArrayTwo = , 'Third element' + 'Fourth element' 25 | 26 | $ArrayOne + $ArrayTwo 27 | ``` 28 | 29 | To create a jagged array: 30 | 31 | ```powershell 32 | $JaggedArray = @( 33 | (1, 2, 3, 4, 5), 34 | (6, 7, 8), 35 | (9, 10, 11, 12, 13, 14), 36 | (15, 16, 17, 18), 37 | (19, 20) 38 | ) 39 | ``` 40 | 41 | To create a non-jagged array: 42 | 43 | ```powershell 44 | $MultiDimensionalArray = New-Object -TypeName "int[,]" 4, 5 45 | $Count = 1 46 | for ([int]$i = 0; $i -lt 4; $i++) { 47 | for ([int]$j = 0; $j -lt 5; $j++) { 48 | $MultiDimensionalArray[$i,$j] = $Count 49 | $Count++ 50 | } 51 | } 52 | 53 | # Access a random member from within the array: 54 | $MultiDimensionalArray[2,3] 55 | ``` 56 | 57 | To access an element from the array, use the index: 58 | 59 | ```powershell 60 | $Array = , 'First element' + 'Second element' 61 | 62 | $Array[1] 63 | ``` 64 | 65 | To see if an array contains an element: 66 | 67 | ```powershell 68 | $Array -contains 'Second element' 69 | ``` 70 | 71 | To match patterns in an array: 72 | 73 | ```powershell 74 | $Array -match 'element' 75 | 76 | $Array -match 'First' 77 | ``` 78 | 79 | To remove an element from an array: 80 | 81 | ```powershell 82 | # Create a new object of type System.Collections.ArrayList 83 | $NewArray = New-Object -TypeName System.Collections.ArrayList 84 | 85 | # Use the Add() method of the object to add elements 86 | $NewArray.Add('First element') 87 | $NewArray.Add('Second element') 88 | $NewArray.Add('Third element') 89 | 90 | # To remove an element from the array, use the Remove() method from within the object 91 | $NewArray.Remove('Second element') 92 | ``` 93 | 94 | The other way of removing objects from an array is to filter unwanted elements using `Where-Object` and then, assigning those values to a new variable. 95 | 96 | To compare two arrays, use the `Compare-Object` cmdlet. 97 | 98 | ```powershell 99 | $ArrayOne = , 'First element' + 'Second element' + 'Third element' 100 | $ArrayTwo = , 'Third element' + 'Fourth element' + 'Fifth element' 101 | 102 | Compare-Object -ReferenceObject $ArrayOne -DifferenceObject $ArrayTwo 103 | ``` 104 | 105 | ## Hashtables 106 | 107 | To initialize a hashtable: 108 | 109 | ```powershell 110 | $MyHashtable = @{} # As opposed to @() for arrays 111 | ``` 112 | 113 | To add elements to a hashtable: 114 | 115 | ```powershell 116 | $MyHashtable['FirstElement'] = 1 117 | $MyHashtable['SecondElement'] = 2 118 | ``` 119 | 120 | To access the elements in a hashtable: 121 | 122 | ```powershell 123 | $MyHashtable['SecondElement'] 124 | ``` 125 | 126 | To see if the hashtable contains a certain element (find by name): 127 | 128 | ```powershell 129 | $MyHashtable.Contains['FirstElement'] 130 | ``` 131 | 132 | To remove an element from a hashtable: 133 | 134 | ```powershell 135 | $MyHashtable.Remove['SecondElement'] 136 | ``` 137 | 138 | You can also use a method to add elements to a hashtable: 139 | 140 | ```powershell 141 | $MyHashtable.Add('ThirdElement', 3) 142 | ``` 143 | 144 | To sort a hashtable: 145 | 146 | ```powershell 147 | # Based on the names 148 | $MyHashtable.GetEnumerator() | Sort-Object Name 149 | 150 | # Based on the values 151 | $MyHashtable.GetEnumerator() | Sort-Object Value 152 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-10.md: -------------------------------------------------------------------------------- 1 | # Handling Files and Directories 2 | 3 | To read contents from a file: 4 | 5 | ```powershell 6 | # To get everything within the file 7 | Get-Content './path/to/file.ext' 8 | 9 | # To get the first, say, 5 lines from the file, as a single block 10 | Get-Content './path/to/file.ext' -ReadCount 7 | Select-Object -First 1 11 | 12 | # To get the last, say, 5 lines of the file 13 | Get-Content './path/to/file.ext' -Tail 5 14 | 15 | # If you would like to keep the file open for reading, such as when reading logs at the terminal 16 | Get-Content './path/to/file.ext' -Tail 5 -Wait 17 | 18 | # To get, say, the eleventh to the thirteenth lines of the file 19 | Get-Content './path/to/file.ext' | Select-Object -Skip 10 -First 3 20 | 21 | # To measure the number of characters, words and lines in the file 22 | Get-Content './path/to/file.ext' | Measure-Object -Character -Word -Line 23 | 24 | # Get-Content imports contents line-by-line. Each line is a separate object. If you would like all the contents as a single block, use 25 | Get-Content './path/to/file.ext' -Raw 26 | ``` 27 | 28 | To send output to a file 29 | 30 | ```powershell 31 | # Let us assume you want to send the current date to a file 32 | 33 | 34 | # Using an operator 35 | Get-Date > ./path/to/output-file.txt 36 | 37 | # Using a cmdlet 38 | Get-Date | Out-File ./path/to/output-file.txt 39 | 40 | # To send errors to a file (by "polluting" the Success stream) 41 | Get-Content ./nonexistent/path.ext 2>&1 | Out-File ./path/to/desired-file.txt 42 | 43 | # To send errors to a file (by not "polluting" the Success stream) 44 | Get-Content ./nonexistent/path.ext 2> ./path/to/desired-file.txt 45 | 46 | # To send output of Write-Host or Write-Information to a file (by "polluting" the Success stream) 47 | Write-Host "This is just a host message" 6>&1 | Out-File ./path/to/desired-file.txt 48 | 49 | # Alternative, using only redirection operators 50 | Write-Host "This is just a host message" 6>&1> ./path/to/desired-file.txt 51 | 52 | # To send output of Write-Host or Write-Information to a file (by not "polluting" the Success stream) 53 | Write-Host "This is just a host message" 6> ./path/to/desired-file.txt 54 | 55 | # To send contents to both, a file, as well as down the pipeline 56 | Get-Date | Tee-Object ./path/to/desired-file.txt 57 | # In this case, since no pipeline follows Tee-Object, the ouptut will appear on the host 58 | ``` 59 | 60 | Here is a list of all the streams in PowerShell, in order: 61 | 62 | 1. Success stream 63 | 2. Error stream 64 | 3. Warning stream 65 | 4. Verbose stream 66 | 5. Debug stream 67 | 6. Information stream 68 | 69 | Today, the Information stream also handles content sent to the host. The host was not part of any stream until PowerShell 5.0. 70 | 71 | Only the contents of the Success stream go through the pipeline. Therefore, if the contents of any other stream are to be sent to through the pipeline, the content must be redirected to the Success stream first. 72 | 73 | To manipulate the contents of a file: 74 | 75 | ```powershell 76 | # To send contents to a file using an alternate method or replace the contents 77 | Set-Content './path/to/file.ext' "The content that you would like the file to have" 78 | 79 | # To add content to a file 80 | Add-Content './path/to/file.ext' "The content you would like to add to the file" 81 | 82 | # To clear contents of a file 83 | Clear-Content './path/to/file.ext' 84 | ``` 85 | 86 | To search for a string in a file: 87 | 88 | ```powershell 89 | # A simple listing of all occurrences 90 | Select-String -Pattern 'MyPattern' -Path './path/to/file.ext' 91 | 92 | # To get the context of each of the occurrences (two lines before and after the occurrence) 93 | Select-String -Pattern 'MyPattern' -Path './path/to/file.ext' -Context 2, 2 94 | 95 | # To perform a search on multiple files excluding a few 96 | Select-String -Pattern 'MyPattern' -Path './path/to/*.ext' -Exclude '*ExclusionFilenamePattern*' 97 | ``` 98 | 99 | To know where the script is running from (querying within the script, not outside) 100 | 101 | ```powershell 102 | $PsScriptRoot 103 | ``` 104 | 105 | To know the complete path to the script that was run (querying within the script, not outside): 106 | 107 | ```powershell 108 | $PsCommandPath 109 | ``` 110 | 111 | To create paths irrespective of the platform: 112 | 113 | ```powershell 114 | # Simple path join 115 | Join-Path -Path 'DesiredPath' -ChildPath 'DesiredChildPath' 116 | 117 | # Creating a single child path within multiple parent paths 118 | Join-Path /home, /etc, /var, /boot -ChildPath MyDir 119 | 120 | # Creating multiple child paths within a single parent path 121 | 'file1.txt', 'file2.txt', 'file3.txt' | ForEach-Object { Join-Path -Path $HOME/dir/ -ChildPath $PSItem } 122 | ``` 123 | 124 | To search for the existence of paths with a certain pattern (using a wildcard) 125 | 126 | ```powershell 127 | # To simply resolve all the paths that match that pattern 128 | Resolve-Path /home/*/random 129 | 130 | # To split paths at the path separator 131 | Split-Path -Parent # Gives the path omitting the last location, `random` in this case 132 | Split-Path -Leaf # Shows the last location `random`, in this case 133 | ``` 134 | 135 | To actually create files/directories at the paths you created using `Join-Path` 136 | 137 | ```powershell 138 | Join-Path dir-01, dir-02, dir-03, dir-04 -ChildPath demo.txt | ForEach- 139 | Object { New-Item $PSItem -ItemType File } 140 | ``` 141 | 142 | To rename files/directories 143 | 144 | ```powershell 145 | Rename-Item -Path oldname.ext -NewName newname.ext 146 | ``` 147 | 148 | To copy items from one point to another 149 | 150 | ```powershell 151 | Copy-Item -Path /path/to/file.ext -Destination ./destination/directory # This retains the file name 152 | ``` 153 | 154 | To delete a file/directory 155 | 156 | ```powershell 157 | # To delete a file 158 | Remove-Item ./path/to/file.ext 159 | 160 | # To delete a directory that contains items within itself 161 | Remove-Item ./path/to/directory/ -Recurse 162 | # If `-Recurse` is not used, and PowerShell finds contents within the specified directory, it would prompt whether you would like to go with a recursive deletion 163 | ``` 164 | 165 | To convert JSON into a PowerShell object: 166 | 167 | ```powershell 168 | ConvertFrom-Json 'JSON string here' # Yup, it's that simple 169 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-11.md: -------------------------------------------------------------------------------- 1 | # Building Scripts and Functions 2 | 3 | When calling a script, the path to which contains a space (without actually retaining anything from the script, in the session) 4 | 5 | ```powershell 6 | & './path to/script.ps1' 7 | ``` 8 | 9 | When the entities within the script (such as variables or functions) have to be retained in the session: 10 | 11 | ```powershell 12 | . './path to/script.ps1' 13 | ``` 14 | 15 | To read content from the console: 16 | 17 | ```powershell 18 | Read-Host -Prompt "Enter prompt here" 19 | ``` 20 | 21 | To display the progress of execution 22 | 23 | ```powershell 24 | $TerminatingCondition = 25 # (I'm counting to 25 seconds) 25 | $CurrentCondition = 0 # (You know this part now) 26 | 27 | do { 28 | Write-Progress -Activity 'Counting to 25' -Status "Elapsed time: $CurrentCondition seconds" -PercentComplete ($CurrentCondition / $TerminatingCondition * 100) 29 | Start-Sleep 1 # Because this should happen at a human speed 30 | $CurrentCondition++ # Because progress is everything 31 | } until ($CurrentCondition -eq $TerminatingCondition) # Why use the boring do-while? 32 | ``` 33 | 34 | To make a parameter out of a variable: 35 | 36 | ```powershell 37 | param ($TerminatingCondition) # Any reasonable number of variables work here; just separate them with a comma each 38 | ``` 39 | 40 | To convert a script into a function: 41 | 42 | ```powershell 43 | function New-MyFunction { 44 | # blah-blah, including the `param ()` block if you would like 45 | } 46 | ``` 47 | 48 | To call the function: 49 | 50 | ```powershell 51 | # Run the script by dot-sourcing it, so the function is loaded onto the session 52 | . './path to/script.ps1' 53 | 54 | # Call the function with the parameters, just as you would a cmdlet 55 | New-MyFunction -TerminatingCondition 5 56 | ``` 57 | 58 | To create a script block instead of a function 59 | 60 | ```powershell 61 | # Define the script block 62 | $MyScriptBlock = { 63 | # blah-blah 64 | } 65 | 66 | # Call the script block using the calling operator 67 | & $MyScriptBlock 68 | ``` 69 | 70 | To measure the running duration of a certain command, script or a function: 71 | 72 | ```powershell 73 | Measure-Command New-MyFunction 74 | ``` 75 | 76 | ## Important points to remember: 77 | 78 | 1. `*-Host` cmdlets send/receive content to/from the Information stream (and subsequently, the host). This stream does not interact with the pipeline. 79 | 2. Simply call the variable whose value you would like the function to return; there is no need to use the `return` keyword. However, make every function return only one kind of object because it affects how the output is used. Use `Get-Member` to see what object is output. 80 | 3. Name the functions like you would name cmdlets; use approved verbs. -------------------------------------------------------------------------------- /cheatsheets/chapter-12.md: -------------------------------------------------------------------------------- 1 | # Advanced Concepts of Functions 2 | 3 | To set a parameter as positional and mandatory (0 stands for position 1): 4 | 5 | ```powershell 6 | param ( 7 | [Parameter(Mandatory=$true, Position=0)] 8 | [datatype] 9 | $MyParameter 10 | ) 11 | ``` 12 | 13 | To assign a default value to a parameter: 14 | 15 | ```powershell 16 | param ( 17 | [Parameter(Mandatory=$false)] 18 | [datatype] 19 | $MyParameter = 21 20 | ) 21 | ``` 22 | 23 | To set a parameter alias for a parameter: 24 | 25 | ```powershell 26 | param ( 27 | [Parameter(Mandatory=$true, Position=0)] 28 | [Alias("mp")] 29 | [datatype] 30 | $MyParameter 31 | ) 32 | ``` 33 | 34 | To create a parameter set: 35 | 36 | ```powershell 37 | [CmdletBinding(DefaultParameterSetName='First')] 38 | param ( 39 | # To make a parameter part of multiple parameter sets 40 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='First')] 41 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='Second')] 42 | [string] 43 | $ParamCommon, 44 | 45 | [Parameter(Mandatory=$true, Position=1, ParameterSetName='First')] 46 | [string] 47 | $ParamOne, 48 | 49 | [Parameter(Mandatory=$true, Position=1, 50 | ParameterSetName='Second')] 51 | [string] 52 | $ParamTwo 53 | ) 54 | ``` 55 | 56 | To add parameter validation: 57 | 58 | ```powershell 59 | param ( 60 | [Parameter(Mandatory=$false, Position=0)] 61 | [ValidateSet(5, 10, 15, 20)] 62 | [int] 63 | $MyParameter 64 | ) 65 | ``` 66 | 67 | To add a simple dependency to a function, use the `begin` block: 68 | 69 | ```powershell 70 | function New-MyFunction { 71 | param ( 72 | $MyParameter 73 | ) 74 | begin { 75 | # Call the dependencies here 76 | } 77 | # Main function body 78 | } 79 | ``` 80 | 81 | Alternatively, use a `process` and an `end`: 82 | 83 | ```powershell 84 | function New-MyFunction { 85 | param ( 86 | $MyParameter 87 | ) 88 | begin { 89 | # Dependencies 90 | } 91 | process { 92 | # Main function body 93 | } 94 | end { 95 | # Cleanup 96 | } 97 | } 98 | ``` 99 | 100 | To break out of the function itself if a dependency fails: 101 | 102 | ```powershell 103 | begin { 104 | try { 105 | # Load dependency with -ErrorAction Stop 106 | } 107 | catch { 108 | break 109 | } 110 | } 111 | ``` 112 | 113 | To add a confirmation prompt and `WhatIf` in a function: 114 | 115 | ```powershell 116 | function New-MyFunction { 117 | [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess=$true)] 118 | param ( 119 | # Your parameter block 120 | ) 121 | process { 122 | if ($PsCmdlet.ShouldProcess("Item on which the operation is performed", "Action that would be performed")) { 123 | # Perform the action 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | To add help to functions, use the command-based help syntax. 130 | 131 | ```powershell 132 | function New-MyFunction { 133 | <# 134 | .SYNOPSIS 135 | # A short summary of what your function does. 136 | 137 | .DESCRIPTION 138 | Describe what the function does. 139 | 140 | .PARAMETER ParameterOne 141 | Helpful information about the parameter. 142 | 143 | .PARAMETER ParameterTwo 144 | Helpful information about the parameter. 145 | 146 | .EXAMPLE 147 | New-MyFunction -ParameterOne 'Value' 148 | 149 | .NOTES 150 | Any additional notes about the function (even attribution) 151 | #> 152 | } 153 | ``` 154 | 155 | **Tip**: Start a comment block right above the function declaration to get the fields populated automatically. 156 | 157 | To add support for pipeline input: 158 | 159 | ```powershell 160 | function New-MyFunction { 161 | param ( 162 | # The path to the file (or the name) 163 | [Parameter(ValueFromPipeline)] 164 | [string[]] 165 | $MyParameter 166 | ) 167 | } 168 | ``` 169 | 170 | The following kinds of pipeline input are allowed: 171 | 172 | - Accept input by type 173 | - With coercion 174 | - Without coercion 175 | - Accept input by parameter name 176 | - With coercion 177 | - Without coercion 178 | 179 | If your script contains several functions, which are already being called in a certain order from within each other, but you would like to improve readability, use the `Main` function: 180 | 181 | ```powershell 182 | function Main { 183 | New-MyFunctionThree 184 | } 185 | 186 | function New-MyFunction { 187 | # Some tasks 188 | } 189 | 190 | function New-MyFunctionTwo { 191 | New-MyFunction 192 | } 193 | 194 | function New-MyFunctionThree { 195 | New-MyFunctionTwo 196 | } 197 | 198 | Main 199 | ``` 200 | 201 | If you are writing a script module, and would only like to expose a single function to the user: 202 | 203 | ```powershell 204 | function New-MyFunction { 205 | # Some tasks 206 | } 207 | 208 | function New-MyFunctionTwo { 209 | New-MyFunction 210 | } 211 | 212 | function New-MyFunctionThree { 213 | New-MyFunctionTwo 214 | } 215 | 216 | Export-ModuleMember New-MyFunctionThree 217 | ``` 218 | 219 | To do something when a certain module is called using `Remove-Module` (such as cleanup), add the following to the end of the module file: 220 | 221 | ```powershell 222 | $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { 223 | # Anything you would like done as part of module removal 224 | } 225 | ``` 226 | 227 | If you would like to ensure that the aforementioned steps be taken even if the PowerShell session is closed, append the file with the content below: 228 | 229 | ```powershell 230 | Register-EngineEvent PowerShell.Exiting { 231 | # Your cleanup steps here 232 | } 233 | ``` -------------------------------------------------------------------------------- /cheatsheets/chapter-13.md: -------------------------------------------------------------------------------- 1 | # Debugging and Error Handling 2 | 3 | To enable debugging on a script, either: 4 | 5 | 1. Set the `DebugPreference` to `Continue`, or 6 | 2. Add (even an empty) `[CmdletBinding()]` line to the beginning of the function 7 | 8 | Run the function with the `-Debug` switch to get debug information displayed on the host. 9 | 10 | ```powershell 11 | New-MyFunction -MyParameter 'Value' -Debug 12 | ``` 13 | 14 | If you would like to step through each line in the function as part of debugging: 15 | 16 | ```powershell 17 | # Set debugging on 18 | Set-PsDebug -Step 19 | 20 | # Call your function 21 | New-MyFunction -MyParameter 'Value' 22 | 23 | # Exit the debug mode using the -Off switch 24 | Set-PsDebug -Off 25 | ``` 26 | 27 | To get into the trace mode for degugging, use the `-Trace` switch. 28 | 29 | ```powershell 30 | Set-PsDebug -Trace 1 # or 2 31 | ``` 32 | 33 | To set a breakpoint: 34 | 35 | ```powershell 36 | Set-PsBreakpoint -Script ./path/to/script.ps1 -Line 5 37 | ``` 38 | 39 | To list out all the breakpoints or remove the breakpoints: 40 | 41 | ```powershell 42 | # List breakpoints 43 | Get-PsBreakpoint 44 | 45 | # Remove breakpoints 46 | Remove-PsBreakpoint -Id 1 # as in the relevant ID as per Get-PsBreakpoint 47 | ``` 48 | 49 | To set a conditional breakpoint (breakpoint that works if a certain condition is met): 50 | 51 | ```powershell 52 | # First, set the condition 53 | $Condition = { if ($Count -lt 2 <# Or any other relevant condition #>) { break } } 54 | 55 | # Now, set the breakpoint on the script 56 | Set-PsBreakpoint ./path/to/script.ps1 -Line 5 -Action $Condition 57 | ``` 58 | 59 | To see if there was an error in the previous statement (within a script): 60 | 61 | ```powershell 62 | $? 63 | # Returns a Boolean output; TRUE means 'successfully executed' 64 | ``` 65 | 66 | If a native command was run and the command exited in an error, the error is recorded in the automatic variable, `$LASTEXITCODE`. 67 | 68 | To hide errors at the terminal, (but continue to record them), use either: 69 | 70 | ```powershell 71 | # The common switch parameter 72 | New-MyFunction -ErrorAction SilentlyContinue 73 | 74 | # Or set the error action preference (use carefully; not recommended unless you know what you're doing) 75 | Set-Variable ErrorActionPreference 'SilentlyContinue' 76 | 77 | # Alternatively 78 | $ErrorActionPreference = 'SilentlyContinue' 79 | ``` 80 | 81 | To list out all the errors encountered in the current session (unless the error variable was cleared) 82 | 83 | ```powershell 84 | $Error 85 | ``` 86 | 87 | Treat this variable like a LIFO array, meaning, `$Error[0]` is the last error that was encountered. There are several properties in the variable. 88 | 89 | To clear the error variable, use the `Clear()` method baked into it: 90 | 91 | ```powershell 92 | $Error.Clear() 93 | ``` 94 | 95 | To control flow based on errors, use the `try`-`catch`-`finally` construct: 96 | 97 | ```powershell 98 | try { 99 | # Some action here, with `-ErrorAction Stop` to make it terminating 100 | } 101 | catch [System.IO.DirectoryNotFoundException] <# Or any exception name as shown by the `Exception:` line of the error #> { 102 | # Actions to take in case of this exception 103 | } 104 | catch { 105 | # Catch-all statment: In case of any exception other than the above 106 | } 107 | finally { 108 | # The `finally` actions 109 | } 110 | ``` -------------------------------------------------------------------------------- /miscellaneous/Microsoft.VSCode_profile.ps1: -------------------------------------------------------------------------------- 1 | function prompt { 2 | $Location = (Get-Location).Path.ToString() 3 | switch -Wildcard ($Location) { 4 | "/home/$env:USERNAME" { $Location = '~'; break } 5 | "/home/$env:USERNAME/Documents" { $Location = 'Documents'; break } 6 | "/home/$env:USERNAME/Downloads" { $Location = 'Downloads'; break } 7 | "/home/$env:USERNAME/Pictures" { $Location = 'Pictures'; break } 8 | "/home/$env:USERNAME/Videos" { $Location = 'Videos'; break } 9 | "/home/$env:USERNAME/Music" { $Location = 'Music'; break } 10 | "/home/$env:USERNAME/Documents/code" { $Location = 'Code'; break } 11 | "/home/$env:USERNAME/*" { $Location = $Location.Replace("/home/$env:USERNAME/", '~/'); break } 12 | Default { } 13 | } 14 | 15 | Write-Host "PS " -NoNewline 16 | Write-Host ` 17 | ($($env:USERNAME) + "@" + "$([System.Net.Dns]::GetHostByName((hostname)).HostName) ") ` 18 | -NoNewLine -ForegroundColor Cyan 19 | Write-Host "$Location" -NoNewline -ForegroundColor Green 20 | Write-Host ("`n> ") -NoNewline 21 | return " " 22 | } --------------------------------------------------------------------------------