Tillbaka i Östergötland

Flew back into Sverige (pronounced: sv-air-ee-yay), yesterday. I never thought that I would say this but I think I’m getting tired of travelling/flying. On the realsies. As my friend, HomeSlice (only code names from here-on out), used to say, “For real, real, not play, play.

Something you’ll eventually figure out, if you ever go to Stockholm, but I’ll ruin the surprise for you: They have GSM repeaters under-ground, so you can all of that roaming goodness without the need of that pesky thing called the Earth getting in the way. (Pretty sure this is a GSM-band antenna, at least. Can confirm: Your cellphone will work underground.)

Arlanda Express. Do it. From Arlanda airport to Stockholm Central Station in like 20 minutes. Seriously. Do it. It does all the kph’s.

After a few hours’ wait (during which, I hit-up Forex bank to pay the fee for my ID-Kort, ate some Burger King and felt disgusted with myself, and smoked copious amounts of cigarettes), it was off to Norrköping via the train.

I was the only one in train car for the majority of the ride. Yes, you read that correctly: I had the train car to myself. It was simultaneously great and a bit disheartening. It was great because people suck, in general and en masse. It was disheartening because it was like a three hour train ride and I get bored super-easily. I read some of my book, programmed in C#, and surfed the internet to try to fill those hours.

I toss the lack of other people up to it being 21:00 (local time), it was a 1st class coach (which was like €10 more), and it was an older type train, with older type train cars (read:we were in no WiFi country).

Yes, there’s even video. Let’s see if we can get WordPress to play well with it.

http://www.iseelondoniseefrance.se/wp-content/uploads/2018/10/img_0497.mov

If you get a chance to grab a train via SJ in Sweden, I highly recommend the double-decker. Yes. They have one. Yes. It’s just as awesome as you’d think it’d be.

Today, I got up and did the needful, and then went to my appointment for my ID-Kort (Swedish ID Card).

Afterwards, I had to have meatballs for lunch (because Sweden and köttbullar), so I headed over to Tropikhuset and had some lunch.

Afterwards, it was a lot of walking but given how rainy and generally just miserable it’s been, I haven’t been snapping that many photos.

I do have to mention that during my walk I did swing by an old school book store and I think I have officially found heaven – especially at those prices.

So, that’s it for now. Maybe I’ll be more in the photo-taking mood as the week progresses. We’ll see.

Until then, dear reader, as Smokey the Bear says, “Only you can prevent forest fires…”

En Dag i Stockholm Del Två

So, today started off with breakfast in the hotel, which is where the Kalles Caviar photo that finished the last post originated from.

After breakfast, it was a quick shower, realising that I had left almost all of my toiletries at the hotel in Linköping (womp-womp).

First was to go to the Riksdag area, again. The Riksdag, itself, takes up almost half of the island called Helgeandsholmen and the area of the city is called Gamla Stan (literally, the old town).

This is also where the Stockholms Medeltidsmuseum (literally, Stockholm’s Mideival Times Museum) is located.

It’s a museum and it’s free, so of course I went through it! 😊

After that, it was time to get up close and personal with the Riksdag. Apparently, you need to have pre-booked tours; so, guess what I want to do the next go around?

Then, some more walking because why not?

Then, I stumbled upon another church, S:t Jacobs Kyrka. I think that the church is in the Norrmalm area of the city. Yes, it was a bit of a walk.

Just behind S:t Jacobs Kyrka is Kungsträdgården (literally, king’s tree garden), a fairly large park that also hosts an fairly large ice skating arena in winter.

After that, it was time to grab a bite to eat and I figured that it was time to see what all of the fuss about Max was.

Turns out that they’re pretty environmentally conscious and friendly, so I can see what all of the fuss was about.

The fries were worth passing on but the grilled halloumi nacho burger and the cloud berry shake were worth it.

After that, it was to the hotel to pick up my bag and head to Arlanda via the Arlanda Express. A train that goes 180km/h? Yes, please.

Hopped on the plane back to Dublin.

Back in Dublin, the passport control line was brutal. To give you an idea, the plane landed around 19:00. I didn’t get out of passport control until around 20:00.

Fun-filled facts: You have to have your GNIB card and cannot use your phone when talking to GARDAÍ. Either could be a €3000 fine. Not that I forgot my card or used my phone but I overheard the threats (?), so just trying to help you out. The More You Know™️

Now, I’m back in Dublin and I have to go right back to Sweden, soon, so expect a new post before the month is through. 😊

Until then, dear reader, as Mork used say, “Nanu nanu!”

En Dag i Stockholm.

So, a day in Stockholm was between yesterday and today. Well, if you want to hyper literal with it, it was less than a full day but who’s counting ticks, yeah?

Anyway, I got a room with a view at Radisson Blu Waterfront (in the Norrmalm area of Stockholm …or is it really Kungsholmen?). When they say, “with a view”, they aren’t lying.

That church-like tower off to the right is the Stadshuset (literally, house of the city), also known to us in English as city hall. You can tour the Stadshuset and I highly suggest you do so, if you ever get the chance. I, myself, have been twice and it’s a rather lovely building.

Any visit to Stockholm isn’t complete without a visit to Riksdagen.

The Riksdag is where the magic of the Swedish government happens (not to be outdone by knugen, to be sure) and, currently, they’re trying to form a coalition to form a complete government. There’s rumours that Annie Lööf will become the new stasminister, which I hope turn out to be true.

Next up was S:ta Clara kyrka. Unlike the many castles of Europe, I cannot seem to get tired of going into churches. Perhaps, there’s something to them or the fact the overall exposure time to churches is far less because their footprint is much smaller. Who knows…

S:ta Clara kyrka was nice but it was being used at the time that I had popped-in, so I snapped a photo or two and ducked out of there.

I then headed over to Drottninggatan (literally, Queen Street), hoping to go to Dressman before it got too late in the day.

I had duplicitous reasons for going to Dressman. The first was that I was looking for the cashier that had flirted with me in Swedish years before (I know, I know…) and the second was that I was running out of muscle shirts (always layer in cold environments).

After navigating the whole affair, mostly in Swedish, I was off to snag something to eat.

Drottninghof. I can not recommend it enough. Almost every time that I’ve been in Stockholm and had the time to afford a visit to this restaurant, I’ve always gone without fail.

This time, my meal consisted of toast skagen (literally, shrimp toast), regnbågsfilé (literally, rainbow fillet), and the dessert of the day.

After that, it was a bit more walking around before going back to the hotel to sleep and catching my flight today.

Did I mention the view of the hotel room? I’m not trying to show off, more like I’m trying to convince you to treat yourself, if ever you should go.

Finally, if you’ve ever been to Sweden, you’re all too familiar with the Kalles phenomenon. If you haven’t, let me explain: Kalles is a brand of caviar and the caviar, itself, is packaged and sold in tubes. When people talk about it, it’s almost always followed by the word caviar, Kalles Caviar. It’s a thing here, kind of like snus, but the only difference is that I’ve seen people use snus.

So, that’s all there is from a very brief time in Stockholm. I’ll probably tour the city a bit more before my flight today but won’t get a chance to update the blog until late tonight or tomorrow. Better to just assume this is the end of it, at the moment.

Until next time, dear reader, as the Vulcans say, “Live long and prosper.”

En Promenad runt Linköping

So, for Saturday and Sunday, I stuck mainly around the Linköping (pronounced: len-show-ping) area. It’s a decent walk about in autumn (yes, I’m trying to drop “fall” from my vocabulary for any seasonal references) and walking is good for you, or so I hear.

First up was the Trädgårdsföringen park (not to be confused with the park of the same name in Göteborg). I’ve taken plenty of photos of the park in the past. So, to save you from that mundane banality of seeing a panel of photos of it, again, here’s only a few. 🙂

After that, it was necessary to visit Linköpings Domkyrka (pronounced: dum-sheeyir-kah, said very quickly). It’s a beauty of a church and the acoustics of it are astounding, to the say the least.

After the domkyrka, it was a bit more of walking around town, killing time before bed.

The reason why it needed to be an early night for me is because I was off to Stockholm on the morrow. There won’t be anything on Stockholm in the next post.

Until then, dear reader, have a good one and – in the immortal words of Bill S. Preston, Esq. and Ted Theodore Logan, “Be excellent to each other!”

En Dag i Göteborg

I took an early morning bus (Nettbuss, a.k.a. bus4you) from Linköping to Göteborg. Early morning, for context, is only 7am local time (CEST) but that meant I had to interrupt my beauty sleep to catch it and those of you who know me know that I could use as much of that as plausibly possible.

The bus itself was pretty comfortable. If I could give you a comparative, it was like the AirCoach (in Dublin) meets the double-decker bus.

The views were pretty amazing, I have to say, but – then again – maybe I’m preferential to the Swedish countryside.

The bus took about 4 hours to get to Göteborg and it wasn’t too bad of a trip; albeit, the nicotine cravings started kicking in when we got out to lake country…

The bus takes you right to Göteborg Central Station, which is super convenient, as the tram lines run right outside the front doors (veritably). The inside of the station is pretty amazeballs and it’s something worth seeing, I think, if you ever get the chance.

After a quick lunch meeting, it was time to be off walking around the town. The pictures don’t do the city justice, to be sure, but they’re all I have to show you this city’s majesty.

After a brief walk about, it was time to do one of my favourite things: Visit a museum. In this case, it was Göteborgs Stadsmuseum. It cost a few quid (60SEK, which is about €6) and is cheap enough that it’s worthwhile. After all, there are three floors to the museum, itself.

Pictures of a museum? Of course there’s pictures!

And, then? No “and then”! And, then…? Well, and, then, it was time for more walking about the city.

Whilst walking about, I found my next favourite thing: A park. Nordic parks, in my opinion (I’m very opinionated, aren’t I?), are the best in the world. The principle reason, for me, is because most parks are open to the public 24×7, because the public pays for them and the public, in turn, gets to use them any time that they want.

This park, specifically, was called “Trädgårdsföreningen” and is located in the centre of Göteborg. It has everything you’d expect at a large-sized park, even a botanical garden (that’s also free).

After the park (but still, technically, within it), I grabbed a bit of Earl Gray and had a piece of blueberry cheesecake. This would, normally, be a very pedestrian thing to do; an uneventful event in the normal happenstance of life. Yet, during my afternoon tea, I was joined by a brave little fellow, with whom I ended-up sharing some of my cheesecake (more specifically, the crust) with.

Sadly, I had bought my savouries shortly before they closed but worry not: The little bird had more than a fair share of that cheesecake crust.

Then, it was off to dally about the town, again, until it was time to take the (very late) train home – which is where I’m writing this from. I love living in the future.

Whilst I was about town, I found something that I thought my roommate might enjoy. Wouldn’t you know it: He has one with 20 ports. Living in the future, he is.

I also found an apt quote around town that I think I will just close this off with. It’s not the best quote ever, in the history of ever, but it’s quite fitting.

Until next time. 🙂

Jag är i Sverige igen.

So, as the title infers, I’m back in Sweden again. I took a plane from Dublin to Stockholm and then the train from Stockholm to Linköping (pronounced “len-shōping”).

Back in Linköping, I’m in my regular hotel and life is back into the Swedish modality of things.

For example, in the style of “treat yourself” (see Parks and Rec), I took myself to a semi-fancy dinner at Gula Huset (literally, the golden [or yellow] house) and then a movie, Night School. Can’t say that I recommend the movie but a comedy was definitely in order, what with everything going on.

After the movie, it was stroll around town to take in the visages and off to bed to catch an early bus. The destination? Göteborg (or “Gothenburg” in English).

The Göteborg adventures are soon (still on the way there), so stay posted. 😊

Netlogon: Cross-Forest Delayed Authentication Requests Cause Subsequent (and Continuous) Authentication Failures

NOTE: This post – drafted, composed, written, and published by me – originally appeared on https://blogs.technet.microsoft.com/johnbai and is potentially (c) Microsoft.

One of the longest debugging experiences I’ve ever had to debug, so far, in Exchange was a code bug that exists in the Netlogon code. I hope to cover what this bug was, how it manifested, and the fix that was implemented by the Windows developer to resolve the issue. So, this is going to be a long one….

Picture (Worth 1,000 Words)

Basics
Netlogon sessions use RPC (remote-procedure call) sessions with domain controllers to communicate authentication requests to domains. In the cases of cross-forest authentication requests, regardless of the type of trust created (e.g.: one-way transitive, one-way intransitive, etc.), the cross-forest authentication requests are forwarded (via the trust) to the responsible domain. In this case, the responsible domains exist in the customer’s on-premises environments.

So, in the above example, you’ll see that the client communicates with the Café server. During authentication, the Café passes the request to the managed domain controller. The managed domain controller will see the trust and communicate the authentication request across the trust and receive only an NT status response back for the request from the customer’s domain controllers.

When Repro Cometh
The condition that causes repro to start occurring is when the local domain controller (in this case, the managed domain controller in the illustration above) is awaiting a response from the customer’s domain controller for an authentication request. In the authentication pipeline, if this request times-out, it’s considered a re-triable exception – which is important for later. Because this exception is retriable, a the Café server doesn’t consider that the authentication request has failed. Also, during this same time, the Café server may build a new Netlogon session with a new domain controller, which is where our problem begins to surface.

The Netlogon code has a single object reference for the domain controller’s name for the current Netlogon session on the current RPC session it should be using. (If you’re familiar with native/unmanaged code, the reference to the domain controller’s name is a pointer to a wchar_t value.) But remember: We’ve not disposed of the previous session because it’s considered re-triable. So, since Netlogon can only communicate on one session per one RPC channel, we now have two Netlogon sessions with two RPC channels. The non-disposed of session is in red and the new session is in green in the illustration above.

The Bug
The bug is that all subsequent authentication requests traverse the red authentication path but use the domain controller’s name that was obtained from the creation of the green authentication path (the domain controller’s name is supplied in the authentication request as is defined in the specifications). This causes all subsequent authentication requests to fail, no matter the destination forest, because the domain controller receives a request that it should not process.

Verifying Repro
The best way to verify the repro of this bug is to look at the Netlogon logs. If you see 0xc0000122 (STATUS_INVALID_COMPUTER_NAME), then you’ve hit repro of this specific condition. In Exchange, this will bubble-up via the app pool in IIS as a 401 Unauthorised (which makes chasing the bug a bit more complicated).

The Fix
Windows dev determined that the best way to fix this was to tear down both the Netlogon and RPC sessions, regardless of current status. This has been verified as working in RS3 builds of Windows 10/Server 2016 and is currently being tested in RS1 builds of Windows 10/Server 2016.

Windows 10: Removing Groove, Facebook, and Other Unwanted Apps

NOTE: This post – drafted, composed, written, and published by me – originally appeared on https://blogs.technet.microsoft.com/johnbai and is potentially (c) Microsoft.

A few months ago, someone very dear to me asked why they couldn’t remove unwanted apps from Windows 10 – mainly over privacy and right to decline concerns. I took it up as a sort of personal challenge to find a way to rid these apps for other people, as well, whilst I was attempting to figure out how to do this for them specifically. So, I give you the result – which is still a work in progress.


function Remove-UnwantedApps
{
# Attempt to remove Facebook (if it exists)
$facebookPkg = Get-AppxPackage -AllUsers | Where{$_.Name -like '*Facebook*'}
if($facebookPkg -ne $null)
{
Remove-AppxPackage $facebookPkg.PackageFullName -Confirm:$FALSE
}

# Attempt to remove Groove Music
$grooveMusicPkg = Get-AppxPackage -AllUsers | Where{$_.PackageFullName -ilike ‘*ZuneMusic*’}
if($grooveMusicPkg -ne $null)
{
Remove-AppxPackage $grooveMusicPkg.PackageFullName -Confirm:$FALSE
}

# Attempt to remove Groove Video
$grooveVideoPkg = Get-AppxPackage -AllUsers | Where{$_.Name -like ‘*ZuneVideo*’}
if($grooveVideoPkg -ne $null)
{
Remove-AppxPackage $grooveVideoPkg.PackageFullName -Confirm:$FALSE
}

# Attempt to remove all things XBox.
$xboxPkgs = Get-AppxPackage -AllUsers | Where{$_.Name -like ‘*Xbox*’}
foreach($xboxPkg in $xboxPkgs)
{
Remove-AppxPackage $xboxPkg.PackageFullName -Confirm:$FALSE
}

# Attempt to remove the Advertising apps
$advertPkgs = Get-AppxPackage -AllUsers | Where{$_.Name -like ‘*Microsoft.Advertising*’}
foreach($advertPkg in $advertPkgs)
{
Remove-AppxPackage $advertPkg.PackageFullName -Confirm:$FALSE
}

# Attempt to remove the Bing apps
$bingPkgs = Get-AppxPackage -AllUsers | Where{$_.Name -like ‘*Microsoft.Bing*’}
foreach($bingPkg in $bingPkgs)
{
Remove-AppxPackage $bingPkg.PackageFullName -Confirm:$FALSE
}

# Attempt to remove the Messaging package
$msgPkg = Get-AppxPackage -AllUsers | Where{$_.Name -like ‘*Microsoft.Messaging*’}
Remove-AppxPackage $msgPkg.PackageFullName -Confirm:$FALSE

# Attempt to remove Windows Store dependent packages
$storeDepPkgs = Get-AppxPackage -AllUsers | Where{$_.Dependencies -ilike ‘*Store*’ -and $_.Name -inotlike ‘*WindowsStore*’}
foreach($sdPkg in $storeDepPkgs)
{
Remove-AppxPackage $sdPkg.PackageFullName -Confirm:$FALSE
}

# Attempt to remove Windows Store package
$storePkg = Get-AppxPackage -AllUsers | Where{$_.Name -ilike ‘*WindowsStore*’}
if($storePkg -ne $null)
{
Remove-AppxPackage $storePkg.PackageFullName -Confirm:$FALSE
}

# Attempt to disable Cortana via the Registry
$searchRegistyPath = Test-Path “HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search”
if($searchRegistyPath -eq $FALSE)
{
# Create the Windows Search Registry Key
New-Item -Path “HKLM:\SOFTWARE\Policies\Microsoft\Windows” -Name “Windows Search”

# Create the AllowCortana Registry Key
New-Item -Path “HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search” -Name “AllowCortana”

# Disable Cortana via the path
Set-Item -Path “HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search\AllowCortana” -Value 0
}
else
{
# Let’s ensure the child item exists.
$allowCortanaRegistryPath = Test-Path “HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search\AllowCortana”
if($allowCortanaRegistryPath -eq $FALSE)
{
# Create the AllowCortana Registry Key
New-Item -Path “HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search” -Name “AllowCortana”
}

# Disable Cortana via the path
Set-Item -Path “HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search\AllowCortana” -Value 0
}

# Stop the Windows Search Service
$searchService = Get-Service WSearch
Stop-Service -InputObject $searchService -Force

# Attempt to remove Cortana via the App Package
$cortanaPkg = Get-AppxPackage -AllUsers | Where{$_.PackageFullName -ilike ‘*Cort*’}
foreach($cPkg in $cortanaPkg)
{
Remove-AppxPackage $cPkg.PackageFullName -Confirm:$FALSE
}

# Restart the system, so the registy changes take effect in the Windows Search Service’s OnStart() method body.
}

Please note that this has only been tested on a local machine and, so far, validates. This can produce undesired results, such as not being able to install packages from the Windows Store in the future, since the Windows Store is removed via this script. YOU HAVE BEEN WARNED.

To use: Copy the red text into an Administrative session in PowerShell (right-click the PowerShell icon –> Run as Administrator). Run ‘Remove-UnwantedApps’ at the new line, after the red code lines from above have been pasted into the PowerShell session. Restart your machine. Profit.

O365 and Exchange 2016/Exchange 2013: Understanding the UserPhoto API

NOTE: This post – drafted, composed, written, and published by me – originally appeared on https://blogs.technet.microsoft.com/johnbai and is potentially (c) Microsoft.

We recently had an issue for an Enterprise Cloud customer, in which the photo was not rendering for the user – which was uploaded to AD (and synced over via MMSSPP to the managed environment). It was sussed that the issue was customer-caused, as the customer was modifying the photo via the PowerShell commandlets and had deleted the photo.

Despite the fact that the customer had disabled the OWA functionality to change the user photo in OWA, the customer was using the PowerShell commandlets to modify this object, which is the same interface/commands that OWA utilises behind the scenes.

To explain this behaviour in further detail: When you delete a photo from a mailbox in Exchange (or Exchange Online), it changes the UserPhotoCacheId MAPI property value to ‘0’ and this signifies to the UserPhoto API that the photo has been deleted. When this occurs, we will not fall-back to the ADPhotoHandler to call the photo from AD because the user explicitly deleted the photo. This scenario is by-design of the UserPhoto API.

To rectify this behaviour, clear the MAPI property by running ‘Remove-UserPhoto -ClearMailboxPhotoRecord’.

Keep in mind, as well, that Exchange uses a CachingPhotoHandler and that the photos stored on disk have a TTL of 7 days.

I have written a script to help ascertain the MAPI properties on a given mailbox, obtain the location of the cached photos on disk, and obtain the IPM.UserPhoto item from the user’s mailbox (on-premises):


function Get-UserPhotoDataOnPremises
{
param([string]$User)
Write-Warning -Message "The source of our powers comes from a Cracker Jack™ box, so this will take a minute. Please be patient..."
if([System.String]::IsNullOrEmpty($User))
{
throw [System.NullReferenceException]::new("The user value cannot be null.")
}
else
{
$mbx = Get-Mailbox $User
}

[string]$scriptPath = $env:ExchangeInstallPath + “Scripts\ManagedStoreDiagnosticFunctions.ps1”
# Dot-load script into function
. $scriptPath

[GUID]$guid = $mbx.ExchangeGuid.Guid
[int]$mbxNumber = Get-StoreQuery -Database $mbx.Database -Query “SELECT MailboxNumber FROM Mailbox WHERE MailboxGuid=’$guid'” | Select -ExpandProperty MailboxNumber
[string]$folderId = Get-StoreQuery -Database $mbx.Database -Query “SELECT FolderId FROM Folder WHERE MailboxNumber=’$mbxNumber’ AND DisplayName=’$([System.String]::Empty)'” | Select -ExpandProperty FolderId
$global:item = Get-StoreQuery -Database $mbx.Database -Query “SELECT * FROM Message WHERE MailboxNumber=’$mbxNumber’ AND FolderId=’$folderId’ AND MessageClass=’IPM.UserPhoto'” -Unlimited
[string]$previewPhotoCachedId = (Get-StoreQuery -Database $mbx.Database -Query “SELECT UserPhotoPreviewCacheId FROM Mailbox WHERE MailboxNumber=’$mbxNumber'” -Unlimited).p7C1B0003
[string]$photoCacheId = (Get-StoreQuery -Database $mbx.Database -Query “SELECT UserPhotoCacheId FROM Mailbox WHERE MailboxNumber=’$mbxNumber'” -Unlimited).p7C1A0003

# Obtain files on disk (if any)
$smtp = $mbx.WindowsEmailAddress.Address
$smtpAtIndex = $smtp.IndexOf(“@”)
$smtpAtIndexPlusOne = $smtpAtIndex + 1
$smtpDotIndex = $smtp.LastIndexOf(“.”)
$smtpNewLength = $smtpDotIndex – $smtpAtIndexPlusOne
$subString = $smtp.Substring($smtpAtIndexPlusOne, $smtpNewLength)
$queryString = “_$subString”
$preSubString = $smtp.Substring(0, $smtpAtIndex)
$srvr = Get-MailboxDatabaseCopyStatus $mbx.Database.Name | Where{$_.Status -contains ‘Mounted’} | Select -ExpandProperty MailboxServer
$folderPathUnc = “\\$($srvr)\” + $env:ExchangeInstallPath.Replace(“:”, “$”) + “\ClientAccess\photos”
$obj = Get-ChildItem -Path $folderPathUnc -Filter “*$($queryString)*”
$folderPath = $obj.FullName
$obj2 = Get-ChildItem -Path $folderPath
$fullPaths = @()
foreach($o in $obj2)
{
$picObj = Get-ChildItem -Path $o.FullName -Filter “*$($preSubString)*”
$fullPaths += New-Object PSobject -Property @{
Name=$picObj.Name
Size=$picObj.Length
Location=$picObj.FullName
}
}

if([System.String]::IsNullOrEmpty($item.MessageId) -eq $FALSE)
{
$string = “IPM.UserPhoto item found in user’s mailbox. The object can be found in ” + “$” + “item”
Write-Host $string
}
if($previewPhotoCachedId)
{
Write-Host “UserPhotoPreviewCacheId found: $previewPhotoCachedId”
}
if($photoCacheId)
{
Write-Host “UserPhotoCacheId found: $photoCacheId”
}
Write-Host -ForegroundColor Green “Photo files found on disk on $($srvr) for $($smtp):”
$fullPaths | FL
}

Here’s an example as run from my lab:


[PS] E:\>Get-UserPhotoDataOnPremises -User Administrator
WARNING: The source of our powers comes from a Cracker Jack™ box, so this will take a minute. Please be patient...
IPM.UserPhoto item found in user's mailbox. The object can be found in $item
UserPhotoPreviewCacheId found: -88512737
UserPhotoCacheId found: -88512737
Photo files found on disk on [REDACTED] for [email protected]:

Name : _Administrator-8EEDD78A2D804372C17E9FABD151BCD5.jpg
Location : \\[REDACTED]\E$\exchsrvr\ClientAccess\photos\_contoso.se-BBED6250E228D4F38F5F19BF4F1A6823\HR648x648\_Administrator-8EEDD78A2D804372C17E9FABD151BCD5.jpg
Size : 79838

Name : _Administrator-8EEDD78A2D804372C17E9FABD151BCD5.jpg
Location : \\[REDACTED]\E$\exchsrvr\ClientAccess\photos\_contoso.se-BBED6250E228D4F38F5F19BF4F1A6823\HR96x96\_Administrator-8EEDD78A2D804372C17E9FABD151BCD5.jpg
Size : 3224

I’ve also written a method in C# to test obtain the photo via the EWS GetUserPhoto REST method:


///
/// Creates an EWS request for a user's photo at each standard size.
///

/// Vanity name of your endpoint.
/// Smtp Address of the user you’re targeting.
private static void GetPhotos(string vanityName, string username)
{
int[] sizeInts = new[] { 48, 64, 96, 120, 240, 360, 432, 504, 648 };
Parallel.ForEach(sizeInts, delegate (int i)
{
Uri targetUri = new Uri($”https://{vanityName}/EWS/Exchange.asmx/s/GetUserPhoto?email={username}&size=HR{i}x{i}”);
Console.WriteLine($”Targeting: {targetUri}”);
try
{
HttpWebRequest newWebRequest = (HttpWebRequest)WebRequest.Create(targetUri);
newWebRequest.UserAgent = “Enterprise Cloud UserPhoto EWS Client”;
newWebRequest.Credentials = new NetworkCredential(“[UserName]”, “[PassWord]”);
using (HttpWebResponse newWebResponse = (HttpWebResponse)newWebRequest.GetResponse())
{
if (newWebResponse.StatusCode == HttpStatusCode.OK)
{
Console.WriteLine($”Size {i} photo found.”);
}
else if(newWebResponse.StatusCode == HttpStatusCode.NotFound)
{
Console.WriteLine($”Photo API states that a photo cannot be found for the size {i}x{i}”);
}
else
{
Console.WriteLine($”Unexpected http response received: {newWebResponse.StatusCode}”);
}
}

}
catch (Exception e)
{
Console.WriteLine(e.Message);
}

});
}

If you run into any problems with the script or have any questions or concerns around the UserPhoto API, feel free to let know! 🙂

2017 Project: Documented Code Samples

NOTE: This post – drafted, composed, written, and published by me – originally appeared on https://blogs.technet.microsoft.com/johnbai and is potentially (c) Microsoft.

In an effort to empower more people across the planet to learn, but for it not to be an arduous journey in doing so, I’ve started heavily commenting code so that people can learn from it. This will be the first of many code ventures that I attempt to do and then share with comments, so that others can learn from it. The reason for the commenting with the links is because I, myself, have often wondered what ‘x’ does or why ‘y’ may have been used; so, in typical learning fashion, I set off to my favourite search engine and undertake to read more on the class, method, property, etc. To save someone the time and heartache of doing that, I include the .NET MSDN/TechNet documentation with the code – to assist self-driven learning.

In this first of series, DownloadBingImage, Wes (Mills) was making a PowerShell script to download the Bing Search Image. Kevin (Miller) asked him if it was in a service or task and Wes replied that it was a task, so I challenged myself to write it into a Windows Service. I rose to the occasion – mostly, out of self-interest in Windows Services, as it would help me to learn more about coding fundamentals in this area.

And, so, a few months of trial and error and I have a working version of the service installed on my local machine and it downloads the image[s]. This same exact code is what I’m sharing with you.

You can find a copy of the code with in-line comments and links to the classes/methods here. If you want to start at the main service entry point, look here.

It should go without saying that I am not a professional developer (albeit, my role does require understanding all aspects of the SDLC), so if you have comments or are confused as to why I did something a particular way (from a professional developer’s perspective) and it doesn’t quite make sense, that’s why: I’m more of a hobbyist than what others would call a “professional”, at this point.

I hope that this helps at least one person on their journey towards software development. If it’s just one, then the effort was worth it. 🙂

Happy Coding!