How to Undo That Thing You Now Wish You Had Never Done in the First Place

Although we can't guarantee this, we can pretty much guarantee this: once you've switched to Microsoft Lync Server 2010 you'll never have nightmares again. Or, at the very least, you'll never have nightmares about Microsoft Lync Server again. Ever.

Note. A strong statement? Perhaps. On the other hand, Sigmund Freud is universally acknowledged as history's foremost authority on dream studies and dream interpretation. Before writing this article, we carefully read everything Freud ever wrote. Do you know how many references to Lync Server nightmares we found in the collected works of Sigmund Freud? That's right: zero.

Of course, even as you're sitting back reading this article you can probably think of at least one or two potential Lync Server nightmare scenarios. For example, consider this. Your organization has 105 users who have been enabled for Lync Server. In turn, those users have been assigned one of 3 different client policies; the breakdown of client policy assignments looks something like this:

Policy

No. of Users Assigned This Policy

ClientPolicy1

21

ClientPolicy2

13

ClientPolicy3

71

Note. What's that? You say you'd like to able to generate a table similar to this? No problem:

Get-CsUser | Group-Object ClientPolicy | Select-Object Name Count | Format-Table -Autosize

But we digress. We also need to note that there's a reason why a user has been assigned a particular policy. Actually, there could be a lot of reasons why a user has been assigned a particular policy. Maybe he or she belongs to a specified security group, maybe he or she has a specific job title, maybe her or she works in a specified department and in a specific city. For our purposes, it doesn't really matter why a user has been assigned a specific policy. What does matter is that the whole thing is complicated, and you can't simply assume that all the users in a given department have the same client policy. It just doesn't work that way.

With us so far? OK, let's now assume that there's been a change in management, and your new manager has decided that it's silly to have all these different client policies: he wants one policy to be assigned to all users.

Note. Yes, typically we would say "he or she wants …." For some reason, though, any time a crazy decision like this gets made we assume it must be a he and not a she.

So is the nightmare scenario the fact that you have to assign the same client policy to all your Lync Server-enabled users? Nope; that's not a nightmare scenario, not by a long shot. In fact, all that takes is one little PowerShell command:

Get-CsUser | Grant-CsClientPolicy –PolicyName "LitwareincClientPolicy"

Instead, here's the nightmare scenario: no sooner do you make this change then your help desk is deluged with complaints. As it turns out, one policy doesn't work in your organization: different users need different capabilities, and now, by taking those capabilities away, you're preventing people from being able to do their jobs. At that point, your manager turns to you and says, "Well, there's only one thing we can do now: you'll just have to put things back the way they used to be. You need to reassign each person the client policy they had before we assigned them all the LitwareincClientPolicy policy."

So is that going to be a problem? No; that's going to be a nightmare. For better or worse, Lync Server doesn't have any sort of undo feature when it comes to most administrative tasks; for example, there's no way to undo the bulk assignment of policies. Furthermore, Lync Server doesn't keep a history of any kind: you can' just thumb through a list of all the client policies that have ever been assigned to a user and then restore one of those policies. Granted, if you've backed up Active Directory (which is where user data is stored) you could theoretically restore the overwritten data; however, in that case all the other changes made to Active Directory since the time you assigned the LitwareincClientPolicy policy would be lost.

Uh-oh.

So doesn't this mean that you should start having nightmares about Microsoft Lync Server 2010? Believe it or not, the answer is no. Admittedly, Lync Server doesn't have a built-in undo function for administrative tasks. However, if you're willing to write a couple of simple Windows PowerShell scripts, you can create your own undo function, at least for many of the things you'll do in Lync Server.

Like, say, bulk assignment of client policies. In the scenario we just described, the problem isn't that we did bulk assignment of client policies; instead, the problem is that, if anything went wrong with that bulk assignment there was no easy way to put things back the way they used to be. Why is there no easy way to put things back the way they used to be? You got it: because there's no easy way of knowing how things used to be. That's why we decided to assign client policies using the following script:

$backupData = (Get-CsUser | Select-Object Identity,ClientPolicy)

$backupData | Export-Csv –Path "C:\Backup\ClientPolicies.csv" –NoTypeInformation

foreach ($user in $backupData)

    {Grant-CsClientPolicy –Identity $user.Identity –PolicyName "LitwareincClientPolicy"

    }

Nice, huh? Except for one thing: what does this script actually do? Well, in the first line, we use the Get-CsUser cmdlet to retrieve information about all the users who have been enabled for Lync Server. We then pipe that data to the Select-Object cmdlet, which grabs the values for just the Identity and the ClientPolicy properties. (Why just those two property values? Because those are the only two property values we care about.) That filtered data – Identity and ClientPolicy – is then stored in a variable named $backupData.

In the second line, we then take the variable $backupData and pipe it to the Export-Csv cmdlet, which creates a comma-separated values file at C:\Backup\ClientPolicies.csv. That file is going to look something like this:

"Identity","ClientPolicy"

"CN=adelaney,OU=Redmond,DC=litwareinc,DC=com","ClientPolicy1"

"CN=aglagdkikh,OU=Redmond,DC=litwareinc,DC=com","ClientPolicy3"

"CN=aolecka,OU=Redmond,DC=litwareinc,DC=com","ClientPolicy2"

Etc., etc. In other words, it's the Identity of each of our Lync Enabled-users along with the name of the client policy that's currently assigned to them.

Note. Good question: what is the NoTypeInformation parameter for? Well, without it, Export-Csv would give us a text file that looked like this:

#TYPE System.Management.Automation.PSCustomObject

"Identity","ClientPolicy"

"CN=adelaney,OU=Redmond,DC=litwareinc,DC=com","ClientPolicy1"

"CN=aglagdkikh,OU=Redmond,DC=litwareinc,DC=com","ClientPolicy3"

"CN=aolecka,OU=Redmond,DC=litwareinc,DC=com","ClientPolicy2"

We don't have any use for that first line of type-related data, so we tacked on the NoTypeInformation parameter to prevent Export-Csv from saving that line of data in the first place.

When Export-Csv finishes running, we'll have a text file that contains the names of all our users and the name of the client policy (if any) that was assigned to each one: that is, a snapshot of our client policy picture before we did the bulk update. And speaking of the bulk update, that's what we do in the final block of code:

foreach ($user in $backupData)

    {Grant-CsClientPolicy –Identity $user.Identity –PolicyName "LitwareincClientPolicy"

    }

Nothing fancy there: we've just set up a foreach loop to loop through each user account stored in $backupData and assign the client policy LitwareincClientPolicy to each account. As soon as we're done with that our client policy breakdown will look like this:

Policy

No. of Users Assigned This Policy

LitwareincClientPolicy

105

Where are our other per-user client policies, like ClientPolicy1, ClientPolicy2, and ClientPolicy3? That's easy: they don't show up in the report because they are no longer assigned to anyone. Per our marching orders, all of our users have been assigned LitwareincClientPolicy instead.

OK, so suppose that was a mistake, suppose we now wish we hadn't assigned everyone the same client policy after all? That's OK; it's definitely nothing to lose sleep over. Don't forget that, before we did our bulk update, we took a snapshot of the way things used to be. Assuming we didn't throw away ClientPolicies.csv (hint: don't throw away ClientPolicies.csv) we can undo our bulk policy assignment simply by running this script:

$backupData = Import-Csv –Path "C:\Backup\ClientPolicies.csv"

foreach ($user in $backupData)

    {Grant-CsClientPolicy –Identity $user.Identity –PolicyName $user.ClientPolicy

    }

So what exactly does this script do? Well, in the first line it uses the Import-Csv cmdlet to read in all that data we stored in ClientPolicies.csv. After the data has been retrieved (and stored in our old friend, the variable $backupData) we then set up a foreach loop to iterate through all that data. Inside the loop we do one thing and one thing only: we assign each user the client policy that he or she used to have assigned to them. That's what this line of code does:

Grant-CsClientPolicy –Identity $user.Identity –PolicyName $user.ClientPolicy

If you're wondering how this works, $user.Identity represents the Identity of the user account and $user.ClientPolicy represents the client policy that this user used to have assigned to them. For example, suppose the first line of user account data in ClientPolicies.csv looks like this:

"CN=adelaney,OU=Redmond,DC=litwareinc,DC=com","ClientPolicy1"

In that case, $user.Identity will be equal to CN=adelaney,OU=Redmond,DC=litwareinc,DC=com and $user.ClientPolicy will be equal to ClientPolicy1. Which also means that the user adelaney is going to be reassigned ClientPolicy1. That's because we'll actually be running this line of code:

Grant-CsClientPolicy –Identity "CN=adelaney,OU=Redmond,DC=litwareinc,DC=com" –PolicyName "ClientPolicy1"

As soon as the script finishes, everything will be as it once was:

Policy

No. of Users Assigned This Policy

ClientPolicy1

21

ClientPolicy2

13

ClientPolicy3

71

Pleasant dreams, eh?

Of course, maybe you don't need to reassign client policies for all of your users; maybe the only people who can't use LitwareincClientPolicy are those people who used to be assigned ClientPolicy1. That's fine; it's easy enough to modify the script so it only reassigns policies to a subset of users (that is, users who used to be assigned ClientPolicy1):

$backupData = Import-Csv –Path "C:\Backup\ClientPolicies.csv"

foreach ($user in $backupData)

    {if ($user.ClientPolicy –eq "ClientPolicy1")

        {Grant-CsClientPolicy –Identity $user.Identity –PolicyName $user.ClientPolicy}

      }

In this modified script, the first thing we do inside the loop is check to see if our user used to be assigned the client policy ClientPolicy1:

if ($user.ClientPolicy –eq "ClientPolicy1")

If that turns out to be true, then we reassign them their old policy. If it's not true (for example, if they used to be assigned ClientPolicy2) then we don't do anything at all.

Pretty cool, huh?

So does this mean you now have a sure-fire way to undo anything you might do in Lync Server? No. Instead, what you have is a way to undo a very specific task: bulk assignment of client policies (something which you might never do anyway). On the other hand, you also have an idea of how you can use a simple script to provide a little peace-of-mind before you undertake a major operation; you could easily modify this script to back up other types of policy data, to back up user information data, to back up client version policy rules, etc., etc. In other words, not only will this approach prevent nightmares, but it might also inspire you to dream up new ways to backup and restore Lync Server information. After all, as George Carlin once said:

"Some people see things that are and ask, 'Why?' Some people dream of things that never were and ask, 'Why not?' Some people have to go to work and don't have time for all that."

Which reminds us: we need to get back to work right now. If you have questions about these scripts, or how you might be able to modify them to fit your own needs, just email us: cspshell@microsoft.com