• April Wrap-Up…and Haiku #100

    The old saying is “March comes in like a lion, goes out like a lamb.” Well, that’s one ferocious lamb because April has been roaring away here in Redmond. Another saying is “April showers bring May flowers.” If that one’s true you’ll be able to spot the Seattle area from space – it’s that very colorful spot over there on the west coast of North America.

     

    Has anyone ever counted the number of words Seattleites have for “rain?” Showers, mist, drizzle, …. Does anyone really know what the difference is between rain and showers?

     

    Well, it’s been raining (or showering) haikus here at the Lync Server PowerShell blog. (How’s that for a segue?) And tomorrow, Friday, April 29, 2011, marks the publishing of the 100th Lync Server PowerShell Haiku. To celebrate we’re going to…well, we’re going to publish the 100th Lync Server PowerShell haiku.

     

    Do we know how to party here or what?

     

    And, in case you missed anything, here’s what else happened in April:

     

    No Conferencing Policy is an Island: Per-Organizer Settings vs. Per-User Settings

    Export to Excel

    How to Export Lync Contacts to Excel

    Export Lync Contacts to Excel (script only)

    Simultaneously Assigning a Policy to Multiple Users

    How to Cheat at Sporcle

     

    And of course the One of These is Not Like the Others challenge is still going on. Plus, we didn’t get to 100 without doing to first 99: find all the haiku’s in the Haiku Archive.

     

    There’s more coming in May, so stay tuned.

     

    Thanks for joining us. We’re going to go watch all those flowers come up.

     

  • Haiku #89

    If only I could

    Recall all the words you wrote.

    Export archiving.

     

    As you probably know, Microsoft Lync Server 2010 enables you to archive all the instant messaging sessions that your users take part in. (Have we talked about that in a previous haiku? Well, now that you mention it, we believe we have. And on the Anniversary of the Oil Expropriation Day, no less!)

     

    Note. We're going to keep things simple today and just talk about instant messaging sessions. However, you can also archive transcripts of your Web conferencing sessions.

     

    Of course, that's all great, except for one thing: now that we have transcripts of all those instant messaging sessions safely tucked away in an archive, how do we actually get those transcripts out of the archive so we can do something with them?

     

    Well, one way you could do this is to open up the archiving database (LcsLog) and try to extract the data for yourself. In theory, that would work. In practice, well, there are 20 or so different database tables to deal with, and users are not identified by their display name or SIP address but by a numeric code (e.g., 1) assigned by the system, and – well, there simply has to be a better way to do this, right? Surprisingly enough, however, there isn't.

     

    No, hey, just kidding. As it turns out, there is a better way to extract instant messaging transcripts from the archiving database: all you have to do is use the aptly-named Export-CsArchivingData cmdlet.

     

    Had you worried there for a second, didn't we?

     

    Export-CsArchivingData is an interesting little cmdlet. What you do is this: you tell Export-CsArchivingData what transcripts you want to export and then you export them.

     

    Ah, good question: what choices do you have when it comes to exporting transcripts? Well, there are primarily two options available to you. First, you need to specify the time period for those transcripts. For example, suppose you want to see all the transcripts for the month of March, 2011. In that case, you'd want to see transcripts for all the sessions that occurred in, well, March, 2011. That would mean sessions that had a start date on or after March 1, 2011, and an end date on or before March 31, 2011. Make sense? Of course it does.

     

    In addition to that, you can limit your exports to sessions that involved a specific user. For example, say you only want to look at the transcripts involving Ken Myer. That's fine; just include the UserUri parameter followed by Ken Myer's SIP address (just be sure you leave off the sip: prefix). When you do that, you'll get only transcripts for sessions that Ken Myer participated in.

     

    You know what? Maybe it would be better if we just showed you a sample command or two. OK. Here's a command that retrieves all the instant messaging sessions that took place in March, 2011:

     

    Export-CsArchivingData –DBInstance atl-sql-001.litwareinc.com\Archinst –StartDate "3/1/2011" –EndDate "3/31/2011" –OutputFolder "C:\Logs"

     

    Pretty easy, huh? Basically we just need to include the DBInstance parameter and specify the location of the Archiving database we want to pull records from, and then the StartDate and EndDate parameters. Oh, and don't forget the OutputFolder, which specifies the folder where the archived data should be stashed.

     

    If you want just the transcripts that pertain to Ken Myer, then tack on the UserUri parameter as well:

     

    Export-CsArchivingData –DBInstance atl-sql-001.litwareinc.com\Archinst –StartDate "3/1/2011" –EndDate "3/31/2011" –OutputFolder "C:\Logs" –UserUri "kenmyer@litwareinc.com"

     

    As we noted earlier, you can configure your system to archive Web conferencing transcripts as well as instant messaging transcripts. If you only want to export the instant messaging transcripts then just add the ExcludeWebConfArchive parameter, like so:

     

    Export-CsArchivingData –DBInstance atl-sql-001.litwareinc.com\Archinst –StartDate "3/1/2011" –EndDate "3/31/2011" –OutputFolder "C:\Logs" -ExcludeWebConfArchive

     

    Man, this is almost as much fun as the Anniversary of the Oil Expropriation Day!

     

    So what does it actually mean to "export" archiving data? Well, when you run the Export-CsArchivingData cmdlet, you'll create a subfolder in the output folder, a subfolder named after the database instance. In our sample, that means we end up with a folder path that looks like this:

     

    C:\Logs\atl-sql-001.litwareinc.com_Archinst

     

    Inside that subfolder you'll have another subfolder, one based on today's date. Because today is April 14, 2011, that meant we ended up with the following subfolder:

     

    C:\Logs\atl-sql-001.litwareinc.com_Archinst\20110414

     

    If you're starting to worry that this is like those little Russian nesting dolls, where every time you open one doll you find another doll inside, well, don't worry: it's not quite that bad. Fortunately, there aren't any subfolders inside this last subfolder; instead, we simply have a bunch of files with names like these:

     

    201104141531008083_1_p2p.eml

    201104141531001730_1_p2p.eml

     

    Note. In case you're wondering, those names are based on the date and time that the individual transcript was exported (we think) and the fact that there were peer-to-peer instant messaging sessions (that's what the p2p is for).

     

    If you have no idea what a .eml file is, well, don't feel bad: we had no idea what a .eml file was either. As it turns out, however, archived transcripts are exported as Outlook Express Electronic Mail files. We have to admit that we were a little skeptical about that, but guess what? When we double-clicked one of those files, they opened up just fine in Outlook Express:

     

     

     

    Is that kind of a hassle? Well, yes, as a matter of fact it is. But, as it turns out, all you have to do is change the .eml file extension to a .mht file extension and you can then open these files in Internet Explorer:

     

     

    Seemed a little easier to deal with than Outlook Express.

     

    Actually, Notepad was our first thought, too. Unfortunately, though, it didn't work. The files opened just fine, but the transcript portion was encoded:

     

     

    Unfortunately, our MIME base64 skills are a little rusty, so we couldn't just look at that message and decode it on the fly. So we went back to using Internet Explorer.

     

    That's pretty much all there is to it. It's not the most sophisticated little exporting system we've ever seen, but it works.

     

    Which is more than can be said about some of us.

     

    Speaking of which, isn't it break time yet ….

     

     

     

  • Haiku #82

    To AV or not

    AV? That's a question for

    Test AV conference.

     

    This past weekend the author of today's haiku was flipping through the TV listings when he noticed at least two shows that were obviously ripping off the plot to the classic movie It's a Wonderful Life.

     

    Note. That's the one problem with doing something truly great: everyone immediately rips off your idea and then proceeds to do a vastly inferior version of whatever it was you already did. Fortunately, the authors of the Lync Server PowerShell blog have figured out a way to combat this intellectual piracy: we start with a vastly inferior version, and dare anyone to try to do worse. Outside of this blog, have you ever seen anyone write a Lync Server PowerShell haiku? See: the system works!

     

    At any rate, the whole idea behind It's a Wonderful Life is this: George Bailey (Jimmy Stewart) thinks the world would be better off if he'd never been born, and an angel comes down from heaven to show him just exactly what the world would be like if he had never been born. So did that get us to wondering what the world would be like if Windows PowerShell had never been added to Lync Server? No, not really. After all, we know what the world would be like: for one thing, we'd actually have to work for a living, rather than just sit around and write haikus all day. That was too depressing to even think about.

     

    Instead, we got to wondering "What would the world be like if the Test-CsAVConference cmdlet had never been created?" Now, you might be thinking, "I doubt that would make much difference; after all, I've never even heard of the Test-CsAVConference cmdlet." But stop and think about that for a moment: if there was no such thing as the Test-CsAVConference cmdlet then how could you verify that you could carry out audio/video conferences in your organization without having to organize and conduct a full-fledged conference? If you there was no such thing as the Test-CsAVConference cmdlet how could you troubleshoot situations where user A can't seem to hold a conference that includes user B? If there was no such thing as the Test-CsAVConference cmdlet then how could you run a command like this one:

     

    Test-CsAVConference –TargetFqdn atl-cs-001.litwareinc.com

     

    Note. OK, OK, relax. Remember, this is all hypothetical: the Test-CsAVConference cmdlet really does exist. Take a deep breath, and we'll try not to panic you anymore.

     

    We can see that while everyone is truly grateful that the Test-CsAVConference cmdlet really does exist, many of you might still be a little puzzled as to what the thing actually does, and why it's so cool that we can run a command like the one we just showed you.

     

    The Test-CsAVConference cmdlet is one of the "synthetic transaction" cmdlets included in Microsoft Lync Server 2010. A synthetic transaction is exactly what the name implies: it's an artificial transaction, a way to ensure that the system can perform some task without actually performing that task. (In this case, verifying that Lync Server can hold an audio/visual conference without actually holding that conference.) In order to use the Test-CsAVConference cmdlet you need to have a pair of user accounts to employ as part of the test. In the command we just showed you, you might have noticed that we didn't specify any user accounts; all we did was include the fully qualified domain name (FQDN) of the pool we wanted to test. Why didn't we include any user accounts? Well, that command assumes that we have used the CsHealthMonitoringConfiguration cmdlets to create a pair of test user accounts for the pool atl-cs-001.litwareinc.com. (If you're not sure what that means, take a look at one of our previous haikus.) What if we haven't created these test accounts, or what if we're troubleshooting a problem and we'd like to use two real user accounts when running this cmdlet? That's fine: you just need to create a pair of Windows PowerShell credential objects before running the test. In other words, you need a set of commands like these:

     

    $cred1 = Get-Credential "litwareinc\pilar"

    $cred2 = Get-Credential "litwareinc\kenmyer"

     

    Test-CsAVConference -TargetFqdn atl-cs-001.litwareinc.com -SenderSipAddress "sip:pilar@litwareinc.com" -SenderCredential $cred1 -ReceiverSipAddress "sip:kenmyer@litwareinc.com" -ReceiverCredential $cred2

     

    Note. One important thing to keep in mind here: if you want to use a pair of actual user accounts when running the Test-CsAVConference cmdlet that means you have to supply both the user name (in the format domain_name\user_name) and the password for each account. In this case, that means you need to know the password for both litwareinc\pilar and litwareinc\kenmyer if you want this command to work.

     

    And what if you don't want that command to work? Well, we hadn't really considered that possibility, but we suppose that, in that case, it probably doesn't matter much, does it?

     

    So what actually happens when you run Test-CsAVConference? Well, the first thing the cmdlet does is try to log the two users on to Lync Server. If that succeeds, the Test-CsAVConference cmdlet then has the first user create an A/V conference, then waits to see if the second user is able to join that conference. After a brief exchange of data, the conference is deleted and the two tests users are logged off.

     

    Incidentally, no actual conference takes place: you won't see Microsoft Lync popping up onscreen, and you won't see any audio or video flitting across the network. Instead, the cmdlet simply verifies that the two users can make the connections necessary to carry out an A/V conference.

     

    And here's a handy little tip: any time you run Test-CsAVConference (or any of the synthetic transaction cmdlets) it's a good idea to tack on the Verbose parameter:

     

    Test-CsAVConference –TargetFqdn atl-cs-001.litwareinc.com -Verbose

     

    Why? Well, the Verbose parameter causes Test-CsAVConference to display a running account of everything it does while conducting a test. For example, without the Verbose parameter we ran a test and got back these results:

     

    TargetFqdn : atl-cs-001.litwareinc.com

    Result     : Failure

    Latency    : 00:00:00

    Error      : 504, Server time-out

    Diagnosis  : ErrorCode=1008,Source=accessproxy.vdomain.com,Reason=Unable to

                 resolve DNS SRV record

                 Microsoft.Rtc.Signaling.DiagnosticHeader

     

    That sort of implies that we're having a DNS problem. However, take a look at some of the detailed information we got back when we added the Verbose parameter:

     

    VERBOSE: 'Register' activity started.

    Sending Registration request:

     Target Fqdn      = pool0.vdomain.com

     User Sip Address = sip:vt1_user1@VTenant1

     Registrar Port = 5061.

    Auth Type 'Trusted' is selected.

    'Register' activity started.

    Sending Registration request:

     Target Fqdn      = pool0.vdomain.com

     User Sip Address = sip:vt1_user2@VTenant1

     Registrar Port = 5061.

    Auth Type 'Trusted' is selected.

    An exception 'The endpoint was unable to register. See the ErrorCode for specific reason.' occurred during Workflow Microsoft.Rtc.SyntheticTransactions.Workflows.STPresenceWorkflow execution.

     

    Take a look at the text in red: The endpoint was unable to register. As it turned out, we had specified a test account that didn't actually exist. When we replaced that bogus account with a real user account (that is, an account that can actually log on to Lync Server), we got these results:

     

    TargetFqdn : atl-cs-001.litwareinc.com

    Result     : Success

    Latency    : 00:00:00

    Error      :

    Diagnosis  :

     

    To be honest, we probably never would have figured that out without the additional information provided by the Verbose parameter.

     

    So there you have it: that's why it's so important that the Test-CsAVConference was created. See you tomorrow,

     

    Note. Is it true that every time you run the Test-CsAVConference cmdlet an angel gets its wings? Well, we think so: that was in the original spec, but we aren't 100% sure if it actually made it into the finished cmdlet or not. We'll get back to you on that.

     

  • Export to Excel

    One of the great things about Windows PowerShell is that you can manage practically your entire system without ever leaving the command line. (Yes, we know, not everyone thinks spending all their time at the command prompt is a great thing, but obviously these few people haven’t spent enough time working with PowerShell yet.) With Windows PowerShell you can type your commands at the command prompt and voilà, your output spews out into the command window right before your eyes. There’s nothing like some instant gratification to brighten up your day.

     

    All right, we’ll admit, there is a small downside to this. Sometimes the output goes spewing by so fast you can’t read it. And if there’s a lot of it, you might not even be able to scroll up and see all of that output. And here’s another problem with this type of output: suppose you want to show it to someone else? Do you call them into your office to take a look at your screen? This is a little inconvenient, especially if they work in a different location. (Plus you might forget to close down your Mahjong game before they see it on your screen.) You also might want to refer to the output later. There are actually a lot of reasons why you might want the output from your command to go somewhere other than the command window.

     

    One of these somewheres might be a Microsoft Excel spreadsheet. If you export to Excel, you can then save the spreadsheet, sort and re-sort the data, format it, and, well, do all the things that you usually do with data in an Excel spreadsheet. And, you can send the spreadsheet to a coworker and continue on with your game of Mahjong.

     

    Exporting output to Excel from PowerShell is actually pretty simple. Here’s a script that retrieves all the processes running on your computer, then exports the process name and ID to Excel.

     

    $objExcel = New-Object -ComObject Excel.Application

     

    $wb = $objExcel.Workbooks.Add()

    $item = $wb.Worksheets.Item(1)

     

    $objExcel.Visible = $True

     

    $procs = Get-Process

     

    $i = 1

     

    foreach ($p in $procs)

    {

        $item.Cells.Item($i,1) = $p.Name

        $item.Cells.Item($i,2) = $p.ID

       

        $i++

    }

     

    $wb.SaveAs("C:\Scripts\Processes.xlsx")

     

    # Close Excel and clean up

    $objExcel.Quit()

     

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($item) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objExcel) | Out-Null

    [System.GC]::Collect()

    [System.GC]::WaitForPendingFinalizers()

     

    Remove-Variable objExcel

    Remove-Variable wb

    Remove-Variable item

     

    If you just look at everything before the comment (# Close Excel and clean up) it doesn’t look too difficult, and it isn’t. We’ll worry about that cleanup stuff at the end.

     

    To start with, the first thing we do in this script is open Excel. We do this by creating a new Excel.Application object:

     

    $objExcel = New-Object -ComObject Excel.Application

     

    This gives us a running instance of Excel. By default, when you open Excel from the Start menu (or by running the Excel.exe application file) Excel opens with a workbook containing three worksheets. However, when you start Excel by creating an Excel.Application object, all you get is an empty instance of Excel; there are no workbooks or worksheets open. That means that before we do anything else we need to create a workbook and open a worksheet in that workbook. We do that like this:

     

    $wb = $objExcel.Workbooks.Add()

    $item = $wb.Worksheets.Item(1)

     

    The first line calls the Add method of the Workbooks collection to add a workbook to Excel. You might think that, in the next line, we could call the Add method of the Worksheets collection to add a worksheet. That makes sense, but what makes sense doesn’t always work (a fact we’re very familiar with). The reason this doesn’t work is because adding the workbook automatically adds the worksheets as well. So all we need to do is use the Item property of the Worksheets collection (with the index 1) to grab hold of the first worksheet in the workbook.

     

    Now, at this point Excel is running and it has an open workbook and worksheets. We can start exporting our data to Excel, save the worksheet, and do whatever else we need to do. But without this next line, you’ll never actually see Excel on your screen:

     

    $objExcel.Visible = $True

     

    Without this line everything you do with Excel will happen in memory and you won’t ever see that Excel is running. This can actually be a very good thing; the script will run faster without the display, and sometimes there really isn’t any need to see what’s happening. But for this example we want to see that data really is being written to the spreadsheet, so we set the Visible property to True to display Excel on the screen.

     

    Now it’s time to retrieve our processes. You’re probably familiar with the Get-Process cmdlet, which retrieves a list of all the running processes on the computer. We call Get-Process and save the output to the variable $procs:

     

    $procs = Get-Process

     

    After setting a counter variable ($i) we then enter a foreach loop to loop through our collection of processes:

     

    foreach ($p in $procs)

     

    The foreach loop is going to go through each process one at a time. Inside the loop we’re going to put the name and ID of each process into our worksheet. We do that with these two lines of code:

     

        $item.Cells.Item($i,1) = $p.Name

        $item.Cells.Item($i,2) = $p.ID

     

    Remember that the $item variable holds the worksheet we’re writing to, Sheet 1. We reference the cells of the worksheet with the Cells property. To specify exactly which cell we want to work with we use the Item property, passing it the row and column of the cell. In line 1 we pass ($i, 1), which means row $i (the value of our counter, which we initially set to 1) and column 1. We assign that cell the value of the Name property of our current process ($p.Name). In the second line we assign the value of the process’s ID property to cell $i, 2; the first time through the loop this is the cell in row 1, column 2.

     

    We then add one to our counter (that’s what $i++ means: add 1 to the value in $i), and loop back around to put the next process into row 2. The counter then gets incremented again (for a value of 3) and we do the same thing with the next process, and so on.

     

    Depending on how fast your computer is and how many processes you have running, you can probably watch the rows and columns filling in as the script runs. When all the processes have been added to the worksheet the foreach loop ends. At this point we’re ready to save. We do that by calling the workbook’s SaveAs method:

     

    $wb.SaveAs("C:\Scripts\Processes.xlsx")

     

    We pass the SaveAs method the full path and filename where we want to save the Excel file. We now have an Excel file saved to our computer that contains a list of all the processes running on our computer.

     

    Note. If the file exists, Excel will prompt you to confirm that you want to overwrite the existing file. To have the script overwrite an existing file without prompting, add the following line to your script:

     

    $objExcel.DisplayAlerts = $False

     

    Then change your SaveAs call to look like this:

     

    $wb.SaveAs("C:\Scripts\Processes.xlsx", 1)

     

    We could end the script here and leave you to manually close Excel. (Keep in mind, however, that if you didn’t set the Visible property to True you wouldn’t be able to do this. You’d either have to stop the excel.exe process, or you’d have to run this next block of code.) In this case, we’re not going to require you to manually close Excel, we’re going to let the script close it for us.

     

    $objExcel.Quit()

     

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($item) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objExcel) | Out-Null

    [System.GC]::Collect()

    [System.GC]::WaitForPendingFinalizers()

     

    Remove-Variable objExcel

    Remove-Variable wb

    Remove-Variable item

     

    Yes, this looks pretty complicated. And if you dig way down into the details of how it all works, it actually is pretty complicated. But we’re not going to do that. Why not? Because we don’t actually need to. (No, really, we’re not just being lazy this time.) Instead, we’re going to give you the high-level look at all this, then tell you to just be sure to stick this at the end of your script and you’ll be fine. And you will.

     

    So let’s start with the first line:

     

    $objExcel.Quit()

     

    This line is pretty simple. We call the Quit method of the Excel object to shut down Excel. You might think at this point that we should be done: we shut down Excel, what else do we need to do? Well, there is one more thing: we have to clean up after ourselves.

     

    Now, we could go into a lot of details about managed code and unmanaged code, handles and events and the inner workings of the .NET Framework. But as we said, there’s no need to do that here. All we’re doing in the rest of this script is getting rid of some things that are still hanging around using up memory on our computer. As it turns out, shutting down Excel doesn’t actually get rid of the object we created or even stop the Excel.exe process that we started on the computer. As far as our computer is concerned, the Excel process is still running. In order to get rid of it completely, we need to do three things: release the Excel objects we created, collect the garbage, and wait for the finalizers to finish.

     

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($item) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objExcel) | Out-Null

    [System.GC]::Collect()

    [System.GC]::WaitForPendingFinalizers()

     

    The first three lines release our workbook object ($wb), our worksheet object ($item), and our Excel object ($objExcel). By “releasing” the objects we’re telling.NET, which keeps track of all the objects we’re using, that we’re not using them anymore.

     

    Note. The ReleaseComObject method returns an integer value indicating the success or failure of the method call. When the method runs successfully the value returned is 0. In this script we call ReleaseComObject three times, once for each Excel object, meaning we get three zeroes returned, which happen to get displayed to the command window. We decided we didn’t need to see three zeroes every time we ran the script, so we piped the output of ReleaseComObject to the Out-Null cmdlet, which suppresses the display. You can leave out the call to Out-Null if you want to see the three zeroes in your window after your script runs.

     

    After the objects are released we call the Collect method. This method does what’s called garbage collection. Garbage collection refers to freeing up memory that’s no longer in use. Normally Windows and .NET are very good at cleaning up memory, but sometimes they need a little help. In this case we need to call Collect to tell .NET to free up the memory from the objects we just told it we’re not using anymore.

     

    Finally, we call the WaitForPendingFinalizers method. This is another part of garbage collection. Basically it just makes our script sit and wait and not do anything else until .NET has finished cleaning up the memory.

     

    The very last thing we do in our script is get rid of the variables we were using to refer to our Excel objects:

     

    Remove-Variable objExcel

    Remove-Variable wb

    Remove-Variable item

     

    You don’t have to do this part, it won’t cause any problems with performance or memory if you don’t. Calling Remove-Variable just deletes the variable and is good scripting practice; it makes sure you don’t inadvertently try to use those variables again now that we’ve gotten rid of the objects associated with them.

     

    That’s probably more than you wanted to know about .NET and garbage collection. But like we said at the beginning, the only thing you really need to know is that anytime you use a script like this one to open Excel from within Windows PowerShell, you need to include all that cleanup stuff after you close it if you don’t want extra processes running silently in the background on your computer.

     

    Note. We wrote an article a few years ago where all we needed to include at the end of a script like this were these two lines:

     

    $objExcel.Quit()

     

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null

     

    That worked at the time. Of course, back then we were running Windows PowerShell 1.0 and Excel 2003. We’re now on Windows PowerShell 2.0 and Excel 2010, and that doesn’t work anymore. Now you need the whole thing:

     

    $objExcel.Quit()

     

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($item) | Out-Null

    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objExcel) | Out-Null

    [System.GC]::Collect()

    [System.GC]::WaitForPendingFinalizers()

     

    A little more coding, but just copy and paste every time you need it; copying six lines isn’t much more trouble than copying two lines.

     

     

     

    And, finally, that’s it. There are a lot of other things you can do with Excel from a PowerShell script, like formatting and sorting and so on, but in this case we are too lazy – er, busy – to get into that today.

     

  • Haiku #93

    How can we turn that

    Frown upside down? Try running

    Test Registration.

     

    Hello, and welcome to the 93rd edition of the Lync Server PowerShell daily haiku. Today we had planned on talking about the other Lync Server PowerShell blog author and her propensity for being grouchy all the time. [Note from that other author: Not all the time.] However, we can't do that: after all, she has turned over a new leaf, and has taken a vow to remain upbeat and positive from now on. So much for that plan.

     

    Note. How long do we expect that new attitude to last? Let's put it this way: we're already working on tomorrow's haiku.

     

    In the meantime, we still have to write today's haiku, and so we decided to focus on the fact that today is the 93rd daily haiku in our series of Lync Server PowerShell daily haikus. (Yes, we know: times does fly when you're having fun, doesn't it?) As it turns out, the number 93 is very significant and has a lot of deep meaning; for example, Wikipedia notes that the number 93 is "… the natural number following 92 and preceding 94." See what we mean? Not only that, but the aliquot sum of 93 is "… 35 within the aliquot sequence (93,35,13,1,0) 93 being the fourth composite number in the 13-aliquot tree"!

     

    Hey, would we kid you about something like that?

     

    Note. What do you mean "What's an aliquot sum?" OK, on the off-chance that there are a few people out there who don't know what an aliquot sum is (yes, you should be embarrassed), an aliquot sum is …hang on a second here … oh, it's the sum of all the divisors of a number. (What else would it be?) 93 can be divided by 1, 3, and 31; add those together and you get an aliquot sum of 35:

     

    1 + 3 + 31 = 35

     

    This is interesting because … well, just because, OK?

     

    All right, let's see, what else we can talk about? Well, 93 is the 47th odd number; that's pretty exciting. Oh, and 93 is the product of the 2nd and the 11th prime numbers: 3 x 31 = 93! And – ah, never mind. To tell you the truth, these haikus were a lot easier to write back in the days when the other blog author was grouchy all the time.

     

    On the other hand, now that she has vowed to be upbeat and positive we should help her out by discussing something that's really upbeat and positive: the Test-CsRegistration cmdlet. What's so great about Test-CsRegistration? Well, for one thing, the aliquot sum of Test-CsRegistration is 35 within the aliquot sequence (93,35,13,1,0), 93 being the fourth composite number in the 13-aliquot tree. Perhaps more important than that (hey, we said perhaps), Test-CsRegistration is an example of a synthetic transaction cmdlet that enables you to verify that users can log on to Microsoft Lync Server 2010.

     

    Is that useful? You bet it is. For example, let's assume you've created some health monitoring test accounts for a Registrar pool; if you aren't sure what health monitoring test accounts are, see this daily haiku for more information.

     

    Note. Does Microsoft know that any time we want to point people to more information we point them to some crazy haiku rather than some official product documentation? Apparently not.

     

    At any rate, if you have set up health monitoring test accounts for a pool you can periodically run a command like this one to verify that users can log on to that pool:

     

    Test-CsRegistration -TargetFqdn atl-cs-001.litwareinc.com

     

    It really is that simple. In this case, Test-CsRegistration will attempt to sign on to Lync Server using one of the test accounts and then, if successful, will attempt to log off from the system. If all goes well, you'll get back a report similar to this:

     

    TargetFqdn : atl-cs-001.litwareinc.com

    Result     : Success

    Latency    : 00:00:02.4850725

    Error      :

    Diagnosis  :

     

    And what if things don't go well? Then you'll get back a report like this:

     

    TargetFqdn : atl-cs-001.litwareinc.com

    Result     : Failure

    Latency    : 00:00:00

    Error      : 404, Not Found

    Diagnosis  : ErrorCode=4005,Source=atl-cs-001.litwareinc.com,Reason=Destination URI either not enabled for SIP or does not exist

     

    In this case, we got the error because we tried using a test account that doesn't actually exist.

     

    Speaking of which, here's a quick tip. If you do get an error, you might want to rerun the command using the Verbose parameter:

     

    Test-CsRegistration -TargetFqdn atl-cs-001.litwareinc.com -Verbose

     

    When you do that, Test-CsRegistration will give you a step-by-step account of everything it's trying to do. In our case, that included the following report:

     

    User Sip Address = sip:tester@litwareinc.com

    Registrar Port = 5061.

    Auth Type 'IWA' is selected.

    An exception 'The endpoint is unable to register. See the ErrorCode for specific reason.' occurred.

     

    That tells us that the problem was with the account sip:tester@litwareinc.com.

     

    Test-CsRegistration can also be run under the credentials of an actual user account (as opposed to the credentials of a test user). Suppose Ken Myer calls and says he can't log on to Lync Server. If you know Ken's SIP address, and if you know Ken's password, you can use a pair of commands like this to test Ken's ability to log on to Lync Server:

     

    $cred1 = Get-Credential "litwareinc\kenmyer"

     

    Test-CsRegistration -TargetFqdn atl-cs-001.litwareinc.com -UserCredential $cred1 -UserSipAddress "sip:kenmyer@litwareinc.com" -Verbose

     

    Note that we added the Verbose parameter when we called Test-CsRegistration. That way, if the command does fail and Ken is unable to logon, we should get some pretty good insight into why he is unable to logon.

     

    And there you have it: Test-CsRegistration. Is it a cmdlet we should have written about a long time ago? Probably; after all, it's a useful cmdlet to know about, and, had we written about it months ago, the other blog author wouldn't have had any reason to be grouchy. Oh, well, better late than never, right? 93!

     

    Note. For followers of Thelema, 93 means "Do what thou wilt shall be the whole of the Law." That's calculated by – well, we'll let you do the calculations yourself. In the meantime, we're going to start up an office pool to see how long it is before the other blog author stops being upbeat and positive. We're betting that will be right about the time she starts to read today's haiku.

     

    Although, come to think of it, that seems to happen to her every time she reads one of the daily haikus. Must be a coincidence.