2012-12-11

Import AD groups from CSV file using PowerShell

I recently had to migrate about 200 groups containing about 1500 members from one AD domain to an other. To do this manually would probably drive everyone insane, so I got some clues from the http://powershellcommunity.org/ on how to export my groups from the old domain.

I changed the Get-QADUser cmdlet to  Get-ADObject, as my groups have other groups as members.

Import were quite straight forward, and I made a script that adds (or updates) renamed groups (all my groups had either L_ or DL_ as name prefix, and these had to be changed), adds the group description and then adds members (and this supports groups as members, and creates groups if they do not exist yet).

I even added some caching, to speed it up.


#
# AD-Import script
# 
# Copyright © 2012 -  Rune Nordbøe Skillingstad <rune.skillingstad@ntnu.no>
#
# License: GPLv2 - http://www.fsf.org/licensing/licenses/info/GPLv2.html

## 
# Config
##

# File containing import data
# Needed fields are 'Group Name', 'Group Description', 'Member Name'
# If you encounter UTF-8 problems (group names or descriptions containing 
# stray ?'s), your file is most likely ANSI encoded, recode to UTF-8 to
# fix
$CSVFile = "import.csv"

# DN for new groups 
$GroupDN    = "OU=MyGroups,OU=MyDepartment,DC=ad,DC=tld"
$Domain     = "AD-TLD"

# Type and Scope of new groups
# Types = 'Security' or 'Distribution'
# Scopes = 'Global' 'Universal' or 'DomainLocal'
$GroupType  = "Security"
$GroupScope = "Universal"

# To search and replace, uncomment and use these two
$ReplaceMatch = '^D?L_'
$ReplaceTo    = "NEW_"

# Set this to Continue for debugging
$DebugPreference = "SilentlyContinue"  # "Continue"

##
# The code
##
Function GetGroup {
 param([string]$group)
 if($ReplaceMatch -and $ReplaceTo) {
  $group = $group -replace $ReplaceMatch, $ReplaceTo
 } 
 if(!($group -match "^$Domain")) {
  $group = "$Domain\$group"
 }
 $qadgroup = Get-QADGroup -SearchRoot "$GroupDN" "$group"
 if($qadgroup) {
  Write-Debug "Found existing group $group"
 } else {
  $group = $group -replace "^$Domain\\", ""
  Write-Debug "Creating new group $group ... "
  $qadgroup = New-QADGroup -ParentContainer "$GroupDN" -name "$group" `
   -samAccountName "$group" -GroupType "$GroupType" -GroupScope "Universal"
 }
 return $qadgroup
}

$MemberCache = @{}
Function GetMember {
 param([string]$member)
 if($ReplaceMatch -and $ReplaceTo) {
  $member = $member -replace $ReplaceMatch, $ReplaceTo
 }
 if(!($member -match "^$Domain")) {
  $member = "$Domain\$member"
 }
 if($MemberCache.ContainsKey($member)) {
  $qadobject = $MemberCache.Get_Item($member)
  Write-Debug "Found cached object $qadobject"
 } else {
  $qadobject = Get-QADObject $member
  if($qadobject) {
   Write-Debug "Found uncached object $qadobject"
  } else {
   Write-Debug "Not found... suspecting missing group"
   $qadobject = GetGroup $member
  }
  $MemberCache.Add($member, $qadobject)
 }
 return $qadobject
}

$import = Import-CSV $CSVFile
$prev  = ""
Foreach($line in $import) {
 if($line."Group Name" -ne $prev) {
  $prev = $line."Group Name"
  $qadgroup = GetGroup $prev
  if($ReplaceMatch -and $ReplaceTo) {
   $description = $line."Group Description" -replace $ReplaceMatch, $ReplaceTo
  } else {
   $description = $line."Group Description"
  }
  if($qadgroup.Description -ne $description) {
   Write-Debug ("Updating description to " + $qadgroup.Name)
   Set-QADGroup -identity $qadgroup.DN -description $description | Out-Null
  }
 }
 $member = GetMember $line."Member Name"
 if([array]$qadgroup.Member -notcontains $member.DN) {
  Write-Debug "Member not found in group"
  Add-QADGroupMember -identity $qadgroup.DN -Member $member.Name | Out-Null
 } else {
  Write-Debug "Member exists in group"
 }

}

2012-09-17

Delete old/unwanted updates from WSUS

I wanted to delete all updates related to the "Drivers" classification in WSUS, as we no longer uses this solution for such updates.

I discovered that the "Server Cleanup Wizard" would not help me.

First of all, I removed all approvals/declines for all Drivers in the WSUS MMC (or GUI if you like). By running the cleanup wizard, all downloaded files where removed. The updates were still in the WSUS database (SUSDB).

I did some searches, and found PoshWSUS, and the "Remove-WSUSUpdate" commandlet. This uses the WSUS API and removes updates from the database based on the search feature in the API. Some experimenting by providing some known driver vendor names, confirmed that this worked.

I still wanted to remove all updates from the driver classification, and preferably automatic.

There seems to be few methods in the WSUS API to filter on classifications (other than the create-new-auto-approve method), so I had to use my limping Powershell powers and write something myself.
$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer('localhost',$False)
[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null
$wsus.getupdates() | Where {$_.UpdateClassificationTitle -eq 'Drivers'} | ForEach-Object { $wsus.DeleteUpdate($_.Id. UpdateID); Write-Host $_.Title removed }

As I write this, these lines are happily removing my odd 18 thousand drivers updates (slooooow).
Screenshot



If you ever stumble upon this blog, searching for something like this,I have to warn you that I will not guarantee any success using this, and that you might end up with a broken WSUS install.

2012-09-14

Ubuntu upstart and init.d - the pain

I've started using start/stop/restart from the new and improved upstart package on Ubuntu (as I got tired of the warnings when using the old /etc/init.d/ way).

I like it, but it is a major pain in the back to remember what service is converted to upstart and not, and this were starting to give me quite an irritation. During a coffee break, I uttered that these upstart commands should check if there were an init.d script if the service were missing from /etc/init, and that got me thinking. It would be trivial to write an patch, but I guess the reason this feature is missing, already is discussed in the upstart community, and probably never would be accepted.

My solution is to use functions in bash and wrap around these commands.

I made a master function _start()



function _start() {
  if [ -f /etc/init/$1.conf ]; then
    /sbin/$2 $1
  elif [ -x /etc/init.d/$1 ]; then
    /etc/init.d/$1 $2
  else
    echo "$2: Unkown job: $1"
    return 1
  fi;
}

This master is used by the three small wrapper functions start(), stop() and restart():



function start() {
  _start $1 start;
}

function stop() {
  _start $1 stop;
}

function restart() {
  _start $1 restart;
}

I've put all four functions into /etc/profile.d/upstart.sh and now I don't have to remember which service is using what. To reduce the need of cut and paste, you can download upstart.sh directly.

2012-06-26

Xocai - dette er Streisand-effekten

Helsesjokolademafien (Sjokoservice Norge) forsøker å true en norsk blogger som har publisert to blogginnlegg om produktet og distrubusjonemetoden; "Xocai, en sunn sjokolade? Er det helse i hver bit?" og "30 punkter du bør undersøke før du starter som Xocai distributør"

Å forsøke å stille denne kritikken er en sak, men å i tillegg distribuere bloggerens navn, adresse, familieinformasjon er strengt tatt å regne som oppvigleri

Med dette gir jeg min fulle støtte til den anonyme bloggeren, og oppfordrer alle til å bidra til at Xocai og  Sjokoservice Norge får den oppmerksomheten de fortjener.

2012-01-27

Lokalavisa - ikke lokal nok?

Jeg ble kontaktet av programkomiteen til Hellkonferansen, med forespørsel om jeg kunne tenke meg å delta i en plenumsdebatt om lokalaviser. Nå er det kanskje overraskende for flere at jeg skulle bli forespurt om noe slikt, men saken er at jeg er ansvarlig redaktør i en frivillig nettavis, nemlig Frøya.no. I debatten skulle også redaktør i Hitra-Frøya, Bjørnar Rønningen delta.

I dag kom det kontrabeskjed, debatten er avlyst pga at en annen redaktør ikke kunne stille. Synd, for jeg hadde gledet meg til dette.

Jeg har siden forespørselen tenkt over temaet en god del, og i og med jeg nå ikke får kommet med disse i debattform, kjenner jeg behovet for å synse litt om dette fenomenet med nye uavhengige nettaviser.

Senest i dag dukket det opp en ny nettavis, Flatangernytt. Hvorfor dukker disse bloggene/nettavisen opp? Spørsmålet har nok flere svar, og hovedårsaken er nok en følelse av at de eksisterende papiravisene ikke gir det folk forventer. Dette monopolet på nyhetsformidling har nå fått en utfordrer i blogger/nettaviser, som har en marginal kostnad med å drive en nettside i forhold til det å utgi en avis på papir.

Den klassiske lokalavisa lever av å selge en papirutgave to ganger i uka. Inntektene er en balanse mellom annonseinntekter, abonnement og løssalg. Disse avisene tapper papirutgaven for "verdi" ved å publisere nyheter på nett, og dette er nok årsaken til at de fleste slike aviser har sparsomt med stoff på sine nettsider.

Dagens nettbrukere er vant til at nyheter er tilgjengelig på nett forholdsvis kort tid etter at hendelsen som dekkes har skjedd. Om en papiravis kommer ut på tirsdag og fredag, vil mesteparten av stoffet være flere dager gammel i det avisa går i trykken. De fleste etablerte aviser, har opplevd nedgang i opplagstall, og nettets rolle i nyhetsformidling er hovedårsaken.

Når jeg på en lørdagsmorgen setter meg i sofaen med en kopp kaffe og avisene, er det ikke aviser i papirform, men via et nettbrett eller en laptop. Her har jeg nyheter fra hele verden tilgjengelig med enkle trykk, og jeg kan velge og vrake blant formidlere av nyheter. I stadig større grad er disse kildene til nyheter, kronikker og debatter vært via andre kilder enn de etablerte mediehusene.

Debatter er en annen sak hvor papirutgivelser taper terreng; leserbrevets tid er på hell framfor blogger og debattforum (både dedikerte og via nettavisene). Tiden da man leste i avisen, fant fram den gamle Remington-skrivemaskina og hamret avgårde et dirrende leserbrev, ventet en uke før det kom på trykk, og nok en uke før motdebattant svarte er over. De nye nettavisene har kanskje ikke stjålet noe fra avisene, men avisene har tapt på generelt grunnlag ovenfor nettet for medium.

Et annet område hvor papirutgivelser taper terreng, er de gode gamle kjøp og salg-rubrikkene. Den største konkurrentene der, er nok Finn.no og Zett.no. Det ironiske er at Finn.no eies av Schibsted og Polaris, mens Zett.no eies av A-pressen. De store mediehusene har altså bidratt til sin egen nedgang i rubrikksalg. Forøvrig har Facebook nå inntatt en stor rolle i kjøp og salg, og e fleste byer, tettsteder og kommuner har nå grupper for kjøp og salg, samt gis bort og byttes. Gruppen "Kjøpe - Selge - Bytte - Gis bort - Frøya" er et eksempel på en slik gruppe som nok har sørget for at antallet rubrikker i Hitra-Frøya har gått drastisk ned.

Årsakene til papiravisens nedgang og inntog av alternative blogger/nettaviser er en sammensatt sak, og årsakene til initiativet til å starte et alternativ kan være mange. Det er stor grunn til å tro at en årsaken til veldig mange er en missfornøydhet med de eksisterende papiraviser. Noen er nok missfornøyd med aktiviteten og at nyhetene er gamle når de kommer, andre er missfornøyd med redaksjonene og deres vinklinger på ting og så finnes det slike som meg, som er en stor forbruker av nettet og som synes det eneste negative med en nettavis, er at den er vanskelig å tenne opp i peisen med.

Det er flere områder hvor vi som bloggere/uavhengige nettaviser er som amatører og regne i forhold til de etablerte redaksjonene; vi har som regel ikke økonomi til å ha folk ansatt og jobber på dugnad, har sjelden medlemskap i Mediebedriftenes Landsforening, vi er ikke like flinke til å følge Vær varsom-plakaten, vi konkurrerer med profesjonelle annonseselgere i papiravisene. Vi burde altså ikke ha en sjans til seriøst å utfordre papiravisene, men det viser seg at vi alt har tat en del av kaken. Jeg tror jeg har pekt på noen av de viktigste elementene for papiravisenes nedgang og tilveksten av uavhengige, lokalgeografiske nyhetsformidlere.

2011-02-18

Spotify web services - a Perl example

IRC might be an oldskool medium today, but it is still used both for fun and a lot of open source projects.

From time to time, people tend to post Spotify URLs. Some uses the spotify: scheme and others uses the http://open.spotify.com/ format.

The identifiers for these URLs make no sense for humans, and to translate them, I made this Perl script, added a regexp hook in a bot and can now identify if I want to listen to what is pasted.

10:05 <@runesk> spotify:track:0UZRFYMoz9xmeE2AQUhTDl
10:05 <@Crunchy> [SPOTIFY: Daft Punk / Human After All / 2. The Prime Time Of Your Life]


The script uses the CPAN modules LWP::UserAgent and JSON and the Spotify Metadata API


#!/usr/bin/perl -w
#
# Query the Spotify web services for artist, track and album info
#
# Copyright 2011 - Rune Nordbøe Skillingstad
#
# License: GPLv2 - http://www.fsf.org/licensing/licenses/info/GPLv2.html
#
use warnings;
use strict;

use LWP::UserAgent;
use JSON;

if(!@ARGV || $ARGV[0] eq "") {
print "HALP! Nothing to look up :(\n";
exit(1);
}

my $query = join(" ", @ARGV);
if ($query !~ /(http:\/\/open.spotify.com\/|spotify:)(album|artist|track)([:\/])([a-zA-Z0-9]+)\/?/) {
print STDERR "Illegal query string\n";
exit(1);
}

my $ws_url = "http://ws.spotify.com/lookup/1/.json?uri=";

my $ua = LWP::UserAgent->new(env_proxy=>1,
keep_alive=>0, timeout=>5);

my $res = $ua->request(HTTP::Request->new('GET', $ws_url . $query));
if ($res->is_success()) {
my $json = decode_json($res->content);
if($json->{info}->{type} eq "album") {
print "[SPOTIFY: " . $json->{album}->{artist} ." / ". $json->{album}->{name} . "]\n";
} elsif($json->{info}->{type} eq "track") {
print "[SPOTIFY: " . join(", ", map{$_->{name}}@{$json->{track}->{artists}}) .
" / " . $json->{track}->{album}->{name} .
" / " . $json->{track}->{'track-number'} .
". " . $json->{track}->{name} . "]\n";
} elsif($json->{info}->{type} eq "artist") {
print "[SPOTIFY: " . $json->{artist}->{name} . "]\n";
} else {
# Add Use Data::Dumper and uncomment to debug
# print STDERR Dumper($json);
}
}

exit(0);:


The regexp in line 21 /(http:\/\/open.spotify.com\/|spotify:)(album|artist|track)([:\/])([a-zA-Z0-9]+)\/?/
is identical to the one used as a hook.

2011-02-15

Themes - a nuisance or a must?

From time to time, you will stumble upon tools, programs, utilities, web sites etc having spent an incredible amount of time to make themes.

Yesterday I installed EqualLogic SAN HeadQuarters.

SAN HeadQuarters enables you to monitor multiple PS Series groups from a single graphical interface. It gathers and formats performance data and other vital group information. Analyzing the data might help you improve performance and more effectively allocate group resources.

Dell/EqualLogic states that this is "All-Inclusive Enterprise-Class Data Management".

The amount of time they have spent making themes for this "Enterprise-Class" tool, is impressive.

  • NavigationPane
  • SkinNavigationPane
  • Caramel
  • Money Twins
  • Lilian
  • The Saphalt World
  • iMagniary
  • Black
  • Blue
  • Office 2007 Blue
  • Office 2007 Black
  • Office 2007 Silver
  • Office 2007 Silver
  • Office 2007 Green
  • Office 2007 Pink
  • Coffee
  • Liquid Sky
  • London Liquid Sky
  • Glass Oceans
  • Startdust
  • Xmas 2008 Blue
  • Valentine
  • McSkin
  • Summer 2008
  • Pumpkin
  • Dark Side
  • Springtime
  • Darkroom
  • Foggy
  • Hig Contrast
  • Seven
  • Seven Classic
  • Sharp
  • Sharp Plus


Some are just colour schemes, but others have custom graphics.


Pumpkin


Springtime


Summer 2008


Valentine


Xmas 2008 Blue