So i started to look into the idea of adding photos of our staff to our GAL and thought I would note down some things i learnt on the way. The first i would suggest is to have a good read to the below 2 URL’s as they have documented quite well what you need to do and how to do it. I will go over the basic steps, and tips i learnt along the way.
GAL Photos in Exchange 2010 and Outlook 2010
http://msexchangeteam.com/archive/2010/03/10/454223.aspx
GAL Photos: Frequently Asked Questions
http://msexchangeteam.com/archive/2010/06/01/455005.aspx
My Comments
The word GAL and Exchange get thrown about a bit so its easy to get confused with where the image is being stored. It actually goes into Active Directory and is stored under thumbnailPhoto field as a hexadecimal. You can upload it a number of ways but i chose to use an Exchange powershell command.
Uploading the image
The command that i used to do this in my environment is as follows:
1 |
Import-RecipientDataProperty -Identity "Ivan Dretvic" -Picture -FileData ([Byte[]]$(Get-Content -Path "C:\photos\ivand.jpg" -Encoding Byte -ReadCount 0)) |
Its very simple to use this script to upload an individual photo. If you want to do a large number of photos you can do a search for similar script that caters for it, or the lazy way by using Excel to get a list of all the formatted commands (with some playing around) and paste output of all the lines in PS. I had to made sure all the images were with the username accordingly so i could do it in one big hit.
Formatting the Image
This part took most of the time for me. The end result should be a 96×96 pixel image (recommended by MS) with a size of 2-5kb. The limit is 10kb but there is no need to have it bigger than 5kb. Two programs i used to resize all the images (in batch) were called EasyImageModifier and ThumbMaker. They are great because they are under 1MB each, are portable apps that work with Win7, and are free.
Other notes
Removing image from a user: The only way i have found so far to remove a user’s thumbnailPhoto was to go to ADSIEDIT and manually remove the information by clicking on the clear button. I am yet to find a PS script to do this.
Including images in Offline Address book: An option to take note on is whether you want the Offline Address Book to hold the actual photo, or just the reference to the AD location. For large organisations its not recommend, but for smaller companies it is feasable to do so.
The benefits of this is you will be able to view the photos when a clients machine is offline. (note it will still work if you are running in Outlook Anywhere mode without this setting)
Program to upload individual photos: A good bloke by the name of Paul Adams wrote a little program (and even modified it to clear the photo) that i now use to make individual changes moving forward. It would be fantastic for more development of this type of software so that i could pass on the role to HR but dont want to give admin rights. Maybe MS will provide that one day, for now here is the software im talking about, its code and the compiled version for you to use. (permission granted by Paul Adams – Contact details can be provided on request.)
ImportUserPictureToAD 1.4.exe (.zip file)
Screenshot of the software in action.
And finally the source code for the above program. Please reference Paul Adams as the author if you are going to update the code in any way.
Paul was kind enough to provide the complete solution file to help someone continue the development of this. It has been written in Visual C# 2010 Express.
ImportUserPictureToAD 1.4.exe source code (the project file is corrupted and i cannot find the original)
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.DirectoryServices; namespace UpdateUserImage { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void SaveButton_Click(object sender, EventArgs e) { AddPictureToUser("cn=" + txtUserName.Text + "," + txtLDAPString.Text, txtDomainController.Text, txtImageLocation.Text); } void AddPictureToUser(string strDN, string strDCName, string strFileName) { try { //check size of file and make sure it is less than // 300x300 and smaller than 25k if (ImageSizeIsAcceptable(strFileName) == true) { // Open file System.IO.FileStream inFile = new System.IO.FileStream(strFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read); // Retrive Data into a byte array variable byte[] binaryData = new byte[inFile.Length]; int bytesRead = inFile.Read(binaryData, 0, (int)inFile.Length); inFile.Close(); inFile.Dispose(); // Connect to AD System.DirectoryServices.DirectoryEntry myUser = new System.DirectoryServices.DirectoryEntry(@"LDAP://" + strDCName + @"/" + strDN); if (chkJpegPhoto.Checked) { // Clear existing picture if exists if (myUser.Properties["jpegPhoto"] != null) { myUser.Properties["jpegPhoto"].Clear(); } // Update attribute with binary data from file myUser.Properties["jpegPhoto"].Add(binaryData); } if (chkThumbnailPhoto.Checked) { if (myUser.Properties["thumbnailPhoto"] != null) { myUser.Properties["thumbnailPhoto"].Clear(); } myUser.Properties["thumbnailPhoto"].Add(binaryData); } myUser.CommitChanges(); //close/dispose user object myUser.Close(); myUser.Dispose(); //reset the binary data to keep it clean between operatons binaryData = null; MessageBox.Show("No errors were encountered during the operation.","Epic Win",MessageBoxButtons.OK,MessageBoxIcon.Information); } else { MessageBox.Show("The image must be less than 300 pixels wide and 300 pixels high, and less than 25,000 bytes..", "Hold up there, toughguy", MessageBoxButtons.OK, MessageBoxIcon.Hand); } } catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error); } } void ClearUserPicture(string strDN, string strDCName) { try { // Connect to AD System.DirectoryServices.DirectoryEntry myUser = new System.DirectoryServices.DirectoryEntry(@"LDAP://" + strDCName + @"/" + strDN); if (chkJpegPhoto.Checked) { // Clear existing picture if exists if (myUser.Properties["jpegPhoto"] != null) { myUser.Properties["jpegPhoto"].Clear(); } } if (chkThumbnailPhoto.Checked) { if (myUser.Properties["thumbnailPhoto"] != null) { myUser.Properties["thumbnailPhoto"].Clear(); } } myUser.CommitChanges(); //close/dispose user object myUser.Close(); myUser.Dispose(); MessageBox.Show("User image was cleared!", "Epic Win", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error); } } private void SelectFileButton_Click(object sender, EventArgs e) { ofd.Filter = "Jpeg files (*.jpg)|*.jpg"; ofd.FileName = ""; ofd.ShowDialog(); if (ofd.FileName.Length > 0) { this.txtImageLocation.Text = ofd.FileName; try { this.imagePanel.BackgroundImage = Image.FromFile(ofd.FileName); this.imagePanel.BackgroundImageLayout = ImageLayout.Stretch; } catch (Exception ex1) { MessageBox.Show(ex1.ToString()); } } } private bool ImageSizeIsAcceptable(string strFileName) { bool isOK = false; //get size of file in bytes System.IO.FileStream inFile = new System.IO.FileStream(strFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read); Int64 len = inFile.Length; inFile.Close(); inFile.Dispose(); if ((imagePanel.BackgroundImage.Width <= 300) && (imagePanel.BackgroundImage.Height <= 300) && (len < 25000)) { isOK = true; } return isOK; } private void btnEraseImage_Click(object sender, EventArgs e) { ClearUserPicture("cn=" + txtUserName.Text + "," + txtLDAPString.Text, txtDomainController.Text); } } } |
Comments welcome.
use codetwo software..and its free..
Hi Sajid,
I have used CJWDEV version and use it, but only the single use one. The software you have mentioned looks quite good. I have only viewed their video but seems quite capable, and appears to be free, even though you need to enter details to get to download page.
Their details are:
CodeTwo Active Directory Photos
http://www.codetwo.com/freeware/active-directory-photos/
Kind Regards,
Ivan
Hi Ivan,
I was looking to download your “ImportUserPictureToAD-ProjectSourceCode” however it appears to be a broken link.
Are you able to provide me with a working link please?
Thank you.
Hi Mirza,
Apologies but it appears that the file is corrupted and missing. I have searched but am not able to find the original. The source code is there however the project file (which would import easily into VisualStudio) is gone.
Since writing this document i have been using Chris Wright’s program called ADPhotoEdit and have found it to do exactly what I want. I have spoken to the author and since purchased his other AD software because i have found it very useful.
http://cjwdev.co.uk/Software/ADPhotoEdit/Info.html
Regards,
Ivan
Just thought I would mention that I’ve made a free application for editing the thumbnailPhoto attribute that offers a few more features than the software you mentioned in this article and is a bit more user friendly. If you want to check it out you can download the free edition here: http://cjwdev.co.uk/Software/ADPhotoEdit/Info.html
There is a commercial version but the free edition should be able to do everything most people want to do, the commercial version just allows you to do bulk imports and bulk exports.
Thanks
Chris