Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm writing a program in C# that adds a picture to an Active Directory user object (in the ThumbnailPhoto attribute). The program runs on another PC than the Exchange Server 2010. Using PowerShell remoting I'm able to, for example, enable a mailbox for the AD user.

When I execute the following commands in PowerShell (on another PC than the Exchange Server), it works perfectly:

enter-pssession exchange_server_fqdn
$cred = get-credential          ( domain\administrator , **** )
$sess = new-pssession -configurationName Microsoft.Exchange -ConnectionUri http://exchange_server_fqdn/PowerShell/ -Authentication Kerberos -Credential $cred
import-pssession $sess
Import-RecipientDataProperty -Identity ADusername -Picture -FileData ([Byte[]]$(Get-Content -Path 'C:\\person.jpg' -Encoding Byte -ReadCount 0))
remove-pssession $sess

But I need to execute the commands from C#. The following code runs (no exception is thrown), but the image isn't added to the AD user object:

string username = "xxx";
string password = "yyy";
string exchangeServer = "zzz"; // FQDN of Exchange Server 2010
string liveIdconnectionUri = "http://" + exchangeServer + "/Powershell?serializationLevel=Full";

// credentials
SecureString passwordSecureString = new SecureString();
foreach (char c in password) passwordSecureString.AppendChar(c);
PSCredential credential = new PSCredential(username, passwordSecureString);

WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
    new Uri(liveIdconnectionUri),
    "http://schemas.microsoft.com/powershell/Microsoft.Exchange",
    credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;

PowerShell powershell = PowerShell.Create();
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();
powershell.Runspace = runspace;
PSCommand command = new PSCommand();

// Add ThumbnailPhoto attribute to Active Directory user
// This command works when executed on the Exchange Server 2010 in the Exchange Management Shell,
// But it doens't work when executed in C#.
command.AddScript("Import-RecipientDataProperty -Identity ADusername -Picture -FileData ([Byte[]]$(Get-Content -Path 'C:\\person.jpg' -Encoding Byte -ReadCount 0))");

powershell.Commands = command;
try {
    Collection<PSObject> commandResults = powershell.Invoke<PSObject>();
    foreach (PSObject result in commandResults) Console.WriteLine(result.ToString());
}
catch (Exception ex) {
    Console.WriteLine(ex.Message);
}

The only problem in the code is the line with command.AddScript. If I replace that line with, for example, this:

command.AddCommand("Get-Mailbox");
command.AddParameter("Identity", "ADusername");

... then it does work. Enabling a mailbox also works.

How can I execute the command to add an image to an Active Directory user object from C# (using PowerShell remoting)?

Working code based on accepted answer:

DirectoryEntry container = new DirectoryEntry(LDAP_URI + USERS_DIR);
DirectoryEntry user = container.Children.Add("cn=" + username, "user");

// Set other property's of the user object:
//// user.Properties ["xxx"].Value = "yyy";
//// ...

byte [] buffer;
FileStream fileStream = new FileStream(@"c:\photo.jpg", FileMode.Open, FileAccess.Read);

try {
    int length = (int) fileStream.Length;  // get file length
    buffer = new byte [length];            // create buffer
    int count;                             // actual number of bytes read
    int sum = 0;                           // total number of bytes read

    // read until Read method returns 0 (end of the stream has been reached)
    while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
        sum += count;  // sum is a buffer offset for next reading
}

finally {
    fileStream.Close();
}

user.Properties ["thumbnailPhoto"].Value = buffer;

user.CommitChanges();
share|improve this question
any full source code that works ? – Kiquenet Sep 5 '12 at 9:18

2 Answers

up vote 0 down vote accepted

I would take a different approach:

byte[] pictureBytes = //Use a FileStream to read the data

command.AddCommand("Import-RecipientPropertyData");
command.AddParameter("Identity", "fooUser");
command.AddParameter("Picture", "$true");
command.AddParameter("Encoding", "Byte");
command.AddParameter("ReadCount", 0);
command.AddParameter(FileData, pictureBytes);

Alternatively, you could just read the picture in to your pictureBytes and use ADSI (DirectoryEntry) to write directly to the thumbnailPhoto attribute. Just make sure that pictureBytes is <= 10KB as that's the max on the attribute in AD.

share|improve this answer
FileStream combined with ADSI did the trick! See my question for the code. – Korneel May 30 '12 at 6:52

This sounds like an authentication problem. Even though you're running the command on the Exchange server, it's actually being executed against Active Directory, and there's no guarantee which domain controller it will execute against. This is the double-hop scenario, which can be solved by using CredSSP in your WS-Man remoting session.

share|improve this answer
Are you sure? Because when I execute the command directly in PowerShell, it works. My guess was that it has something to do with .addScript(). The Exchange server and the domain controller are on the same machine, I think, but I'm not sure about that. – Korneel May 24 '12 at 13:32
Yes, when you execute it directly in PowerShell, there is no "double-hop" scenario. Rather, when you use PowerShell Remoting, you introduce the double-hop scenario as I mentioned before. blogs.msdn.com/b/clustering/archive/2009/06/25/9803001.aspx – Trevor Sullivan May 24 '12 at 14:21
I tried this, but it didn't help. Thank you, though. – Korneel May 24 '12 at 14:41

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.