Lors de l'installation d'un serveur Exchange 2007 chez un client, ce dernier m'a demandé de reprendre dans les calendriers de chacun des utilisateurs tous les événements d'une application d'agenda au format web.
Cette application web s'appelle Ovidentia. C'est une application fonctionnant sous Apache/PHP/MySQL. Ma première idée était de trouver un moyen d'exporter ces événements au format csv (format universellement connu et facilement manipulable par la suite pour automatiser d'une manière ou d'une autre un import). Fonctionnant sous MySQL, je pouvais utiliser un connecteur ODBC pour faire une requête dans Excel pour générer ce csv.
Toutefois, cela ne réglait pas mon souci pour importer les événements dans Outlook, lié à l'Exchange. Surtout, étant donné que j'avais plus de 50 utilisateurs à traiter, comment l'automatiser, sachant que je devais récupérer tous les événements depuis début 2009...
Enfin, j'ai réussi à trouver le script qui allait bien, qui plus est en PowerShell ! En fait, je me suis basé sur ce script Create Calendar Appointments Automatically Based on Existing Calendar Appointments.

Puis, je l'ai modifié, pour intégrer la requête ODBC qui va bien et les traitements qui vont bien pour la création des événements.
Pour cet exemple, les prérequis sont :
- pilote ODBC MySQL
- Powershell 2.0

J'ai donc créé 3 fichiers pour me permettre tout ça :

requete.sql :

SELECT
		bab_users.id,
		bab_users.nickname,
		bab_users.email,
		bab_cal_events.id,
		bab_cal_events.title,
		bab_cal_events.description,
		bab_cal_events.location,
		bab_cal_events.start_date,
		bab_cal_events.end_date,
		bab_cal_events.bprivate
	FROM bab_users, bab_cal_events, bab_cal_events_owners, bab_calendar
	WHERE bab_calendar.owner=bab_users.id
	AND bab_cal_events_owners.id_cal = bab_calendar.id
	AND bab_cal_events.id = bab_cal_events_owners.id_event
	AND bab_cal_events.start_date >= '#startdate#'
	AND bab_users.nickname ='#username#'
	GROUP BY
		bab_users.id,
		bab_users.nickname,
		bab_users.email,
		bab_cal_events.id,
		bab_cal_events.title,
		bab_cal_events.description,
		bab_cal_events.location,
		bab_cal_events.start_date,
		bab_cal_events.end_date,
		bab_cal_events.bprivate
	ORDER BY bab_cal_events.start_date

agenda.ps1 (LE script d'importation) Il est important d'exécuter ce script sur le poste de l'utilisateur et connecté avec sa session :

param($username, $action="synchro", $reel=$false)
 
function HelpCommand($value) {
$howto = @"
agenda.ps1 username action [reel ($false|$true)]
	"username" : le nom de l'utilisateur dans Ovidentia
 
	"action" : quelle action doit-on effectuer :
		import : il s'agit d'une première synchronisation, complète, depuis le 01/01/2009
		synchro (par défaut) : il s'agit d'une synchronisation Ovidentia->Outlook pour tous les nouveaux événements 
							   (ou événements modifiés) de la date du jour jusqu'à ....
 
	"reel" : doit-on réellement mettre à jour ou non (presque équivalent à -Whatif)
		$false (par défaut) : juste pour tester
		$true  : mettre réellement à jour
 
Exemple 1 :
agenda.ps1 gmaison import $false
	Cet exemple permet de simuler l'import de l'agenda de gmaison depuis le 01/01/2009
 
Exemple 2 :
agenda.ps1 gmaison synchro $true
	Cet exemple permet de synchroniser l'agenda de gmaison pour tous les événements à venir depuis la date d'exécution du script
 
"@
$howto
}
trap [Exception] {
	Write-error "Une erreur est arrivée. Veuillez vérifier vos paramètres"
	HelpCommand
	break;
}
 
if ($username.tolower() -eq "-help") {
   HelpCommand
   break
}
 
#si on a oublié de préciser le username
if ($username -eq $null) {
	[string]$username = read-host -prompt "veuillez saisir le nom réseau de l'utilisateur "
}
 
#ici, on récupère la requête pour l'utilisateur
$filepath = "requete.sql"
# ou bien si les noms utilisateurs sont identiques au login réseau
#[string]$nomutilisateur = [environment]::username
 
[string]$requete = get-content $filepath
 
$requete = $requete.replace("#username#",$username).tolower()
if ($action -eq "import") {
	$startdate = "2009-01-01"
} else {
	$startdate = (Get-Date -Format "yyyy-MM-dd").toString()
}
$requete = $requete.replace("#startdate#",$startdate).tolower()
 
#ici, on crée la connexion pour pouvoir récupérer tous ses rendez vous
$commande = $null
$connexion = $null
 
$commande = new-object system.data.odbc.odbccommand
$connexion = new-object system.data.odbc.odbcconnection
$commande.connection = $connexion
 
# attention : il faut créer l'entrée odbc pour ovidentia
$dsn = "driver=mysql odbc 5.1 driver;uid=exchange;pwd=exchange;port=3306;database=ovidentia;server=serveur"
$connexion.connectionstring = $dsn
 
$connexion.open()
 
#ici, on exécute la requête et on récupère les données
$commande.commandtext = $requete
$dataadapter = new-object system.data.odbc.odbcdataadapter($commande)
$dataset = new-object system.data.dataset
$nombre= $dataadapter.fill($dataset)
 
# on démarre outlook
if ($reel) {
	$outlookapp = new-object -comobject outlook.application
	$allcalitems = $outlookapp.getnamespace("MAPI").getdefaultfolder(9).items
}
 
#puis on insère les données dans le calendrier
foreach ($table in $dataset.tables)
{
	$max_lignes = $table.rows.count
	$i=0
 
	foreach ($row in $table.rows)
    {
 
		$iduser = $row.id
		$nickname = $row.nickname
		$email = $row.email
		$eveid = $row.id1
		$titre = $row.title
		$desc = $row.description
		$lieu = $row.location
		$datedeb = $row.start_date
		$datefin = $row.end_date
		$prive = $row.bprivate
 
		#write-host $nickname" - "$eveid" -> "$datedeb
 
		# on a récupéré toutes les données liées à l'événement
		# maintenant, on crée l'événement
 
		if ($reel) {
 
			if ($action -eq "synchro") {
 
				# ici, on va rechercher si l'événement utilisateur existe
				$event = $allcalitems | where-object{$_.userproperties.find("OvidentiaID", $true).value -eq $eveid -and $_.start -ge [datetime]$startdate}
			} else {
				$event = $null
			}
 
			# s'il n'existe pas, on le crée
			if ($event -eq $null) {
				$event = $outlookapp.createitem(1)
				$prop = $event.userproperties.add("OvidentiaID", 20)
 
			}else{
				$prop = $event.userproperties.find("OvidentiaID", $true)
			}
 
			# quoiqu'il arrive, on le met à jour
			$event.subject = "$titre"
			$event.location = $lieu
			$event.body = $desc
			$event.start = [datetime]$datedeb
			$event.end = [datetime]$datefin
			if ($prive -eq "y") {
				$event.sensitivity = 2
			} else {
				$event.sensitivity = 0
			}
			$maintenant = get-date
			if ($datedeb -ge $maintenant) {
				$event.reminderset = $true
			} else {
				$event.reminderset = $false
			}
 
			$prop.Value = $eveid;
 
			$event.close(0)
		} else {
			write-host @"
$iduser 
$nickname 
$email 
$eveid 
$titre 
$desc 
$lieu 
$datedeb 
$datefin 
$prive 
"@
			$todisplay
		}
 
 
		[int]$pourcent = $i++ / $max_lignes *100
		write-progress "importation des événements du calendrier" "en cours" -percentcomplete $pourcent -id 1
    }
}

J'ai également créé un script qui me permet, sur un poste d'un utilisateur, d'automatiser l'exécution du script ci-dessus par l'intermédiaire du planificateur de tâche. exec_task.ps1 la tâche doit être configurée pour être exécutée avec le compte de l'utilisateur :

param($username, $action, $reel)
#Récupération de la date du jour
$dt = Get-Date -format "yyyyMMdd_hhmm"
 
# construction du chemin où se trouve ce script et celui d'import de l'agenda
# mais également le chemin du fichier de log
$MyPath = (split-path $myinvocation.Mycommand.Definition)
$file = $MyPath +"\"+[Environment]::UserName+"_"+$dt + ".log"
 
#Démarrage de la récupération de la console (pour le log)
start-transcript $file
#en cas d'erreur on arrête la récupération du log
trap { stop-transcript; break}
 
Set-Location $MyPath
.\agenda.ps1 $username $action $reel
 
stop-transcript