Thursday, January 28, 2016

Bulk Assign/Remove CRM Licenses using PowerShell and O365

This is the third article in the series of using PowerShell with O365 and CRM. The first article was an Introduction To PowerShell Using O365.html and the second one was Assign or Remove a CRM license to a user using PowerShell in O365  In this article, we will be taking this one step further and

  • Assigning/Removing CRM licenses in bulk by reading from a file
  • Add error handling logic when assigning licenses
  • Output the results to the command prompt and prompt the user if he/she wishes to export the results.
  • Export the results out into csv file along with any error reasons for further reference and troubleshooting
First, we will create a file with the list of users that need to be assigned with a CRM license. Please note that in order to assign a license to a user, we need to obtain the UserPrincipalName of the user. This is generally of the format username@domainname.com. Below is the format of the file that I have used. I will call this file "Users.txt"


Bulk Assign Licenses

Now, let's launch PowerShell ISE and get started with the code to assign licenses to the above users. Obviously, before we assign a license to a user, we need to ensure that we have enough available licenses or else the code will throw an error. Check out my Introduction article on how to find out the number of available licenses.

I have added lots of comments to my code below and hence most of it is self-explanatory. Here is the complete script to bulk assign users
#-------------------------------------------------------
# Purpose        : To assign O365 CRM license to users
# Author         : Srihari Radhakrishnan
# Created On     : November 30 2015
#------------------------------------------------------- 


Connect-MsolService

#Declare the path for the users 
$UsersInputFilePath = Get-Content "D:\Users.txt" # Declare the file from which the user email addresses should be picked
$UsersOutputFilePath = "D:\UserLicenses.csv" # Declare the output file where results should be exported to
$Output = @() # Create an array to store the output results

#Add the license service options that need to be disabled when assigning a standard CRM license

# To find the additional service options that get enabled when assigning the CRM License, please use the code below to check
# Get-MsolAccountSku | Where-Object {$_.SkuPartNumber -eq "CRMSTANDARD"} | ForEach-Object {$_.ServiceStatus}

#Results
#-----------------------------------------------------------------------------
# MDM_SALES_COLLABORATION = Microsoft Dynamics Marketing Sales Collaboration
# NBPROFESSIONALFORCRM = Microsoft Social Engagement Professional
#-----------------------------------------------------------------------------

#Create the License Options with the Disabled Service Plans
$CRMLicense = New-MsolLicenseOptions -AccountSkuId testinc:CRMSTANDARD -DisabledPlans MDM_SALES_COLLABORATION, NBPROFESSIONALFORCRM

#Iterate through the users file and assign the licenses.
foreach($user in $UsersInputFilePath)
{
    #Set the CRM License to the user while disabling the additional services for that license
    Try
        {
            $ErrorActionPreference = "Stop" # Declare $ErrorActionPreference in order to catch the non-terminating error while assigning the licenses  
            Write-Host "Assigning License for " $user;
            Set-MsolUser -UserPrincipalName $user -UsageLocation "US"
            $LicenseOutcome  = Set-MsolUserLicense -UserPrincipalName $user -AddLicenses "testinc:CRMSTANDARD" -LicenseOptions $CRMLicense            
            if($?)
            {
                Write-Host "Success"
                $user | Add-Member -Type NoteProperty -Name Username -Value $user 
                $user | Add-Member -Type NoteProperty -Name Result -Value "Success"
                $Output+=$user
            }
        }
     Catch
        {
            Write-Host "Exception occured while trying to assign the license for the user " $user; # Write the custom error 
            Write-Host $Error[0].Exception; # Throw the actual error
            $user | Add-Member -Type NoteProperty -Name Username -Value $user 
            $user | Add-Member -Type NoteProperty -Name Result -Value "Failure"
            $user | Add-Member -Type NoteProperty -Name ErrorReason -Value $Error[0].Exception
            $Output+=$user
        }
     Finally
        {
            $ErrorActionPreference = "Continue"; # Reset $ErrorActionPreference variable so that the loop can continue
        }
    
}

$Input = Read-Host -Prompt "End of Process. Do you want to export the results? Type Y/N"
if($Input -eq "Y")
{  
  $Output | Select-Object UserName, Result, ErrorReason | Export-Csv $UsersOutputFilePath -NoTypeInformation
  Write-Host "CSV Exported to " $UsersOutputFilePath
}
else
{
  exit
}

Bulk Remove Licenses
The below code will be used to remove the CRM licenses
#-------------------------------------------------------
# Purpose        : To remove O365 CRM license from users
# Author         : Srihari Radhakrishnan
# Created On     : November 30 2015
#-------------------------------------------------------

Connect-MsolService

#Declare the path for the users 
$usersFile = Get-Content "D:\Users.txt" 

#Iterate through the users file and remove the licenses.
foreach($user in $usersFile)
{
     Try
        {
            $ErrorActionPreference = "Stop" # Declare $ErrorActionPreference in order to catch the non-terminating error while assigning the licenses  
            Write-Host "Removing License for " $user;
            $LicenseOutcome  = Set-MsolUserLicense -UserPrincipalName $user -RemoveLicenses "testinc:CRMSTANDARD"          
            if($?)
            {
                Write-Host "Success";
            }
        }
     Catch
        {
            Write-Host "Exception occured while trying to remove the license for the user " $user; # Write the custom error 
            Write-Host $Error[0].Exception; # Throw the actual error
        }
     Finally
        {
            $ErrorActionPreference = "Continue"; # Reset $ErrorActionPreference variable so that the loop can continue
        }
    
}

Read-Host -Prompt "Press any key to Exit"

Wednesday, January 27, 2016

Assign or Remove a CRM license to a user using PowerShell in O365

Assign a License
In order to assign a CRM license to a user, we know that one should login into the O365 Portal using an O365 admin account and then go to the list of users and then provide a CRM license manually to the user as shown below



Let's see how to do the same task using PowerShell. For those who are new to PowerShell in O365, please check out my introduction article Introduction to PowerShell using O365

Once you have the Azure PowerShell window open, the following command will easily assign the CRM license to a user
Connect-MsolService

Set-MsolUserLicense -UserPrincipalName "srihari@testinc.onmicrosoft.com" -AddLicenses "testinc:CRMSTANDARD"

License Options
The above code will assign all the license options under Dynamics CRM to the user. What is a license option?
In the below screenshot, a Dynamics CRM license has 3 License options:
  • Microsoft Dynamics Marketing Sales Collaboration
  • Microsoft Social Engagement Professional
  • Microsoft Dynamics CRM Online Professional











Well, what if we just want to assign ONLY the 3rd license option (Microsoft Dynamics CRM Online Professional) and not assign the other two?
In that case, our code needs to be updated as below.

First, we need to retrieve the license options schema name for CRM using the code below
Connect-MsolService

Get-MsolAccountSku | Where-Object {$_.SkuPartNumber -eq "CRMSTANDARD"} | ForEach-Object {$_.ServiceStatus}

Results:

Great, now that we know the schema names of the different license options under the CRM license, we will create a LicenseOption variable and add these license plans as "Diabled Plans" for that license
$CRMLicense = New-MsolLicenseOptions -AccountSkuId testinc:CRMSTANDARD -DisabledPlans MDM_SALES_COLLABORATION, NBPROFESSIONALFORCRM

Finally, the complete code to assign a license to a user with ONLY the CRM license option enabled would look like
Connect-MsolService

# To find the additional service options that get enabled when assigning the CRM License, please use the code below to check
# Get-MsolAccountSku | Where-Object {$_.SkuPartNumber -eq "CRMSTANDARD"} | ForEach-Object {$_.ServiceStatus}

#Results
#-----------------------------------------------------------------------------
# MDM_SALES_COLLABORATION = Microsoft Dynamics Marketing Sales Collaboration
# NBPROFESSIONALFORCRM = Microsoft Social Engagement Professional
#-----------------------------------------------------------------------------

#Create the License Options with the Disabled Service Plans
$CRMLicense = New-MsolLicenseOptions -AccountSkuId testinc:CRMSTANDARD -DisabledPlans MDM_SALES_COLLABORATION, NBPROFESSIONALFORCRM

Set-MsolUserLicense -UserPrincipalName "srihari@testinc.onmicrosoft.com" -AddLicenses "testinc:CRMSTANDARD" -LicenseOptions $CRMLicense

That's it! ..this is how it would now appear from the O365 UI



Removing a License
Removing a license is very straightforward as well. The following line of code will remove the license from a user
Set-MsolUserLicense -UserPrincipalName "srihari@testinc.onmicrosoft.com"-RemoveLicenses "testinc:CRMSTANDARD"

Introduction to Powershell using O365 and Dynamics CRM or Dynamics Marketing

This is a first in a series of articles that will explain how to use powershell to perform different tasks in O365 related to Dynamics CRM or Marketing.

First things first, you need to have the following pre-requisites before you can proceed:

  • An O365 Account
  • CRM Online that is part of the O365 tenant.
  • At minimum, "user administrator" privileges on O365 in order to be able to assign licenses to users in O365
  • Windows Azure Active Directory Module for Windows PowerShell. Note that this is different download from the normal powershell that comes installed on your server or laptop.


Now, let's get started.
  • Right click on "Windows Azure Active Directory Module for Windows PowerShell" icon from your taskbar and choose "Windows PowerShell ISE" or in case you do not have this on your taskbar, just launch powershell and type ISE.
  • This will launch the PowerShell ISE which is the scripting environment for PowerShell. Type Connect-MsolService in order to connect to O365. This will prompt you to key in your credentials and if you have sufficient permissions, you will be able to connect to O365.
  • Once connected, you can type in various powershell commands to perform different activities. 
The following code will connect to O365 and get a list of all the subscriptions that I have in my O365 tenant along with a count of the available and consumed license units. I will then export this data to a csv file

Connect-MsolService

Get-MsolAccountSku | Export-Csv D:\Srihari\O365\License\LicencesSplit.csv



The output will look something like this:

Tuesday, August 26, 2014

Setting up CRM Online with Exchange Online using Server Side Synchronization

I was trying to setup Server Side Synchronization using CRM Online and Exchange online as part of the Office 365 offerings. For some reason, I found the process to be somewhat confusing.

After several trial and error sessions, I have documented the below process that has always worked for me since. Hope this helps someone out there as well :)

1. First, create a CRM Online Trial Account
2. Then from the portal (https://portal.microsoftonline.com/), under Admin - Select Office 365

3. Choose Purchase Services and choose Trial for Office 365 Enterprise E3 trial or above.
4. Choose all the services and wait for approx. 10 mins for the services to get activated.

5. After purchasing the E3 Trial, we need to ensure that all the users in the organizations are also assigned to the purchased subscription. To do that, click on “Users and Groups” and select the users in your organization and then click on Edit


6. Then navigate to “Licenses” and then select all the subscriptions licenses and then click on Submit.

7. Wait until all your subscriptions in the command bar and under the Admin section get enabled as shown below:

8. Login into CRM. Click on Settings and go to System Settings. In the email tab, set everything to Server Side Synchronization and click on OK

9. Then click on Settings and choose Email Server Profiles

10. Open the currently Active Email Server Profile

11. Set the Auto-Discover to “No” and specify the Incoming and the Outgoing Server location as shown below (This should ideally work with “Auto Discover” set to “Yes” but for some reason it was not working for me unless I give the credentials as specified below).

12. Click on Mailboxes->Select all the mailboxes and then click on “Apply Default Email Settings”

13. Then click on “Test and Enable Mailboxes”. Ensure that the status for Incoming and Outgoing is successful.

14. (Optional): Finally, go to the user’s Personal Options page. Set the Track emails options to track “All email messages”. That’s it.

Wednesday, June 4, 2014

Exception message: Table alias is not unique amongst all top-level table and join aliases CRM 2013

Recently I was trying to create a bunch of different views and I was using an existing view to "Save As" another view.
However, after publishing my new views, I would keep getting the error "Exception message: Table alias a_d8c7aa10e688e31180c200155d53e454 is not unique amongst all top-level table and join aliases" in the Event Viewer

When I looked into the FetchXml in SavedQueryBase table for the view that I just created, I would see that there were indeed two "linked entity" tags in the FetchXml.
Searching the internet, I found mostly 2 dirty work around's and 1 perfect work around (perfect for me) to resolve this issue.

Dirty Work Around

1. Export the Solution with the entity inside it. Open the customization.xml and remove the duplicate reference to the "linked entity" tag. Import the solution.
2. Get the FetchXml for the view from the SavedQueryBase table and remove the duplicate reference to the "linked entity" tag. Update the FetchXml back into the SavedQueryBase table for that view and then Publish the Entity from CRM.

Perfect Work Around (Perfect for my situation)

I just happened to accidentally read the comments left by one of the users in one of the blogs while researching a fix for this issue. I can't remember the blog or the commenter but all thanks to him for the issue resolution.

Root Cause
This is a bug in the product and apparently there was a fix released for this in CRM 2011 UR 12 but no fix has been released so far for CRM 2013 until UR 2.
The Root Cause is that if you already have columns from a "linked entity" in your "Entity Advanced Find View", any further views that you create that reference columns from a linked entity will throw an error.

Resolution
1. Go to the Entity Customization
2. Click on Views
3. Open the Entity Advanced Find View (Ex. Contact Advanced Find View)
4. Remove any references to columns that may have been added from another entity. i.e these fields do no exist inside the current entity.
5. Save and Publish the entity.

Thats it! Now, I was able to create all my views as usual without getting any errors. Hope this helps someone.

Thursday, February 20, 2014

Retrieve CRM Form Metadata Xml

The below method helps us to retrieve the FormXml of a CRM Entity
        private string GetFormXml(string websiteName, string entityName, IOrganizationService service)
        {
            string formXml = "";

            try
            {
                RetrieveEntityRequest mdRequest = new RetrieveEntityRequest()
                {
                    EntityFilters = EntityFilters.Attributes,
                    LogicalName = entityName,
                    RetrieveAsIfPublished = true
                };
                // Execute the request
                RetrieveEntityResponse entityResponse = (RetrieveEntityResponse)service.Execute(mdRequest);
                //EntityMetadata entityData = entityResponse.EntityMetadata;y

                var entityTypeCode = entityResponse.EntityMetadata.ObjectTypeCode.Value;

                QueryExpression query = new QueryExpression("systemform");
                query.ColumnSet = new ColumnSet("formxml");
                query.Criteria.AddCondition(new ConditionExpression("name", ConditionOperator.Equal, websiteName));
                query.Criteria.AddCondition(new ConditionExpression("objecttypecode", ConditionOperator.Equal, entityTypeCode));

                RetrieveMultipleRequest retrieveRequest = new RetrieveMultipleRequest();
                retrieveRequest.Query = query;

                var results = (RetrieveMultipleResponse)service.Execute(retrieveRequest);
                formXml = results.EntityCollection.Entities[0].Attributes["formxml"].ToString();

                return formXml;

            }            
            catch (FaultException ex)
            {
                throw new FaultException("Error occured while instantiating the CRM Service " + ex.ToString());
            }
            
        }

Wednesday, February 19, 2014

Retrieving the optionset label

The below code helps us to retrieve the label of the option set. Please note that the "optionValue" is the integer value of the label that you want to retrieve.
        public static string GetOptionSetLabel(string entityName, string attributeName, int optionValue, IOrganizationService service)
        {
            try
            {
                RetrieveAttributeRequest retrieveAttributeRequest =
                            new RetrieveAttributeRequest
                            {
                                EntityLogicalName = entityName,
                                LogicalName = attributeName,
                                RetrieveAsIfPublished = true
                            };

                // Execute the request.
                RetrieveAttributeResponse retrieveAttributeResponse =
                    (RetrieveAttributeResponse)service.Execute(retrieveAttributeRequest);

                // Access the retrieved attribute.
                PicklistAttributeMetadata retrievedPicklistAttributeMetadata = (PicklistAttributeMetadata)retrieveAttributeResponse.AttributeMetadata;

                // Get the current options list for the retrieved attribute.
                OptionMetadata[] optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();

                foreach (OptionMetadata option in optionList)
                {
                    if (option.Value == optionValue)
                    {
                        return option.Label.UserLocalizedLabel.Label;
                    }
                }
            }
            catch(Exception ex)
            {
                WriteErrorLog(entityName, "Error while retrieving the optionset labels " + ex.ToString(), service);
            }

            return String.Empty;
        }