PowerShell: Get the Windows Azure Certificate

If you’re automating Windows Azure using Windows PowerShell, one of the first things you’ll probably notice is that you need a management certificate to connect to the Windows Azure subscription that you’re attempting to view or modify. Management certificates are associated to a Windows Azure subscription inside the Management Portal, under the Hosted Services, Storage Accounts & CDN –> Management Certificates section (see screenshot).

Windows Azure - Management CertificatesOnce you have associated a management certificate with a subscription, it needs to be imported into your local computer’s certificate store so that it can be used from PowerShell to manage the Azure subscription. To do this, follow these directions:

  1. Open MMC
  2. Add the certificates snap-in (for current user or local computer)
  3. Navigate to the Personal –> Certificates “folder”
  4. Right-click the Certificates node and select All Tasks –> Import
  5. Select the file on the filesystem that contains the certificate
  6. Select the Personal certificate store if necessary and finish the import wizard
Next, you’ll need to grab the certificate’s thumbprint, which is basically a unique identifier that differentiates it from other certificates. To grab this, follow these steps:
  1. Double-click the certificate in the Certificates MMC snap-in
  2. Visit the Details tab
  3. Scroll down to the Thumbprint field and copy the value into Metapad or equivalent

Now that you’ve taken down the thumbprint of the certificate, you can use PowerShell to retrieve the certificate from the current user’s certificate store. If the certificate resides in the local computer’s certificate store, you’ll have to replace “CurrentUser” with “LocalMachine.” To get the certificate, check out the code below.

1
2
$AzureCertThumbprint = '4DAE6C3F444F21972B0823467C229605';
$AzureCert = Get-Item -Path cert:\CurrentUser\My\$AzureCertThumbprint;

The $AzureCert variable now holds a reference to the management certificate, and you can now use this to manage your Windows Azure subscription!

PowerShell: Twitter Folks

Hey folks, here are some of the top tweeters on the topic of PowerShell! All of them come with my strong recommendation to follow them! You will be in good company, and will probably learn a LOT, if you keep in touch with these ridiculously smart folks.

Jeffrey Snover – https://twitter.com/jsnover

Don Jones – https://twitter.com/concentrateddon

Jon Walz – https://twitter.com/jonwalz

Hal Rottenbeg – https://twitter.com/halr9000

Doug Finke – https://twitter.com/dfinke

Boe Prox – https://twitter.com/proxb

Adam Driscoll – https://twitter.com/adamdriscoll

Ravikanth Chaganti – https://twitter.com/ravikanth
Continue reading

PowerShell: Backup documents on Dropbox securely with encryption

Like many other people out there, you’re probably looking for a way to backup your documents regularly, reliably, and securely on some sort of central storage service. Dropbox is a great option for this, since they provide a fair amount of storage for free, and their annual cost for additional storage is pretty fair. Unfortunately, there have been wide reports of privacy concerns around Dropbox’s ability to release information to the government, which are concerns that I also share. That’s alright though, because as we’ll see, we can still use the service to securely backup our files.

Advertisement: Please take a moment to check out Arvixe PersonalClassASP web hosting!

Since we cannot confidently store our files on Dropbox in plain sight, we should ensure that they are encrypted before they’re transferred to the backup service. There are built-in capabilities to encrypt files in Windows, but I prefer the security and performance of the popular, open-source 7-Zip application. 7-Zip is an ideal tool for compressing and encrypting documents, because it is fast, uses secure AES-256 encryption, and offers the option to encrypt the compressed file headers. The last option I mentioned is important to  prevent people from reading the file names inside the archive; by default, 7-Zip only encrypts the document data, and not the document metadata.

7-Zip is only part of the equation, however. We need to schedule these backup jobs to run on a regular schedule so that we are constantly getting up-to-date backups. Additionally, we may desire to add multiple folder paths to add to a single zip file, which isn’t easily done using 7-Zip by itself. This is where Microsoft Windows PowerShell comes into play. We can use PowerShell to make multiple calls to the 7-Zip executable, which is otherwise very command line friendly, and add files from multiple folders to the same aggregate (eg. daily) archive file.

I recently wrote a PowerShell script that will assist with creating automated backups of my information, and wanted to share it with the community. This script not only performs a daily backup of the specified folders (using the -Path parameter), but it also downloads and installs 7-Zip if the system it’s being executed on doesn’t have it installed already.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
function Backup-FolderToDropbox {
    [CmdletBinding()]
    param (
          [string] $Password
        , [string[]] $Path
        , [string] $BackupFolder
        , [switch] $Force
    )

    Set-PSDebug -Strict;
    # REQUIRES PowerShell version 3.0 CTP2 or later

    # Define source and backup folder
    $BackupName = '{0}Documents.7z' -f (Get-Date -Format 'yyyy-MM-dd');
    Remove-Item -Path "$env:TEMP\$BackupName" -Force:$Force -ErrorAction SilentlyContinue;

    # Create a new System.Net.WebClient object
    $WebClient = New-Object -TypeName System.Net.WebClient;
    # Define URL to download 7-Zip if it's unavailable
    $7ZipUrl = 'http://downloads.sourceforge.net/sevenzip/7z920-x64.msi';
    $7ZipDownloadPath = "$env:TEMP\7z920-x64.msi";
    $7ZipInstalled = $true;

    try {
        # Get path to 7-Zip executable
        $7ZipPath = Resolve-Path `
            -Path ((Get-Item -Path HKLM:\SOFTWARE\7-Zip -ErrorAction SilentlyContinue).GetValue("Path") + '\7z.exe');
        if (!$7ZipPath) {
            $7ZipInstalled = $false;
        }
    }
    catch {
        $7ZipInstalled = $false;
    }

    # Download and install 7-Zip if it's not installed
    if (!$7ZipInstalled) {
        # Download the 7-Zip installer
        $WebClient.DownloadFile($7ZipUrl, $7ZipDownloadPath);

        # Install 7-Zip package
        Start-Process -Wait `
            -FilePath msiexec.exe `
            -ArgumentList ('/package "{0}" /quiet /l*v "{1}"' -f $7ZipDownloadPath, "$env:TEMP\7-Zip Install.log");

        # Remove the downloaded installer file
        Remove-Item -Path $7ZipDownloadPath -Force -ErrorAction SilentlyContinue;
    }

    foreach ($FolderPath in $Path) {
        # Define command line arguments for 7-Zip
        $Arguments = 'a -y -p{0} -t7z -mhe=on -mtc=on "{1}" "{2}"' -f $Password, $BackupName, $FolderPath;
        # For debugging: Write-Host -Object $Arguments;
        Start-Process `
            -RedirectStandardOutput "$env:TEMP\7-Zip Output.log" `
            -WorkingDirectory $env:TEMP `
            -NoNewWindow `
            -Wait `
            -FilePath $7ZipPath.Path `
            -ArgumentList $Arguments;
    };

    # Copy backup file to Dropbox folder
    Copy-Item -Path ('{0}\{1}' -f $env:TEMP, $BackupName) -Destination $BackupFolder -Force:$Force;
}

Clear-Host;
Backup-FolderToDropbox -Password foobar -Path c:\MyDocs -BackupFolder c:\Dropbox\Backups -Force;

Copy Filenames to Clipboard with PowerShell

Have you ever wanted to copy a large amount of data to the Windows clipboard, but haven’t known quite how to do it? How about this scenario: you have a folder full of files, and you want to get a list of the files’ full names / paths. That’s pretty easy to do with a simple Get-ChildItem PowerShell command, but how do you then get the data into say, an e-mail, or paste it into a file? Sometimes it’s just easiest to have the data copied to the Windows clipboard for a more arbitrary usage.

This simple PowerShell script will take a folder path and copy all of the file paths to the Windows clipboard.

Important: The Windows clipboard requires that the application be running in Single-Threaded Apartment (STA) mode, so if you’re using PowerShell v2, please be sure to run PowerShell with the –STA parameter. If you use Multi-Threaded Apartment (MTA), the [Clipboard]::SetText() static method will fail.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function Copy-FileNamesToClipboard {
    # The CmdletBinding() attribute
    [CmdletBinding()]

    # Define parameters for the PowerShell v2 advanced function
    param (
        # Define a mandatory parameter called Path, which will be the path to gather files from
        [Parameter(Mandatory = $true)]
        [string] $Path
    )

    process {
        # Load the Presentation Core assembly, which contains the System.Windows.Clipboard class
        [void][Reflection.Assembly]::LoadWithPartialName("PresentationCore");

        # Initialize the text variable to an empty string
        $text = [string]::Empty;

        # Get a list of files in the specified directory
        $FileList = Get-ChildItem -Path $Path;

        # For each file in the directory, add the file path and a new line to $text
        foreach ($File in $FileList) {
            $text += ("{0}`n" -f $File.FullName);
        };

        # Set the Clipboard text to the string we just got
        [System.Windows.Clipboard]::SetText($text);
    };
};

Copy-FileNamesToClipboard -Path c:\Users\Trevor\Documents\;

PowerShell: Prompt Function to Monitor Memory Usage

Have you ever wanted to monitor your memory utilization in a PowerShell instance, but may not want to continually issue commands to determine it? Introducing …… the PowerShell Prompt to monitor memory utilization!!

function prompt {
    "$('{0:n2}' -f ([double](Get-Process -Id $pid).WorkingSet/1MB)) MB> "
}

Here’s the result:

image

PowerShell: Finding Friday the 13th

Update (2012-01-13): Justin Dearing (aka @zippy1981) informed me that it would be more efficient to look at the 13th of each month, and test if it was a Friday. In theory at least, he’s absolutely correct; I wrote the function the first way I thought of it, and I always welcome suggested improvements.

This morning I noticed that it was Friday the 13th. No, I didn’t realize it by looking at the Windows 7 system clock. I realized it because I had the worst morning waking up for the past month. I started wondering when the next Friday the 13th would be, and how often they occurred. To satisfy my curiosity, I immediately thought to write a PowerShell advanced function to find them! This was also partially inspired by Jeff Hicks’ posting 13 PowerShell scriptblocks for Friday the 13th.

There are two parameters to this function:

  • StartDate (default to "today")
  • EndDate (default to "today" +1460 days, which is roughly 4 years in the future)

You can call this function using neither parameter, one of them, or both of them. Both parameters are [System.DateTime] structs, and PowerShell will automatically try to parse any string value passed into them. Here is an example:

Find-Friday13th -StartDate 2000-01-01 -EndDate 2005-01-01

And here is the function!

<#
    .Synopsis
    This function finds Friday the 13ths.

    .Parameter StartDate
    The date you want to begin searching for Friday the 13ths.
    .Parameter EndDate
    The end date you want to search for Friday the 13ths.

    .Outputs
    [System.DateTime] objects that represent Friday the 13ths.

    .Notes
    Written by Trevor Sullivan (pcgeek86@gmail.com) on Friday, January 13, 2012.
#>
function Find-Friday13th {
    [CmdletBinding()]
    param (
        [DateTime] $EndDate = ((Get-Date) + ([TimeSpan]::FromDays(1460)))
        , [DateTime] $StartDate = (Get-Date)
    )

    # Inform user that the $EndDate parameter value must be greater than the $StartDate parameter value
    if ($EndDate -lt $StartDate) {
        Write-Error -Message 'The EndDate must be greater than the StartDate!';
        return;
    }

    # Get the next Friday after $StartDate
    while ($StartDate.DayOfWeek -ne 'Friday') {
        Write-Host "Finding next Friday";
        $StartDate = $StartDate.Add([TimeSpan]::FromDays(1));
    }

    # While $StartDate is less than $EndDate, add 7 days
    while ($StartDate -lt $EndDate) {
        # If the Day # is 13, then write the [DateTime] object to the pipeline
        if ($StartDate.Day -eq 13) {
            Write-Output -InputObject $StartDate;
        }
        # Add 7 days to $StartDate (next Friday after current)
        $StartDate = $StartDate.Add([TimeSpan]::FromDays(7));
    }
}

# Call the function
Find-Friday13th -EndDate 2017-12-31

 

Here’s what the function’s output looks like. The objects returned to the pipeline are all [System.DateTime] objects, which are automatically being ToString()’d.

image

PowerShell: Move ConfigMgr Collections

Introduction

If you work with Microsoft System Center Configuration Manager (SCCM / ConfigMgr) 2007 in any capacity, you probably are familiar with the concept of "collections" and how painful they can be to work with sometimes. The ConfigMgr console does not provide any method of moving a collection from one parent to another, and the GUI is pretty slow to work with.

image

So what’s the solution here? PowerShell, of course!

PowerShell Code

Here is a PowerShell function that will allow you to move a ConfigMgr collection either by name or by collection ID.

Note: Select all of the function text top-to-bottom, and you can retrieve the text that is cut off towards the right.

<#
    .Synopsis
    This function allows you to re-assing the parent for a ConfigMgr collection to a new collection ID

    .Author
    Trevor Sullivan (pcgeek86@gmail.com)

    .Example
    c:PS> Move-SccmCollection -SccmServer sccm01 -SiteCode LAB -CollectionID LAB00159 -ParentCollectionID LAB000150;

    Description
    -----------

    This command moves the ConfigMgr collection with ID "LAB000159" to being a child of collection ID "LAB000150".

    .Example
    c:PS> Move-SccmCollection -SccmServer sccm01 -SiteCode LAB -CollectionName 'Visual Studio' -ParentCollectionID Microsoft;

    Description
    -----------

    This command moves the ConfigMgr collection named "Visual Studio" to being a child of the collection named "Microsoft". Note that you do not need to specify quotes around the parameter value if it does not contain spaces.

    .Notes
    This function is untested with collection links. It is not known whether or not this will remove existing collection links.
#>
function Move-SccmCollection {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)] [string] ${SccmServer}
        , [Parameter(Mandatory = $true)] [string] ${SiteCode}
        , [Parameter(ParameterSetName = "ByCollectionID", Mandatory = $true)] [string] ${CollectionID}
        , [Parameter(ParameterSetName = "ByCollectionID", Mandatory = $true)] [string] ${ParentCollectionID}
        , [Parameter(ParameterSetName = "ByCollectionName", Mandatory = $true)] [string] ${CollectionName}
        , [Parameter(ParameterSetName = "ByCollectionName", Mandatory = $true)] [string] ${ParentCollectionName}
    )

    # Set-PSDebug -Strict;

    # Ensure that ConfigMgr site server is available
    if (-not (Test-Connection -ComputerName $SccmServer -Count 1)) {
        return;
    }

    # Obtain references to collection and parent collection
    switch ($PSCmdlet.ParameterSetName) {
        # Use the "ByCollectionID" PowerShell parameter set to retrieve collection references by ID
        'ByCollectionID' {
            ${CollectionRelationship} = @(Get-WmiObject -ComputerName $SccmServer -Namespace rootsmssite_$SiteCode -Class SMS_CollectToSubCollect -Filter "subCollectionID = '$CollectionID'")[0];
            ${Collection} = @([wmi]("\{0}rootsmssite_{1}:SMS_Collection.CollectionID='{2}'" -f ${SccmServer}, ${SiteCode}, ${CollectionID}))[0];
            ${ParentCollection} = @([wmi]("\{0}rootsmssite_{1}:SMS_Collection.CollectionID='{2}'" -f ${SccmServer}, ${SiteCode}, ${ParentCollectionID}))[0];
        }
        # Use the "ByCollectionName" PowerShell parameter set to retrieve collection references by name
        'ByCollectionName' {
            ${Collection} = [wmi](@(Get-WmiObject -ComputerName $SccmServer -Namespace rootsmssite_$SiteCode -Class SMS_Collection -Filter ("Name = '{0}'" -f ${CollectionName}))[0].__PATH);
            ${ParentCollection} = [wmi](@(Get-WmiObject -ComputerName $SccmServer -Namespace rootsmssite_$SiteCode -Class SMS_Collection -Filter ("Name = '{0}'" -f ${ParentCollectionName}))[0].__PATH);
            ${CollectionRelationship} = @(Get-WmiObject -ComputerName $SccmServer -Namespace rootsmssite_$SiteCode -Class SMS_CollectToSubCollect -Filter ("subCollectionID = '{0}'" -f ${Collection}.CollectionID))[0];
        }
    } 
    
    # If references to both the child and [new] parent collection were obtained, then move on
    if (${Collection} -and ${ParentCollection}) {
        Write-Verbose -Message ('Setting parent collection for {0}:{1} to {2}:{3}' -f `
            ${Collection}.CollectionID `
            , ${Collection}.Name `
            , ${ParentCollection}.CollectionID `
            , ${ParentCollection}.Name);
        ${CollectionRelationship}.parentCollectionID = ${ParentCollection}.CollectionID;
        # Create the new collection relationship (this [oddly] spawns a NEW instance of SMS_CollectToSubCollect), so we have to clean up the original one
        ${CollectionRelationship}.Put();

        # Clean up all other collection relantionships for this collection
        ${OldCollectionRelationshipList} = @(Get-WmiObject -ComputerName $SccmServer -Namespace rootsmssite_$SiteCode -Class SMS_CollectToSubCollect -Filter ("subCollectionID = '{0}' and parentCollectionID <> '{1}'" -f ${Collection}.CollectionID, ${ParentCollection}.CollectionID));
        foreach (${OldCollectionRelationship} in ${OldCollectionRelationshipList}) {
            ${OldCollectionRelationship}.Delete();
        }
    }
    else {
        Write-Warning -Message 'Please ensure that you have entered a valid collection ID or name';
    }
}

 

Here is an example of how to use this function to move a collection based on their collection IDs:

Move-SccmCollection -SccmServer sccm01.mybiz.loc -SiteCode LAB -CollectionID LAB00011 -ParentCollectionID LAB00022;

Here is an example of how to use the function to move a collection based on the collection name:

Move-SccmCollection -SccmServer sccm01.mybiz.loc -SiteCode LAB -CollectionName ‘Visual Studio’ -ParentCollectionID Microsoft;

PowerShell: PowerEvents Module Update to 0.3 Alpha

imageIf you haven’t already checked it out, I wrote and published a PowerShell module on CodePlex a little over a year ago. It’s called PowerEvents for Windows PowerShell, and allows you to easily work with permanent WMI (Windows Management Instrumentation) event subscriptions. Some folks may not be aware that I’ve also written comprehensive documentation on the theory behind WMI events and why they’re useful. This ~30-page PDF document is included in the PowerEvents download, and is useful even if you do not want to use the PowerEvents module.

As a bonus, the PowerEvents module was mentioned just recently in the PowerScripting Podcast (listen around 1h19m)!!

http://powerscripting.wordpress.com/2012/01/09/episode-171-listener-call-in/

Listen to my interview with the PowerScripting Podcast back in December 2010!

http://powerscripting.wordpress.com/2010/12/13/episode-134-trevor-sullivan-on-wmi-events-in-powershell/

PowerEvents Download Link: http://powerevents.codeplex.com/

PowerShell: Report / Check the Size of ConfigMgr Task Sequences

Introduction

In Microsoft System Center Configuration Manager 2007 operating system deployment (OSD), there is a limitation of 4MB for task sequence XML data. This is discussed in a couple of locations:

The Technet document linked to above says the following:

Extremely large task sequences can exceed the 4-MB limit for the task sequence file size. If this limit is exceeded, an error is generated.

Solution: To check the task sequence file size, export the task sequence to a known location and check the size of the resulting .xml file.

Basically, the Technet troubleshooting article is suggesting that you would need to go into the ConfigMgr console, right-click a task sequence, export it to a XML file, and then pull up the file properties. That’s fine for one-off troubleshooting, but what if you had 1000 task sequences and needed to know how large all of them were? Read on to find out how!

Continue reading

PowerShell: List Strongly Typed Names in Global Assembly Cache

I dislike using deprecated commands or APIs when I know that there’s a more modern method of performing an action. I also generally prefer to use Windows PowerShell as a .NET scripting language, rather than constantly relying on cmdlets. To be sure, I use a balance of both concepts, since cmdlets can save a whole lot of coding a lot of the time.

Every time I want to load an assembly into PowerShell, the first thing that pops into my mind is using the LoadWithPartialName() method:

1
[Reflection.Assembly]::LoadWithPartialName();

Unfortunately Microsoft recommends against using that static method, as it is deprecated, and recommends use of other methods such as LoadFile() or Load() like:

1
[Reflection.Assembly]::Load(StronglyTypedAssemblyName);

In the interest of not breaking my conscience, I would like to use this method, but the problem then becomes that I have to constantly figure out what the strongly-typed name of the assembly I want is. To help solve this problem, I decided to write a PowerShell script that extracts information from the .NET assemblies in the Global Assembly Cache (GAC), since those are generally the most common ones I’ll need to reference.

Continue reading