Showing posts with label powershell. Show all posts
Showing posts with label powershell. Show all posts

Monday, June 21, 2010

Exporting MongoDb with Powershell

I'm just going to leave this right here. This fixes up the output from mongoexport to create an array, suitable for XMLSpy...

function Export-Wmi
{
 Param (
  [Parameter(mandatory=$true)]
  [string]
  $DbName,
  
  [Parameter(mandatory=$true)]
  [array]
  $CollectionNames
 )
 
 Process {
  $CollectionNames |
  foreach {
   $output = ""
   $cmd = ""
   $docs = C:\Path\To\Mongo\bin\mongoexport.exe `
    --db $DbName `
    --collection $_ `
    2>$null
   
   $output += "["
   $bfirst = $true
   foreach( $doc in $docs )
   { 
    if( -not $bfirst )
    {
     $output += ","
    }
    
    $output += $doc
    
    $bfirst = $false
   }
   
   $output += "]"
   
   $output | Out-File -FilePath "C:\Path\To\Data\$_.json"  
  }
 }
}

Export-Wmi -DbName "reports" -CollectionNames @("coll1", "coll2" )

Wednesday, June 2, 2010

Powershell, MongoDB, and WMI

I had reason to write a short cmdlet which converts WMI objects to MongoDB docs. It ain't much, but it works so far:

 [Cmdlet( VerbsData.ConvertTo, "MongoDoc" )]
 public class ConvertToMongoDoc : Cmdlet
 {
  [Parameter( Mandatory = true, ValueFromPipeline = true )]
  public PSObject InputObject { set; get; }

  protected override void ProcessRecord()
  {
   Document converted = (Document)WmiConvert( InputObject.BaseObject );
   this.WriteObject( converted );
  }

  protected object WmiConvert( object obj )
  {
   if( null == obj )
    return null;

   object newObj = obj;


   Type objType = obj.GetType();

   TypeCode objTypeCode = Type.GetTypeCode( objType );
   String objTypeName = objType.FullName;
   
   if( objType.IsGenericType )
    return obj;

   switch ( objTypeCode )
   {
    case TypeCode.String:
     return obj;
   }

   switch ( objTypeName  )
   {
    case "System.TimeSpan":
     return ( (TimeSpan)obj ).Ticks;

    case "System.Int16":
    case "System.UInt16":
     return System.Convert.ToInt32( obj );

    case "System.UInt64":
    case "System.UInt32":
     return System.Convert.ToInt64( obj );

    case "System.Byte":
     return null;

    case "MongoDB.Driver.Document":
     return obj;
   }

   if( objType.IsArray )
   {
    ArrayList aTmp = new ArrayList();
    foreach ( var s in (object[])obj )
    {
     aTmp.Add( WmiConvert( s ) );
    }
    return aTmp;
   }

   if( 0 == objTypeName.IndexOf( "System.Management." ) )
   {
    Document tmpdoc = null;

    switch( objTypeName )
    {
     case "System.Management.ManagementObject":
     case "System.Management.ManagementBaseObject":
      tmpdoc = new Document();
      ManagementBaseObject mbo = (ManagementBaseObject) obj;
      tmpdoc.Add( "WmiPath", mbo.SystemProperties["__PATH"].Value );
      tmpdoc.Add( "WmiServer", mbo.SystemProperties["__SERVER"].Value );
      AddPropsToDoc( tmpdoc, mbo.Properties );

      newObj = tmpdoc;
      break;

     default:
      break;
    }

    return newObj;
   }
   return newObj;
  }

  protected void AddPropsToDoc( Document doc, object obj )
  {
   foreach ( var propData in (PropertyDataCollection)obj )
      {
       doc.Add( propData.Name, WmiConvert( propData.Value )  );
      }
  }
 }

Hope this helps.

Friday, May 14, 2010

Enumerating Powershell NoteProperties

I recently was asked to stuff a custom PSObject into a bunch of key value pairs, so I thought I'd put it here. Is there a better way?

$props = ($File | get-member -MemberType NoteProperty)
foreach( $p in $props )
{
     # write-verbose (invoke-expression "`$File.$($p.Name)")
     $doc.Add( $p.Name, (invoke-expression "`$File.$($p.Name)") )
}

Tuesday, May 11, 2010

Powershell grep'ing

I've been asked about searching and replacing in files a few times, so I thought I'd put an example up here. This is a way to do it - and you'll almost certainly want to change it around to better suit your scenario.

cd C:\Users\michael.DOMAIN\Workspace\Powershell
function pgrep
{
 Param (
 
  [string]
  [parameter(mandatory=$true)]
  $Like,
 
  [bool]
  $Recurse = $false,
  
  [string]
  $BasePath
 )
 
 Process {
  if( -not $BasePath )
  {
   $BasePath = $PWD
  }
  
  # there's gotta be a slicker way
  $recurse_string = ""
  if( $Recurse )
  {
   $recurse_string = "-Recurse"
  }
  
  Invoke-Expression "Get-ChildItem -Path $BasePath $recurse_string" | 
  where {$_.PSIsContainer -eq $false } |
  foreach {
 
   $file = $_
   # $file.FullName
   # Get-Content -Path $file.FullName | where { $_ -eq "MachineName" }
   if( Get-Content -Path $file.FullName | where { $_ -like $Like } )
   {
    $file
   }
  } 
 }
}

function preplace
{
 Param (
  [string]
  $Path,
  
  [string]
  $From,
  
  [string]
  $To
 )
 
 Process {
  $fi = New-Object -TypeName System.IO.FileInfo -ArgumentList $Path
  $tmpfile = "$($fi.DirectoryName)\$($fi.Name).tmp"
  Remove-Item -Path $tmpfile -ErrorAction SilentlyContinue
 
  Get-Content $Path | 
   foreach {
    $line = $_
    $line.Replace( $From, $To )
   } |
   Add-Content -Path $tmpfile
  
  Remove-Item -Path $Path -ErrorAction SilentlyContinue
  Rename-Item -Path $tmpfile $Path
 }
}


pgrep -Like "*MachineName*" -BasePath C:\Users\michael.DOMAIN\Workspace\Powershell |

foreach {
 $fi = $_
 Write-Host "updating $($fi.FullName)"
 preplace -Path $fi.FullName -From "MachineName" -To "MachineFoo"
}

Sunday, May 2, 2010

Getting Command Line Parameters Using Powershell

I'm just going to leave this here...
(gwmi win32_process -filter "name='powershell.exe'").commandLine

Wednesday, December 9, 2009

Capturing Output as String and Variable in Powershell

I'm embedding a posh shell into a C# app I'm writing, and I needed a way to capture the text output of the app, as well as the results as an array. This is what I came up with:

Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();

runspace.SessionStateProxy.SetVariable( "location", "c:\\" );
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript( "get-childitem $location" );

// this splits the output into the text and variable
Command cmd = new Command( "tee-object" );
// this is where the result array will end up
cmd.Parameters.Add( "variable", "invoke_results" );
pipeline.Commands.Add( cmd );
// forces the rest of the output to string
pipeline.Commands.Add( "out-string" );

Collection<PSObject> results = pipeline.Invoke();

var errors = pipeline.Error;

if( 0 < errors.Count )
{
 // there might be more. For now, we read the first
 var oneError = errors.Read( 1 );
 ConsoleColor formerConsoleColor = Console.ForegroundColor;
 Console.ForegroundColor = ConsoleColor.Red;
 Console.WriteLine( "Error: {0}", oneError[0] );
 Console.ForegroundColor = formerConsoleColor;
}
else
// hooray, no errors
{
 object[] resArray = (object[])runspace.SessionStateProxy.GetVariable( "invoke_results" );

 Func<List<String>, object, List<String>> nameAcc =
  ( n, o ) =>
  {
   n.Add( o.ToString() );
   return n;
  };

 List<String> names = resArray.AsEnumerable().Aggregate( new List<String>(),
        nameAcc
  );

 foreach( var list in names )
 {
  Console.WriteLine( list );
 }
}

runspace.Close();
This was much easier than trying to implement my own custom host.

Tuesday, November 24, 2009

Listing User Last Logon with Powershell

This script assumes that you have enabled auditing on successful logins (by default, it doesn't).

The general process it follows is:
  • retrieve the Security event log
  • pulls login information up to the last reboot
  • gets unique usernames and the time they logged in
  • writes it all out to a text file
It's still a little raw, but it works. It runs very slow over the network; I'll work up one that uses the PSJob facilities...


function getLastBoot( $computername )
{
$wmi = Get-WmiObject -Class Win32_OperatingSystem
return $wmi.ConvertToDateTime( $wmi.LastBootUpTime )
}

function getTopDates()
{
$logins = @()
$input | foreach {
$rec = $_

# this is ugly...I'm going thru the list twice
$hasit = ($logins | where {($_.UserName -eq $rec.UserName) -and ($_.MachineName -eq $rec.MachineName)})

if( $hasit )
{
for( $x = 0; $x -lt $logins.Count; $x++ )
{
if(($rec.UserName -eq $logins[ $x ].UserName ) -and
($rec.MachineName -eq $logins[ $x ].MachineName ))
{
$logins[ $x ] = $rec
}
}
}
else
{
$logins = $logins + @(,$rec)
}
}
return $logins
}

$start_time = (Get-Date)
Write-Host "starting all $start_time"

$target_computers = @( "dal1mspwb16",
"dal1mspwb19",
"dal1mspwb36",
"dal1mspwb37",
"dal1mspwb12",
"dal1mspwb35")

# $target_computers = @( "dal1msdwb34" )

$target_computers | foreach {
$target = $_
$lastboot = getLastBoot( $target )

Remove-Item "iis_logins_$target.txt" -ErrorAction SilentlyContinue


Write-Host "processing $target :" (get-date)
Get-EventLog -LogName "Security" -ComputerName $target -After $lastboot |
select -Property UserName, MachineName, TimeGenerated -Unique |
sort -Property TimeGenerated |
getTopDates |
Out-File -Append -FilePath "iis_logins_$target.txt"

Write-Host "completed $target :" (get-date)
}

$end_time = (Get-Date)

Write-Host "complete"
Write-Host "Started: $start_time"
Write-Host "Finished: $end_time"
Write-Host ($end_time - $start_time)

Thursday, November 19, 2009

Creating System Restore Points with Powershell

Getting ready to install a new video driver? What about that "interesting" piece of software you found?

Aren't you worried that it is going to screw up your computer?

Well, if so, Windows XP/Vista/7 have a facility known as "System Restore Points". Basically, these are snapshots of your filesystem. There is a good chance they are already enabled, and being used by Windows Update.

What about those other times, though?

If you have Powershell 2.0 installed (whaddya mean, you don't? Get on it!), then you have a couple of commands to help you out:

Checkpoint-computer creates a system restore point.

Restore-computer reverts to the specified restore point

So, before you install that problematic driver/update/app, this is a quick and dirty way to cover your butt.

Be warned, though, this will restore everything - including any changes to any files you may have made.

Thursday, November 12, 2009

Powershell v1.0, IIS6, and remote machines

I found this snippet on the web:


$computer="server"
$co = new-object System.Management.ConnectionOptions
#$co.Username="domain\username"
#$co.Password="password"
$co.Authentication=[System.Management.AuthenticationLevel]::PacketPrivacy
#$co.EnablePrivileges=$true;
$wmi = New-Object System.Management.ManagementObjectSearcher
$wmi.Query="Select * From IIsApplicationPool"
$wmi.Scope.Path="\\$computer\root\MicrosoftIISv2"
$wmi.Scope.Options=$co
$wmi.Get() | foreach { $_.name }
In PowerShell v2.0 there is a new parameter, -Authentication, to specify
the authentication level (one line):
gwmi -class IIsApplicationPool -namespace "root\MicrosoftIISv2" -computer
$computer -authentication PacketPrivacy | foreach { $_.name}

Thursday, September 3, 2009

Pash - PowerShell for Unix

Regular readers of this blog - both of you (Hi Mom, and that guy in Australia who subscribed to the RSS) - know that I loves me some MS PowerShell. I've called it a "game changer", because it greatly simplifies Windows administration. It is good enough that I've abandoned cygwin on my Windows systems; PowerShell is better than bash.

Well, somebody has released a version of PowerShell that runs on *nix systems: Pash. It builds on Mono, so there is a huge library of objects for it to work on. I don't know how well it works, yet, but I'm certainly going to be trying it.

Monday, July 20, 2009

Powershell, and log4net

EZ money:

"--- Loading log4net ---"

[System.Reflection.Assembly]::LoadFrom("$pslib\log4net.dll") | out-null;
"Loaded log4net.dll"

[System.IO.FileInfo]$fi = new-object System.IO.FileInfo "$pslib\log4net.xml"
[log4net.Config.XmlConfigurator]::Configure( $fi )
"log4net configured with $pslib\log4net.xml"

$log = [log4net.LogManager]::getLogger("default")
"`$log = (new logger `"default`")"

""
'Cause sometimes we wanna be fancy...

Sunday, July 12, 2009

Powershell $profile - Is This Great Filler, or What?

Some new java stuff, a couple of nice little helper functions: qfind, for searching the filesystem and title, to change the console title.
$projects = $env:USERPROFILE + "\Documents\Visual Studio 2008\Projects"
$sysdir = $env:USERPROFILE + "\sys"

# misc stuff
new-alias -name npp -value "C:\Program Files\Notepad++\notepad++.exe"
function qfind( $start, $like ) { get-childitem $start -name $like -recurse }

function title( $msg ) { $host.ui.RawUI.WindowTitle = $msg }

# git stuff

$gitexe = $sysdir + "\Git\bin\git.exe"
new-alias -name vi -value $sysdir\Vim\vim72\gvim.exe
$env:EDITOR = "npp"
new-alias -Name git -Value $gitexe

# java stuff

$jdk = "jdk1.6.0_12"
$java_home = "C:\Program Files\Java\$jdk"
$env:JAVA_HOME = $java_home

new-alias -name jruby -value $sysdir\jruby\bin\jruby.bat

new-alias -name jar -value "$java_home\bin\jar.exe"
new-alias -name ant -value "c:\sys\ant\bin\ant.bat"
new-alias -name javac -value "$java_home\bin\javac.exe"
function jetty() { java -jar start.jar etc/jetty.xml }
$env:CATALINA_HOME = "c:\sys\tomcat6"
new-alias -name tomcat -value "c:\sys\tomcat6\bin\startup.bat"

# MS Stuff

new-alias -name nant -value "$sysdir\nant\bin\nant.exe"
new-alias -name msbuild -value C:\Windows\Microsoft.NET\Framework\v2.0.50727\msbuild.exe

title( "General" )

Hope this helps. At least it is harder to lose :)

Update: Once I got to looking at it here, I decided it could be better laid out. So, it is a little prettier, now.

Friday, June 19, 2009

Finding Event Log Messages With Powershell

Wooo! Big update!

Actually, I just don't want to lose this...
get-eventlog -ComputerName dalmobdev08 -logname application | where-object {$_.Message -match "corrupt"} | select-object -First 10


Finds event log entries with the word "corrupt" in the message.

Tuesday, June 16, 2009

MSBuild Powershell Extension

Haven't tested it, or even downloaded it. Just saving it, for now.

Allows embedding Powershell scripts into MSBuild files.

Here's a page that has some other ideas, and disses the embedded approach.

Update: I decided I didn't really like the approach. I'm kind of down using MSBuild for anything other than building, anyway.

Wednesday, June 10, 2009

Listing Add/Remove Programs with Powershell, redux

Man, I barely get the thing posted, and already there's complaints about having to install stuff.

Okay, fine:

$target = "somecomputer"

$arp_key = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
$type = [Microsoft.Win32.RegistryHive]::LocalMachine
$hive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($type, $target)

$regkey = $hive.OpenSubKey($arp_key)

foreach( $subkey_name in $regkey.GetSubKeyNames() )
{
$subkey = $hive.OpenSubKey( $arp_key + "\" + $subkey_name )
$display_name = $subkey.GetValue( "DisplayName" )
$display_version = $subkey.GetValue( "DisplayVersion" )

if( $display_name -ne [string].Empty )
{
write-host @($display_name + " (" + $display_version + ")")
}

# foreach( $val_name in $subkey.GetValueNames() )
# {
# write-host "name: " $val_name -nonewline */
# write-host "value: " $subkey.GetValue( $val_name )
# }
}


No installation required. It is rather naive - if there is no display name, then it is skipped. If you've got a better version, please let me know. This one feels icky, to me.

I left in a chunk, remarked out, to show how to get to the other values.

Listing Add/Remove Programs with Powershell

Shamelessly stole this from here, which apparently stole it from someplace else...

First, run this:
set-content RegProgs.mof @'
#pragma namespace("\\\\.\\root\\cimv2")
instance of __Win32Provider as $Instprov
{
Name ="RegProv" ;
ClsID = "{fe9af5c0-d3b6-11ce-a5b6-00aa00680c3f}" ;
};
instance of __InstanceProviderRegistration
{
Provider =$InstProv;
SupportsPut =TRUE;
SupportsGet =TRUE;
SupportsDelete =FALSE;
SupportsEnumeration = TRUE;
};

[dynamic, provider("RegProv"),
ProviderClsid("{fe9af5c0-d3b6-11ce-a5b6-00aa00680c3f}"),ClassContext("local|HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall")]
class win32reg_arp
{
[key]
string ProdID;
[PropertyContext("DisplayName")]
string DisplayName;
[PropertyContext("Publisher")]
string Publisher;
[PropertyContext("DisplayVersion")]
string Version;
};

'@

It will create a file named "RegProgs.mof". This file is used by mofcomp.exe, which "compiler parses a file containing MOF statements and adds the classes and class instances defined in the file to the WMI repository. The following code example shows how to run the MOF compiler on a file." (Source)

Run:
mofcomp.exe RegProgs.mof

This will compile and add it to the local WMI repository.

Then, from PowerShell, you can
Get-WmiObject win32reg_arp

The .mof needs to be installed on any machine which you wish to scan.

Update: I have a non-.mof version here.

Friday, April 24, 2009

Versioning, and Versioning on Windows

I'm currently using git for my versioning needs, specifically msysgit on Vista. I got spoiled on distributed version control systems when I was running Ubuntu on my desktop. Bazaar was my choice then, especially since it ran just fine on Windows, too.

I've moved onto git since then, mostly because it looks like it has the most momentum. Now that I've made the switch, I'm happy. It is nice to finally have a tool which fits whatever workflow you want to throw at it, instead of having to adjust your workflow for it.

I've been a proponent of VCS as an IT goon for a long time. They aren't just useful for versioning code, they are useful for versioning all kinds of files. Some of the easiest deployment environments I've worked in and built had their production content under version control. Rollbacks were trivial, the "what changed" question was easily answered, revision numbers were part of change management.

Making a copy of everything isn't versioning, it is only rollback. Copies are just backup. It is good enough, but it isn't particularly smart.

Distributed version control is even cooler than the old fashioned (like, a couple of years ago!) centralized VCS. The previous generation required a centralized server, even if it was one user on the same machine. DVCS lets you start versioning on-the-fly.

Having things versioned makes it easy to fix your mistakes, giving you the freedom to experiment. When you screw up, you just go back to where you started. Or you can see exactly what you changed that might've screwed things up, so you can fix it faster. Are you building up a text document describing whatever it is you're working on, even just as notes? Version it. Seriously.

Of course, on Windows this all sucks. There is a GUI for git, and it is adequate, but it doesn't really unleash the Power of Git. The tools are good enough that there isn't really a reason for not using them, though.

Microsoft's versioning story is very weak. Team Foundation Server ain't gonna cut it. We need it a little bit more integrated. And free. I mean, really, when people are happily giving away the next-gen technology, you can count that part of the product as "obsolete". I'm not holding out for something at the file system level. Yet. (nor network aware, yet, either - but if they want to be better, they've got to get ahead of this)

So, in the meantime, I have a couple of Powershell aliases to help me out. I want to spend more time writing better ones, but to really get into it, I'd need to write it as a cmdlet, and I don't have the time. And I shouldn't have to - something like this should be shipped in the OS. It should be pretty, it should be easy, it should be powerful, and it should be programmable. That's just to catch up.

Here's what I'm using in $profile (I've got gvim installed for my editor. Suck it, notepad):
$vimexe = $env:USERPROFILE + '\sys\vim72\gvim.exe'
new-alias -name vi -value $vimexe

$gitexe = $env:USERPROFILE + '\sys\git\bin\git.exe'
new-alias -Name git -Value $gitexe
# git wants a HOME
$env:HOME = $env:USERPROFILE
$env:EDITOR=$vimexe

I know, not much to go with. More complex aliases are prohibitive, and functions look like functions, parenthesis and all. As I get a better understanding of how best to use git, I'm coming up with what it will take to simplify my own specific workflow. I'll share them as I do them.