Friday, January 18, 2013

Signing multiple PowerShell Scripts at once

I have an environment where Active Directory GPO ensures that Script Execution Policy is set to "Allsigned".  As you know, only signed script will be allowed to be executed.   I will not go into the details on how to sign a script, you can look at the following link to learn more about it:

Signing PowerShell Scripts by Scott Hanselman

Ok, where were we?  Yes, so you want to automate the process of signing bunch of scripts that already gone through some sort of gate-keeping process (you validated, tested, inspected, re-validated,re-tested,re-inspected ... and so on). Scripts are stored in a secure share \\network.location.com\Repository\scripts2Bsigned\.

Although this is not the best security practice to allow automation to sign bunch of scripts, I had to go that route for this specific instance.  So svc_signer's personal store had the script-signing certificate associated with it.   (IF svc_signer were to interactively logged into that server, and run certmgr.msc, it would see the correct cert under its personal store).  So for this purpose, a task runs frequently to look for ps1 files  in that location for signing:

# Create Log output$logpath = “\\network.location.com\Repository\logs” $Outputfile = “{0}\{1:yyyy.MM.dd-hh.mm.ss}-sign-scripts.txt” -f $logpath, $(Get-Date) $starttime = Get-Date
write-output
“**** Script signing time: $starttime ****” | Out-File $Outputfile -append # Looks for ps1 file for signing$scriptdir = “\\network.location.com\Repository\scripts2Bsigned\” $scripts = get-childitem -recurse $scriptdir | where-object{($_.Extension -ieq '.PS1')} #Ensures the files are converted to UTF8 format $list = @() foreach ($script in $scripts) {
$list += $script.fullname }
foreach($File in $list){ $TempFile = “$($File).UTF8" get-content $File | out-file $TempFile -Encoding UTF8 remove-item $File rename-item $TempFile $File }
# If a code-signing cert already not present, enter SmartCard device that has the certificate

$cert = Get-ChildItem cert:\CurrentUser\my -codesigning

# look for all the scripts with ps1 extension
$Files = Get-ChildItem $scriptdir *.ps1 -Recurse

# For each file found get its file path and sign the script Foreach($file in $Files) {
$a = $file.fullname Set-AuthenticodeSignature -filepath $a -Certificate $cert -IncludeChain ALL -force | Out-File $Outputfile -append }
$finishtime = Get-Date write-output “**** script finished $finishtime ****” | Out-File $Outputfile -append #END

In my scenario, I also decided to move the scripts to another folder, so only scripts stays in the share are those that need signing.  Even if you leave the signed script there, and they get signed again, it is not a big deal.

Wednesday, September 28, 2011

Setting up a secure WinRM / PowerShell environment

For the last couple of weeks, I have been working with a team to plan out WS-MAN (a.k.a. Windows Management Framework) layout for ongoing support and administration.   I am not going to go into too much details of the entire project, but share some tidbits with you.

The goal of my project was to setup a secure Windows management framework for number of sysadmin type who will use PowerShell to perform day to day administration of many (many) servers running Windows Server 2003 and Windows 2008 R2  (and plethora of Roles, Features and third party applications).   I should mention that the Admins are in a different network than the servers they are managing and they are managing resources that are part of number of resource domains  (Mostly still Active Directory 2003 Forest/Domain scenario).  All these resource domains trust the domain those admins are users of.

Some key points to consider

-All the communication has to be secure (HTTPS)
-All servers will have "Allsigned"  execution Policy
-You know what goes with above?  Yep, Code Signing with certificate
-No Communication over HTTP
-Also no direct communication to all the servers, so a Admin/Jump Server has to be in place.
-Scripts has to be checked in via a code management software (like TFS)

Before I go on, I want to mention one thing.   I had a wrong impression on "TrustedHosts" setting in WinRM.  I really thought it was as cool as it sounded like to me.  Sigh!

Well, I wanted "TrustedHosts" configuration to be a way to restrict who I want in or not.  In my scenario, especially its being a Domain environment, TrustedHosts doesn't really have much role to play.  I would like for somebody to tell me otherwise.

So to tackle WinRM over HTTPS, I had to run

Enable HTTPS:
WinRM QuickConfig -Transport:HTTPS

->  I got an error saying cannot create a WinRM listener. Machine doesn't have an appropriate certificate.  A Certificate must have a CN matching the hostname.

I had to take care of that by adding a certificate for the server (CN matching the hostname).  Since we have an Enterprise Root Certificate Authority in place, I could easily use that to generate my cert  (Setting up a Certificate Authority is way simpler than it sound).   Of course, you can also use one of those well known CA (e.g. Verisign, GeoTrust etc).

After installing the certificate with hostname.mycorp.com, my "Enable HTTPS" command went without a hitch.

By default, WinRM uses Port 5985 (HTTP) for transport.   Even though your authentication still secure (uses Kerberos), rest of your communication are done over HTTP and not very secure.

If you want to only allow HTTPS communication and disable HTTP option:

Disable HTTP:
WinRM delete winrm/config/listener?Address=*+Transport=HTTP

Now that you have WinRM talking over TCP/5986 (HTTPS), and both your server and your client trust the CA that issued those certificate, you have a way to restrict who can get to your box or not.

After some concern brought up to the table, we also decided to make our (Admins) job a little harder.  We decided not to allow everybody (Admins) access the entire farm from their workstation.  Instead, they will go via a Jump Server type solution (which will be known as the management server).   So we will only allow TCP/5986 traffic to those mgmt servers, and then to the entire farm.  Another words, and admin will have to open an interactive session with the mgmt server and run his/her script that will go out to the rest.   Code Signing option will be integrated with whatever IDE we choose to go with running from the mgmt server.




Tuesday, September 27, 2011

Another Text Parsing with PowerShell

Here's a quick function example with Text Parsing in it.   :)

So when you want to just get the IP from the IPConfig , you run get-MyIP

Function get-MyIP
{
        param($outputstring)
        Process {
                If ($outputstring=ipconfig |where-object {$_-match "ipv4"} |foreach-object {$_.Split(":")[1].Trim()})
                                {
                    "Ip address : $outputstring"
                 } else {
                    "Unable to retrieve your IP"
                    }
                 }
}


As you may have noticed,  its a rather simple function .. your output look like this

PS C:\scripting> get-MyIP
Ip address : 192.168.1.6

Well, I'll be playing with more Text Parsing and share what I think are interesting .. :)

Parsing Text with PowerShell

Lately I do a lot of things with PowerShell, but very little text parsing. I think it takes me longer to think of a best way to achieve what usually I'm searching for ... besides, I'm not that good at "Regular Expression". So that has been a discouraging factor when it comes to parsing text.  Anyway, but I've been playing with Split and Join  a little for fun and wanna show you something very simple.

So I used the dsget utility to lookup a Domain Group membership. The actual command will look something like this...

dsget group "CN=Domain Admins, dc=mydomain,dc=com" -members

In my current scenario, I don't want to sue any tools/method that will require RPC port (and I think the dsget utility strictly uses LDAP port 389).

So I ran into an issue with groups that are "Domain Local" type and contains Foreign Security Principals. Another words, when you do dsget against one of those groups contains users from trusted Domain, you see nothing but SID .. no friendly names. Arrgh!

So for that I need a function to convert the SID, but I had to first clean out other garbage from the dsget and only list the SID so I can later perform a foreach to convert to friendly names.

Anyway, so I performed the lookup against the group and got a output like this

"CN=S-1-5-21-10882098-2476436-466898754-546853, CN=ForeignSecurityPrincipals, DC=mydom, DC=com"
So I put above into a variable
$Users = dsget group "CN=Domain Admins, dc=mydomain,dc=com" -members

Now break it down ...
$getSID=$Users |where-object {$_ -match "CN=S"} |ForEach-Object {$_.Split("CN=,")} |Select-String -Pattern "S-*" |Where-Object {$_.Line -like "S-*"}

Now I get bunch of these

S-1-5-21-1099217021-36939962319-411451142-96491
S-1-5-21-1099217021-36939962319-411451142-90117
S-1-5-21-1099217021-36939962319-411451142-96129
S-1-5-21-1099217021-36939962319-411451142-96250
so I have the SID I wanted which I could convert by using a function like this

function Translate-SID
{
    param($sid)
    $ID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    $User = $ID.Translate( [System.Security.Principal.NTAccount])
    $User.Value
}


and converting line by line like this

foreach ($uSID in $getSID){Translate-SID $uSID}

You can do the same as above by using other utilities such as netSH, wmic, and so on.  

Friday, September 23, 2011

PowerShell 3.0

Well, as you and I have anticipated, Microsoft will be releasing PowerShell 3.0 and the Latest WinRM (3.0) with Windows 8.  So for now, we can enjoy the CTP1 (http://www.microsoft.com/download/en/details.aspx?id=27548 )

Right off the bat, I saw that few cmdlet got added to the default install (I am testing it in a Standard Windows 2008 R2 Std server with SP1 and .NET 4.0)    SP1 and .Net 4.0 is required


PS C:\> get-host

Name             : ConsoleHost
Version          : 3.0
Before:
PS C:\> (Get-Command -CommandType "CmdLet").count
236


After:
PS C:\> (Get-Command -CommandType "CmdLet").count
463


and with cmdlet,function,alias ...

Before:
PS C:\> (Get-Command).count
412

After
PS C:\> (Get-Command).count
657
from a Quick Look, I see a lot of new stuff ..    I will have to play with them some more ....  (Here's a copy and Paste from Microsoft site) ...


Windows PowerShell 3.0 Some of the new features in Windows PowerShell 3.0 include:
  • Workflows
    Workflows that run long-running activities (in sequence or in parallel) to perform complex, larger management tasks, such as multi-machine application provisioning. Using the Windows Workflow Foundation at the command line, Windows PowerShell workflows are repeatable, parallelizable, interruptible, and recoverable.
  • Robust Sessions
    Robust sessions that automatically recover from network failures and interruptions and allow you to disconnect from the session, shut down the computer, and reconnect from a different computer without interrupting the task.
  • Scheduled Jobs Scheduled jobs that run regularly or in response to an event.
  • Delegated Administration
    Commands that can be executed with a delegated set of credentials so users with limited permissions can run critical jobs
  • Simplified Language Syntax Simplified language syntax that make commands and scripts look a lot less like code and a lot more like natural language.
  • Cmdlet Discovery
    Improved cmdlet discovery and automatic module loading that make it easier to find and run any of the cmdlets installed on your computer.
  • Show-Command
    Show-Command, a cmdlet and ISE Add-On that helps users find the right cmdlet, view its parameters in a dialog box, and run it.
WMI
WMI in Windows Management Framework 3.0 CTP1 introduces:
  • A new provider development model
    This new model brings down the cost of provider development and removes the dependency on COM.
  • A new MI Client API to perform standard CIM operations.
    The API can be used to interact with any standard WsMan + CIMOM implementation, allowing management applications on Windows to manage non-Windows computers.
  • The ability to write Windows PowerShell cmdlets in native code
    The new WMI Provider APIs supports an extended Windows PowerShell semantics API allowing you to provide rich Windows PowerShell semantics. e.g., Verbose, Error, Warning, WhatIf, Confirm, Progress
WinRM With Windows Management Framework 3.0 CTP1:
  • Connections are more robust
    Session disconnect and reconnect, with or without client session reconstruction, allows long-running tasks to continue even when the session in which they were started is closed and the client computer is shut down. This feature also allows administrators to reconnect from different computers to check the status of remote running tasks and get results.
  • Connections are more resilient
    In Windows PowerShell 3.0 CTP1, connections can survive short-term network failures; the client-server connection is not severed at the first sign of trouble. If network problems persist, the client is safely disconnected and can reconnect by using the Connect-PSSession or Receive-PSSession cmdlets.
Windows PowerShell Web Service Windows PowerShell Web Service enables an administrator to expose a set of PowerShell cmdlets as a RESTful web endpoint accessible via the Open Data Protocol (OData). This provides remote access to invoke cmdlets from both Windows and non-Windows clients.

Wednesday, August 17, 2011

How do you load SharePoint Management Shell from Standard PowerShell Console

Just type the following lines on the PowerShell Console

Add-PSSnapin Microsoft.SharePoint.PowerShell
write-output "SharePoint CmdLet are Ready to go!"

or Simply Add those lines to your Profile

(e.g. Notepad $PROFILE)

*Do a Write-Output anyway just so you are in the newline (but change the text to whatever :)) )

Sunday, August 14, 2011

Sharepoint 2010 PowerShell CmdLet

PS D:\scripting> (Get-Command Where-Object {$_.CommandType -eq "CmdLet" -and $_.Name -like "*-SP*"}).count

531

Wow, 531 PowerShell CmdLet on SharePoint 2010! Now that just tells me that SharePoint team is taking Powershell very seriously. I just wish they wouldn't implement it as PSSnapin but instead module. I think the load time for SharePoint 2010 Management Shell would have been way faster too. Oh well, maybe next update. :)

For now, just load the Snapin to our regular console. I have another Blog post on that.