Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all 3333 articles
Browse latest View live

Weekend Scripter: Deciphering Windows PowerShell Shorthand

$
0
0

Summary: Learn how to decipher Windows PowerShell shorthand notation in this guest post written by Tim Warner.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome a new guest blogger today, Timothy Warner. Tim is an author and evangelist for Pluralsight, a premier online IT pro and development training company. Tim is the author of Sams Teach Yourself Windows PowerShell in 24 Hours, a contributor at PowerShell.org, and a member of the Nashville PowerShell User Group.

Hi everyone.

When I attended the Microsoft Ignite Conference in Chicago last week, I was somewhat surprised by the amount of Windows PowerShell shorthand I saw during product and technology demonstrations.

After all, doesn't The Community Book of PowerShell Practices tell us to confine our alias and positional parameter use to our ad-hoc console code? When we're sharing scripts with others, we should use formal cmdlet names and named parameters.

As a technical trainer, I was especially sensitive to the baffled faces I saw in those Ignite session audiences. Admittedly, these IT professionals in all likelihood have yet to board the Windows PowerShell train. In this blog post, I'll share three simple ways to translate terse Windows PowerShell code into a more formal, shareable format. Let's get started!

Method #1: Use Get-Alias

As our case study example, consider the following Windows PowerShell pipeline:

ps | ? { $_.CPU -gt 100 } | select ProcessName, CPU | sort CPU -des | ft -a

Sure, if you're already comfortable with Windows PowerShell and/or you have familiarity with Linux, you understand what's going on here. But put yourself in the shoes of the newcomer for a minute. What the heck does all that gobbledygook mean?

   Note  This pipeline shows all processes that have used over 100 seconds of processor time. We pare down our output
   by selecting only the ProcessName and CPU properties, sorting by CPU in descending order, and showing a data table
   with automatically sized column widths.

Enter the Get-Alias core cmdlet. If you haven't already done so, fire up an elevated Windows PowerShell console session and inspect the command Help:

Get-Help -Name Get-Alias -ShowWindow

Pay attention to two parameters in particular. The -Name parameter specifies the name of the alias, and the -Definition parameter maps to the associated Windows PowerShell command. Thus, we can do the following to resolve ps to its associated cmdlet:

Get-Alias -Name ps

   Note  You may run into problems if you're researching a custom alias that others defined on their computers. We're
   concerned only with default, built-in aliases.

Did you try it? The output tells you that ps is an alias that maps to Get-Process. This is logical for *NIX users who are accustomed to using ps to view running processes on their systems.

Let's use the same Get-Alias method to resolve the other aliases in our example pipeline:

Get-Alias -Name ?

Get-Alias -Name select

Get-Alias -Name sort

Get-Alias -Name ft

Do you know what -gt means? That is simply a comparison operator for "greater than." Please read the conceptual Help, about_comparison_operators, for more information.

Besides the abundance of aliases, the other "sticky wicket" in our sample pipeline is its liberal use of positional parameters. For instance, take a look at this pipeline section:

select processname, CPU

The previous statement works because if you look at the Select-Object Help, you'll learn that the position 1 parameter is -Property:

PS C:\> Get-Help -Name Select-Object -Parameter Property

-Property <Object[]>

    Specifies the properties to select. Wildcards are permitted.

    The value of the Property parameter can be a new calculated property. To

    create a calculated, property, use a hash table. Valid keys are:

    -- Name (or Label) <string>

    -- Expression <string> or <script block>

    Required?                    false

    Position?                    1

    Default value

    Accept pipeline input?       false

    Accept wildcard characters?  true

Please be courteous to your audience and always use named parameters in your shareable Windows PowerShell code! For instance, I would refactor this Select-Object statement in the following way:

Select-Object -Property ProcessName, CPU

Method #2: Use the Script Analyzer

If you're using Windows PowerShell 5.0 Preview, we can use the built-in PowerShell Script Analyzer module to help us resolve those pesky aliases.

The Script Analyzer is a flexible platform for static code analysis. The module's default ruleset was created by the PowerShell.org community as the result of a heated brainstorming session regarding Windows PowerShell best practices.

For grins, I added our case study pipeline to a script named testscript.ps1 stored in the root of my D: drive. Let's point the Script Analyzer at the one-line script and see what happens:

Invoke-ScriptAnalyzer -Path 'D:\TestScript.ps1'

The following image shows a bit of my output:

Image of menu

As you can see, the Script Analyzer output flags the use of aliases and the use of positional parameters, among many other code aspects. The output also notifies you which lines in your script contain rule violations.

Method #3: Use ISESteroids

Dr. Tobias Weltner gave us ISESteroids as a way to inject Windows PowerShell best practices "automagically" into our scripts by means of refactoring logic. ISESteroids (as you may already know) is a commercial add-on to the good ol' Windows PowerShell Integrated Scripting Environment (ISE).

We'll begin by downloading and updating the add-on in Windows PowerShell 5.0 Preview:

Install-Module -Name ISESteroids -Force

Update-Module -Name ISESteroids -Force

Now open an administrative Windows PowerShell ISE session and load the add-in from the ISE console:

Start-Steroids

This tool is awesome! Take a look at the following image, in which you can see the ToolTip Help in addition to an instant refactor button that will instantly convert your aliases to full cmdlet names:

Image of menu

To address positional parameters, click the refactor button, navigate to the Advanced pane in the Add-in window, expand the Command section, find CommandPositionalParameter, and click Apply.

Boom! All your parameters are named now. I show you all this in the following image:

Image of command output

To sweep up the shavings...

Remember that you need to provide only enough characters for Windows PowerShell to identify a switch parameter. Thus, in our sample code, you could expand -des to -Descending and -a to -AutoSize if you want to.

Of course, we also have Tab completion! If you've never used it, try typing format-t, pressing Tab, and typing -a followed by another Tab.

I hope you found this post helpful. At the very least, I hope I reminded you of the critical importance of "spelling out" every last bit of our Windows PowerShell code if it's intended for public consumption. Take care, and "Happy PowerShelling"!

~Tim

Thanks, Tim!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy


PowerTip: Use PowerShell to Parse Delimiters in File

$
0
0

Summary: Use Windows PowerShell to parse file delimiters in a file.

Hey, Scripting Guy! Question How can I use Windows PowerShell to break out lines in a text file that are delimited by “\\”? Here is the file content:

PS C:\> Get-Content C:\fso\AnEmptyFile.txt

This is content

\\mydata\\more stuff

\\addmydata\\addmore stuff\\evenmore

Hey, Scripting Guy! Answer Use the –Delimiter parameter of the Get-Content cmdlet. Here is an example with the associated output:

PS C:\> Get-Content C:\fso\AnEmptyFile.txt -Delimiter \\

This is content

\\

mydata\\

more stuff

\\

addmydata\\

addmore stuff\\

evenmore

PowerShell Custom Type Module

$
0
0

Summary: Create custom types in Windows PowerShell with a free module by Jon Newman.

Microsoft Scripting Guy, Ed Wilson, is here.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blogger who was with us in 2011, when he wrote Automate Facebook with a Free PowerShell Module

My name is Jon Newman, and I’m an old hand at Windows PowerShell. I was on the Windows PowerShell 1.0 team, with Bruce, Jim, and all the rest. My primary responsibility was for pipeline execution and error handling. I started at Microsoft in September 1989 (25 years ago!). I wrote management UI and infrastructure for Windows Server for 23 years, then a couple years ago, I switched to service engineering (aka operations).

I created module CType for use in my operations work. I want to restrict the input to certain functions to be the custom objects that are generated by other functions (in my case, the Connection object). In addition, I have ~100 SQL queries that generate streams of System.Data.DataRow objects, and I want to manage the output objects and their formatting without having to hand code a similar number of custom C# classes. I have been using and improving CType for over six months, and it has been really useful to me.

I talked about this idea with Ed at the PowerShell Summit in April 2014. Now I finally have a decent implementation and installation infrastructure. Thanks also to Jason Shirk for reviewing my work and suggesting the “CType {}” syntax.

Installing CType

If you want to cut to the chase, I have made CType available in several ways:

  • The MSI installer. This will install the CType module to your user profile.
  • The NuGet package. NuGet is good for incorporating CType into your Visual Studio project and keeping it up-to-date.
  • The Chocolatey package. Chocolatey is good for installing CType to many computers and virutal machines via automation and keeping it up-to-date.

Run Get-Help about_CType, and you are on your way!

Why create custom types in Windows PowerShell?

  1. You can specify parameters such that only an instance of your custom type can be used as input. For example, you could define:

Function New-MyConnection

{

    [OutputType([MyCompany.MyModule.Connection])]

    [CmdletBinding()]

    param(

        # parameters

        )

    # ...

}

function Get-MyData

{

    [CmdletBinding()]

    param(

        [MyCompany.MyModule.Connection][parameter(Mandatory=$true)]$Connection

        # other parameters

        )

    # ...

}

In this example, Get-MyData will only accept an object of the type MyCompany.MyModule.Connection, which presumably was emitted by New-MyConnection. Note that only “real” .NET types will work for this purpose—it isn’t enough to simply add TypeName as a decoration with $connection.PSObject.TypeNames.Insert(0,$typename).

  1. You can decorate the type with formatting metadata and other Windows PowerShell type decorations. In this case, it actually would be enough to call $obj.PSObject.TypeNames.Insert(0,$typename).
  2. Windows PowerShell has other issues with the raw System.Data.DataRow class, which I will discuss later.

Why use CType to create custom types?

Add-Type gives you complete flexibility to define your custom types. You could use Add-Type plus Update-FormatData directly and not bother with CType. However, there are a number of reasons why you might want to use CType:

  1. Add-Type requires you to define your class in CSharp, JScript, or VisualBasic. Scripters may not be comfortable in these programmer languages.
  2. These programming languages make it difficult to create type definitions “downstream in the pipeline,” which well-constructed Windows PowerShell commands can do. For example, the Add-CType parameters include:

[string][parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]$PropertyName,

        [string][parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]$PropertyType,

CType takes care of translating this into C#. This allows you to generate types dynamically and in a concise manner. I will demonstrate this later.

  1. Update-FormatData requires input in a very cumbersome XML schema. CType also takes care of generating this. The current implementation of CType only supports simple table formats.

What about PowerShell 5.0 class declarations?

Windows PowerShell 5.0 introduces syntax for class declarations, where Desired State Configuration (DSC) is the first target scenario. These are “real” .NET classes, and they can be used as function parameter types. (At this writing, I can only comment on the implementation of Windows PowerShell class declarations in the WMF 5.0 November 2014 Tech Preview.)

You can define your classes by using Windows PowerShell 5.0 class declarations rather than CType if you like; however, there are several advantages to using the CType module:

  1. CType does not require WMF 5.0. Many customers will not install the still-to-be-released (at this writing) official Windows PowerShell 5.0 across all their servers for years, especially customers with dependencies on previous versions of Windows PowerShell (like old versions of System Center).
  2. The class declaration syntax in CType isn’t really all that different from CSharp. As with Add-Type, it would be difficult to create Windows PowerShell class declarations “downstream in the pipeline.”
  3. At this writing, Windows PowerShell class declarations only support short class names. They do not currently support namespaces or class inheritance.

   Note It would be possible to implement a variant of CType layered over Windows PowerShell classes rather than over
   Add-Type. I can’t identify any compelling advantages in making that change. There is nothing wrong with Windows
   PowerShell classes—they just aren’t targeted at this scenario.

Creating a type with function CType

CType is very easy to use. Simply call CType to add each of your custom types. If you are writing a module, add CType to RequiredModules in your .psd1 file, and create the types in the module initialization for your module. Note that a .NET type can only be defined once, so you do not want to call CType for any class more than once. Here is an example:

CType MyCompany.MyNamespace.MyClass {

    property string Name -Width 20

    property string HostName -Width 20

    property DateTime WhenCreated -Width 25

    property DateTime WhenDeleted -Width 25

}

When the type is defined, you can create an instance by using New-Object. You don’t have to specify all the properties, and you can change their values at any time.

New-Object –TypeName MyCompany.MyNamespace.MyClass –Property @{

       Name = “StringValue”

       WhenCreated = [DateTime]'2015.01.01'

       WhenDeleted = [DateTime]::Now

       }

One cool thing about this syntax is that you can use Windows PowerShell structures such as loops, and conditionals, inside the CType declaration. Here is a very simple example:

CType MyCompany.MyNamespace.MyClass {

    property string Name -Width 20

    property string HostName -Width 20

    if ($IncludeTimePropertiesWithThisType)

    {

        property DateTime WhenCreated -Width $DateTimeWidth

        property DateTime WhenDeleted -Width $DateTimeWidth

    }

}

Creating a type with Add-CType

If you want to create types by using classic Windows PowerShell pipelines rather than the “little language” for function CType, this is an alternate syntax that does exactly the same thing.

   Note New-Object is only one way to come up with these objects. Select-Object, ConvertFrom-CSV, or any other way to
   generate objects with properties PropertyType and PropertyName (and optionally PropertyTableWidth and/or
   PropertyKeywords) will work.

@(

(New-Object PSObject -Property @{PropertyType='string';   PropertyName='Name';        PropertyTableWidth=20}),

(New-Object PSObject -Property @{PropertyType='string';   PropertyName='HostName';    PropertyTableWidth=20}),

(New-Object PSObject -Property @{PropertyType='DateTime'; PropertyName='WhenCreated'; PropertyTableWidth=25}),

(New-Object PSObject -Property @{PropertyType='DateTime'; PropertyName='WhenDeleted';

PropertyTableWidth=25}) `

) | Add-CType -TypeName MyCompany.MyNamespace.MyClass

I actually wrote Add-CType first, then Jason Shirk suggested the “function CType” syntax.

SQL wrapper classes

If you have created wrappers for SQL queries by using Windows PowerShell, you may have noticed some idiosyncrasies using the System.Data.DataRow class in Windows PowerShell, for example:

System.DBNull: If a particular result row does not have a defined value for a particular column, you may see some property values come back as a reference to the singleton instance of type System.DBNull. This has the advantage of distinguishing between an empty cell and an actual zero or empty string result value. Unfortunately, when Windows PowerShell converts System.DBNull to Boolean, it comes out as $true, so statements like...

if ($row.Property) {DoThis()}

...will actually execute DoThis() when the value is System.DBNull. This is pretty confusing, but it probably can no longer be fixed in Windows PowerShell without breaking backward compatibility. Without the wrapper class, you would have to use a workaround like this:

if (($row.JoinedProperty –notis [System.DBNull]) –and $row.JoinedProperty) {DoThis()}

The wrapper class takes care of this problem, by turning the System.DBNull value back to null.

Extra methods

System.Data.DataRow objects contain SQL-specific methods, like BeginEdit(), which are probably not relevant to users of your script. The wrapper hides these methods.

You create SQL wrapper classes like this:

CType MyCompany.MyNamespace.MyWrapperClass {

    sqlproperty string Name -Width 20

    sqlproperty string HostName -Width 20

    sqlproperty DateTime WhenCreated -Width 25

    sqlproperty DateTime WhenDeleted -Width 25

}

~or~

@(

(New-Object PSObject -Property @{PropertyType='string';   PropertyName='Name';        PropertyTableWidth=20}),

(New-Object PSObject -Property @{PropertyType='string';   PropertyName='HostName';    PropertyTableWidth=20}),

(New-Object PSObject -Property @{PropertyType='DateTime'; PropertyName='WhenCreated'; PropertyTableWidth=25}),

(New-Object PSObject -Property @{PropertyType='DateTime'; PropertyName='WhenDeleted';

PropertyTableWidth=25}) `

) | Add-CType -TypeName MyCompany.MyNamespace.MyWrapperClass -SqlWrapper

You create instances like this:

$connection = New-Object System.Data.SqlClient.SqlConnection $connectionString

$command = New-Object System.Data.SqlClient.SqlCommand $commandString,$connection

$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $command

$dataset = New-Object System.Data.DataSet

$null = $adapter.Fill($dataSet)

$result = $tables[0].Rows | ConvertTo-CTypeSqlWrapper -ClassName MyCompany.MyNamespace.MyWrapperClass

If you already have functions that generate DataRow objects, use ConvertTo-CTypeDeclaration to create an initial CType wrapper for them. Simply add your type name, add a parent type name (or remove that clause), add widths, and change the property order as desired for formatting, and your declaration is ready!

That’s it! You can contact me through the following Comment section. I would love to see bugs, suggestions, questions, and new scenarios. Feedback is really important to me—it’s how I decide whether to invest more time in a project like this one. I also have a site on GitHub where you can report issues: Welcome to Issues!

Also, please contact me if you have any interest in assisting with TeslaFacebookModule. (“Start-Car” anyone?) And tell Elon Musk to hurry up…

~Jon

Thank you, Jon.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Read Text File in Raw Mode with PowerShell

$
0
0

Summary: Use Windows PowerShell to read a text file in raw mode.

Hey, Scripting Guy! Question How can I use Windows PowerShell to read a text file as a single line of text, not as an array of strings created by
           end-of-line returns?

Hey, Scripting Guy! Answer Use the –Raw parameter with Get-Content. This forces Windows PowerShell to ignore new line characters and
           end-of-line returns, and instead, return the contents of the file as one string. Here is an example:

Get-Content C:\fso\AnEmptyFile.txt -Raw

PowerShell and the Active Directory Schema: Part 1

$
0
0

Summary: Guest blogger, Andy Schneider, discusses extending the Active Directory schema.

Microsoft Scripting Guy, Ed Wilson, is here. We welcome back guest blogger, Andy Schneider. Andy has a two-part blog series that will conclude tomorrow.

Andy Schneider is the Identity and Access Management Architect for IT Services at Avanade. His team’s job is to ensure that the right people can access the right things at the right time. Andy is very passionate about automation and the DevOps movement. He is a huge fan of Windows PowerShell, and he has been using it since it was first released as Monad. Andy’s personal blog is Get-PowerShell, and his twitter handle is @andys146.

This is the first post in a two-part series that walks through some information about the Active Directory Schema and Windows PowerShell. This post provides a short overview of what the schema is and then goes into some details about how we can use Windows PowerShell to look at the schema. Tomorrow, I will walk through the process of adding custom classes and attributes to the schema with pure Windows PowerShell code.

Many Active Directory administrators are often fearful of doing anything that requires changes to the schema in Active Directory. However, if you have been administering Active Directory for any length of time, you have probably had to extend the schema for at least one or two software installations or upgrades—for example, installing Exchange or Lync, or upgrading domain controllers to a new operating system.

However, the practice of adding custom extensions that only your company would use is often frowned upon. The fact is that with good planning and understanding, extending the schema is actually pretty straightforward and should not induce a great amount of fear. There is a great article written by Brian Desmond that addresses this issue and goes into detail about planning for and designing schema extensions: Extending the Active Directory Schema.

I think one of the biggest concerns with schema extensions is that they cannot be removed. In IT, you always need to know when you are driving down a one-way street and cannot turn back. Schema extensions are a one-way street.

However, it is actually possible to disable an extension. You can set the isDefunct property on a schema object to True, and the class that had the attribute will no longer be able to use it. For more information, see Deactivating Schema Objects.

As with anything in IT, it is always good to have solid backup and restore plans for your Active Directory environment. Also, always test your changes in a development or test environment before you go hacking around in your production Active Directory environment.

With that background, let’s get going...

The Active Directory schema consists of two major categories: classes and attributes. This is very similar to the objects that we know and love in Windows PowerShell. An object in Windows PowerShell is based on a class, and that class has certain properties. Similarly, Active Directory has classes, and these classes have attributes.

Two very common classes in Active Directory are the user and computer classes. The user class has a bunch of attributes that you have probably seen, such as samAccountName, userAccountControl, sn, and givenName. If you use the Get-ADUser cmdlet and specify –properties *, you can see all the attributes on the returned users. These properties refer to the attributes of the user class.

Let’s do a little spelunking around Active Directory and see what we can come up with in regards to dealing with the schema.

By using the Get-ADRootDSE cmdlet, we can find all kinds of cool information about our Active Directory environment. It turns out that one of those properties is called SchemaNamingContext.

Image of command output

This looks promising. There is no Get-ADSchema cmdlet, but there is a generic Get-ADObject cmdlet, which can get any kind of object we want from Active Directory. We simply need to provide the correct path to search for objects. If we set the SearchBase to the SchemaNamingContext distinguished name, we can get the following:

Image of command output

Like Get-ADUser, Get-ADObject does not return all the properties by default. You have to specify them. When we do this, we find all kinds of goodies in these objects. Let’s look for the user class object. You can try this out with the following code:

$schemaPath = (Get-ADRootDSE).schemaNamingContext

Get-ADObject -filter * -SearchBase $schemaPath -Properties * | where Name -like "user"

Image of command output

There are a lot of properties here that we can look at and see what’s going on. In the next post, I will take a look at which of these properties are necessary to create custom attributes.

~Andy

Thanks Andy, I'm looking forward to Part 2 tomorrow.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find Specific PowerShell Cmdlet Help

$
0
0

Summary: Learn how to find help for a specific Windows PowerShell cmdlet parameter.

Hey, Scripting Guy! Question How can I find more information about a specific Windows PowerShell parameter that I saw a presenter use?

Hey, Scripting Guy! Answer Use the Get-Help cmdlet, and specify the Windows PowerShell cmdlet and parameter names.
           This example returns help about the Raw parameter for the Get-Content cmdlet:

get-help get-content -Parameter raw

PowerShell and the Active Directory Schema: Part 2

$
0
0

Summary: Guest blogger, Andy Schneider, continues his discussion about extending the Active Directory schema.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Andy Schneider, for Part 2 of his series. If you missed yesterday's post, see PowerShell and the Active Directory Schema: Part 1.

Yesterday, we looked at what the Active Directory schema is and how to access details of the schema by using Windows PowerShell. In this post, we are going to look at how we can look at the schema, and also update the schema. If you didn’t get a chance to read Extending the Active Directory Schema by Brian Desmond, I highly suggest you read through it to gain an understanding of the ramifications of schema extensions and how to properly plan for them.

Yesterday, I left you with the following lines of code which would give us all kinds of information about the schema objects we have in Active Directory:

$schemaPath = (Get-ADRootDSE).schemaNamingContext

Get-ADObject -filter * -SearchBase $schemaPath -Properties * | where Name -like "user"

Image of command output

The question now is, “Which ones do we care about if we want to add our own schema extensions to the user class?”

The way I went about this was to start looking at how schema extensions are typically done. More often than not, extensions are implemented using the LDAP Data Interchange Format, also known as LDIF. There is a tool in Windows called LDIFDE.exe that you can use to import and export data. LDIFDE.exe can also be used to add attributes and classes to the Active Directory schema.

In addition to examining LDIF files, I also went through the manual process of using the schema management tools to see what kinds of input go into the GUI. Following is a screenshot of that menu.

   Note You have to register a DLL to be able to get the Schema Management MMC. You can do this with the
   command regsvr32 schmmgmt.dll.

Image of menu

By looking through a variety of LDIF file examples and the GUI tool, we can figure out what we need to supply when we create new attributes. Here is a short list:

Attribute

Description

Name

Name of the attribute

lDAPDisplayName

LDAP display name

attributeID

Unique Object Identifier, known as an OID

oMSyntax

Determines type of data stored in the attribute (int, string, bool). 
For reference, see Syntaxes.

isSingleValued

True or False. Attribute may have more than one value
(for example, proxyAddresses)

adminDescription

Description of the attribute

The one slightly tricky attribute that we need to worry about is attributeID. This needs to be a unique OID. For production, you really should register for a Private Enterprise Number with IANA. You can do this here.

However, for development purposes, we can use the following code to generate an OID:

Function New-AttributeID {

    $Prefix="1.2.840.113556.1.8000.2554"

    $GUID=[System.Guid]::NewGuid().ToString()

    $Parts=@()

    $Parts+=[UInt64]::Parse($guid.SubString(0,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(4,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(9,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(14,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(19,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(24,6),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(30,6),"AllowHexSpecifier")

    $oid=[String]::Format("{0}.{1}.{2}.{3}.{4}.{5}.{6}.{7}",$prefix,$Parts[0],

                 $Parts[1],$Parts[2],$Parts[3],$Parts[4],$Parts[5],$Parts[6])

        $oid

}

Image of command output

For this demonstration, we are going to add a new attribute to the user class called asFavoriteColor. I am using the “as” prefix simply to identify this attribute. You can use your company’s initials or some other way to distinguish your custom extensions from other attributes. To create a new attribute, we are going to use the New-ADObject cmdlet.

The path is going to be in the schemaNamingContext container, which we already have. We have also seen that schema attribute objects are of the type attributeSchema. New schema class objects are of the type classSchema.

The only thing that remains are some of these properties that are unique to schema objects. We will need to store these in a hash table and pass them in via the –OtherAttributes parameter:

$schemaPath = (Get-ADRootDSE).schemaNamingContext

$oid = New-AttributeID

$attributes = @{

      lDAPDisplayName = 'asFavoriteColor';

      attributeId = $oid;

      oMSyntax = 20;

      attributeSyntax = "2.5.5.4";

      isSingleValued = $true;

      adminDescription = 'favorite colors are really important';

      searchflags = 1

      }

New-ADObject -Name asFavoriteColor -Type attributeSchema -Path $schemapath -OtherAttributes $attributes

When we create that new object, we can see our new attribute!

get-adobject -SearchBase $schemaPath -Filter 'name -eq "asFavoriteColor"'

Image of command output

However, we aren’t quite done. We have an attribute, but it hasn’t been attached to the user class yet. To attach an attribute to an existing class, we have to update the class to include the attribute. This was not the easiest thing to figure out.

After looking at a few LDIF files, I finally realized that there was a property on the schemaClass objects called mayContain. This is a list of all the attributes associated with a particular class. From here, we can get the user schema object and add the new attribute to its mayContain property.

$userSchema = get-adobject -SearchBase $schemapath -Filter 'name -eq "user"'

$userSchema | Set-ADObject -Add @{mayContain = 'asFavoriteColor'} 

Now we can finally set and get the asFavoriteColor property on a user object.

Image of command output

We can wrap up all this in a nice function. I set this up so I can take data from the pipeline by property names, so I can easily import a bunch of attributes from a CSV file. I also added support for ShouldProcess and set the ConfirmImpact value to High. Here is my code:

Function New-AttributeID {

    $Prefix="1.2.840.113556.1.8000.2554"

    $GUID=[System.Guid]::NewGuid().ToString()

    $Parts=@()

    $Parts+=[UInt64]::Parse($guid.SubString(0,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(4,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(9,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(14,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(19,4),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(24,6),"AllowHexSpecifier")

    $Parts+=[UInt64]::Parse($guid.SubString(30,6),"AllowHexSpecifier")

    $AttributeID=[String]::Format("{0}.{1}.{2}.{3}.{4}.{5}.{6}.{7}",$prefix,$Parts[0],

                                    $Parts[1],$Parts[2],$Parts[3],$Parts[4],$Parts[5],$Parts[6])

    $oid

}

Function Update-Schema {

[CmdletBinding(SupportsShouldProcess,ConfirmImpact='High')]

param(

[Parameter(Mandatory,ValueFromPipelinebyPropertyName)]

$Name,

[Parameter(Mandatory,ValueFromPipelinebyPropertyName)]

[Alias('DisplayName')]

$LDAPDisplayName,

[Parameter(Mandatory,ValueFromPipelinebyPropertyName)]

[Alias('Description')]

$AdminDescription,

[Parameter(Mandatory,ValueFromPipelinebyPropertyName)]

[Alias('SingleValued')]

$IsSingleValued,

[Parameter(ValueFromPipelinebyPropertyName)]

[Alias('OID')]

$AttributeID = (New-OID)

)

BEGIN {}

PROCESS {

  $schemaPath = (Get-ADRootDSE).schemaNamingContext      

  $type = 'attributeSchema'

  switch ($isSingleValued)

  {

   'True'  {$IsSingleValued = $true}

   'False' {$IsSingleValued = $false}

   default {$IsSingleValued = $true}

  }

  $attributes = @{

      lDAPDisplayName = $Name;

      attributeId = $AttributeID;

      oMSyntax = 20;

      attributeSyntax = "2.5.5.4";

      isSingleValued = $IsSingleValued;

      adminDescription = $AdminDescription;

      searchflags = 1

      }

    $ConfirmationMessage = "$schemaPath. This cannot be undone"

    $Caption = 'Updating Active Directory Schema' 

    if ($PSCmdlet.ShouldProcess($ConfirmationMessage,$Caption))

    {

      New-ADObject -Name $Name -Type $type -Path $schemapath -OtherAttributes $attributes

      $userSchema = get-adobject -SearchBase $schemapath -Filter 'name -eq "user"'

      $userSchema | Set-ADObject -Add @{mayContain = $Name}

    }

}

END {}

}

You can download this code from GitHub: PowerShell to Udpate Active Directory Schema.

This purpose of this series and code is to show you the basics of how to use native Windows PowerShell to extend the Active Directory schema. I would like to reiterate that any schema modifications should be well thought out and planned for. There are many more attributes that can be used and that could be useful. You can use this code as a basic framework for updating the schema with your company’s requirements and standards.

~Andy

Thank you Andy. This is some awesome material.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Return Specific Number of PowerShell Cmdlets

$
0
0

Summary: Learn how to return only a specific number of Windows PowerShell cmdlets.

Hey, Scripting Guy! Question How can I find only four Windows PowerShell cmdlets that use the verb Get, without seeing a
           long scrolling list of found cmdlets?

Hey, Scripting Guy! Answer Use the TotalCount parameter of the Get-Command cmdlet, for example:

Get-Command -Verb get -TotalCount 4


Using PowerShell Grid View for User Info

$
0
0

Summary: Learn how to use the Windows PowerShell grid view to display user information in this guest post by Tim Bolton and Don Walker.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Tim Bolton...

Hi all,

This project was a joint effort with my coworker, Don Walker (@EngineeringDon). In my current work environment, the character set used for user IDs that are stored in Active Directory can be a mixed bag of uppercase letters, lowercase letters, or a mix of both. This is due in part to a large Active Directory merger project that is currently underway.

Due to that condition and the numerous applications that we use (which are case sensitive), one of the most asked daily questions we get is, “Can you tell me if this user ID is uppercase or lowercase?”

Normally, this will be followed by being asked to check the user principal name (UPN), lockout status, email address, and so on.  Initially, I would output this information to a text file and send it to the requester by Instant Messenger or email.

However, this would open the door to check for the groups that the user is a member of and inevitably, “Can you get a list of all of the members of <Group>?”

You may be asking, "Why not tell them to use Windows PowerShell?"

Well, yes, we have instructed the requesters on how to load Windows PowerShell and how they could get this information themselves, but the lack of a GUI is almost an impossible sell point for some people. So we used this as an opportunity to teach and assist them at the same time.

I recently came across a couple of articles by Ed Wilson (Microsoft Scripting Guy), Jeff Hicks (The Lonely Administrator), and Mike F Robbins (Computing Solutions) where they demonstrated using Out-GridView with –Passthru so that you can select properties within GridView and pass them to the pipeline. Those articles inspired this little project we called “Get-UserInfo”—or as Adam Bertram ‏(@adbertram) referred to it, “The poor man’s forms.” Thanks for the advice on this, Adam!

How it works…

The function is stored in the requester’s Windows PowerShell profile, so they simply need to open a Windows PowerShell session and type Get-UserInfo and then enter the user ID.

Note  RSAT and Window PowerShell 4.0 are needed on the computer.

Image of command output

The user ID is passed as $Identity where Get-ADUser is used to gather the user’s information. Note the -OutPutMode Single parameter. This is used to allow the requester to select a user in GridView, and it passes that information to the next piece of the script.

Image of command output

As shown here, you now can see the basic (most requested) information of the user:

Image of command output

When you select a user and select OK, you can use the Get-ADPrincipalGroupMembership cmdlet to display the groups that the user is a member of:

Image of command output

Notice that the title will display the user ID with a message for the requester to select an Active Directory group and select OK if they want to see all of the group’s members.

Image of command output

When you select a group and select, the members of the group you selected using (Get-ADGroup <name> -Properties Members).Members is displayed.

Image of command output

Why did we use Get-ADGroup Members instead of Get-ADGroupMember? Remember I mentioned a large Active Directory merger project that is currently underway? Unfortunately, not all of the group members are in the main domain where the group resides, and Get-ADGroupMember will fail if it runs into any foreign security principals.

This took a while to figure out because Dsmod worked as did (Get-ADGroup <name> -Properties Members). I could also see the members in Active Directory Users and Computers.

Image of command

Here is the end result. Notice that the title will have the name of the group you looked up:

Image of command output

For now, this is what the requesters are using. So far, they really appreciate the GUI look and feel. We are making updates daily including:

  • Error handling for empty input
  • Error handling for empty Active Directory objects
  • Functions for formatting string output
  • Custom objects with custom names (because I like them that way)
  • Orphaned security identifiers excluded from results
  • More verbose commenting

Future enhancement ideas include (if desired):

  • Switch to form user interface (UI) if more complexity is required
  • Move actions into their own function for flexibility and expanded UI
  • Create additional output functions such as exporting to CSV and gather additional information about the selected user

~Tim and Don

Thanks Tim and Don. Looking forward to seeing your future ideas at work.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Use PowerShell Grid View to Display Logon Info

$
0
0

Summary: Learn how to display user logon information.

Hey, Scripting Guy! Question How can I use Windows PowerShell to display user logon information in an easy graphical manner?

Hey, Scripting Guy! Answer Create two hash tables using environmental variables, and pipe the input to the Out-Gridview cmdlet,
           for example:

@{"username" = $env:USERNAME}, @{"Computername"=$env:COMPUTERNAME} |
Out-GridView -Title "user logon info"

Note This is a single-line command broken at the pipeline character for display on the blog.

PowerShell Code Breaks: Break Line, Not Code

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about line continuation without breaking Windows PowerShell code.

Hey, Scripting Guy! Question Hey, Scripting Guy!

I have a problem sometimes when I copy code from the Hey, Scripting Guy! Blog. It does not seem to work. I try to take out spaces, but it still doesn’t work. I am at my wits end because the code should do exactly what I want it to do, but I cannot get it to work. Can you help me?

—BG

Hey, Scripting Guy! Answer Hello BG,

Microsoft Scripting Guy, Ed Wilson, is here. It is definitely summer here in Charlotte, North Carolina. When the sun comes up, it is already hot, and when the sun goes down, it is hot. At night, well...it is still hot. Anyway, one thing that goes well with hot weather, is hot tea—it is very refreshing. So I have a nice cup of Gunpowder green tea and a biscotti. I decided to use my Windows Surface Pro 3 to check my email sent to scripter@microsoft.com, and I ran across your email.

Why break code in the first place?

When writing code that will be printed in a book, I need to keep my code lines to around 80 characters in length. When writing code that will be placed on a blog, I need to keep within constraints of my current blogging platform, which is around 85 characters, but not much more than that.

As an IT pro, I like to keep my code to a little less than the width of my monitor. So this means that if I do not need to scroll to see the rest of my code, I am cool. As a best practice, I recommend that when you write Windows PowerShell code (or any code for that matter), you avoid scrolling to see the remainder of the code. This is because it makes reading and troubleshooting code a problem.

With high resolution video cards and monitors, this line constraint is not a huge deal, but for books, it becomes a major problem—especially because so many Windows PowerShell cmdlet names are very long, as are many .NET Framework namespaces, classes, and member names. Typing is not a major issue because we have IntelliSense and Tab completion, but restricting code to 80 characters becomes a major pain.

Please keep this in mind: There is no virtue in writing "narrow code” (that is, code that is 80 lines or less in length) simply for the sake of having narrow code. To do so often requires the use of intermediate variable assignments, use of enumeration numeric values, and other techniques that can make code more difficult to read.

If you are not writing code for a book or for a blog post, as far as I am concerned, if the code fits on the screen, you are cool. If you have great eye sight and can read 4 pixel characters at 1920 x 1080 (or greater) resolution, you can get an lot of code on a single line without the need to break your code. On the other hand, if you bump your font size to real large and you have a screen resolution of 600 x 800, your code lines will be short to avoid scrolling.

In Windows PowerShell, there is the line continuation character. It is the backtick ( ` ), but it needs to be placed properly or it does not work. So how do I break my code lines without breaking the code?

What's the best way to break code?

When a line of code is too long, it needs to continue on the next line. This is often referred to as line continuation, or sometimes breaking your code (because you break the line into pieces, not because you actually broke the code and it no longer works). However, at times, this is exactly what happens—a piece of perfectly working code becomes nonfunctioning because it was inproperly broken into pieces.

If I have a command such as the one here, I do not need to continue the command on another line:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME

But when I decide that I want to select specific properties from the object, I am going to continue to the next line. In this particular case, the best way to do this is to place the pipeline character on the end of the line, and then place the Select-Object cmdlet on the following line. If I do not choose too many properties, I can avoid continuing the second line to a third line. This is shown here:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64

But, what if I want more than the four properties I have selected on the second line? How do I continue to the third line? In this case, the comma will serve as my line continuation because Windows PowerShell knows that if I have a comma at the end of a line, and I continue property selection on the next line, everything will be fine. This code is shown here:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64,

VirtualMemorySize

As shown here, I could have used the line continuation character, but it was not necessary:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64, `

VirtualMemorySize

I also could have placed the line continuation character at the end of the line, and added a comma on the next line, as shown here:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64 `

, VirtualMemorySize

This code looks strange to me, but as shown here, it does work:

Image of command output

In the following code, it might seem that the backtick is required because it generates an error message if it is left out:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64,

VirtualMemorySize |

Format-Table -Property `

    ProcessName, ID

Image of command output

However, remember that I can leave out line continuation from properties that are separated by commas. So all I need to do is to revise the way the code appears a bit:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64,

VirtualMemorySize |

Format-Table -Property ProcessName,

    ID

I can also break code when I open or close braces. This is shown here:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64,

VirtualMemorySize |

Format-Table -Property ProcessName,

    ID, @{

     Label = 'CPU'; Expression = { [int]$_.cpu }

     }

In addition, I can break when I open or close a parenthesis:

Get-Process -Name powershell_ise -ComputerName $env:COMPUTERNAME |

Select-Object -Property ProcessName, Id, CPU, VirtualMemorySize64,

VirtualMemorySize |

Format-Table -Property ProcessName,

    ID, @{

     Label = 'CPU'; Expression = { [int]$_.cpu }},

     @{ Label = 'VM64';

        Expression = {(

         $_.VirtualMemorySize64

         ).Tostring()

         }}

I can even break at the period (or dot). In the following example, I move the method call to the next line. It looks really strange, but Windows PowerShell knows how to interpret it:

Image of command

The best thing is to leave out the line continuation character and simply break at the pipe, the comma, the brace, or the parenthesis because Windows PowerShell is expecting it anyway, and you avoid a potential source of failure.

BG, that is all there is to using Windows PowerShell line continuation. Join me tomorrow when I have a guest post about single versus double quotation marks.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Line Continuation in PowerShell

$
0
0

Summary: Ensure a line continuation in Windows PowerShell.

Hey, Scripting Guy! Question What the line continuation character should use to ensure my Windows PowerShell code
           continues to the next line?

Hey, Scripting Guy! Answer The line continuation character in Windows PowerShell is the backtick.
           Type a <space>, the backtick, and then press ENTER, for example:

Get-Process -Name `
"Powershell_ISE"

Weekend Scripter: Understanding Quotation Marks in PowerShell

$
0
0

Summary: Guest blogger, Don Walker, explores using single and double quotation marks in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome new guest blogger, Don Walker. Don is a coworker of guest blogger, Tim Bolton, and he worked with Tim on a project that Tim blogged about on June 18, 2015, Using PowerShell Grid View for User Info.

Here's Don's contact information:

Twitter: @EngineeringDon
LinkedIn: www.linkedin.com/in/EngineeringDon

A while back, I was asked to explain the difference between using single quotation marks ( ) and double quotation marks
( ) to define a string value, and when to use each. The feedback I received encouraged me to turn my explanation into an article so I could share it with others. My first thought was, “I don’t need to do that. How to define strings is too simple a topic.”

But thinking more about it, it is worth sharing. Understanding the difference between strings defined by single quotation marks or double quotation marks can save a huge amount of time and frustration. The two methods may initially look exactly the same, but the underlying action that occurs is very different.

Windows PowerShell can't recognize a string value unless delimiters (or boundaries) are defined by the user. In Windows PowerShell, single and double quotation marks are used to define the string. Essentially, “here is my start” and “here is my end.”

Here is an example of strings being defined and set to variables:

Image of command output

Notice that in the last example, the string is not contained in quotation marks so Windows PowerShell mistakenly confuses it for a command instead of a string value.

Single quotation marks define a literal string

Literal strings treat each character as a fixed value. What you see is what you get. Here is an example of how a string with single quotation marks ignores the variable character in Windows PowerShell ( $ ) and treats it as a regular dollar sign:

Image of command

Using single quotation marks for basic strings is a best practice because there are no unexpected surprises with literal values. The only surprise that I have found has been attempting to use a single quotation mark character within a string that uses single quotation marks. 

Image of command output

Windows PowerShell has a built-in remedy for this. Place an additional single quotation mark immediately after the first instance, for example:

Image of command

Windows PowerShell also gives you the ability to define literal strings and concatenate them with variable values to form more complex strings, for example:

Image of command

Double quotation marks define more dynamic parsing string

Parsing strings analyze each character within and parse certain characters with a special action, such as recognizing a variable and retrieving its value. This allows placing string values within a delimited string. Here is an example of using double quotation marks in a string to parse the value of a variable ($foo = ‘bar’) and place it within the string’s value:

Image of command

This is the most common way to use double quotation marks in a string. As you get into loops and functions within your scripting, injecting changing variable values into static string output will keep your script’s output consistent. A real-world example of this could be a script that targets multiple servers and returns a string value if a certain process is running:

“${TargetServer} has ${process} running”

In addition to parsing variables, strings defined within double quotation marks also parse the backtick character ( ` ) to define escape characters and literal character values. Escape characters allow special actions to occur when processing the string, such as inserting a Tab space or starting a new line.

If a backtick is used, but it does not call an escape character, the character immediately after it will be a literal value and ignore any parsing rules. I recommend being familiar with the following backtick usage:

  • `n– insert new line
  • `t– insert horizontal tab
  • `$– insert dollar sign instead of parsing a variable value
  • `” – insert double quotation mark character instead of ending string
  • ``– insert a single backtick character

Here is an example of the backtick being used as an escape character:

Image of command

It is up to you as the Windows PowerShell user to decide which quotation mark style works best for your situation, but here are my final thoughts on the subject:

  • Single quotation marks ( ) define a literal string. What you see is what you get.
  • Strings that use single quotation marks are the preferred method.
  • Double quotation marks ( “ ) define a dynamic, parsing string.
  • Strings that use double quotation marks are most commonly used to invoke expressions or remote commands within a script.
  • Strings begin and end with the same type of quotation mark (single or double).
  • You should familiarize yourself with escape characters, if you haven’t already.

~Don

Thanks Don. This is a very helpful post.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Get Computer Name

$
0
0

Summary: Use Windows PowerShell to get your computer name.

Hey, Scripting Guy! Question How can I use Windows PowerShell to easily retrieve my computer name?

Hey, Scripting Guy! Answer Use the environmental variable ComputerName:

$env:computername

Weekend Scripter: Start PowerShell Scripting for Office 365 Sites

$
0
0

Summary: Guest blogger, Michael Blumenthal shows how to findWindows PowerShell cmdlets that are available for Office 365 sites.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a new guest blogger, Michael Blumenthal, who we met in Chicago in May at Microsoft Ignite. Here is a little bit about Michael:

Photo of Michael Blumenthal

Michael Blumenthal is a technical solution evangelist with PSC Group and a Microsoft Office 365 MVP. As such, he is an experienced architect for SharePoint, and business technology advisor. Michael has deep SharePoint expertise, with two decades of development, infrastructure, and administration experience. He’s designed, implemented, and supported intranets, extranets, and Internet sites for clients in markets as diverse as banking, energy, government, and transportation.

Michael holds numerous Microsoft certifications (including most of the SharePoint certifications) and the Certified Associate in Project Management (CAPM) from the Project Management Institute. He can be found year round speaking on technology topics, including SharePoint and Windows PowerShell, at Chicago-area technology events and user groups.

Michael's contact info:

Blog: Michael Blumenthal, Office 365 MVP
Twitter: @michaelbl

You need Windows PowerShell 4.0

I assume you already have Windows PowerShell 4.0 installed. You will want two cmdlet libraries:

First, get the SharePoint Online Management Shell and install it. This will give you...wait for it…

37 SharePoint Online cmdlets.

I counted them this way:

get-command -Noun SPO* | where moduleName -like Microsoft.Online.SharePoint* | measure

Thirty-seven? Yes, only 37! But in SharePoint 2013 on premise, I had hundreds of SharePoint cmdlets available to me. What on earth will I do?

First, let’s look at what these 37 are:

PS C:\Windows\system32> get-command -Noun SPO* | where moduleName -like Microsoft.Online.SharePoint* |
Select-Object Name | Sort-Object Name

Name

----

Add-SPOUser

Connect-SPOService

ConvertTo-SPOMigrationTargetedPackage

Disconnect-SPOService

Get-SPOAppErrors

Get-SPOAppInfo

Get-SPODeletedSite

Get-SPOExternalUser

Get-SPOSite

Get-SPOSiteGroup

Get-SPOTenant

Get-SPOTenantLogEntry

Get-SPOTenantLogLastAvailableTimeInUtc

Get-SPOTenantSyncClientRestriction

Get-SPOUser

Get-SPOWebTemplate

New-SPOMigrationPackage

New-SPOSite

New-SPOSiteGroup

Remove-SPODeletedSite

Remove-SPOExternalUser

Remove-SPOSite

Remove-SPOSiteGroup

Remove-SPOTenantSyncClientRestriction

Remove-SPOUser

Repair-SPOSite

Request-SPOPersonalSite

Request-SPOUpgradeEvaluationSite

Restore-SPODeletedSite

Set-SPOSite

Set-SPOSiteGroup

Set-SPOTenant

Set-SPOTenantSyncClientRestriction

Set-SPOUser

Submit-SPOMigration

Test-SPOSite

Upgrade-SPOSite

What about missing cmdlets?

What? No SPOWeb? Well, let’s get more cmdlets then! Gary LaPointe to the rescue! Go to APTILLON Sharepoint Automation and click Installer: Lapointe.SharePointOnline.PowerShell.msi.

Run that.

Now how many cmdlets do you have?

get-command -Noun SPO* | where moduleName -like Lapointe.SharePointOnline.* | measure

45! Yay! What are they?

PS C:\Windows\system32> get-command -Noun SPO* | where moduleName -like Lapointe.SharePointOnline.* |
Select-Object Name | Sort-Object Name

Name

----

Add-SPOContentType

Add-SPOField

Add-SPOListViewWebPart

Add-SPOSolution

Add-SPOWebPart

Connect-SPOSite

Disable-SPOFeature

Disconnect-SPOSite

Enable-SPOFeature

Export-SPOSearchConfiguration

Export-SPOTaxonomy

Get-SPOContentType

Get-SPOContextSite

Get-SPOEventReceiver

Get-SPOFeature

Get-SPOFile

Get-SPOFolder

Get-SPOList

Get-SPOTerm

Get-SPOTermGroup

Get-SPOTermSet

Get-SPOTermStore

Get-SPOWeb

Get-SPOWebPart

Import-SPOSearchConfiguration

Import-SPOTaxonomy

Install-SPOSolution

Invoke-SPORestMethod

New-SPOContentType

New-SPOField

New-SPOFile

New-SPOList

New-SPOListFolder

New-SPOListItem

New-SPOTermGroup

New-SPOTermSet

New-SPOWeb

New-SPOWikiPage

Remove-SPOContentType

Remove-SPOList

Remove-SPOWeb

Set-SPOList

Set-SPOWeb

Set-SPOWebTheme

Set-SPOWikiPageLayout

Even more SharePoint cmdlets

Ah, that’s more like it! Cmdlets to work with lists and libraries. Now we’re talking!

However, if you want more cmdlets and some examples, take a look at this CodePlex project: Client-side SharePoint PowerShell.

Now that we have the cmdlets in the SharePoint Online Management Console and the cmdlets from Gary LaPointe, we can do things like this:

function global:Get-SPOWebInfo {

<#

.SYNOPSIS

         This demonstrates the use of some cmdlets for Office 365 Sites.

    .DESCRIPTION

         This demonstrates the use of some cmdlets for Office 365 Sites using both Microsoft's and Gary LaPointe's cmdlets for SharePoint Online.

    .PARAMETER TenantAdminURL

        The Admin URL for your O365 Tenant.  For example, if your tenant is https://foo.sharepoint.com, your Admin URL is https://foo-admin.sharepoint.com.

    .PARAMETER  Login

        Your O365 login email.

    .PARAMETER  Password

        Your O365 login password.

    .EXAMPLE

     get-SPOWebInfo -TenantAdminURL foo.sharepoint.com -Login login@somedomain.com -password SomeStrongPasswordGoesHere 

     .OUTPUTS

         Writes out the URL of every web in every site.

    .NOTES

         Author: Michael Blumenthal, PSC Group LLC

#>

Param

          (

            [parameter(Mandatory=$true, HelpMessage="Enter the Admin URL for your O365 tenant.")]

            [Uri]

            $TenantURL,

             [parameter(Mandatory=$true, HelpMessage="Enter your admin login.")]

            [System.String]

            $Login,

            [System.Security.SecureString][Parameter(Mandatory=$true, HelpMessage="Enter your super secret strong password.")]

            $password

          ) 

    $credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $Login, $password

    Connect-SPOService -Url $TenantURL -Credential $credential 

     $SiteCollections = Get-SPOSite -Detailed

     foreach ($SiteCollection in $SiteCollections)

     {

      write-host $SiteCollection.Url -foregroundcolor Blue 

            $rootwebUrl = $SiteCollection.Url

            try {

                Connect-SPOSite -Credential $credential -Url $rootwebURL 

                $allwebs = Get-SPOWeb -Detail 

                #each web is a Microsoft.SharePoint.Client.Web

          foreach ($web in $allwebs)

          { 

                    write-host "In " $web.Title "at" $web.Url           

                }

            }

            Catch [Microsoft.SharePoint.Client.ServerUnauthorizedAccessException] {

                Write-Host -ForegroundColor Yellow "SUAE error."

            }

            Catch [System.Net.WebException] {

                Write-Host -ForegroundColor Yellow "WebException."

                Write-Host -ForegroundColor Yellow $_.exception.Message

            }

            Catch {

                $ErrorMessage = $_.Exception.Message

                $FailedItem = $_.Exception.ItemName

                $ExceptionType = $_.Exception.GetType().FullName;

                Write-host -ForegroundColor Yellow "Other exception: " $ExceptionType

                Write-host -ForegroundColor Yellow Message: $ErrorMessage

                Write-host -ForegroundColor Yellow Failed Item $FailedItem

            }

     }

    Disconnect-SPOService 

}

get-command Get-SPOWebInfo 

 Let's check it out

Now run Get-SPOWebInfo, and give it a try!

Image of command output

~Michael

Thank you, Michael. Great introduction to finding and using SharePoint cmdlet resources.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


PowerTip: Use PowerShell to Generate Tenant Site Report

$
0
0

Summary: Use Windows PowerShell to generate a report about Office 365 tenant sites.

Hey, Scripting Guy! Question How can I use Windows PowerShell to generate a report about my Office 365 tenant sites?

Hey, Scripting Guy! Answer Install the SharePoint Online Management Shell and install the cmdlet library for SharePoint Online
           (provided by MVP, Gary LaPointe). This will give you 82 cmdlets.

Using Windows PowerShell with Staged Migrations: Part 1

$
0
0

Summary: Use Windows PowerShell to add and verify email domains in Office 365.

Honorary Scripting Guy, Sean Kearney, is here. This week we’re going to touch on something that should be useful to many IT professionals who are trying to get themselves on to Office 365—that is a staged migration.

When you are moving to Office 365, a staged migration is the preferable option if you’re starting from an Exchange Server 2007 or 2003 environment. It allows you to build out mailboxes in Office 365 without really needing to interfere will mail flow while the process occurs.

It’s not a perfect process because you still need to re-create permissions and reconnect your users to the remote online environment, but it does allow for “staging.” It offers the opportunity to see what does or does not work after a migration and to move staff in groups because you can move them back and forth pretty seamlessly while this process occurs.

To establish a staged migration, of course you’ll need the standard stuff running for Office 365 such as:

  • Online tenant with a “.onmicrosoft.com” address
  • DirSync (or another directory synchronization tool to populate the accounts remotely)

One of the key things you’ll need to do in your tenant is add the domains that you want to manage and receive email. When you log in to the Office 365 portal, you’ll do this by clicking the Domains tab in the left pane.

Image of menu

Then next screen shows your default domain, which will be something like contoso.onmicrosoft.com. Click + Add domain to begin adding a new domain for receiving email. Even if you don't flip the MX records, this process is needed to create an accepted domain for the Office 365 Exchange system.

Today we’re going to pick on EnergizedTech.com as my sample.

Image of menu

You’ll get a simple little 3-point wizard to help you create the addition in Office 365. Start by clicking Let’s get started. (Yes, thank you, Captain Obvious!)

Image of menu

Enter the name of the domain where you want to receive email. In our example, it will be EnergizedTech.com. Then click Next to continue.

Image of menu

We could automatically sign-in to the Registrar to complete this process if we had the credentials, but in many cases, we’ll be working for clients. So verification can be done with a TXT record added on to the DNS servers. We’ll click here to get the details for the client.

Image of menu

You will receive a screen that indicates the data you can communicate back to your client. This will be a TXT record with a value of MS=xxxxxx (where xxxxxx is some number assigned by Microsoft).

Image of menu

At this point, you would normally communicate the information to the client, and log in to the portal occasionally to check if the status of the domain has been updated from Setup not started, which indicates that the domain has not yet been verified as yours by Microsoft (the TXT record did not get created).

Image of menu

This is where Windows PowerShell comes in. Logging and constantly refreshing a webpage is slow and tedious. If you needed to create multiple domains for the client, and you had to continuously check the status, the process would eat your time.

You’ll need to have the Microsoft Online cmdlets installed to perform this. For a great reference about getting and installing the necessary binaries, see Manage Azure AD using Windows PowerShell.

After you have them installed, authenticate to Office 365 by using the Connect-MSolservice cmdlet. Then use the following line to add a domain. In our example, we are adding the Contoso.com domain to Office 365.

New-MsolDomain –name Contoso.com

Image of command output

You can see the current status is Unverified. By using the Get-MsolDomain cmdlet, you can check the status of a domain as follows:

Get-MsolDomain –DomainName Contoso.com

This will currently echo the same output as the previous cmdlet because nothing has changed. When the change is made on the DNS servers and it cascades through the Internet, you will see the status change to Verified as shown here:

Image of command output

You can also use Windows PowerShell to get the information required for the DNS records. You can use the Get-MsolDomainVerificationDns cmdlet to extract this information:

Get-MsolDomainVerificationDns –DomainName Contoso.com

Image of command output

The object label contains the actual properties for the TXT record. We can use a little magic with Windows Powershell to pull the information and format it for the client.

Why?

With one record I could simply copy the needed information. But what if I am dealing with multiple domains? Would it not be far nicer to get a list from the client and then email the required changes to them?

First we grab the Label property from the object:

(Get-MsolDomainVerificationDns –DomainName Contoso.com).label

Image of command output

Then run a Split method against the period character ( ‘.’ ) to make it into an array:

(Get-MsolDomainVerificationDns –DomainName Contoso.com).label.split(‘.’)

Image of command output

We can then easily access the data we need for the TXT record through the first array element and leverage a substring to pull out the remaining information:

(Get-MsolDomainVerificationDns –DomainName Contoso.com).label.split(‘.’)[0].substring(2)

Image of command output

With some simple Windows PowerShell string magic, we can build this into a line of information to deliver to the client:

“DNS Record TXT, Value MS=$((Get-MsolDomainVerificationDns –DomainName Contoso.com).label.split(‘.’)[0].substring(2))”

If you had a list of domains, we could target all domains that were not verified like this:

Get-MsolDomain –status Unverified

….and then run that against our previous magic to capture a complete list for our client on the Clipboard and to paste it into an email:

Get-MsolDomain –status Unverified | Foreach { “DNS Record TXT,
Value MS=$((Get-MsolDomainVerificationDns –DomainName $_.Name).label.split(‘.’)[0].substring(2))”} | clip

Now you have an easy way in Office 365 to make these changes and track them!

Pop in tomorrow when we look further into our staged migration and learn how Windows PowerShell can make it easier!

I invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then remember eat your cmdlets every day with a taste of creativity.

Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy 

PowerTip: Use PowerShell to List All Domains in Office 365 Subscription

$
0
0

Summary: Use Windows Powershell and the MSOnline cmdlets to identify all Internet domains attached to an Office 365 subscription.

Hey, Scripting Guy! Question How can I use Windows PowerShell to easily audit an Office 365 subscription for domains that are attached to it?

Hey, Scripting Guy! Answer Use the Get-MSolDomain cmdlet, and if you want a list in a CSV file for auditing purposes, add
           the Export parameter, for example:

Get-MsolDomain | Export-CSV o365domains.csv

Using Windows PowerShell with Staged Migrations: Part 2

$
0
0

Summary: Use Windows PowerShell to upload and start staged migrations.

Honorary Scripting Guy, Sean Kearney, is here to get you up and running with staged migrations in Office 365. Yesterday, we added and verified email domains (see Using Windows PowerShell with Staged Migrations: Part 1). Our next step is to actually begin staging user content in Office 365.

There are some prerequisites you’ll need, of course:

  • An Active Directory account with full read and write access to mailboxes within Exchange Server.
  • An endpoint that is the connection in Office 365 to the on-premise Exchange server.
  • Synchronization of data between the on-premise Active Directory and the Office 365 environment.

With these in place, you can perform a staged migration. The staged migration builds content in Office 365 by synchronizing selected user mailboxes via Outlook Anywhere. When the synchronization process is complete, it establishes forwarding email from the on-premise Exchange server to the Office 365 environment.

The actual process of a staged migration is simple:

  • Upload a CSV file with the email addresses of users to migrate to Office 365 and start the migration.
  • Check for the status of the migration in Office 365, identify any errors, and mitigate them.
  • When the migration is complete, convert the on-premise mailboxes to enabled users.
  • License the users and, optionally, change their login IDs.

So your first task is to provide a CSV file. The formatting is quite simple. In our example, this file is for three users who are being staged to Office 365:

EmailAddress,Password,ForceChangePassword

zbeeblebrox@contoso.com,,False

fprefect@contoso.com,,False

adent@contoso.com,,False

If you have DirSync with password sync, or ADFS, the last fields stay as displayed. If you had to supply passwords, you would enclose them in the second field. If you’d like the password to change when the user logs in change the False to True.

Following is an example with a provided password that the user must change after the first login to Office 365:

EmailAddress,Password,ForceChangePassword

tmacmillan@contoso.com,PasswordNew1,True

At this point, if you were logged in to Office 365, you would access your Exchange environment and select Mailboxes under Recipients:

Image of menu

Image of menu

After you click Mailboxes, click the Migration tab. From here you can click the plus sign ( + ) and choose Migrate to Exchange Online:

Image of menu

You would then choose the option for a staged migration:

Image of menu

On the next screen, browse to the CSV file you created earlier. As you can see in our example, it has identified that you are going to migrate three mailboxes:

Image of menu

The next few screens that appear allow you to verify the internal name of your Exchange server and the Outlook Anywhere endpoint. Finally, you will be asked if you want to start the staged migration automatically or manually.

As you can see, the process is not actually difficult. There are, however, no scheduling options within the Office 365 interface if you set it up manually.

The good news is that we can use Windows Powershell to reproduce all of this! We can use the Office 365 cmdlets for managing the remote migrations, and we can use the New-MigrationBatch cmdlet.

For a staged migration, we capture the currently available migration endpoint in Office 365 with the Get-Migration endpoint cmdlet:

$MigrationEndpoint=Get-MigrationEndpoint

Then we capture the CSV file in binary format for the cmdlet:

$MigrationBatch=’C:\Migration\HSG-Staging-1.csv’

$CSVData=[System.io.file]::Readallbytes($MigrationBatch)

$Batchname=’StageBatch1’

New-MigrationBatch –name $Batchname –sourcendpoint $MigrationEndpoint.identity –csvdata $CSVData

After the migration batch is created, you can access it by using the Start-MigrationBatch cmdlet:

Start-MigrationBatch –identity ‘StageBatch1’

Or you could write this into a Windows PowerShell script to schedule it. You could (if you had access to an Azure Automation instance) even schedule it and other batches to launch at the times you prefer.

But how do we tell what is the result? How do we know what did or did not happen? How do we know how long it took?

...that is a story for tomorrow.

I invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then remember eat your cmdlets every day with a taste of creativity.

Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy

PowerTip: Use PowerShell to Get List of Migration Batches in Office 365

$
0
0

Summary: Use Windows PowerShell to identify all migration batches and their status in Office 365.

Hey, Scripting Guy! Question Rather than navigating the Office 365 console to check the status of a migration, is there an easy way
           to do it with Windows PowerShell?

Hey, Scripting Guy! Answer To get list of all migration batches in Office 365 and their status, simply run Get-MigrationBatch.

Viewing all 3333 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>