giovedì 27 dicembre 2012

Script COLORATI! - Livello AVANZATO

Ciao a tutti, oggi tratterò un argomento che sta a cuore a molti "scriptomani": come utilizzare i COLORI negli script BATCH (DOS, command line, command prompt, linea di comando, insomma...).

L'utilizzo dei colori è un annosa questione... i metodi proposti hanno tutti quanti qualche compromesso a cui attenersi (ad esempio due punti nella parola colorata, cicli di codice incomprensibile da includere e chi più ne ha più ne metta).

Oggi, per voi, ho messo insieme una cosa MAGICA! Uno script che permette di definire il colore e lo sfondo (potremmo chiamarla evidenziazione) di ciascuna parola di un messaggio che si vuole scrivere a schermo, includendo UNA SOLA RIGA DI CODICE all'interno del vostro script: la chiamata al mio, passandogli le parole come parametro (fino a 9 parole per riga, che mi sembra PIU' CHE SUFFICIENTE).

MA VOLETE METTERE richiedere un'azione ad un utente così:

con richiedergliela COSI' ?!?!?!?!?!?!?:




Allora cominciamo?
Ecco lo script:

giovedì 20 settembre 2012

Whitelist di un dominio per il filtro contenuti di Exchange 2010

Ciao a tutti, un post molto veloce...


Questo script permette di aggiungere un dominio alla whitelist del filtro contenuti di Exchange 2010. Permette di DELEGARE questo compito a qualcun'altro, che magari non si intende di powershell, in quanto richiede il dominio da aggiungere in una simpatica finestrella con un input di testo, che può essere compilata da chiunque.

L'avevo scritto tempo fa per un exchange 2007 ed ora l'ho aggiornato per la nuova versione di questo meraviglioso server.

Leggendolo, inoltre, ci si spiega per sommi capi anche come richiamare oggetti visual basic da powershell (WOW!).

Per eseguirlo, basta trascinare il file (nominatelo con estensione ".ps1") nella console Exchange Management Shell e premere invio.


mercoledì 19 settembre 2012

più che DIMEZZARE i tempi di backup di un DAG Exchange 2010 - livello medio

Ciao a tutti, mi è capitato di osservare qualche problemino strano sul backup di un DAG exchange 2010.

DAG (Database Access Group) è un sistema Exchange ad alta affidabilità che, mediante la replica dei database delle cassette postali su più servers, mantenendone attiva una sola copia, permette di ovviare ai problemi di un singolo server in maniera automatica e quasi trasparente per l'utente.
Questa tecnologia si basa "oscuramente" sul cluster di failover di Microsoft.
Dico "oscuramente" perchè non si ha il controllo di tutte le risorse del cluster, e ci si trova ad avere una gestione "splittata" tra le console di Exchange e quella di Failover Cluster.


Ho 4 servers di Exchange, 2 in un sito e 2 in un altro, geograficamente distante.
I software di Backup Cluster-aware e ExchangeVSS-aware permettono di fare il backup della soluzione Exchange senza preoccuparsi di quale sia il server attivo (e se sia online oppure offline per un guasto o una  manutenzione).
In poche parole, questi software si rivolgono all'IP del cluster, sia esso assegnato ad un nodo o ad un altro dei servers facenti parte del DAG (che sono automaticamente anche nodi del failover cluster).

Questa architettura può generare alcuni problemi di performance (ma anche di altra natura):

1- se il cluster group è sul server 1, nel sito primario, e il database delle cassette postali ATTIVO è sul server 2, sempre nel sito primario, il software di backup interroga il server1, chiedendogli di passare il database. Questo poi interroga il server 2.... con il risultato che l'agente del sw di backup sta girando inutilmente nel network-spazio.... praticamente il server del backup chiama server 1, che chiama server 2 ....

2- se il cluster group è addirittura girato su un nodo del sito SECONDARIO, il backup è forzato a passare dalla WAN.

nel caso 1 vedremmo le performances del nostro backup degradarsi anche del 300% o più, nel caso 2... probabilmente non basterebbe tutta la notte a terminare il backup e, se configurato bene, si auto-terminerebbe, fallendo.

Queste ( ed altre) cose possono succedere perchè il cluster group (che detiene l'indirizzo IP del DAG) NON è forzato ad attivarsi sul server che detiene la copia attiva del DB.



Del resto questo è logico, dato che i servers di exchange possono detenere più Database attivi su più servers. Potremmo anche avere un database attivo per ogni server con utenti che si connettono all'infrastruttura in base a quale sito e poi a quale database ospita la loro cassetta postale.
in questa condizione il server 1 deterrebbe la copia attiva di DB1 e le repliche di DB2 DB3 e DB4, il server 2 deterrebbe la copia attiva di DB2 e le replice di DB1, DB3 e DB4, e così via.

... se poi, come nel mio caso, il software di backup si crea un PROPRIO GRUPPO CLUSTER, con cui comunicare.... ...le cose si complicano non poco.


Ma torniamo a noi. Vediamo di "PETTINARE" la situazione.
Come fare in modo che l'agente di backup giri sullo stesso server che detiene la copia attiva del database delle cassette postali che vogliamo backuppare, in modo che i nostri backup siano sempre "scattanti"?


Powershell ha le risposte...

giovedì 2 agosto 2012

Abilitare le reti ad-hoc su Android DIRETTAMENTE dal cellulare / tablet ! - lo Scriptomane - livello MEDIO

Oggi ci spingiamo in un mondo che non avevo mai trattato in queste pagine: il meraviglioso mondo di android (e quindi anche un po' di linux...).

Chi mi conosce sa che posseggo, tra l'altro, un tablet Galaxy Tab 7" prima versione (P1000) e che mi trovo benissimo.

Chi possiede un tablet android spesso si trova nell'antipatica situazione di dover acquistare e mantenere una SIM separata per navigare in internet su questo dispositivo in mobilità, perchè gli è impossibile fare il tethering dal telefono: il tablet non ne vuole sapere di vedere la connessione e tantomeno di connettersi!
Altri addirittura ci devono rinunciare, avendo a disposizione un tablet solo Wireless (WiFi). 

Questa antipatica situazione è generata dal fatto che Android non supporta le conessioni wireless ad-hoc. O meglio: non le supporta nativamente....


Oggi voglio spiegare come ho fatto ad abilitare le connessioni suddette e finalmente navigare attraverso il mio nokia 5800 con la applicazione Joikuspot (Grande app a pagamento che abilita il tethering dal telefono nokia. Cosa 9 euro, ma cosa sono 9 euro una-tantum, se potete risparmiarne 9 al mese?).

Non parlerò qui di come acquistare, installare, usare Joikuspot, anche perchè è davvero molto semplice.
Se qualcuno ha qualche necessità può commentare questo post e cercherò di aiutare quanto posso...


DISCLAIMER:
Prima di tutto, mettiamo in chiaro una cosa: se decidi di seguire questa procedura, lo fai a tuo rischio e pericolo. Se "bricchi" il dispositivo, non venire da me.
Il fatto che io sia riuscito, seguendo questa procedura, non vuol dire con certezza che riuscirai anche tu.



... dunque, i RINGRAZIAMENTI:
Grazie infinite a androidtablethacks.com per questo bellissimo articolo
Grazie tantissime a downloadandroidrom.com per hostare questo file 


Ora Partiamo. Useremo un approccio abbastanza diverso dall'articolo, in quanto non vogliamo utilizzare un computer: vogliamo farlo direttamente dal tablet!!!

PREREQUISITI:
  • Dispositivo android gingerbread o superiore ROOTATO.
  • Applicazione Android Terminal Emulator installata.
  • file adhoctablets.zip, scaricabile da qui. ATTENZIONE: si deve essere sicuri che il wpa_supplicant sia quello adatto per il proprio dispositivo. Nel caso non trovaste il wpa_supplicant corretto, ce ne sono parecchie versioni disponibili a partire dal sito forums.xda-developers.com o da altri siti... Cercate per il vostro modello e versione.
  • Applicazione per un-zippare i file (nell'esempio l'ho estratto e messo in /sdcard/adhocenabler)
  • 20 minuti di tempo circa.


FASE OPERATIVA (finalmente):
  1. Disattiva l'antenna WiFi (sufficiente dalla barra delle notifiche)
  2. Metti il telefono in USB debug mode, con impostazioni->applicazioni->sviluppo. (non dovrebbe nemmeno servire, ma "just to be on the safe side")
  3. lancia l'applicazione Android Terminal Emulator
  4. scrivi i seguenti comandi:

    su
    -
    mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system
    cp /system/bin/wpa_supplicant /sdcard/adhocenabler/wpa_supplicant.original
    cp /sdcard/adhocenabler/wpa_supplicant /system/bin/.
    chmod 755 /system/bin/wpa_supplicant
    chown system.wifi /data/misc/wifi/wpa_supplicant.conf
    exit

  5. Riavvia il tablet
  6. Attiva l'antenna WiFi 
  7. Divertiti :-)

Durante la procedura, ho fatto una copia di backup del  file di sistema originale (meglio sempre fare un backup, prima di tutto), così se qualcosa andasse storto e la nostra WiFi non si attivasse più, si potrà tornare indietro con questi passi:
 
PROCEDURA DI RESTORE:
  1. Disattiva l'antenna WiFi (sufficiente dalla barra delle notifiche)
  2. Metti il telefono in USB debug mode, con impostazioni->applicazioni->sviluppo. (non dovrebbe nemmeno servire, ma "just to be on the safe side")
  3. lancia l'applicazione Android Terminal Emulator
  4. scrivi i seguenti comandi:

    su
    -
    mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system
    cp /sdcard/adhocenabler/wpa_supplicant.original /system/bin/wpa_supplicant
    chmod 755 /system/bin/wpa_supplicant
    chown system.wifi /data/misc/wifi/wpa_supplicant.conf
    exit


  5. Riavvia il tablet
  6. Attiva l'antenna WiFi 



Spero di aver solleticato la vostra curiosità.

Ciao a tutti!

mercoledì 18 luglio 2012

Tenere sincronizzate le utenze tra server diversi Microsoft SQL server 2008 o 2005. - Lo Scriptomane - livello AVANZATO.

A volte è necessario inventarsi delle soluzioni proprie a problemi noti.
Queste sono proprio le sfide che fanno entrare in gioco gli scriptomani...
Qualcuno costruisce la sua piccola fortuna vendendole a caro prezzo, io le regalo a voi...

Ad esempio, ci sono N situazioni, in cui ci si può mettere, che richiedono di mantenere aggiornate le utenze tra più server SQL (ok, ok, solo se si è sistemisti e si lavora con SQL in aziende ad un certo livello, altrimenti hanno un unico server database e il problema non si pone... Già... e ringraziare quando hanno un server per il database ed uno diverso per il dominio e per la posta... LOL).

Una di queste situazioni è quando si crea un SQL mirroring ed il database è acceduto da tutti gli utenti windows dell'azienda, perchè il gestionale utilizza le credenziali windows per la connessione al database. E' una cosa normalissima e, anzi, molto utile. E' il caso di Microsoft® Navision® e di tanti altri. 
Altre occasioni possono essere un database "mirrorato" molto modificato sulle permissions, oppure Utenti Windows che accedono al DB che devono cambiare nel tempo, per un motivo o per l'altro.



Microsoft offre degli strumenti favolosi per replicare, trasferire, backuppare i dati, ma da SQL server 2005 in avanti c'è un "behaviour by design" (comportamento di default) che mal si sposa con la maggior parte delle situazioni in cui si ha bisogno di amministrare un DB su più servers:

gli utenti sono registrati sia a livello di servers che a livello di database, cosicchè si ha l'antipatica situazione in cui il database potrebbe contenere delle utenze che il server non contiene (si parla di "orphaned users").

Quegli utenti non solo non riusciranno ad accedere, ma saranno completamente fuori sincronia anche nel momento in cui si ricreasse l'utente windows a livello di server. Per riattivarli si dovranno cancellare dal DB e poi ricrearli sia nel server che nel DB (anche con un unica procedura).

Purtroppo la mamma di SQL non ha pensato a questa situazione, offrendo stumenti integrati: ha lasciato a noi "scriptomani" il compito di farlo!

Per fortuna qualcosa c'è: esiste un articolo della KB che mostra uno script per aggiornare le utenze di un server SQL remoto, partendo da uno locale. Purtroppo questo script è a tre fasi e non è automatizzabile (scriptabile) e quindi non ci piace... Quello che voglio fare qui è creare uno script che si possa schedulare su SQL perchè si "arrangi" a tenere sincronizzate le utenze quando meglio ci fa comodo.

Allora ecco qui lo script SQL che ho realizzato partendo da questo articolo della KB, insieme al mio "collega" Cladio C. (tanto dell'adeguamento è opera sua).



USE master
GO
DECLARE @name sysname
DECLARE @type varchar (1)
DECLARE @hasaccess int
DECLARE @denylogin int
DECLARE @is_disabled int
DECLARE @PWD_varbinary  varbinary (256)
DECLARE @PWD_string  varchar (514)
DECLARE @SID_varbinary varbinary (85)
DECLARE @SID_string varchar (514)
DECLARE @tmpstr  varchar (1024)
DECLARE @is_policy_checked varchar (3)
DECLARE @is_expiration_checked varchar (3)
DECLARE @defaultdb nvarchar(max)

DECLARE login_curs CURSOR FOR
      SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
      [].msdb.sys.server_principals p LEFT JOIN [
].msdb.sys.syslogins l
      ON ( l.name = p.name ) WHERE p.type IN ( 'G', 'U' ) AND p.name <> 'sa' AND p.name <> 'NT AUTHORITY\SERVIZIO DI RETE' AND p.name collate Latin1_General_CI_AS NOT IN (SELECT name from sys.server_principals) AND p.is_disabled = 0
OPEN login_curs

    FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin

    WHILE (@@fetch_status <> -1)
    BEGIN
          IF (@@fetch_status <> -2)
          BEGIN
           
            SET @tmpstr = '-- Login: ' + @name
           
            SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']'
           
            IF (@denylogin = 1)
            BEGIN -- login is denied access
              SET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( @name )
            END
            ELSE IF (@hasaccess = 0)
            BEGIN -- login exists but does not have access
              SET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( @name )
            END
            IF (@is_disabled = 1)
            BEGIN -- login is disabled
              SET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME( @name ) + ' DISABLE'
            END
           
            EXECUTE(@tmpstr)
            PRINT 'Utente ' + @name + ' Creato!'
           
          END

            FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
    END
CLOSE login_curs
DEALLOCATE login_curs


Prima di eseguire lo script (che si esegue sul server di DESTINAZIONE), si deve aggiungere il server di origine come server collegato, in modo da permetterci di fare query sul server come se fosse in locale.
Per fare questo, da SQL Server Management Studio:

Collegati all'istanza di DESTINAZIONE
Espandi "Oggetti Server" -> "server collegati"
se il server non è già presente, fai click di destro sul nodo "server collegati" e scegli "nuovo server collegato..." (vedi immagine 1)

immagine 1


Inserisci il nome del server collegato (io metto sempre il FQDN del server) nella scheda generale, seleziona SQL Server  e spostati nella scheda protezione.
Qui seleziona semplicemente la spunta "Verranno effettuate con il contesto di protezione corrente dell'account di accesso" e poi dai OK (immagine 2).

immagine2



A questo punto vedremo il server collegato.


Ora che abbiamo visto i prerequisiti, guardiamo la parte più interessante... lo script!
Commentiamolo insieme:



USE master
GO
DECLARE @name sysname
DECLARE @type varchar (1)
DECLARE @hasaccess int
DECLARE @denylogin int
DECLARE @is_disabled int
DECLARE @PWD_varbinary  varbinary (256)
DECLARE @PWD_string  varchar (514)
DECLARE @SID_varbinary varbinary (85)
DECLARE @SID_string varchar (514)
DECLARE @tmpstr  varchar (1024)
DECLARE @is_policy_checked varchar (3)
DECLARE @is_expiration_checked varchar (3)
DECLARE @defaultdb nvarchar(max)


dichiaro le variabili iniziali

 DECLARE login_curs CURSOR FOR







Start di un cursore in cui verranno messi i risultati che vado a leggere con al query successiva.

SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
      [].msdb.sys.server_principals p LEFT JOIN [
].msdb.sys.syslogins l
      ON ( l.name = p.name ) WHERE p.type IN ( 'G', 'U' ) AND p.name <> 'sa' AND p.name <> 'NT AUTHORITY\SERVIZIO DI RETE' AND p.name collate Latin1_General_CI_AS NOT IN (SELECT name from sys.server_principals) AND p.is_disabled = 0


Letteralmente: SELEZIONA i campi che ci interessano DALLA tabella msdb.sys.server_principals del server COLLEGATO (ORIGINE), unita alla tabella syslogins, DOVE il "type" è "G" o "U" (ovvero gli utenti WINDOWS) E il nome è diverso da "sa" E il nome è diverso da 'NT AUTHORITY\SERVIZIO DI RETE' E il nome (in collation del server di ORIGINE **) NON E' già presente nella tabella dei logins locale (quindi nel server di DESTINAZIONE).



OPEN login_curs

    FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin

    WHILE (@@fetch_status <> -1)




Apri il cursore e cicla queste azioni per ogni record, fino alla fine (quando @@fetc_status sarà = a -1) 


    BEGIN
          IF (@@fetch_status <> -2)
          BEGIN
           
            SET @tmpstr = '-- Login: ' + @name
           
            SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']'
           
            IF (@denylogin = 1)
            BEGIN -- login is denied access
              SET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( @name )
            END
            ELSE IF (@hasaccess = 0)
            BEGIN -- login exists but does not have access
              SET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( @name )
            END
            IF (@is_disabled = 1)
            BEGIN -- login is disabled
              SET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME( @name ) + ' DISABLE'
            END
           
            EXECUTE(@tmpstr)
            PRINT 'Utente ' + @name + ' Creato!'
 


Costruisco la query che andrà eseguita con i pezzi recuperati, poi la eseguo (EXECUTE) e mando un messaggio in console (PRINT) che riporta il nome utente creato.            
          END
 
fine del set di operazioni da ciclare.

            FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
    END
CLOSE login_curs
DEALLOCATE login_curs


leggi il prossimo record e, se presente, ri-cicla, altrimenti finisci (END), poi chiudi il cursore e libera la memoria.


L'ho schedulato e sincronizza tutte le utenze da un mesetto...
FIKO, no?


Ciao a tutti e alla prossima!

mercoledì 28 marzo 2012

Riorganizzare o rinominare i files immagine e MP3 con i dati EXIF e ID3 (e, ovviamente, leggere i dati EXIF e ID3) - Parte 2 - Lo Scriptomane - livello MEDIO

Eccomi finalmente a scrivere la seconda parte di questo articolo per riorganizzare le nostre gallerie fotografiche e / o di files MP3.


Nel precedente articolo ( http://scriptomane.blogspot.com/#!/2011/07/riorganizzare-o-rinominare-i-files.html ) abbiamo capito come utilizzare uno script vbscript ciclando la sua esecuzione su una cartella di files in un batch.

Questo, però, chiaramente può soltanto leggere le informazioni presenti nei files.


Ora aggiungiamo una funzione che, invece, ottiene esattamente la proprietà indicata come criterio di ordinamento, crea le diverse directory per ogni valore presente nei files e li sposta secondo questo criterio.




Creiamo un nuovo file: riordinaCollection.vbs



Set args = Wscript.Arguments
StrFolderNamet = args.Item(0)

intPropertyt = args.Item(1)

If args.length < 2 Then
  wscript.echo "Si devono fornire almeno due parametri a questo script."
  wscript.echo "SINTASSI: "
  wscript.echo "cscript nomeScript.vbs "
Wscript.quit
End if
'VERIFICARE LA PARTE IN GRASSETTO!!!!!




function fnRiordinaFiles(StrFolderName, intProperty)
        dim objShell
        dim objFolder
       
        set objShell = CreateObject("shell.application")
        set objFolder = objShell.NameSpace(StrFolderName)
       

        if (not objFolder is nothing) then
            set colItems = objFolder.Items
            For Each objFolderItem in colItems
                 if (not objFolderItem Is Nothing) then
                    dim objInfo, objInfoTip
 'ottengo la proprietà desiderata del file.
                     objInfo = objFolder.GetDetailsOf(objFolderItem, intProperty)
                        if (not objInfo = "") Then

' provo ad istanziare la sottocartella (così se esiste già lo rilevo)
                             set objSubFolder = objShell.NameSpace(StrFolderName & "\" & objInfo)

' se la cartella non esiste, la creo come sottocartella della cartella superiore (uso il metodo NewFolder)
                             if (objSubFolder is nothing) then
                                objFolder.newFolder(objInfo)
                                set objSubFolder = objShell.NameSpace(StrFolderName & "\" & objInfo)
                             end if

' uso il metodo MoveHere dell'oggetto sottocartella, per spostare il file nella cartella corrispondente alla sua proprietà.
                            objSubFolder.MoveHere(objFolderItem)
                           
                        end if
                     objInfoTip = objFolder.GetDetailsOf(objFolderItem, -1)
                   
                end if
           
                set objFolderItem = nothing
            Next
           end if
          
       
        set objFolder = nothing
        set objShell = nothing
    end function


fnRiordinaFiles StrFolderNamet, intPropertyt