Posts

Showing posts with the label PowerShell

Using Saved Credentials with PowerShell Scripts

Most of the time in a Windows environment, a Windows PowerShell script runs in the security context of the user account that is running it. If you have a scheduled task that runs a PowerShell script then you can specify a user account (service account) that is used to run the task. The service account needs to be assigned the necessary permissions to perform any actions in the script. In some cases, you need to script to access remote resources and sign in. For example, if you have a scheduled task that pulls reporting information from Office 365. PowerShell has a method for storing encrypted credentials that can only be accessed the user account that stored them. The code below prompts you for a credential and then stored is encrypted in an XML file. $credential = Get-Credential $credential | Export-CliXml D:\credential.xml To retrieve the credential and using it within a script, you read it from the XML file: $credential = Import-CliXml D:\credential.xml If you created the XML file w...

ACMESharp and Visual Studio Code Error

Image
I lost a fair bit of time troubleshooting an error that turned out to be an odd compatibility issue between the ACMESharp module and Visual Studio Code. Hopefully this saves someone else the time. In Visual Studio Code, when running Submit-ACMECertificate, I got this error: Submit-ACMECertificate : Error resolving type specified in JSON 'ACMESharp.PKI.CsrDetails, ACMESharp'. Path '$type', line 2, position 48. At line:1 char:1 + Submit-ACMECertificate nosub + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~     + CategoryInfo          : NotSpecified: (:) [Submit-ACMECertificate], JsonSerializationException     + FullyQualifiedErrorId : Newtonsoft.Json.JsonSerializationException,ACMESharp.POSH.SubmitCertificate I read a bunch of stuff about Newtonsoft.Json being installed in the Global Assembly Cache, but it wasn't on my computer. I tested the same script on my desktop instead of the laptop. Nope, same error. It turned out that the co...

Script to Synchronize Primary Email Address with UPN

When planning an Office 365 implementation, it is best practice to start by assuming that UPN for signing in to Office 365 should match the user email address. If you don't configure it this way, then users have two separate items (their UPN for signing in and their email address) that look very similar. In many cases users are confused by the similarity. If you are synchronizing  your on-premises Active Directory with Office 365 (in most cases you do) then you need to set the UPN for the on-premises user accounts with the correct UPN. The UPN from on-premises user accounts is synchronized to Office 365 to create the ID for signing in. Most organizations are not using the UPN on user accounts for authentication on-premises. The option has been there since Windows 2000, but most organizations still use the domainname\username format for authentication. However, you need to verify if any user accounts are using the UPN for authentication before making this change. At minimum, you sho...

Script to Remove Old Domains from User Email Addresses

When managing email addresses and domains in Exchange Server, old email addresses are never removed automatically. This is good because it ensures that email addresses on a mailbox are never accidentally lost. However, you may want to clean up old domains or address formats that are no longer in use. Some common scenarios where you might want to remove an old domain: An SMB deployment of Exchange Server where a .local domain was added as the first domain for email addresses. Old GroupWise addresses are left in place from an older migration. Obsolete domain left over from a company merger many years ago I often find that obsolete domains are identified when I run IDFix as part of preparing to migrate to Office 365. To simplify the removal of obsolete domains, I have created the following script. A few things to note: You need to set $RemovePattern to identify the domain to be removed. Any email addresses matching this pattern will be removed from proxyAddresses attribute in Active Direc...

Change All UPNs in a Domain

I needed to update all UPNs in a domain today. It was pretty quick to figure out, but here is one line to take care of it for you. Get-ADUser -Filter * | ForEach-Object { Set-ADUser $_ -UserPrincipalName ($_.UserPrincipalName).Replace("OldDomain","NewDomain")} Remember to make the pattern in the OldDomain unique enough that you don't accidentally change things you don't intend to. For example, if you are changing from a .local domain in the UPN to a .com, make sure that you replace ".local" and not "local" on the off chance one of the user IDs includes "local" in the name. If there are any user accounts without a UPN, then an error is generated for those accounts. My domain had 4 accounts without a UPN: krbtgt - default account used for kerberos IWAM_ServerName - Old IIS account from Windows 2003 IUSR_ServerName - Old IIS account from Windows 2003 support_XXXXXXX - Used by Help and Support service

Suppress Results when Adding Items to an ArrayList

I ran into a mildly annoying feature when adding items to an array list when using PowerShell today. An array list is an expandable array of items with better performance than a normal array when working with large data sets. Each time I added an item to the array list, it echoed back the index number of the list item. When I added the first item in the list, the number 0 was displayed on the screen. Adding a second item would echo back the number 1. For example: $proxylist.add($a) 0 I would prefer my script to be silent when running except when there is data that I want to display. However, there is no option obviously available for that purpose. Instead, you need to redirect the output to $null. There are a few ways to do this and any one will work: $proxylist.add($a) > $null $proxylist.add($a) | Out-Null [void]$proxylist.add($a)

PowerShell Script for Math Homework

My daughter needs to practice her multiplication tables. So, I came up with a little script that can help. You can use the script on any Windows computer. Copy the code below into a text file and then name that file something like multiply.ps1 . The file needs to end in .ps1 for Windows to recognize it as PowerShell. You may also need to allow PowerShell scripts on your computer. Open a PowerShell prompt and run Set-ExecutionPolicy RemoteSigned . If you have the file saved on your desktop, right-click it and select Run with Windows PowerShell Clear-Host $questions = Read-Host "How many questions?" For($i=1;$i -le $questions;$i++) { $first = Get-Random -Minimum 0 -Maximum 10 $second = Get-Random -Minimum 0 -Maximum 10 $answer = $first * $second Do { Clear-Host Write-Host "$first x $second = ??" $response = Read-Host "Enter your answer" If ($response -eq $answer) { Write-Host...

PowerShell Learning Resources

I'm doing some onsite PowerShell training this week and realized that I mention lots of resources but haven't provided a list of them anywhere for easy access. So, this posting is my best summary of Windows PowerShell related learning content from Microsoft. There are also a bunch of my links to my blog articles that I use as examples in class. General Resources Microsoft makes a lot of content available online for free. Here is a high level list: Microsoft Virtual Academy - Free online courses https://mva.microsoft.com/ Technet Labs Online - Free online labs that include activities. However, the VMs are not restricted and you can use them for any testing you want to do. https://technet.microsoft.com/en-us/virtuallabs Microsoft Ignite - The session videos from Microsoft's yearly technical conference. https://myignite.microsoft.com/videos Channel 9 - Videos from Microsoft development teams and conferences. Search for whatever you're interested in. https://channel9.m...

Finding Stale SIDs on GPOs

One of my clients has a tool from Microsoft that scans the AD infrastructure and generates a report of items that can fixed/improved. One of the items on a recent report was stale SIDs on GPOs that could affect GPO processing. However, the tools didn't give us the stales SIDs. Just said we had them. First, let's talk about what a stale SID is... All Windows security is based on a Security Identifier (SID) that is unique for each user or group. In the Access Control List (ACL) for an resource, it is the SID that is assigned permissions, not the name of a user or group. The Windows tools just translate that SID back to a user or group name for use to manage them easier. A stale SID occurs when a user or group has been assigned permissions to access a resource and the user or group is later deleted. There is no link back from the user or group to where the permissions have been assigned. So, Windows cannot go back and remove the SID from the ACL. The SID that's left behind wit...

How you do things matters (PowerShell Performance)

I've run into a couple of things over the last month or so that were interesting from a PowerShell perspective. With relatively small amounts of data, performance really isn't a concern. If you're only working with a few hundred or few thousand items PowerShell is pretty quick no matter what. However, when you work with larger datasets, how you do things can make a big difference. Reducing the number of operations The first thing I want to highlight is that that the number of operations affects performance. I was recently building some scripts to build a test environment by copying the AD structure from production. I based all of my work on AD Mirror ( https://gallery.technet.microsoft.com/scriptcenter/AD-Mirror-PowerShell-Module-331b1b12 ). I ran into a performance issue populating group membership. The code from AD Mirror had the list of group members in a variable and added them by using a foreach loop like this: foreach ($m in $newMembers) {      Add-ADGroupMembe...

Script to Create Migration Batches

Migration batches are a nice feature introduced in Exchange 2013 for managing mailbox moves. In general they work pretty well, but it can be a bit awkward to work with batches in large organizations. The graphical interface only displays 500 mailboxes at a time and this can be limiting. To get the batches exactly as you want them, you often end up exporting a list of mailboxes to a CSV file and then cutting that down into batches. You can create a batch by importing a CSV file. To simplify this process, I've created a script that takes all the mailboxes from a list of source databases and generates the CSV files in batch sizes that you specify. For example, you can use both Admin1DB and Admin2DB as your source databases. If you specify a batch size of 200 mailboxes, then you'll get CSV files with 200 mailboxes except for the final CSV which has the left overs. The script can also automatically create the migration batches if you turn that option on. In the script, you can speci...

Update Mount-ADDatabase for PowerShell v2

I'm working on some Active Directory (AD) disaster recovery projects right now and one of the recovery methods we're implementing is AD snapshots. With AD snapshots, you have a copy of your AD data to identify and recover from accidental changes. The client I'm working with has Windows 2008 R2 with PowerShell 2.0 for their domain controllers. PowerShell is my preferred method for automating anything at this point but AD snapshots don't have any PowerShell cmdlets. Fortunately Ashley McGlone, a Microsoft PFE, has created some PowerShell functions that help you manage and use AD snapshots. One of the coolest things in there is a function (Repair-ADAttribute) that lets you pull attributes from the snapshot and apply them to the same object in the production AD. You can read more about these functions and download them from these two locations: https://blogs.technet.microsoft.com/ashleymcglone/2014/04/24/oh-snap-active-directory-attribute-recovery-with-powershell/ https://g...

Finding the User or Group Name from a SID

I'm working on project where we needed to set AD security permissions in a test environment based on the permission based on production. When I generated a report of AD permissions that had been applied, several of the entries came back with SID numbers instead of user or group names. Typically this means that the user or group has been deleted, but I wanted to confirm. I wanted to take the SID and identify the user or group account that was associated with it. After a quick search I found a few examples that looked similar to this: $objSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-21-1454471165-1004335555-1606985555-5555") $objUser = $objSID.Translate([System.Security.Principal.NTAccount]) $objUser.Value Above example taken from: https://technet.microsoft.com/en-us/library/ff730940.aspx It seemed to me that there had to be an easier way using the ActiveDirectory module for PowerShell which isn't used by these examples. Good news, there is...

Finding PCs Infected with WORM_ZLADER.B

A virus has recently been making the rounds that propagates by renaming folders and storing an executable file in a Recycle Bin folder. Just today we saw this on some shared folders. Here is a link with more info about the virus: http://www.trendmicro.com/vinfo/us/threat-encyclopedia/malware/worm_zlader.b So, we can identify that someone is infected by finding the $Recycle.Bin folders in the shares. But we need to track down where this came from. To do that we want to see the owner of the $Recycle.Bin folder because that is the person that created it. You can't get the owner of the $Recycle.Bin folder by using Windows Explorer because Explorer treats this as a special folder type and limits what you can see. However, you can find the owner by using Get-ACL in PowerShell. On a large file server with many shares, it's useful to scan the whole server to verify the extent of the infection. The script below starts in the current directory, finds all of the Recycle.Bin folders, displ...

Error Using PowerShell Direct

If you've not heard of it, PowerShell Direct is a nifty new way that you can connect with virtual machines on a Windows 10 or Windows Server 2016 Hyper-V host. PowerShell Direct lets you enter PowerShell remoting sessions or invoke commands on a VM from the Hyper-V without requiring network connectivity. To enter a PowerShell remoting session: Enter-PSSession -VMName NameOfVM I've played with this a bit before and it's pretty cool. I've never had any issues with it. However, today when working with some VMs provided for a course I'm working on I got longer version of this error when trying to connect: Enter-PSSession : An error has occurred which Windows PowerShell cannot handle. A remote session might have ended. + FullyQualifiedErrorId :  CreateRemoteRunspaceForVMFailed,Microsoft.PowerShell.Commands.EnterPSSessionCommand This same error message is provided if your authentication credentials fail. So, I reset the local administrator password on the VM, but no fix....

The Total Data Received From the Remote Client Exceeded Allowed Maximum

Working with a large number of mailboxes is usually about the same a working with a small number of mailboxes, except that you need to include -ResultSize Unlimited in your Get-* cmdlets. However, I recently ran into the following error when getting a large list of mailboxes (approx 38K) with Get-Mailbox and piping them to Set-Mailbox: Sending data to a remote command failed with the following error message: The total data received from the remote client exceeded allowed maximum. Allowed maximum is 524288000. In the end this seemed to be a limitation of piping from Get-Mailbox to Set-Mailbox. For example, this would generate the error: Get-Mailbox -ResultSize Unlimited | Set-Mailbox -SingleItemRecoveryEnabled $true One option to work around this is to chunk up the work and do it by database instead: Get-Mailbox -Database XXX -ResultSize Unlimited | Set-Mailbox -SingleItemRecoveryEnabled $true However, my preferred workaround was this: $mbx=Get-Mailbox -ResultSize Unlimited foreach($m ...

Getting Status Totals in PowerShell

Image
I was doing an Exchange migration on the weekend and had a large number of move requests. Being the slightly OCD computer person as most of us are, I wanted to see how things were progressing occasionally. However, I didn't want to be dumping stuff into spreadsheets and be counting items. After few false starts, I ran across the Group-Object cmdlet. For me, this cmdlet is in the same category as Measure-Object. I've never had a need in the past. Now I'm happy to have it. My solution: Get-MoveRequest | Group-Object -Property Status The results looked like this:

Synchronizing Remote IP Ranges Across Recieve Connectors

Exchange 2010 and later do a nice job of providing high availability with database availability groups (DAGs) and load balancing. However, one configuration detail doesn't automatically synchronize between multiple Exchange servers, and that is receive connectors. If you create receive connectors for relaying output from printers or scanners then the connector you create is unique on each server. That's fine if you are pointing the devices at individual Exchange servers but to have high availability, you need multiple load balanced servers with the same configuration. To do this, you need to create the same receive connectors on each server. During intial setup creating 2 or 4 receive connectors with the same settings for authentication and such isn't too big a deal. The item that's a pain is the remote IP ranges that are allowed to use the receive connector. Many organizations have a large list of individual IP addresses that are allowed to use the receive connector. I...

Script for Exchange 2013 Message Tracking

Exchange Server 2010 had a graphical utility for analyzing message tracking logs. Unfortunately, this tool was removed from Exchange Server 2013. Instead in Exchange Server 2013, you have only the Get-MessageTrackingLog cmdlet. The Get-MessageTrackingLog cmdlet is a pain in the butt for a few reasons: You need to memorize the syntax . Most of it is pretty straight forward, but you need to remember the correct parameters for searching by sender, recipient, or subject. It only searches the local server by default . Without specifying servers, it only searches the local Exchange server that you're running the tool on. In a lot of cases, you need to see information from all your servers to track it down. While working on a message delivery problem this week, I wrote up a short script help with simple message tracking based on time, sender, recipient, or message subject. The script is as follows: Write-Host "Current Date/Time: $(Get-Date)" $StartTime = Read-Host "Start...

Script to Remove Old IIS Logs

One of the ongoing issues I seem to run into is Exchange servers running low on disk space for the C: drive. When this happens messages stop flowing because Exchange doesn't want to run out of disk space. Much of the time, the disk space on C: is being eaten up by IIS logs. IIS does not have any functionality to automatically delete old logs. So, I've seen servers with years of logs stored in C:\Inetpub\Logs\. Here is a script that you can schedule as a task to remove old IIS log files: # Adjust these two variables $iisLogDir = "C:\inetpub\logs" $deleteAfterDays = 14 #calculate date for deletion $removeDate = (Get-Date).AddDays(-$deleteAfterDays) #Delete Files Get-ChildItem -Path $iisLogDir -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $removeDate } | Remove-Item -Force This calculates the age of the file based on the creation time. If you want it to be based on modified time use $_.LastWriteTime instead. The Where-Object command uses !$_...