Getting Rid of Policies You Thought You Already Got Rid Of

You know how, in life, there are things that you know you really really shouldn't do and yet, for some reason, you go ahead and do them anyway? And then you almost immediately slap yourself on the forehead and say, "Oh man, why did I do that?" Well, with that in mind, we — what's that? You say that's never happened to you? You've never done something and then almost immediately regretted doing it? Wow; that's impressive. And interesting, too, seeing as how that very phenomenon is happening to us right now. (As in, "Oh man, why did we ever start this article out like that?")

OK, if you've never actually done something and then later come to regret it, just use your imagination and pretend something like that could happen to you. How could something like that happen to you? Well, here's one way. Let's pretend you've created a per-user external access policy (named RedmondAccessPolicy) and you've assigned that policy to the user Ken Myer. You've now decided that you don't really need that policy after all. With that in mind, you try to delete the policy by running a Windows PowerShell command like this:

Remove-CsExternalAccessPolicy –Policy RedmondAccessPolicy

Seems straightforward enough, doesn't it? However, when you press ENTER the policy isn't immediately removed. Instead, you get back a confirmation prompt similar to this:

The policy "RedmondAccessPolicy" is currently assigned to one or more users. Assign a different policy to the users before removing this one.

The policy is in use.

[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):

At this point you should press N for No, meaning that you don't want to delete the policy after all. After canceling the Remove command, you should unassign RedmondAccessPolicy from any users currently assigned the policy, and then go ahead and remove the policy.

That's what you should do. On the other hand, there are all sorts of things that we should do, but never actually do. (For example, it is estimated that 80% of all home exercise equipment never gets used.) Pressed for time, and not wanting to figure out how to unassign policies that have been assigned to users, you instead press Y and delete the policy. In other words, you deleted a per-user policy that's currently assigned to one or more users. Big deal; what could be the harm in that, right?

The answer to that question depends on your definition of "harm." Admittedly, nothing catastrophic is going to happen: Lync Server will keep chugging away without any problem, and all your users will be able to log on just fine, including users who had been assigned RedmondAccessPolicy.

But while nothing truly harmful will result from deleting a policy that is currently assigned to at least one user, something that can be a bit of a nuisance will happen. Suppose you now decide to use the Get-CsUser cmdlet to return the display names of all your users. That means running a command similar to this:

Get-CsUser | Select-Object DisplayName

Here's what you'll get back when you run that command:

DisplayName

-----------

Pilar Ackerman

Patrick Hines

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:kenmyer@litwareincn.com" has been removed from configuration store.

Ken Myer

Ann Reagan

Our call to Get-CsUser is designed to retrieve information about all our user accounts that have been enabled for Lync Server, including the information pertaining to Ken Myer, the user who had been assigned the policy RedmondAccessPolicy. And, as you can see, as soon as Get-CsUser runs into Ken Myer's account it … helpfully … reminds us that the external access policy previously assigned to Ken Myer has been deleted:

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:kenmyer@litwareincn.com" has been removed from configuration store.

Not a big deal but, then again, most of us don't need to run the Get-CsUser cmdlet in order to be nagged.

After all, that's what spouses are for.

And what if RedmondAccessPolicy has been assigned to all our users? Well, in that case, our output would look like this:

DisplayName

-----------

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:pilar@litwareincn.com" has been removed from configuration store.

Pilar Ackerman

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:pathines@litwareincn.com" has been removed from configuration store.

Patrick Hines

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:kenmyer@litwareincn.com" has been removed from configuration store.

Ken Myer

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:annreagan@litwareincn.com" has been removed from configuration store.

Ann Reagan

Granted, it's not the end of the world, but it's not exactly the kind of output we'd like to see, either.

Note. If this is the kind of output that you'd like to see, well, so be it. Beauty is in the eye of the beholder and all that, you know.

Before we go any further, we should note that there is an easy way to suppress those warnings, although we don't recommend that you go that route. All you need to do is set the value of PowerShell's $WarningPreference variable to SilentlyContinue:

$WarningPreference = "SilentlyContinue"

Do that and the warnings will disappear:

DisplayName

-----------

Pilar Ackerman

Patrick Hines

Ken Myer

Ann Reagan

So why don’t we recommend that you just suppress these warnings? Two reasons. First, $WarningPreference is a global setting: it's going to suppress all the warnings PowerShell wants to give you. That might be OK in this instance. However, there might be some warning messages that you really do need to see. But those are going to be suppressed, too.

Second, while you've managed to get Lync Server PowerShell to stop nagging you, you haven't truly solved the problem. For example, suppose you run this command:

Get-CsUser "Ken Myer"

Here's what you'll get back:

Identity : CN=Ken Myer,CN=Users,DC=litware,DC=com

VoicePolicy :

ConferencingPolicy : RedmondConferencingPolicy

PresencePolicy :

DialPlan :

LocationPolicy :

ClientPolicy :

ClientVersionPolicy :

ArchivingPolicy :

PinPolicy :

ExternalAccessPolicy : 14

HostedVoiceMail :

HostedVoicemailPolicy :

HostingProvider : SRV:

RegistrarPool : pool0.litwareinc.com

Enabled : True

SipAddress : sip:kenmyer@litwareinc.com

LineURI :

EnterpriseVoiceEnabled : False

HomeServer : CN=Lc Services,CN=Microsoft,CN=co1:2,CN=Pools,CN=RTC Service,CN=Services,CN=Configuration,DC=litwareinc,DC=com

DisplayName : Ken Myer

SamAccountName : kenmyer

Notice that, according to Get-CsUser, Ken Myer has been assigned the external access policy named 14. That's … interesting …. If Get-CsUser told us that Ken Myer was still assigned the policy RedmondAccessPolicy, well, we could understand that. (We wouldn't be happy about it, but we could understand it.) But what's the "14" supposed to mean? And why is it showing up in our output?

To explain all that, let’s start by re-examining that warning message we were so quick to suppress:

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:kenmyer@litwareincn.com" has been removed from configuration store.

 

As you can see, the warning tells us that an external access policy with the Identity "14" has been removed from the system. Is that just a coincidence? After all, didn't the policy we delete have the identity RedmondAccessPolicy?

Well, yes, it did. However, that policy also had an "Anchor" value of 14. Suppose, before we deleted RedmondAccessPolicy, we had run this command:

Get-CsExternalAccessPolicy | Select-Object Anchor

If we had run that command, we'd have gotten back something that look like this:

Anchor

------

(14) RedmondAccessPolicy

The value (14, the number inside the parentheses) is the Anchor value for the property: a unique numeric value assigned to each per-user policy you create. Anchor values are automatically handed out by Lync Server at the time you create the policy, and are handed out in consecutive fashion: RedmondAccessPolicy got an Anchor value of 14 because it was the 14th per-user external access policy we created. These numbers are unique (among policy types, that is), they are read-only, and they never get re-used: even though we've deleted the external access policy with the Anchor value 14, that value will never, ever be assigned to one of the per-user external access policies, not even if we delete all of our per-user policies and start over again from scratch.

Cross our hearts: Anchor values don't get re-used.

As an administrator, you don't have to worry about Anchors; that's why they don't show up by default when you look at the property values of a policy. But, under the covers, Lync Server uses these Anchor values to keep track of policies. As we're about to see, the Anchor value is the value that actually gets stored in Active Directory. When you run Get-CsUser, the cmdlet typically looks at the Anchor value for the external access policy, then queries the Central Management Store to get the name (Identity) associated with that value. In turn, Get-CsUser displays the Identity on screen rather than the Anchor value. However, policy 14 no longer exists, which means that Get-CsUser can't find an Identity to display. As a result, it displays the only information it has: the Anchor value.

Note. Well, that and the warning message, which really says, "I tried to find this policy, but it looks like you deleted it, even though I told you not delete it. But did you listen to me …."

As we sort of implied a moment ago, policy information is actually stored in two different places. Information about the policy itself (that is, the policy Identity and all the policy property values) is stored in the Central Management Store. However, the Central Management Store does not keep a list of all the users who have been assigned a given policy. Instead, that information is stored in Active Directory, as part of the individual user accounts. If you want a list of all the users who have been assigned a given policy, Get-CsUser is going to query Active Directory for that information.

Let's show you exactly what we're talking about. As it turns out, we just happen to have a user (Ken Myer) who has been assigned several per-user policies. With that in mind, let's open up Active Directory Users and Computers and turn on the advanced features (click View and then click Advanced Features). Let's right-click Ken Myer's user account, click Properties, and then click the Attribute Editor tab. If we scroll down a ways, we'll eventually see something similar to this:

And if we zoom in on the msRTCSIP-UserPolicies attribute we should see something like this:

What do you think of that?!?

OK, you're not quite as impressed as we thought you'd be. Maybe we should explain exactly what it is we're looking at. What we have here are paired values that represent the policies that have been assigned to this user account. The paired value 0=500016779 indicates the pool policy that has been assigned to the user; in other words, this tells the system which Registrar pool the user account has been homed on. (The value 500016779 is an internal identifier that Lync Server assigned to the pool at the time the pool was created.) The other paired values in this example are as follows:

· 1=12 – Indicates that the user has been assigned a conferencing policy (the value 1) that has an anchor value of 12.

· 21=14 – Indicates that the user has been assigned an external access policy (the value 21) that has an anchor value of 14.

· 7=9 – Indicates that the user has been assigned a voice policy (the value 7) that has an anchor value of 9.

And because we knew you'd ask, wefooled around a little with this and, yes, it appears that you could (in theory, anyway) assign policies by directly modifying the msRTCSIP-UserPolicies attribute. For example, if we grabbed a user account and added the value 7=9 to this attribute then, as near as we can tell, the voice policy with the anchor value 9 will be assigned to that user. We didn't do much in the way of verifying that the policy settings are all properly applied, but it seems to work.

Having said that, does that mean that we recommend you assign policies by modifying the msRTCSIP-UserPolicies attribute rather than by using Grant-Cs cmdlets? Heavens no; that would be a major mistake. Why? Well, even assuming that this works (and we aren't guaranteeing that it does, not by any means) it would be a major hassle. For example, to assign a client policy to a user you'd have to figure out the value that Lync Server uses to represent a client policy. Having done that, you'd then have to figure out the anchor value of the client policy you want to assign. And then, having done that, you'd have to correctly assign that paired value without messing anything else up (like, say, the user's Registrar policy). We assumed that you would be interested in knowing how the system works, but that doesn't mean that we recommend you trying this yourself. For example, it's interesting to know how brain surgery takes place:

The hair on part of the scalp is shaved. The scalp is cleansed and prepared for surgery. An incision is made through the scalp. The incision may be made behind the hairline and in front of your ear, at the hairline near your neck, or elsewhere, based on where the problem in your brain is located. The scalp is pulled up. A hole is created in the skull. A piece of the skull (a bone flap) is removed. Most of the time, this flap will be placed back after the surgery is over.

However, we don't recommend that you try that yourself, either.

Note. Contrary to popular belief, the authors of the Lync Server PowerShell blog have not tried to perform brain surgery on themselves, or each other.

Although it is hard to tell sometimes, isn't it?

OK, now that we understand how policies work – policy information is kept in the Central Management Store and policy assignment information is kept in Active Directory – let's see what happens when you try to remove a per-user policy. Any time you delete a per-user policy Lync Server does a quick check to see if that policy is currently assigned to any users. If it is, you receive the warning we noted earlier:

The policy "RedmondAccessPolicy" is currently assigned to one or more users. Assign a different policy to the users before removing this one.

The policy is in use.

[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):

However, that's all the system does: it simply warns you that the policy is currently assigned to one or more users. If you go ahead and delete the policy Lync Server will bow to your wishes and the policy information will be removed from the Central Management Server. However, the Remove-Cs cmdlet will not iterate through Active Directory and unassign the policy from all the affected users. For example, if we remove the per-user external access policy that's been assigned to Ken Myer, his Active Directory user account will still include the value 21=14 as part of the msRTCSIP-UserPolicies attribute. That means that, as far as Lync Server is concerned, the external access policy we just deleted (the one that has the anchor value 14) is still assigned to Ken Myer.

So what does this mean for Ken Myer? To begin with, we should note that it won't cause any problems for the user himself. When Ken logs on to Lync Server the system will check to see which policies have been assigned to his account. When Lync Server sees that he (allegedly) is assigned the external access policy that has the anchor value 14 the system will then try to locate that particular policy. If the policy is nowhere to be found (which it won't be, seeing as how we deleted it) then the system will simply default to using the appropriate site policy to manage Ken's external access. And what if Ken's account happens to reside in a site that doesn't have a per-site external access policy? No problem; in that case, Ken will be managed via the global policy.

Note. And at that point you can stop worrying, because there will always be a global policy.

But while there might not be any adverse effects for Ken Myer, there are adverse effects for you, the Lync Server administrator. As we've already seen, any time you look at Ken's user account you'll see warnings similar to this:

WARNING: "ExternalAccessPolicy" with identity "14" assigned to "sip:kenmyer@litwareincn.com" has been removed from configuration store.

 

That's a nuisance, but now here's an actual problem. Suppose you'd like to get a list of all the users who have not been assigned a per-user external access policy. With that in mind, you run a command similar to this:

Get-CsUser –Filter {ExternalAccessPolicy –eq $null} | Select-Object DisplayName

You might expect that Ken Myer will be included in the list of users who do not have a per-user external access policy assigned to them. (That is, user accounts where the value of the ExternalAccessPolicy attribute is null.) Unfortunately, though, Ken Myer will not be included in this list of users. Why not? Because, technically, Ken is still assigned the external user access policy that has an anchor value of 14. Granted, this policy doesn't actually exist. But, for better or worse, Lync Server doesn't care about that. As far as the system is concerned, Ken still has a per-user external access policy. Multiply this by the tens (or even hundreds) of users originally assigned this policy, and, to paraphrase Laurel and Hardy, this is another fine mess we've gotten ourselves into.

So how do we get out of this fine mess we've gotten ourselves into? Well, for starters, we need to scold you a little: if you had paid attention to the original warning we never would have gotten ourselves into this fine mess in the first place. Instead, any time you remove a per-user policy you should do so using two commands. For example:

Get-CsUser –Filter {ExternalAccessPolicy –eq "RedmondAccessPolicy"} | Grant-CsExternalAccessPolicy –PolicyName $null

Remove-CsExternalAccessPolicy –Identity "RedmondAccessPolicy"

What do these two commands do? Well, in the first command, we've used the Get-CsUser cmdlet to retrieve a collection of all the users who have been assigned the external access policy that has the Identity RedmondAccessPolicy. This collection is then piped to the Grant-CsExternalAccessPolicy cmdlet, which assigns a "null" policy to each user currently assigned the policy RedmondAccessPolicy. Assigning a null external access policy effectively unassigns any per-user external access policy assigned to the user.

That means we no longer have any users assigned the policy RedmondAccessPolicy. In turn, that means we can then go ahead and use the Remove-CsExternalAccessPolicy cmdlet to delete the policy.

That's what we should have done; however, that's not what we did do. So how do we fix the problem now that our deleted policy is still seemingly-assigned to a bunch of users? Here's how:

Get-CsUser | Where-Object {$_.ExternalAccessPolicy –match "^14"} | Grant-CsExternalAccessPolicy –PolicyName $null

What we've done here is first use the Get-CsUser cmdlet to retrieve a collection of all the users who are still assigned the external user access policy that has the anchor value 14. Note the syntax of the command, however: we call Get-CsUser without any parameters in order to retrieve a collection of all our user accounts, then pipe that data to the Where-Object cmdlet. In turn, Where-Object picks out all those users whose ExternalAccessPolicy attribute matches the characters 14 (-match "^14$").

Good question: why didn't we just use this command to return all the users still assigned policy 14:

Get-CsUser –Filter {ExternalAccessPolicy –eq "14"}

There's actually a good reason why we didn't use that command: it won't work. Why not? As we know all too well, policy 14 no longer exists. As it turns out, if you reference a non-existent policy in your Filter your command will fail, and you'll get back an error similar to this:

Get-CsUser: Cannot bind parameter 'Filter' to the target. Exception setting "Filter": "Policy "14" is not a user policy. You can assign only a user policy to a specific user."

Note. Would it be possible to create an error message that has even more quote marks than that one? That's something we'll have to look into for Service Pack 1.

In other words, Get-CsUser, used by itself, won't do the trick. That's why we need Where-Object.

And after Where-Object does its thing, the filtered collection (that is, all the users still assigned policy 14) are piped to Grant-CsExternalAcessPolicy, which proceeds to unassign the policy from each account. At that point, we won't have any users assigned the non-existent external access policy and (unless we've deleted even more policies that were currently assigned to one or more users) we should no longer see those … helpful … warnings.

So is the moral of the story this: you should do things right the first time, and never get yourself into these sorts of situations? To tell you the truth, originally that was the moral of the story. However, now that we've had a chance to think about it we've decided that no, that's not the moral of the story after all. Instead, the moral of the story is this: always do things wrong the first time; that way, the PowerShell blog authors will have to write an article that explains how to fix the problem. After all, if no one ever has a problem with PowerShell there won't be much need for the PowerShell blog, and our managers might make us do some real work. And we're really hoping to avoid that at all costs.