Home Cloud & Networking PowerShell for Office 365

PowerShell for Office 365

By Martin Machado
books-svg-icon Book
eBook $35.99 $24.99
Print $43.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $35.99 $24.99
Print $43.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    PowerShell Fundamentals
About this book
While most common administrative tasks are available via the Office 365 admin center, many IT professionals are unaware of the real power that is available to them below the surface. This book aims to educate readers on how learning PowerShell for Offi ce 365 can simplify repetitive and complex administrative tasks, and enable greater control than is available on the surface. The book starts by teaching readers how to access Offi ce 365 through PowerShell and then explains the PowerShell fundamentals required for automating Offi ce 365 tasks. You will then walk through common administrative cmdlets to manage accounts, licensing, and other scenarios such as automating the importing of multiple users,assigning licenses in Office 365, distribution groups, passwords, and so on. Using practical examples, you will learn to enhance your current functionality by working with Exchange Online, and SharePoint Online using PowerShell. Finally, the book will help you effectively manage complex and repetitive tasks (such as license and account management) and build productive reports. By the end of the book, you will have automated major repetitive tasks in Office 365 using PowerShell.
Publication date:
July 2017
Publisher
Packt
Pages
222
ISBN
9781787127999

 

Chapter 1. PowerShell Fundamentals

PowerShell is a command-line environment that is designed for system administrators. It helps you manage and automate administrative tasks on the Windows operating system. With the trend of DevOps, developers are also getting on board with PowerShell.

Microsoft first introduced PowerShell in 2006. 10 years later, in 2016, Microsoft announced that they have made PowerShell open source and cross-platform with support for Windows, macOS X, CentOS, and Ubuntu. The source code is available on GitHub.

Office 365 is a subscription-based SAS offering from Microsoft. To manage Office 365, we have the following options:

  • Office 365 admin center: We can use this web-based administration center provided by Microsoft to manage users, licenses, support tickets, billing and subscription, and other services such as Exchange, SharePoint, and Skype for Business that are part of our Office 365 subscription. To sign up for Office 365, we need to use Office 365 admin center. During this signing up process, we select the unique name for our tenant and the global admin account:
  • Office 365 Admin app: This app allows us to manage Office 365 with limited functionality. We can reset user passwords, manage support tickets, and so on. It is not a full management tool. However, it helps you to be connected to your subscription when you are away from your computer:
  • Office 365 management API: The APIs are designed for developers to create custom apps to manage Office 365: https://msdn.microsoft.com/en-us/office-365/office-365-managment-apis-overview. ISVs use these APIs to create products to manage Office 365. These APIs are out of the scope of this book.
  • PowerShell for Office 365: This is the management tool that complements Office 365 admin center. We can use Office 365 PowerShell automation to quickly manage Office 365 users and licenses, Skype for Business Online, SharePoint Online, and Microsoft Exchange Online, and create reports:

In this chapter, we will discuss the following topics:

  • Why do we need to learn PowerShell for Office 365?
  • PowerShell is a cmdlet-based language with verb-noun syntax
  • How to pass parameters to cmdlets and storing results as a variable
  • How to get help with PowerShell?
  • How PowerShell is an object-oriented language and how to work with objects?
  • Using the if and where statements
  • Using the for and while loops
  • Creating your first script
 

Why do we need to learn PowerShell for Office 365?


Office 365 admin center is an out-of-the-box solution that is designed to handle the most common administrative tasks, such as creating new users and assigning licenses. There are situations where PowerShell for Office 365 helps us save time and effort. If you are a systems administrator, PowerShell is now a must-have skill. PowerShell for Office 365 helps you automate a lot of repetitive tasks and also work with advanced functions that are not available in Office 365 admin center:

  • Office 365 PowerShell can reveal additional information that you cannot see with Office 365 admin center
  • Office 365 has features that you can only configure using Office 365 PowerShell
  • Office 365 PowerShell is great at performing bulk operations
  • Office 365 PowerShell is great for filtering data
  • Office 365 PowerShell allows us to automate repetitive tasks
  • Office 365 PowerShell makes it easy to print or save data
  • Office 365 PowerShell lets you manage operations across server products

I think using the Office 365 admin center is like using public transportation. We have to live with the options provided by Microsoft. If we need something custom, like in the case of transportation when we use taxis or personal vehicles, in Office 365, we can use PowerShell. We still have to write scripts in accordance with Microsoft's guidelines. However, we get far more flexibility and options as compared to Office 365 admin center.

Before we dive deep into PowerShell for Office 365, let's cover the basics of PowerShell in the next few sections.

 

PowerShell is a cmdlet based language with verb-noun syntax


The building blocks of PowerShell are called cmdlets (pronounced command-lets). Cmdlets allow us to get things done in PowerShell. A cmdlet is a lightweight command that is used in the Windows PowerShell environment. The Windows PowerShell runtime evokes these cmdlets within the context of automation scripts that are provided at the command line. We can put multiple cmdlets together into a set of commands to run all at once, or we can place them in a file with the extension .ps1 to create a PowerShell script that we can run manually or using a scheduler. In Office 365, the following cmdlets are commonly used:

  • Get-MsolUser
  • New-MsolUser

In PowerShell, cmdlets follow a pattern with verb-noun syntax. For example, to manage users, the syntax is <Verb>-MSOL<Noun>.

Here, MSOL stands for Microsoft Online.

To manage SharePoint Online, the syntax is <Verb>-SPO<Noun>.

SPO is SharePoint Online.

The following is the list of the most commonly used verbs in PowerShell:

  • Get
  • Set
  • Add
  • New
  • Remove
  • Connect
  • Disconnect
  • Test
  • Enable
  • Disable
  • Invoke
  • Start
  • Stop
 

How to pass parameters to cmdlets and storing results as a variable


A cmdlet is a lightweight command that is used in the Windows PowerShell environment. The Windows PowerShell runtime invokes these cmdlets within the context of automation scripts that are provided at the command line. The Windows PowerShell runtime also invokes them programmatically through Windows PowerShell APIs.

They basically accept input via parameters, perform the operation, and then output the results.

Cmdlets differ from commands in a command-shell environment in the following ways:

  • Cmdlets are instances of .NET Framework classes; they are not standalone executables.
  • Cmdlets can be created from as few as a dozen lines of code.
  • Cmdlets do not generally do their own parsing, error presentation, or output formatting and these operations are normally handled by the Windows PowerShell runtime.
  • Cmdlets process input objects from the pipeline rather than from streams of text, and typically deliver objects as output to the pipeline.
  • Cmdlets are record-oriented because they process a single object at a time.

Parameters

Parameters are the input values that we pass to a cmdlet. For example, if we have to get the time zone, we can use the following cmdlet:

Get-TimeZone

This cmdlet gets the current time zone or a list of available time zones.

These are the parameters:

  • [-Id]: Specifies, as a string array, the ID or IDs of the time zones that this cmdlet gets
  • [-ListAvailable]: Indicates that this cmdlet gets all available time zones
  • [-Name]: Specifies, as a string array, the name or names of the time zones that this cmdlet gets

We can use this command with or without these parameters:

Get-TimeZone

The following screenshot shows the output for the preceding command:

We can use this command with the Name parameter:

Get-TimeZone -Name "*pac*"

The following screenshot shows the output for the preceding command:

We can use this command with the ListAvailable parameter:

Get-TimeZone -ListAvailable

The following screenshot shows the output for the preceding command:

In PowerShell, variables are always prefixed by the character $ and can include any alphanumeric character or underscore in their names. We can store the output from a cmdlet in a variable and use it later on in other cmdlets or for other purposes in the script, such as writing to the host, using it for comparison, or creating another variable, such as this, for example:

$timeZone = Get-TimeZone
Write-Host "The current time zone is " $timeZone

The following screenshot shows the output for the preceding command:

 

How to get help with PowerShell


PowerShell comes with a lot of in-built cmdlets, and with the addition of every new module, the list of available cmdlets increases. You can use your favorite search engine to get more information on a cmdlet. You can do this from the PowerShell window as well. The cmdlet to get the help is this:

Get-Help nameofcmdlet

Here's an, for example:

Get-Help Get-Service

The following screenshot shows the output for the preceding command:

This is useful if we would like to get help but don't want to leave the Command Prompt.

If you would like to get help from the official online documentation, you can use the following cmdlet:

Get-Help Get-Service -online

This will open the online help manual of the cmdlet with your default browser:

If you would like to quickly check whether there are any commands available for a particular service, you can use the following cmdlet:

Get-Command *Service*

This will give all the commands that contain the Service service in all the modules that are loaded:

What is a module?

A module is a combination of multiple PowerShell functionalities like scripts, cmdlets that are built to solve a common purpose. For example, to work with the users and licensing, we have to use the Module MSOnline provided by the Microsoft. You can find more information about the Windows PowerShell module here https://msdn.microsoft.com/en-us/library/dd878324(v=vs.85).aspx.

To find out the members of a cmdlet, we can pipe the Get-Member cmdlet with another cmdlet:

Get-Service | Get-Member

The following screenshot shows the output for the preceding command:

What is a pipe?

Using the pipe character (|), we can select the objects and then perform an action on them.

These three cmdlets--Get-Command, Get-Help, and Get-Member--are important to understand and use, especially when you are new to PowerShell. If you take a closer look, you will find the highlighted letters spell Microsoft compiled HTML help (.chm) files, which were the old-school help files available in Windows. We will use them throughout the book to get additional information on the commands we will use.

 

How PowerShell is an object-oriented language and how to work with objects


PowerShell works with objects, and these objects can have attributes and methods. An attribute is a property or a description. PowerShell is an object-oriented scripting language; however, moderately complex scripts are often written using a procedural/functional approach.

To get the members of any cmdlet, we can pipe the Get-Member cmdlet with any given cmdlet:

Get-TimeZone | Get-Member

The output of the preceding command is shown in the following screenshot:

The type of the input is System.String[] and the type of the output is System.TimeZoneInfo[].

 

Using the if and where statements


When writing scripts, we have to implement business logic that will shape the process. The if and where statements allow us to define logical conditions. For example, if you would like to compare two numbers, you can use the if and else statements and, based on the comparison, take appropriate action.

Conditional statements are the building blocks of any programming and scripting language.

If a certain condition is true, we can run a block of code. The syntax of an if...else is as follows:

if (<test1>)  
  {<statement list 1>} 
elseif (<test2>) 
  {<statement list 2>} 
else 
  {<statement list 3>}

Here's an example:

$a = 6;
 if( $a -eq 5){
   Write-Host "Variable a is equal to 5"
 }
 elseif( $a -eq 4){
   Write-Host "Variable a is equal to 4"
 }
 else
 {
   Write-host "Variable a is not equal to 5 and 4"
 }

The output of this script will be Variable a is not equal to 5 and 4.

We can use a combination of the if...else statements where in the if block we check for a condition: if that condition is true, then we execute a block of code, and if the condition is not true, then we execute another block of code. Sometimes, we can have more than one expected outcome and we can use multiple elseif conditions. The comparison operator -eq returns Boolean values (true or false). If the outcome of the comparison is true, then the associated block of code is executed. Since it is a Boolean value, we can use the reverse logic as well.

We have a lot of comparison operators available in PowerShell:

  • -eq: Equal to
  • -ne: Not equal to
  • -gt: Greater than
  • -ge: Greater than or equal to
  • -lt: Less than
  • -le: Less than or equal to
  • -like: Wildcard match
  • -notlike: Does not match wildcard
  • -match: Regular expression matching
  • -notmatch: Does not match regular expression pattern
  • -contains: Collection contains item
  • -notcontains: Collection does not contain item
  • -in: Item is in a collection

We can use multiple comparison operators in a single if statement. This helps you implement complex scenarios.

You can have multiple if statements or even use nested if statements.

We can Where-Object cmdlet to filter data return by other cmdlets. For example, if we would like to find out the processes running on a computer with the name svcHost we can use the Where-Object cmdlet with the Get-Process cmdlets as shown below.

Get-Process | Where-Object {$_.name -contains "svcHost"}
 

Using the for and while loops


Loops in PowerShell execute a series of commands or cmdlets as long as the condition to run them is true. Loops are helpful for running repetitive tasks inside a PowerShell script. For example, if we need to create five new users, we can use a loop, and inside the loop, we can add the logic to create a new user and execute the loop five times. Loops allow us to write business logic once and then run it repetitively as long as a certain condition is met. To implement loops in PowerShell, we can use the for, foreach, while, do...while, and do...until loops.

In a for loop, we run the command block based on a conditional test. In the following for loop, we are running Write-Host until the value of variable $i is less than 5. In the beginning, the value of variable $i is 0, and every time the loop is executed, we are incrementing the value of $i by 1. During the execution of the loop, when the value of variable $i becomes 5, the loop stops executing:

for ( $i=0; $i -lt 5; $i++)
{
   Write-Host "Value of i is" $i
}

The output of this for loop is as follows:

Using the while, do...while, and do...until loops, we can run loops as long as a condition is true (it is met).

The while loops only use the while keyword, followed by the condition and then the script block, as shown here:

$i=1
 while ($i -le 10)
 {
   Write-Host "Value of i is" $i
   $i++
 }

In this script, the script block inside the while loop will run till the value of the variable $i is less than 10. The output of this while loop is as follows:

The do...while and do...until loops begin with the do keyword, followed by the script block and then by the conditional keyword and the condition.

Here's an example of the do...while loop:

$i=1
 do
 {
   Write-Host "Value of i is" $i
   $i++
 }
 while ($i -le 10)

Here's an example of the do...until loop:

$i=1
 do
 {
   Write-Host "Value of i is" $i
   $i++
 }
 until ($i -gt 10)

Both the examples mentioned here basically implement the same business logic using loops, with slightly different comparison methods. In the do...while loop, the script block will run until the value of the variable $i is less than 10, and in the do...until loop, the script block will run until the value of the variable $i becomes greater than 10. The output of both the loops will be the same as, shown here:

 

Creating your first script


To automate tasks, we need to create and run PowerShell scripts. To run a script, we need to follow these steps:

  1. Configure PowerShell to run scripts: Running a malicious script can harm the computer/server; the default setting for PowerShell is not to run them. We need to change the execution policy of the computer/server to be less restrictive. These steps need to be implemented once.
  2. Store the script: We need to store the script files with the .ps1 extension on the file system.
  3. Edit the script: Add the business logic using commands, cmdlets, variables, and so on.
  4. Run the script: Run the PowerShell script within the PowerShell Command Prompt or ISE.

Let's create our first PowerShell script. We will be creating a script to read the information from a CSV file and then process the information using the for loops and then print it on the screen.

A comma-separated file has information in the form of tables, as shown in the following screenshot. The file has the user's information: their first name, last name, location, and department. We will use this information and create the login names for the users using the format $firstName + "." + $lastName.

The CSV file is as follows:

The script is as follows:

Function generateUserName($firstName, $lastName){
  $userName = $firstName + "." + $lastName
  Return $userName;
}

$userInformation = import-csv 'C:\Demo\UsersInfo.csv'

foreach($user in $userInformation){
  $firstName = $user.FirstName
  $lastName=$user.LastName
  $department =$user.Department
  $office=$user.Office
  $Location=$user.Location
  $userName = generateUserName $firstName $lastName
  Write-Host "The generated user name is " $userName
}

The following screenshot shows the preceding script:

Now let's review this script line by line. As mentioned before, if you are using functions inside a script, it is recommended that you write them at the top of the script. The reason is that when the script is executed, the functions are already loaded.

On line 1 we have declared the function with the name generateUserName, and it accepts two parameters of type string. We can pass complex datatypes as parameters as well.

On line 2, we are creating the value for the username using the following logic:

$firstname + "." + $lastName

On line 3, we are returning that value.

On line 6, we are reading the contents of the CSV file. The Import-CSV cmdlet is a cmdlet to read the contents of a file.

We will be covering this cmdlet in detail because to automate bulk user creation, we can use $userInformation stored in CSV files. We can also use information from other sources, such as plain text files or SharePoint lists. But as of now, CSV files are more common.

The Import-CSV cmdlet reads the information from the .csv file and stores them in the table-like custom objects. Each column header becomes the property and the subsequent rows become the values of the properties. You can find more information about this cmdlet here https://msdn.microsoft.com/en-us/powershell/reference/5.0/microsoft.powershell.utility/import-csv.

Here is the Import-CSV command:

This cmdlet accepts the following parameters.

  • [-Delimiter]: Specifies the delimiter that separates the property values in the CSV file. The default is a comma (,). Enter a character, such as a colon (:). To specify a semicolon (;), enclose it in quotation marks. If you specify a character other than the actual string delimiter in the file, Import-CSV cannot create objects from the CSV strings. Instead, it returns the strings.

  • [-Encoding]: Specifies the type of character encoding that was used in the CSV file. Acceptable values for this parameter are as follows:
    • Unicode
    • UTF7
    • UTF8
    • ASCII
    • UTF32
    • BigEndianUnicode
    • Default
    • OEM

The default is ASCII.

This parameter was introduced in Windows PowerShell 3.0.

  • [ -Header]: Specifies an alternate column header row for the imported file. The column header determines the names of the properties of the object that Import-CSV creates. Enter a comma-separated list of the column headers. Enclose each item in quotation marks (single or double). Do not enclose the header string in quotation marks. If you enter fewer column headers than there are columns, the remaining columns will have no header. If you enter more headers than there are columns, the extra headers are ignored. When using the Header parameter, delete the original header row from the CSV file. Otherwise, Import-CSV creates an extra object from the items in the header row.
  • [-LiteralPath]: Specifies the path to the CSV file to import. Unlike Path, the value of the LiteralPath parameter is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose it in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences.
  • [-Path]: Specifies the path to the CSV file to import. You can also pipe a path to Import-CSV.
  • [-UseCulture]: Indicates that this cmdlet uses the list separator for the current culture as the item delimiter. The default is a comma (,).
  • [CommonParameters]: This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable.

Inputs:

[System.String]: You can pipe a string that contains a path to Import-CSV.

Outputs:

[Object]: This cmdlet returns the objects described by the content in the CSV file.

Let's come back to the script. Once we get the contents of the CSV file in the $userInformation object, we process each row and assign the first name, last name, department, and office to the variables. On line 14, we call the generateUserName function and capture the return value in the variable and display the username on the script:

Note

Using a similar script, we will cover how to create bulk users and assign them licenses in Chapter 3, Azure AD and Licensing Management.

 

Summary


In this chapter, we discussed how PowerShell is a cmdlet-based language with verb-noun syntax. We covered how to pass parameters to cmdlets and store results as variables. We covered how to get help with PowerShell. We covered object-oriented concepts with PowerShell and the concept of objects. We covered how to use the if and where statements. We covered how to use the for and while loops. Finally, we created our first script and used the concepts we covered in the earlier sections to read the contents from a CSV file. In the next chapter, we will cover how can manage Office 365 using PowerShell.

About the Author
  • Martin Machado

    Martin Machado is an MCSD with over 15 years, experience in designing and implementing systems. He moved into consulting as a way to satisfy his curiosity and interest in diverse IT scenarios and applications very early in his career. He started as an Oracle developer and quickly moved into systems integration with Microsoft technologies. He has a passion for designing complex systems and intricate migrations.

    Browse publications by this author
Latest Reviews (4 reviews total)
Took a week or more to get product delivered. Had to send multiple emails for assistance.
Good Offering on Powershell
Order 1 book for $39.99, got charged twice ($79.98) on the same order (would not have known if I did not look at the bank statement), never received such book. Company said that it was my responsibility to resolve with UPS! Will never buy from this company again.
PowerShell for Office 365
Unlock this book and the full library FREE for 7 days
Start now