Archive

Posts Tagged ‘Function’

Function: Get-CsMaliciousCalls – Retrieve Records for User Reported Malicious Calls in Skype for Business

January 25th, 2018 No comments

Description

A little known feature in Skype for Business is the ability for a user to report a malicious call that they’ve received. This can be done by going to Settings>Tools>Report a Call…, as shown below.

At that point, the user is presented a dialog that allows them to report the just completed call:

If you don’t see the ‘Report a Call…’ option in your client, ensure that the option is enabled in the appropriate voice policy. It can be enabled by setting EnableMaliciousCallTracing to $true, such as

Set-CsVoicePolicy -Identity Global -EnableMaliciousCallTracing $true

Clients will pick up that setting at the next policy refresh.

When the call is reported, info is inserted into the ErrorReportView table of the LcsCDR database. However, unless you’re querying for it, you don’t see it, and many don’t even know where it is, or that it’s even there. It doesn’t do much good if your users are reporting the calls, but you have no way to view the data.

There are a couple of ways you can retrieve the data. The first is some custom reports in your Monitoring Server’s reports. Next is a simple SQL query in SQL Server Management Studio against the LcsCDR database for records with a diagnostics ID of 51017. Just use the following:

SELECT * FROM [LcsCDR].[dbo].[ErrorReportView] WHERE [MsDiagId] = '51017'

The last option is to use PowerShell to make the query against the SQL server for the same info. SQL queries in PowerShell are nothing new. They’re relatively fast, and you end up with an array that can be handled like any other object in PowerShell. If you’re using the default instance of SQL server for the LcsCDR database, use the function like this:

Get-CsMaliciousCalls -Server [server name]

If you’re using a named instance, you can just add the instance name to the command, such as this:

Get-CsMaliciousCalls -Server [server name] -Instance [instance name]

As you can see from the screenshot below, the function will return objects with plenty of info. Note that the FromUri field is the user reporting the malicious call (callee), and the ToUri is the caller. We see the MsDiagHeader notes the reason as “Call Identified as malicious by user”.

Obviously, what you do with the information is up to you. You could use PowerShell to further filter the ToUri into an e.164 number and use that to block numbers at your gateways. Or, use an MSPL script on the front end/mediation servers to drop calls with that number. Quite a few possibilities. If you come up with more ways to use the data, drop me a line or post a comment below. For now, toss the function in your PowerShell profile and enjoy your new data view.

Download

This script is available in my GitHub repo at https://github.com/patrichard/Get-CsMaliciousCalls. Feel free to grab it from there, and contribute any updates or improvements.

Function: Get-CsPhoneNumberAssignment – Find Where a Number is Assigned in Skype for Business

January 22nd, 2018 1 comment

Description

One of the problems that can be truly maddening when troubleshooting issues is a 485 ‘ambiguous’ error in Snooper. This is when Skype for Business doesn’t know what to do with an inbound call because the number being called is configured in more than one place. Skype for Business will complain if you try to configure a number more that once in SOME areas, such as several users, but not in all areas. So you’re left with hunting around for a while to figure out where else the number is defined. Meanwhile, users are complaining that calls aren’t working. So I came up with a quick function that will look through all of the areas that a number can be defined, and will list all matches. Additionally, you can use the script to verify that a number is NOT assigned somewhere before assigning it to a resource.

Yes, I know that others have done similar things, notably Tom Arbuthnot’s Get-LyncNumberAssignment :Find #Lync Users/Objects by Phone Number/LineURI #PowerShell and Amanda Debler’s Is that Skype for Business (Lync) Number Free?, as well as other phone number management solutions such as those by Stale Hansen and Lasse Nordvik Wedø. I’ve had a previous version of my script in my profile for a long time and decided to clean it up and make it available.

Is that Skype for Business (Lync) Number Free?

This PowerShell function will look for a full or partial number to see where it is allocated. It looks at the following:

  • User LineUri
  • User PrivateLine
  • Meeting Room LineUri
  • Meeting Room PrivateLine
  • Analog Devices
  • Common Area Phones
  • Unified Messaging (UM) Contacts
  • Dial-In Conferencing Access Numbers
  • Trusted Application Numbers
  • Response Group Numbers

The function accepts input via the named LineUri parameter, or via pipeline. It returns a typical PowerShell object. Here is an example of specifying a full e.164 number.

Get-CsPhoneNumberAssignment -LineUri 12145551212

Example: Specifying a full e.164 number results in a single match. In this case, a user. Click image for larger view.

Specifying a partial number will likely show more matches.

Get-CsPhoneNumberAssignment -LineUri 1214

Example: Specifying a partial number results in several matches. In this case, some users and a dial-in access number. Click image for larger view.

Note that since it must look at all of the related objects in order to build the object, it can take a minute or so to complete. But at least now there is a single command you can run to look in all areas.

Download

This script is available in my GitHub repo at https://github.com/patrichard/Get-CsPhoneNumberAssignment. Feel free to grab it from there, and contribute any updates or improvements.

Function: Get-UpdateInfo – Making It Easy for Your Users to Get the Latest Version of Your Scripts

October 10th, 2016 No comments

updatepromptDescription

As a PowerShell developer, you always want your users to have the latest version of a script. It makes support a lot easier, while also making sure that users have the latest features and bug fixes. But how to encourage that? Well, for me, users of my scripts are typically not within the same environment as me. So Group Policy Objects, logon scripts, etc, aren’t a solution. Having the script automatically check for an update is much easier, and doesn’t require anything from the user1. So let’s take a look at a quick and easy method.

First, we need a repository where the update information will be held. XML is perfect for this. In this example, I created the following file, and saved it as version.xml:

<?xml version="1.0"?>
<catalog>
<article id="1697">
<title>Set-CsFeatures.ps1</title>
<author>Pat Richard</author>
<version>3.9.57</version>
<publish_date>2016-10-08</publish_date>
<description>Installs all required Windows 2012/Windows 2012 R2 components & optional tools.</description>
</article>
</catalog>

This file can reside anywhere. A file path, a web site, wherever. I chose a website for the reasons I mentioned above. You can see the above file in action at https://www.ucunleashed.com/downloads/version.xml. Some key points to the file. Each article I publish going forward will have it’s own “article” node. The ID I chose to tie to it is also the ID of the article’s URL, for consistency sake. In this example, 1697 is the prereq script seen at https://www.ucunleashed.com/1697. The version value is the version of the latest general availability (“GA”) build. We’ll query that value, compare it against the version of the script running the query, and see if it’s newer. Note that there is some other info in the XML file, and that’s irrelevant to what we’re discussing here.

[xml] $xml = (New-Object System.Net.WebClient).DownloadString("https://www.ucunleashed.com/downloads/version.xml")
$Ga = ($xml.catalog.article | Where-Object {$_.id -eq $article}).version

We supply the $article value when making the call. After that, it’s a simple comparison. In the prereq script, near the beginning, I assign a variable, $version, with a value. Let’s say it’s “3.9.55”. We compare $Ga against $Version

$Ga -gt $Version

If it’s true, we know a newer version exists. If it’s false, we know the currently running script is the latest version. In theory, we could also use this to alert of a regression in case we needed to downgrade (gasp!). So let’s put this together. We assign a variable, $xml, to the results of downloading an xml file. Then, we assign $ga to the value of “version” for the specific node within the xml file that contains the info for the article. Lastly, we do our comparison and give some output if there is an update.

[xml] $xml = (New-Object System.Net.WebClient).DownloadString("https://www.ucunleashed.com/downloads/version.xml")
$Ga = ($xml.catalog.article | Where-Object {$_.id -eq $article}).version
if ($Ga -gt $Version){Write-Output "A new version is available!"}

Now, obviously, we can pretty this up a bit. But before we do that, let’s think of issues we could run into. The big one is making sure we have an Internet connection to use to check the XML file. As much as we can often assume there will be one, a LOT of organizations block Internet access to servers as part of their security posture. So we shouldn’t assume. We can check using the following:

[bool] $HasInternetAccess = ([Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]'{DCB00C01-570F-4A9B-8D69-199FDBA5723B}')).IsConnectedToInternet)

And then using an IF loop against $HasInternetAccess. So let’s throw this all into a function we can incorporate into our scripts and modules:

function Get-UpdateInfo {
  [CmdletBinding(SupportsShouldProcess, SupportsPaging)]
  param (
    # Article/script to check for updates
    [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [string] $article
  )
  [bool] $HasInternetAccess = ([Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]'{DCB00C01-570F-4A9B-8D69-199FDBA5723B}')).IsConnectedToInternet)
  if ($HasInternetAccess){
    [xml] $xml = (New-Object System.Net.WebClient).DownloadString("https://www.ucunleashed.com/downloads/version.xml")
    $Ga = ($xml.catalog.article | Where-Object {$_.id -eq $article}).Version    
    if ($Ga -gt $version){
      Write-Log -Level Warn -Message "Outdated version. Version $Ga is latest version. Prompting user" -NoConsole
      $wshell = New-Object -ComObject Wscript.Shell -ErrorAction Stop
      $updatePrompt = $wshell.Popup("A new version ($ga) of the script is available. Would you like to download it?",0,"A new version is available",68)
      if ($updatePrompt -eq 6){
        Start-Process "https://www.ucunleashed.com/$article"
      }
    }
  }else{
    Write-Output "No Internet connectivity. Unable to check online for update info."
  }
} # end function function Get-UpdateInfo

Here we incorporate a simple ComObject popup message to ask if the user wants to download the new version. Since we have assigned the GA number to $ga, we can use that in the popup text, as well, as shown in the image at the beginning of this article. If $updatePrompt is “6”, then the user clicked “Yes” on the popup, and we can take action such as opening a browser window and navigating to the articles page. Or we could download a file, or any of a number of actions. If $updatePrompt is “7”, then the user clicked “No”.

So, as you can see, it’s really not that hard to add an update checker to your scripts. When you release a new version, simply update the XML file to reflect accordingly.

Note: Take care in what kind of characters are in the XML file. Some special characters, such an ampersand (“&”), aren’t handled very well. When in doubt, open a browser window and navigate to the file.

1 – Depending on the action you require once it’s known an update is available.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Function: Set-TimeZone – Configure Time Zone via PowerShell

March 4th, 2013 1 comment

Powershell_logo-137x137Note: This functionality was added into PowerShell version 3.1.For more information, see https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.management/set-timezone

It’s easy to just double-click on the time in the system tray and set the time and time zone when building a new server. You may even have a GPO that does this for you. That’s all fine and dandy. But GPOs don’t work for member servers, and if you build a lot of servers, like I do, it’s a repetitive task – something perfectly suited for PowerShell.

With this function, we do nothing more than call tzutil.exe with the timezone name. Nothing fancy. I added some validation to make sure that the timezone specified is truly one of the 100 valid time zones that tzutile.exe should recognize and accept.

So, now, in your server provisioning script, merely call the function, Set-TimeZone, with the desired timezone. For example:

Set-TimeZone "Pacific Standard Time"

The script defaults to “Eastern Standard Time” if you don’t supply a timezone. That’s it. There is no screen output for this function. Here’s the function:

function Set-TimeZone {
  [CmdletBinding(SupportsShouldProcess = $True)]
  param( 
    [Parameter(ValueFromPipeline = $False, ValueFromPipelineByPropertyName = $True, Mandatory = $False)]
    [ValidateSet("Dateline Standard Time","UTC-11","Hawaiian Standard Time","Alaskan Standard Time","Pacific Standard Time (Mexico)","Pacific Standard Time","US Mountain Standard Time","Mountain Standard Time (Mexico)","Mountain Standard Time","Central America Standard Time","Central Standard Time","Central Standard Time (Mexico)","Canada Central Standard Time","SA Pacific Standard Time","Eastern Standard Time","US Eastern Standard Time","Venezuela Standard Time","Paraguay Standard Time","Atlantic Standard Time","Central Brazilian Standard Time","SA Western Standard Time","Pacific SA Standard Time","Newfoundland Standard Time","E. South America Standard Time","Argentina Standard Time","SA Eastern Standard Time","Greenland Standard Time","Montevideo Standard Time","Bahia Standard Time","UTC-02","Mid-Atlantic Standard Time","Azores Standard Time","Cape Verde Standard Time","Morocco Standard Time","UTC","GMT Standard Time","Greenwich Standard Time","W. Europe Standard Time","Central Europe Standard Time","Romance Standard Time","Central European Standard Time","W. Central Africa Standard Time","Namibia Standard Time","Jordan Standard Time","GTB&nbsp;Standard Time","Middle East Standard Time","Egypt Standard Time","Syria Standard Time","E. Europe Standard Time","South Africa Standard Time","FLE&nbsp;Standard Time","Turkey Standard Time","Israel Standard Time","Arabic Standard Time","Kaliningrad Standard Time","Arab Standard Time","E. Africa Standard Time","Iran Standard Time","Arabian Standard Time","Azerbaijan Standard Time","Russian Standard Time","Mauritius Standard Time","Georgian Standard Time","Caucasus Standard Time","Afghanistan Standard Time","Pakistan Standard Time","West Asia Standard Time","India Standard Time","Sri Lanka Standard Time","Nepal Standard Time","Central Asia Standard Time","Bangladesh Standard Time","Ekaterinburg Standard Time","Myanmar Standard Time","SE Asia Standard Time","N. Central Asia Standard Time","China Standard Time","North Asia Standard Time","Singapore Standard Time","W. Australia Standard Time","Taipei Standard Time","Ulaanbaatar Standard Time","North Asia East Standard Time","Tokyo Standard Time","Korea Standard Time","Cen. Australia Standard Time","AUS Central Standard Time","E. Australia Standard Time","AUS Eastern Standard Time","West Pacific Standard Time","Tasmania Standard Time","Yakutsk&nbsp;Standard Time","Central Pacific Standard Time","Vladivostok Standard Time","New Zealand Standard Time","UTC+12","Fiji Standard Time","Magadan&nbsp;Standard Time","Tonga Standard Time","Samoa Standard Time")]
    [ValidateNotNullOrEmpty()]
    [string]$TimeZone = "Eastern Standard Time"
  ) 

  $process = New-Object System.Diagnostics.Process 
  $process.StartInfo.WindowStyle = "Hidden" 
  $process.StartInfo.FileName = "tzutil.exe" 
  $process.StartInfo.Arguments = "/s `"$TimeZone`"" 
  $process.Start() | Out-Null 
} # end function Set-TimeZone

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Function: New-PSUpdateHelpScheduledTask – Auto Update PowerShell Help

March 1st, 2013 2 comments

Powershell_logo-137x137Probably not of much value to people unless you build a fair amount of servers and have it scripted, but…

One thing you want to do with servers running PowerShell v3.0, such as Windows Server 2012, is to regularly run Update-Help from a PowerShell session to update the help files PowerShell uses for various modules. But this is something that can be easily forgotten, and, who wants to manually do a recurring task, anyways?

Here is a function you can toss in your server build script to create a scheduled task that will automatically update PowerShell help every day at 7pm. The task runs as ‘system’, so we don’t have to worry about storing credentials. Here’s the function

function New-PSUpdateHelpScheduledTask {
	[CmdletBinding(SupportsShouldProcess = $true)]
	param()
	BEGIN{
		[string] $TaskArguments = '-NoProfile -Command "& {Update-Help -Force; Start-Sleep -Seconds 5; Get-ChildItem $env:temp -Recurse -ErrorAction SilentlyContinue | Where-Object {$_.PSIsContainer -eq $True -and $_.GetFiles() -match ''.cab''} | Remove-Item -Recurse -Force}"'
		[string] $TaskProgram =  "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
	}
	PROCESS{	
		$TaskAction = New-ScheduledTaskAction -Execute $TaskProgram -Argument $TaskArguments
		$TaskTrigger = New-ScheduledTaskTrigger -Daily -At 19:00
		Register-ScheduledTask -TaskName "Update PowerShell Help" -Action $TaskAction -Trigger $TaskTrigger -RunLevel Highest -User "system" -Description "Automatically updates PowerShell help." | Out-Null
	}
	END {
		Remove-Variable TaskAction
		Remove-Variable TaskTrigger
	}
} # end function New-PSUpdateHelpTask

and merely call it using

New-PsUpdateHelpScheduledTask

You’ll notice a new scheduled task in Task Scheduler, and we see that it completed successfully.
Update PowerShell Help
Just a couple of lines of code and we’ve removed the need to manually run Update-Help on a server.

Hope this helps!

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Function: Set-AdminUser – Clear AdminCount and Enable Security Inheritance

October 22nd, 2012 25 comments

Powershell_logo-137x137Description

While migrating some users during a Lync migration, I needed to disable users for Lync in one forest, and enable them in another. I ran into a problem where many users in the legacy forest had adminCount set to 1, and security inheritance disabled. The problem is that my account didn’t have rights to disable them in Lync, and I was getting an access denied error. This is common when the user is/was a member of a protected group. I won’t go into the background of adminCount, as it’s well documented.

I knew that as the migration progressed, we would identify more users where this was the case. So I wanted to find a better way of dealing with these. There are several ways of finding users with adminCount set using PowerShell, including

([adsisearcher]"(AdminCount=1)").findall()

and using the ActiveDirectory PowerShell module via

Get-ADuser -LDAPFilter "(admincount=1)" | Select-Object name

and we can look at groups, too, using

Get-ADgroup -LDAPFilter "(admincount=1)" | Select-Object name

Turns out that many of the users (1000+) were no longer members of protected groups, what’s often referred to as Orphaned AdminSD Objects. So we could clear adminCount and enable security inheritance. But doing this manually on 1000+ users isn’t something that any of us wanted to spend time doing.

We can clear adminCount with a one-liner:

Get-AdUser [user name] | Set-AdObject -clear adminCount

But that doesn’t take care of security inheritance, which is the real culprit in my issue. We can use dsacls:

$User = [ADSI] $_.Path
dsacls $User.distinguishedName /p:n

Unfortunately, this involves multiple steps in native PowerShell. So, a function was born.

Set-AdminUser takes input from either the $UserName parameter, or via the pipeline, and clears adminCount, then enables security inheritance. It can process a single user or multiple, such as with the output of Get-ADGroupMember.

Syntax

Set-AdminUser [[-UserName] ] [-WhatIf] [-Confirm] []

Examples

Set-AdminUser [user name]
Get-AdGroupMember [group name] | Set-AdminUser

Code

Here is the function to copy. You can also download it in the download section below.

function Set-AdminUser	{  
	<# 
	.SYNOPSIS
			Clears adminCount, and enables inherited security on a user account.
	
	.DESCRIPTION
			Clears adminCount, and enables inherited security on a user account.
	
	.NOTES
	    Version    	      	: v1.0
	    Wish list						: 
	    Rights Required			: UserAdministrator
	    Sched Task Req'd		: No
	    Lync Version				: N/A
	    Lync Version				: N/A
	    Author       				: Pat Richard, Skype for Business MVP
	    Email/Blog/Twitter	: pat@innervation.com 	https://www.ucunleashed.com @patrichard
	    Dedicated Post			: https://www.ucunleashed.com/1621
	    Disclaimer   				: You running this script means you won't blame me if this breaks your stuff.
	    Info Stolen from 		: http://serverfault.com/questions/304627/powershell-script-to-find-ad-users-with-admincount-0
													: http://morgansimonsen.wordpress.com/2012/01/26/adminsdholder-protected-groups-sdprop-and-moving-mailboxes-in-exchange/
	
	.LINK     
	    
Function: Set-AdminUser – Clear AdminCount and Enable Security Inheritance
.INPUTS You can pipeline input to this command .PARAMETER UserName Create the scheduled task to run the script daily. It does NOT create the required Exchange receive connector. .EXAMPLE Set-AdminUser -UserName [user name] Description ----------- Clears the adminCount of the specified user, and enabled inherited security .EXAMPLE Get-AdGroupMember [group name] | Set-AdminUser Description ----------- Clears the adminCount of all group members, and enabled inherited security #> #Requires -Version 2.0 [CmdletBinding(SupportsShouldProcess = $True)] param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $True, Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$UserName ) Begin{ ## allows inheritance [bool]$isProtected = $false ## preserves inherited rules [bool]$PreserveInheritance = $true } Process{ [string]$dn = (Get-ADUser $UserName).DistinguishedName Set-AdObject -identity $dn -clear adminCount $user = [ADSI]"LDAP://$dn" $acl = $user.objectSecurity Write-Verbose $dn Write-Verbose "Original permissions blocked:" Write-Verbose $acl.AreAccessRulesProtected if ($acl.AreAccessRulesProtected){ $acl.SetAccessRuleProtection($isProtected,$PreserveInheritance) $inherited = $acl.AreAccessRulesProtected $user.commitchanges() Write-Verbose "Updated permissions blocked:" Write-Verbose $acl.AreAccessRulesProtected } } End{ remove-variable acl remove-variable UserName remove-variable isProtected remove-variable PreserveInheritance remove-variable dn remove-variable user } } # end function Set-AdminUser

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.0 – 10-20-2012 Set-AdminUser.v1.0.zip

Categories: PowerShell Tags: ,

Function: New-SignedScript – Easily Sign One or Many Scripts with Your Code Signing Cert

September 20th, 2012 No comments

Signs a PowerShell script with a code signing certificate.

Syntax

New-SignedScript [[-path] ] [-Verbose] [-Debug] [-ErrorAction ] [-WarningAction ] [-ErrorVariable ] [-WarningVariable ] [-OutVariable ] [-OutBuffer ] [-WhatIf] [-Confirm]

Detailed Description

One of the concerns about using a PowerShell script is that it often requires the user to change the Execution Policy on the machine the script is running on. This can cause security concerns, because when the Execution Policy is lowered, any script can run, including those with malicious intent. For more information on setting the Execution Policy, see Set-ExecutionPolicy.

Of course, you need a code signing certificate in order to sign scripts. Fellow Exchange MVP Mike Pfeiffer wrote an informative article, Obtaining a Code Signing Certificate and Signing PowerShell Scripts that covers using an internal Certificate Authority. Third party Certificate Authorities (CAs) such as Digicert also provide code signing certificates. I can’t recommend Digicert enough. I have both a standard code signing certificate and an Extended Validation code signing certificate.

But signing scripts manually can be a little cumbersome. This function gets the current code signing certificate, verifies it’s not expired, and then signs the script. The script will only sign .ps1 files, and will not attempt to sign a script that’s already signed.

Example

New-SignedScript -path [path to script]

such as

New-SignedScript -path .\myscript.ps1

You can also pipeline files to this function, for example:

Get-Item *.ps1 | New-SignedScript

Installation

Nothing special here. Once you have a valid code signing certificate installed, the function should work as designed.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.4 – 09-08-2017 – New-SignedScript.v1.4.zip

v1.3 – 09-18-2016 – New-SignedScript.v1.3.zip

v1.1 – 06-10-2014 – New-SignedScript.v1.1.zip

v1.0 – 09-20-2012 – New-SignedScript.v1.0.zip

Changelog

See changelog for info on latest versions, including bug fixes, code tweaks, etc.

Categories: PowerShell Tags: , ,

Function: New-FirewallRule – Add Windows Firewall Rules Via PowerShell

September 14th, 2012 1 comment

Description

Some of my scripts, namely Get-CsConnections.ps1, require specific firewall rules be created in order to operate correctly. So I set out to automate as much as possible the creation of these rules. A function was born.

This little function can do pretty much everything the Windows Firewall wizard can do. You can specify local and remote ports, local and remote IP addresses, programs, services, direction Inbound or Outbound), TCP/UDP or Any, and more. I borrowed some of the info from the Windows Firewall chm file for the comment-based help file in the function. Just run

Get-Help New-FirewallRule

for detailed help.

I tried to think of (and test) common requirements for firewall rules, but if I’ve left something out, or something isn’t working as expected, feel free to leave a comment below.

Syntax

New-FirewallRule [[-name] ] [[-localPorts] ] [[-remotePorts] ] [[-localAddresses] ]
 [[-remoteAddresses] ] [[-program] ] [[-serviceName] ] [[-description] ] [-outbound] 
[-udp] [-block] [-readonly] [-any] [-domain] [-public] [-private] [-WhatIf] [-Confirm] []

Name This is the name of the firewall rule. As a best practice, give the firewall rule a unique name. If two rules have the same name, then you cannot easily manage them by using the netsh or PowerShell commands. Do not use the name “all” for a firewall rule because that is the name of a netsh command-line tool keyword.

LocalPorts If you are using the TCP or UDP protocol type, you can specify the local port by using one of the choices from the drop-down list or by specifying a port or a list of ports. The local port is the port on the computer on which the firewall profile is applied.

RemotePorts If you are using the TCP or UDP protocol type, you can specify the local port and remote port by using one of the choices from the drop-down list or by specifying a port or a list of ports. The remote port is the port on the computer that is attempting to communicate with the computer on which the firewall profile is applied.

LocalAddresses The local IP address is used by the local computer to determine if the rule applies. The rule applies only to network traffic that goes through a network adapter that is configured to use one of the specified local IP addresses.

RemoteAddresses Specify the remote IP addresses to which the rule applies. Network traffic matches the rule if the destination IP address is one of the addresses in the list.

Program Use this option to match network packets going to or from a specified program. If the program is not running, then no packets match the rule. Type the complete path to the program. You can include environment variables, where appropriate. When you add a program to the rule, Windows Firewall with Advanced Security dynamically opens (unblocks) and closes (blocks) the ports required by the program. When the program is running and listening for incoming traffic, Windows Firewall with Advanced Security opens the required ports; when the program is not running or is not listening for incoming traffic, Windows Firewall with Advanced Security closes the ports. Because of this dynamic behavior, adding programs to a rule is the recommended method for allowing unsolicited incoming traffic through Windows Firewall.

ServiceName Use this option to apply the rule only to services, not to other processes. Specify the short name of the service to which you want the rule to be applied.

Description This is a description of the rule. Use this to provide information about the rule, such as the rule owner, the rule requester, the purpose of the rule, a version number, or the date of creation.

Outbound Configures the rule as outbound. If not specified, the rule is created as inbound.

UDP Use this option to specify that the rule should filter UDP traffic. If not specified, and -Any is also not specified, the rule will filter TCP traffic. Cannot be used with -ANY.

Block Use this option to explicitly block any network packet that matches the firewall rule criteria. The block action takes precedence over the allow action, unless the Override block rules option is selected when the firewall rule is created.

ReadOnly If used, the rule will be created and attributes such as Program, Protocols, and Ports cannot be edited after creation. To change these settings, delete the rule and recreate it.

Any Use the option to filter traffic from any protocol. Cannot be used with -UDP.

Domain Applies when a computer is connected to a network that contains an Active Directory domain controller in which the computer’s domain account resides.

Private Applies when a computer is connected to a network in which the computer’s domain account does not reside, such as a home network. The private profile settings should be more restrictive than the domain profile settings. A network is assigned the private type by a local administrator.

Public Applies when a computer is connected to a domain through a public network, such as one available in airports and coffee shops. The public profile settings should be the most restrictive because the computer is connected to a public network where the security cannot be as tightly controlled as it is in an IT environment. By default, newly discovered networks are assigned the public type.

Examples

There is a vast number of combinations that can be used to create rules. I’ve tested a bunch, but cannot possibly test every conceivable combination. Here are a couple of examples:

New-FirewallRule -Name "Test Rule" -Description "My cool Lync rule" -Domain -Public -Private -Any -Program "C:\Program Files\Microsoft Lync Server 2010\File Transfer Agent\FileTransferAgent.exe" -ReadOnly
New-FirewallRule -name "World Wide Web Services" -description "An inbound rule to allow HTTPS traffic for Internet Information Services (IIS) [TCP 443]" -domain -private -public -localports "443" -Program "System"

Installation

No installation needed. But the function does need to run in an elevated session.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.0 (09-14-2012) New-FirewallRule.v1.0.zip

Changelog

See the changelog for information on features and bugs fixed in various versions.

Categories: PowerShell Tags:

Function: Remove-NicGateway – Removing the Default Gateway on Selected NICs Via PowerShell

March 13th, 2012 No comments

Description

When deploying Database Availability Groups (DAGs) with Exchange 2010, multiple network are generally used. You’ll have a client or “MAPI” network and at least a replication network. I’ve seen some organizations that also deploy backup networks. Each has their own NICs or NIC teams. Only the client network should have a default gateway defined. The rest should not. Static routes are added for the others using the NETSH command.

Setting the NIC properties is sometimes  a manual task, and sometimes a scripted task via PowerShell. On a large project, I needed to run a validation script to ensure that the servers were consistent and ready for the Exchange build, and fix those that could be done via script. I noticed that servers were coming with gateways defined on all of the NIC teams, so I need to resolve this. Turns out, it was a little challenging to do it via PowerShell.

There is apparently no easy way to just remove the gateway. We can easily set it, but my assumption that setting it to $null would work was incorrect. What I ended up doing, with the assistance of Serkan Varoglu, was to change the NIC from static to DHCP, then back to static, defining only the IP address and subnet mask. Not the most direct method, but it works. And, it appears to leave other parameters intact, including DNS servers, suffixes, WINS, etc.

First we use WMI to grab the NIC by name ($NicName):

$Adapter = Get-WmiObject -Class Win32_NetworkAdapter -Filter "NetConnectionID='$NicName'"

Then we get the configuration for the NIC by calling it using the index number of the NIC we got from above:

$Nic = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=$($Adapter.Index)"

Next, we need to grab the NIC’s IP and subnet mask so we can assign them again later:

$NicIP = $Nic.IpAddress[0]
$NicMask = $Nic.IpSubnet[0]

The, we set the NIC to DHCP,

$Nic.EnableDhcp() | Out-Null

And then back to static, using the IP and mask we retrieved from above:

$Nic.EnableStatic($NicIp,$NicMask) | Out-Null

We can wrap this into a function and call it in our validation scripts.

function Remove-NicGateway	{
<#
.SYNOPSIS
	Removes the default gateway on a specified network interface card (NIC)

.DESCRIPTION
	Removes the default gateway on a specified network interface card (NIC) by first setting the NIC to DHCP, and then setting it back to static and not specifying the gateway - just the IP and subnet mask

.NOTES
  Version      				: 1.0
  Rights Required			: Local admin on server
  										: ExecutionPolicy of RemoteSigned or Unrestricted

	Author       				: Pat Richard, Exchange MVP
	Email/Blog/Twitter	: pat@innervation.com 	https://www.ucunleashed.com @patrichard
	Dedicated Blog			: https://www.ucunleashed.com/152

	Author       				: Serkan Varoglu
	Email/Blog/Twitter	: N/A	http://www.get-mailbox.org	@SRKNVRGL

	Disclaimer   				: You running this script means you won't blame me if this breaks your stuff.
	Info Stolen from 		: 

.EXAMPLE
	Remove-NIcGateway -NicName [name of NIC]

.INPUTS
	None. You cannot pipe objects to this script.

#Requires -Version 2.0
#&gt;
	[cmdletBinding(SupportsShouldProcess = $true)]
	param(
		[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No NIC name specified")]
		[ValidateNotNullOrEmpty()]
		[string]$NicName
	)
	$Adapter = Get-WmiObject -Class&nbsp;Win32_NetworkAdapter -Filter "NetConnectionID='$NicName'"
	$Nic = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=$($Adapter.Index)"
	$NicIP = $Nic.IpAddress[0]
	$NicMask = $Nic.IpSubnet[0]
	Write-Verbose "$NicIP $NicMask"
	$Nic.EnableDhcp() | Out-Null
	Start-Sleep -s 5
	Write-Verbose "Setting $NicName to $NicIP $NicMask"
	$Nic.EnableStatic($NicIp,$NicMask) | Out-Null
} # end function Remove-NicGateway

And call it via:

Remove-NicGateway -NicName [NIC/Team Name]

such as

Remove-NicGateway -NicName "Replication"

Hopefully, this will be useful to you.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Function: Set-DriveLabel – Change the Label of a Drive Via PowerShell

March 6th, 2012 1 comment

Powershell_logo-137x137Description

Here’s a simple function to change the label of a drive.

function Set-DriveLabel	{
	<#
	.SYNOPSIS
	  Sets the label on a drive.

	.DESCRIPTION
	  Sets the label on a drive to a user specified value

	.NOTES
	    Version      			: 1.0
	    Rights Required			: Local admin on server
	    					: ExecutionPolicy of RemoteSigned or Unrestricted
	    Exchange Version			: N/A
            Author     				: Pat Richard, Exchange MVP
            Email/Blog/Twitter	                : pat@innervation.com 	https://www.ucunleashed.com @patrichard
            Dedicated Blog			: https://www.ucunleashed.com/1097
            Disclaimer   			: You running this script means you won't blame me if this breaks your stuff.

	.EXAMPLE
		Set-DriveLabel -DriveLetter "d:" -DriveLabel "Data"

	.INPUTS
		None. You cannot pipe objects to this script.

	#Requires -Version 2.0
	#>
	[cmdletBinding(SupportsShouldProcess = $true)]
	param(
		[parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No drive letter specified")]
		[string]$DriveLetter,
		[parameter(Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No drive label specified")]
		[string]$DriveLabel
	)
	Write-Host "Setting drive label - drive $DriveLetter"
	$drive = Get-WmiObject -Class Win32_Volume -Filter "DriveLetter = '$DriveLetter'"
	Set-WmiInstance -input $drive -Arguments @{Label="$DriveLabel"} | Out-Null
	If ((Get-WmiObject -Class Win32_Volume -Filter "DriveLetter = '$DriveLetter'").Label -eq $DriveLabel){
		return $true
	}else{
		return $false
	}
} # end function Set-DriveLabel

You would then call it as such:

Set-DriveLabel -DriveLetter [drive] -DriveLabel [label]

such as

Set-DriveLabel -DriveLetter d: -DriveLabel "Data"

Comment based help is available via

Get-Help Set-DriveLabel

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Categories: PowerShell Tags: ,