<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Cristian Castellari — CV, Progetti e Blog</title>
    <description>Il CV, le esperienze e il blog di Cristian Castellari — si parla tanto di lavoro quanto delle mie passioni personali.</description>
    <link>https://cristiancastellari.it/</link>
    <atom:link href="https://cristiancastellari.it/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 12 Sep 2026 10:57:07 +0200</pubDate>
    <lastBuildDate>Sat, 12 Sep 2026 10:57:07 +0200</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>DocSteer: un tema Jekyll per la documentazione</title>
        <description>&lt;p&gt;Chi lavora in IT passa una parte non piccola della giornata dentro documentazione: runbook, procedure, articoli di knowledge base, wiki interne che nessuno ha più aperto da un anno. E la parte difficile non è quasi mai scriverla — è farla ritrovare a chi ne ha bisogno mesi dopo, con un problema aperto e poca pazienza.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DocSteer&lt;/strong&gt; è un tema Jekyll per esattamente quel tipo di sito: documentazione tecnica, portali di supporto, knowledge base interne. La barra laterale raggruppata, la ricerca e l’indice “In questa pagina” non sono decorazione — sono il motivo per cui il sito esiste.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-un-altro-tema-per-la-documentazione&quot; id=&quot;markdown-toc-perché-un-altro-tema-per-la-documentazione&quot;&gt;Perché un altro tema per la documentazione&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-fa&quot; id=&quot;markdown-toc-cosa-fa&quot;&gt;Cosa fa&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-docsteer&quot; id=&quot;markdown-toc-perché-docsteer&quot;&gt;Perché “DocSteer”&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#come-si-usa&quot; id=&quot;markdown-toc-come-si-usa&quot;&gt;Come si usa&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#come-lho-costruito&quot; id=&quot;markdown-toc-come-lho-costruito&quot;&gt;Come l’ho costruito&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-manca-ancora&quot; id=&quot;markdown-toc-cosa-manca-ancora&quot;&gt;Cosa manca ancora&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-è-gratis&quot; id=&quot;markdown-toc-perché-è-gratis&quot;&gt;Perché è gratis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;perché-un-altro-tema-per-la-documentazione&quot;&gt;Perché un altro tema per la documentazione&lt;/h2&gt;

&lt;p&gt;Di temi Jekyll per la documentazione ce ne sono già parecchi, e alcuni sono ottimi. Quello che volevo io era qualcosa di più stretto: un tema che si mettesse in piedi in un pomeriggio, che non trascinasse dentro una toolchain Node, e che non chiedesse di imparare un altro framework prima di poter scrivere la prima pagina.&lt;/p&gt;

&lt;p&gt;Il tema di questo sito, che pure ho estratto e pubblicato a parte, non ci arrivava: è pensato per un sito personale bilingue con un CV, qualche pagina e un blog. La documentazione è un problema diverso. Serve una barra laterale raggruppata e collassabile, una ricerca che regga su decine di pagine, un indice costruito da solo, un pager avanti/indietro che segua l’ordine della navigazione. Sono tutte cose che si possono aggiungere a mano, una alla volta — ed è esattamente così che si finisce con un tema da blog pieno di pezze.&lt;/p&gt;

&lt;h2 id=&quot;cosa-fa&quot;&gt;Cosa fa&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Sei skin di colore&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aqua&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;violet&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mint&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ember&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;graphite&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clay&lt;/code&gt; — ognuna con variante chiara e scura, definite in un’unica mappa Sass ed emesse come custom property CSS. Il colore di brand di ogni skin supera il contrasto WCAG AA (4.5:1) sul proprio sfondo.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tema scuro&lt;/strong&gt; che segue il sistema operativo, con toggle manuale salvato in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; e nessun lampo bianco al caricamento.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ricerca live&lt;/strong&gt; — un indice JSON generato da Liquid e una modale da tastiera (&lt;kbd&gt;/&lt;/kbd&gt; oppure &lt;kbd&gt;⌘K&lt;/kbd&gt;), senza una sola libreria esterna.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Indice automatico&lt;/strong&gt; costruito dagli heading, con scroll-spy, e pager avanti/indietro che segue da solo l’ordine della navigazione.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pagine FAQ&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;layout: faq&lt;/code&gt; genera un accordion basato su &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;details&amp;gt;&lt;/code&gt; nativo, con un anchor per domanda, dati strutturati &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FAQPage&lt;/code&gt;, e ogni domanda indicizzata come risultato di ricerca a sé.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lightbox immagini&lt;/strong&gt; con gallerie, didascalie e navigazione da tastiera.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Font Awesome 6.5.2 Free&lt;/strong&gt; incluso localmente di default, così il sito funziona offline e dietro i firewall aziendali. Il CDN resta un’opzione.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SEO&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll-seo-tag&lt;/code&gt;, sitemap, feed RSS, Open Graph e JSON-LD.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Niente framework CSS, nessuno step di build Node: circa 7 kB di JavaScript vanilla in tutto, tutto &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defer&lt;/code&gt;, CSS compresso e Sass compilato da Jekyll.&lt;/p&gt;

&lt;p&gt;Chiaro e scuro non sono due temi da mantenere in parallelo: sono la stessa mappa di custom property con valori diversi. Qui la pagina di configurazione tagliata a metà — chiaro a sinistra, scuro a destra — con barra laterale, indice e blocco di codice con pulsante di copia:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image&quot; src=&quot;/static/assets/img/blog/docsteer/light-dark.png&quot; alt=&quot;La stessa pagina di documentazione di DocSteer divisa a metà: tema chiaro a sinistra, tema scuro a destra&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Le sei skin si scelgono dal menu a tavolozza nella navbar, e ognuna ha la sua variante chiara e scura:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/docsteer/skins-menu.png&quot; alt=&quot;Il menu a tavolozza di DocSteer aperto nella navbar, con le sei skin: Aqua, Violet, Mint, Ember, Graphite, Clay&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/docsteer/skins-menu-dark.png&quot; alt=&quot;Il menu a tavolozza di DocSteer aperto nella navbar, con le sei skin: Aqua, Violet, Mint, Ember, Graphite, Clay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Cambiarla non tocca una riga di markup, solo delle custom property. Questa è la stessa identica pagina con la skin Violet:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/docsteer/docs-violet.png&quot; alt=&quot;La stessa pagina di documentazione con la skin Violet: accenti viola su barra laterale, indice e link&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/docsteer/docs-violet-dark.png&quot; alt=&quot;La stessa pagina di documentazione con la skin Violet: accenti viola su barra laterale, indice e link&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La ricerca è la parte a cui tengo di più, perché è quella che decide se una knowledge base è consultabile o è solo un archivio. Si apre da tastiera e raggruppa i risultati per sezione mentre si digita:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/docsteer/search.png&quot; alt=&quot;La ricerca live di DocSteer: risultati raggruppati per sezione, con i termini cercati evidenziati&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/docsteer/search-dark.png&quot; alt=&quot;La ricerca live di DocSteer: risultati raggruppati per sezione, con i termini cercati evidenziati&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;perché-docsteer&quot;&gt;Perché “DocSteer”&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;/dɒkˈstɪə/&lt;/strong&gt; — &lt;em&gt;dok-STÌR&lt;/em&gt;, accento sulla seconda sillaba, in rima con l’inglese &lt;em&gt;beer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Il nome è la descrizione del mestiere. &lt;em&gt;To steer&lt;/em&gt; vuol dire guidare, condurre: la barra laterale, il campo di ricerca e l’indice esistono solo per portare chi legge all’unica pagina che stava cercando. Una documentazione che non lo fa è una cartella di file Markdown con sopra un foglio di stile.&lt;/p&gt;

&lt;p&gt;Il resto l’ho scoperto dopo: &lt;em&gt;steer&lt;/em&gt;, in inglese, è anche un giovane bue. Non era assolutamente il piano. Ma come descrizione di qualcosa che si trascina dietro una pila enorme di documentazione senza mai lamentarsi è difficile darle torto, e ormai ci ho fatto pace.&lt;/p&gt;

&lt;h2 id=&quot;come-si-usa&quot;&gt;Come si usa&lt;/h2&gt;

&lt;p&gt;Due strade. &lt;strong&gt;Come gem&lt;/strong&gt;, aggiungendola al &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jekyll-theme-docsteer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 1.0&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;e poi nel &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-theme-docsteer&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-seo-tag&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-sitemap&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-feed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oppure &lt;strong&gt;clonando il repo&lt;/strong&gt; come starter, per partire da un sito già popolato e svuotarlo man mano. La gem sta &lt;a href=&quot;https://rubygems.org/gems/jekyll-theme-docsteer&quot;&gt;su RubyGems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tutte le opzioni del tema vivono sotto un’unica chiave &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docsteer:&lt;/code&gt; nel &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt; — skin, tema, ricerca, lightbox e il resto:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;docsteer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;aqua&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# aqua | violet | mint | ember | graphite | clay&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;auto&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# auto (segue il sistema) | light | dark&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skin_switcher&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# mostra il menu a tavolozza nella navbar&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;docs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;hotkey&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# &quot;/&quot; e Cmd/Ctrl-K aprono la ricerca&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;lightbox&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;La navigazione — navbar in alto e barra laterale raggruppata — sta tutta in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_data/navigation.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;sidebar&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Getting started&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;fa-solid fa-rocket&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Introduction&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;/docs/introduction/&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Installation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;/docs/installation/&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il pager in fondo a ogni pagina segue da solo l’ordine di questo file: riordini la navigazione e “precedente/successivo” si aggiusta di conseguenza. Il deploy va su GitHub Pages (via Actions), Netlify, Vercel o Cloudflare Pages.&lt;/p&gt;

&lt;p&gt;Demo dal vivo e documentazione completa su &lt;strong&gt;&lt;a href=&quot;https://cristiancastellari.it/docsteer/&quot;&gt;cristiancastellari.it/docsteer&lt;/a&gt;&lt;/strong&gt;; codice su &lt;strong&gt;&lt;a href=&quot;https://github.com/Skyflash/docsteer&quot;&gt;GitHub&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;come-lho-costruito&quot;&gt;Come l’ho costruito&lt;/h2&gt;

&lt;p&gt;Mi sono fatto dare una mano da &lt;strong&gt;Claude Code&lt;/strong&gt; (e da tanti, tanti token in Opus 5). La regola che mi sono dato è la stessa con cui ho &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/&quot;&gt;ricostruito questo sito&lt;/a&gt; qualche settimana fa: prima il piano scritto, poi il codice. Quali layout servivano (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;home&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;doc&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page&lt;/code&gt;), come strutturare l’indice di ricerca, cosa doveva restare configurabile da un solo file — deciso e messo nero su bianco prima di aprire un editor.&lt;/p&gt;

&lt;p&gt;Poi l’implementazione un pezzo alla volta, ricompilando e guardando il risultato prima di passare al successivo. Non è pignoleria: su un tema, quando salti la verifica, il bug non lo trovi tu — lo trova chi lo installa.&lt;/p&gt;

&lt;p&gt;Il che non vuol dire che ne sia uscito perfetto. DocSteer ha già due release alle spalle nello stesso giorno della 1.0.0, e sono tutte cose che si vedono solo &lt;em&gt;usando&lt;/em&gt; il tema, non costruendolo: il burger della navbar non apriva niente fuori dalle pagine di documentazione, i tag in fondo agli articoli erano badge inerti invece di aprire la ricerca, e l’indice di ricerca poteva arrivare dalla cache del browser anche dopo aver pubblicato pagine nuove. Le ho sistemate nella 1.1.0 e nella 1.1.1.&lt;/p&gt;

&lt;h2 id=&quot;cosa-manca-ancora&quot;&gt;Cosa manca ancora&lt;/h2&gt;

&lt;p&gt;Per onestà, due cose che so già.&lt;/p&gt;

&lt;p&gt;I sorgenti Sass usano ancora &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@import&lt;/code&gt;. Dart Sass stampa avvisi di deprecazione — la build è per il resto pulita — e la migrazione a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@use&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@forward&lt;/code&gt; è pianificata, ma tocca ventuno file e in un tema l’output visivo &lt;em&gt;è&lt;/em&gt; il prodotto: una regressione su una delle sei skin, in chiaro o in scuro, resta invisibile finché non la segnala qualcuno. Preferisco farla con calma.&lt;/p&gt;

&lt;p&gt;E gli anchor delle domande nelle pagine FAQ derivano dal testo della domanda: riformularla cambia il frammento URL e rompe i deep link già condivisi. Correggere un refuso è sicuro, riscrivere una domanda va pesato.&lt;/p&gt;

&lt;h2 id=&quot;perché-è-gratis&quot;&gt;Perché è gratis&lt;/h2&gt;

&lt;p&gt;Il tema è MIT, per uso personale e commerciale, senza obbligo di attribuzione: il credito in fondo si toglie da configurazione.&lt;/p&gt;

&lt;p&gt;Le cose che ho fatto gratis — la &lt;a href=&quot;/it/blog/fuori-ufficio/la-traduzione-di-morrowind-e-bloodmoon/&quot;&gt;traduzione di Morrowind&lt;/a&gt;, &lt;a href=&quot;/it/blog/fuori-ufficio/twittero-in-italiano-anche-io/&quot;&gt;quella di Twitter&lt;/a&gt;, un &lt;a href=&quot;/it/blog/tools/ipui-to-ipei/&quot;&gt;convertitore per telefoni DECT&lt;/a&gt; che apro forse una volta all’anno — sono quelle che alla fine hanno aiutato più persone, e quasi sempre in silenzio: nessun commento, nessuna mail, solo qualcuno dall’altra parte del mondo che ha risolto il suo problema e ha chiuso la scheda.&lt;/p&gt;

&lt;p&gt;Un tema per la documentazione ha esattamente lo stesso profilo. Se fa risparmiare una giornata a qualcuno che non incontrerò mai, è già valsa la pena scriverlo — e se vuole &lt;a href=&quot;https://www.buymeacoffee.com/cristiancastellari&quot;&gt;offrirmi un caffè&lt;/a&gt;, tanto meglio.&lt;/p&gt;
</description>
        <pubDate>Tue, 01 Sep 2026 09:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/docsteer-un-tema-jekyll-per-la-documentazione/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/docsteer-un-tema-jekyll-per-la-documentazione/</guid>
        
        <category>jekyll</category>
        
        <category>docsteer</category>
        
        <category>documentazione</category>
        
        <category>open-source</category>
        
        <category>claude-code</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>DocSteer: a Jekyll theme for documentation</title>
        <description>&lt;p&gt;Anyone working in IT spends a fair chunk of the day inside documentation: runbooks, procedures, knowledge base articles, internal wikis nobody has opened in a year. And the hard part is almost never writing it — it is making it findable months later, by someone with an open ticket and very little patience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DocSteer&lt;/strong&gt; is a Jekyll theme for exactly that kind of site: technical documentation, support portals, internal knowledge bases. The grouped sidebar, the search and the “On this page” table of contents are not decoration — they are the reason the site exists.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#why-another-documentation-theme&quot; id=&quot;markdown-toc-why-another-documentation-theme&quot;&gt;Why another documentation theme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-it-does&quot; id=&quot;markdown-toc-what-it-does&quot;&gt;What it does&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-docsteer&quot; id=&quot;markdown-toc-why-docsteer&quot;&gt;Why “DocSteer”&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-to-use-it&quot; id=&quot;markdown-toc-how-to-use-it&quot;&gt;How to use it&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-it-was-built&quot; id=&quot;markdown-toc-how-it-was-built&quot;&gt;How it was built&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#whats-still-missing&quot; id=&quot;markdown-toc-whats-still-missing&quot;&gt;What’s still missing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-its-free&quot; id=&quot;markdown-toc-why-its-free&quot;&gt;Why it’s free&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-another-documentation-theme&quot;&gt;Why another documentation theme&lt;/h2&gt;

&lt;p&gt;There are already plenty of Jekyll documentation themes, and some of them are very good. What I wanted was something narrower: a theme you could stand up in an afternoon, that did not drag in a Node toolchain, and that did not ask you to learn another framework before writing your first page.&lt;/p&gt;

&lt;p&gt;The theme behind this site, which I did extract and publish separately, was never going to get there: it is built for a bilingual personal site with a CV, a handful of pages and a blog. Documentation is a different problem. It needs a grouped, collapsible sidebar, a search that holds up across dozens of pages, a table of contents that builds itself, a prev/next pager that follows the navigation order. Each of those can be bolted on by hand, one at a time — which is precisely how you end up with a blog theme covered in patches.&lt;/p&gt;

&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Six colour skins&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aqua&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;violet&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mint&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ember&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;graphite&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clay&lt;/code&gt; — each with a light and a dark variant, defined as one Sass map and emitted as CSS custom properties. Every skin’s brand colour clears WCAG AA contrast (4.5:1) against its own background.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dark mode&lt;/strong&gt; that follows the OS, with a manual toggle stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; and no white flash on load.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Live search&lt;/strong&gt; — a Liquid-generated JSON index and a keyboard-driven modal (&lt;kbd&gt;/&lt;/kbd&gt; or &lt;kbd&gt;⌘K&lt;/kbd&gt;), with no external library at all.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Auto table of contents&lt;/strong&gt; built from your headings, with scroll-spy, plus a prev/next pager that follows the navigation order on its own.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FAQ pages&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;layout: faq&lt;/code&gt; renders an accordion built on native &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;details&amp;gt;&lt;/code&gt;, with a per-question anchor, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FAQPage&lt;/code&gt; structured data, and each question indexed as its own search result.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Image lightbox&lt;/strong&gt; with galleries, captions and keyboard navigation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Font Awesome 6.5.2 Free&lt;/strong&gt; bundled locally by default, so the site works offline and behind corporate firewalls. The CDN stays an option.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SEO&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll-seo-tag&lt;/code&gt;, sitemap, RSS feed, Open Graph and JSON-LD.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No CSS framework, no Node build step: about 7 kB of vanilla JavaScript in total, all deferred, compressed CSS and Sass compiled by Jekyll.&lt;/p&gt;

&lt;p&gt;Light and dark are not two themes to keep in sync: they are the same map of custom properties with different values. Here is the configuration page cut down the middle — light on the left, dark on the right — with the sidebar, the table of contents and a code block with a copy button:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image&quot; src=&quot;/static/assets/img/blog/docsteer/light-dark.png&quot; alt=&quot;The same DocSteer documentation page split down the middle: light theme on the left, dark theme on the right&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The six skins are picked from the palette menu in the navbar, and each one has its own light and dark variant:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/docsteer/skins-menu.png&quot; alt=&quot;The DocSteer palette menu open in the navbar, showing the six skins: Aqua, Violet, Mint, Ember, Graphite, Clay&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/docsteer/skins-menu-dark.png&quot; alt=&quot;The DocSteer palette menu open in the navbar, showing the six skins: Aqua, Violet, Mint, Ember, Graphite, Clay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Switching one does not touch a line of markup, only custom properties. This is the exact same page with the Violet skin:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/docsteer/docs-violet.png&quot; alt=&quot;The same documentation page with the Violet skin: purple accents on the sidebar, table of contents and links&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/docsteer/docs-violet-dark.png&quot; alt=&quot;The same documentation page with the Violet skin: purple accents on the sidebar, table of contents and links&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Search is the part I care about most, because it is what decides whether a knowledge base is something you consult or just something you archive. It opens from the keyboard and groups results by section as you type:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/docsteer/search.png&quot; alt=&quot;DocSteer live search: results grouped by section, with the searched terms highlighted&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/docsteer/search-dark.png&quot; alt=&quot;DocSteer live search: results grouped by section, with the searched terms highlighted&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-docsteer&quot;&gt;Why “DocSteer”&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;/dɒkˈstɪə/&lt;/strong&gt; — &lt;em&gt;dok-STEER&lt;/em&gt;, stress on the second syllable, rhyming with &lt;em&gt;beer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The name is the job description. Steering is the whole point: the sidebar, the search box and the table of contents exist only to get a reader to the one page they actually came for. A docs site that cannot do that is a folder of Markdown with a stylesheet on top.&lt;/p&gt;

&lt;p&gt;The rest I found out afterwards: a &lt;em&gt;steer&lt;/em&gt; is also a young ox. That was absolutely not the plan. But as a description of something that hauls a large pile of documentation around without ever complaining, it is hard to argue with — and by now I have made my peace with it.&lt;/p&gt;

&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it&lt;/h2&gt;

&lt;p&gt;Two ways. &lt;strong&gt;As a gem&lt;/strong&gt;, added to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jekyll-theme-docsteer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 1.0&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-theme-docsteer&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-seo-tag&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-sitemap&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-feed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or &lt;strong&gt;by cloning the repo&lt;/strong&gt; as a starter, so you begin from a site that is already populated and empty it out as you go. The gem is &lt;a href=&quot;https://rubygems.org/gems/jekyll-theme-docsteer&quot;&gt;on RubyGems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every theme option lives under a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docsteer:&lt;/code&gt; key in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt; — skin, mode, search, lightbox and the rest:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;docsteer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;aqua&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# aqua | violet | mint | ember | graphite | clay&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;auto&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# auto (follow OS) | light | dark&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skin_switcher&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# show the palette dropdown in the navbar&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;docs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;hotkey&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# &quot;/&quot; and Cmd/Ctrl-K open search&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;lightbox&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Navigation — the top navbar and the grouped sidebar — all lives in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_data/navigation.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;sidebar&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Getting started&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;fa-solid fa-rocket&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Introduction&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;/docs/introduction/&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Installation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;/docs/installation/&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The pager at the bottom of every page follows the order of that file on its own: reorder the navigation and “previous/next” rearranges itself. It deploys to GitHub Pages (via Actions), Netlify, Vercel or Cloudflare Pages.&lt;/p&gt;

&lt;p&gt;Live demo and full documentation at &lt;strong&gt;&lt;a href=&quot;https://cristiancastellari.it/docsteer/&quot;&gt;cristiancastellari.it/docsteer&lt;/a&gt;&lt;/strong&gt;; code on &lt;strong&gt;&lt;a href=&quot;https://github.com/Skyflash/docsteer&quot;&gt;GitHub&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-it-was-built&quot;&gt;How it was built&lt;/h2&gt;

&lt;p&gt;I got a hand from &lt;strong&gt;Claude Code&lt;/strong&gt; (and a great many Opus 5 tokens). The rule I set myself is the same one I used when I rebuilt this site a few weeks ago: the written plan first, the code second. Which layouts were needed (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;home&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;doc&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page&lt;/code&gt;), how to structure the search index, what had to stay configurable from a single file — all decided and written down before opening an editor.&lt;/p&gt;

&lt;p&gt;Then the implementation one piece at a time, rebuilding and looking at the result before moving on. That is not fussiness: on a theme, when you skip the check, you are not the one who finds the bug — the person installing it is.&lt;/p&gt;

&lt;p&gt;Which is not to say it came out perfect. DocSteer already has two releases behind it from the same day as 1.0.0, and they are all things you only see by &lt;em&gt;using&lt;/em&gt; the theme rather than building it: the navbar burger did nothing outside documentation pages, the tags at the bottom of an article were inert badges instead of opening the search, and the search index could be served from the browser cache even after publishing new pages. Fixed in 1.1.0 and 1.1.1.&lt;/p&gt;

&lt;h2 id=&quot;whats-still-missing&quot;&gt;What’s still missing&lt;/h2&gt;

&lt;p&gt;Two things I already know about, for honesty’s sake.&lt;/p&gt;

&lt;p&gt;The Sass sources still use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@import&lt;/code&gt;. Dart Sass prints deprecation warnings — the build is otherwise clean — and the migration to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@use&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@forward&lt;/code&gt; is planned, but it touches twenty-one files, and in a theme the visual output &lt;em&gt;is&lt;/em&gt; the product: a regression in one of the six skins, light or dark, stays invisible until somebody reports it. I would rather do that one slowly.&lt;/p&gt;

&lt;p&gt;And the question anchors on FAQ pages are derived from the question text: rewording one changes its URL fragment and breaks deep links already shared. Fixing a typo is safe; rewriting a question needs a second thought.&lt;/p&gt;

&lt;h2 id=&quot;why-its-free&quot;&gt;Why it’s free&lt;/h2&gt;

&lt;p&gt;The theme is MIT, for personal and commercial use, with no attribution required: the footer credit switches off from config.&lt;/p&gt;

&lt;p&gt;The things I have made for free — the Italian translation of Morrowind, the Italian translation of Twitter back in 2009, a &lt;a href=&quot;https://cristiancastellari.it/ipui2ipei/&quot;&gt;converter for DECT phones&lt;/a&gt; I open maybe once a year — turned out to be the ones that helped the most people, and almost always in silence: no comments, no emails, just somebody on the other side of the world who solved their problem and closed the tab.&lt;/p&gt;

&lt;p&gt;A documentation theme has exactly the same profile. If it saves a day for someone I will never meet, it was already worth writing — and if they want to &lt;a href=&quot;https://www.buymeacoffee.com/cristiancastellari&quot;&gt;buy me a coffee&lt;/a&gt;, even better.&lt;/p&gt;
</description>
        <pubDate>Tue, 01 Sep 2026 09:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/en/blog/personal-projects/docsteer-a-jekyll-theme-for-documentation/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/en/blog/personal-projects/docsteer-a-jekyll-theme-for-documentation/</guid>
        
        <category>jekyll</category>
        
        <category>docsteer</category>
        
        <category>documentation</category>
        
        <category>open-source</category>
        
        <category>claude-code</category>
        
        
        <category>Personal Projects</category>
        
      </item>
    
      <item>
        <title>Twitter potrebbe tornare grazie a Operation Bluebird?</title>
        <description>&lt;p&gt;Nel 2009 &lt;a href=&quot;/it/blog/fuori-ufficio/twittero-in-italiano-anche-io/&quot;&gt;ho raccontato&lt;/a&gt; di essere stato scelto dal team di Twitter, insieme a una manciata di altri volontari italiani, per tradurre il sito in italiano — gratis, per lo stesso motivo per cui anni prima avevo tradotto Morrowind: mi piaceva farlo, e il servizio restava gratuito per tutti. Diciassette anni dopo, quel nome che ho contribuito a rendere leggibile in italiano potrebbe tornare. Non per mano di Elon Musk, che nel 2023 lo ha cancellato per trasformarlo in X, ma di una startup che quasi nessuno si aspettava: &lt;strong&gt;Operation Bluebird&lt;/strong&gt;.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#chi-cè-dietro-operation-bluebird&quot; id=&quot;markdown-toc-chi-cè-dietro-operation-bluebird&quot;&gt;Chi c’è dietro Operation Bluebird&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#la-causa-x-corp-v-operation-bluebird&quot; id=&quot;markdown-toc-la-causa-x-corp-v-operation-bluebird&quot;&gt;La causa: X Corp. v. Operation Bluebird&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#un-pronostico-difficile-ma-stimolante&quot; id=&quot;markdown-toc-un-pronostico-difficile-ma-stimolante&quot;&gt;Un pronostico difficile, ma stimolante&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-mio-punto-di-vista-personale&quot; id=&quot;markdown-toc-il-mio-punto-di-vista-personale&quot;&gt;Il mio punto di vista. Personale.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;chi-cè-dietro-operation-bluebird&quot;&gt;Chi c’è dietro Operation Bluebird&lt;/h2&gt;

&lt;p&gt;Operation Bluebird è il progetto di Stephen Coates, che è stato per anni general counsel di Twitter (quello vero, pre-Musk), insieme all’avvocato specializzato in marchi Michael Peroff. Il 26 agosto 2026 hanno lanciato pubblicamente &lt;a href=&quot;https://twitter.now/&quot;&gt;Twitter.now&lt;/a&gt;, attualmente in accesso anticipato e a numero chiuso: si può prenotare il proprio handle, ma l’ingresso vero e proprio è ancora limitato. Nell’interfaccia si ritrovano reply e retweet praticamente identici a quelli dell’originale, con l’aggiunta di uno strumento di fact-checking che il vecchio Twitter non aveva.&lt;/p&gt;

&lt;p&gt;La tesi di fondo di Coates e Peroff è semplice da riassumere: X non usa più il nome “Twitter” né la parola “tweet” da anni, ha rimosso l’uccellino da loghi e comunicazione, quindi quei marchi sono di fatto liberi — e loro vogliono essere i primi a riprenderseli.&lt;/p&gt;

&lt;h2 id=&quot;la-causa-x-corp-v-operation-bluebird&quot;&gt;La causa: X Corp. v. Operation Bluebird&lt;/h2&gt;

&lt;p&gt;Qui la storia smette di essere solo un aneddoto tecnologico e diventa una battaglia legale vera, che ha già un nome ufficiale: &lt;a href=&quot;https://en.wikipedia.org/wiki/X_Corp._v._Operation_Bluebird&quot;&gt;&lt;em&gt;X Corp. v. Operation Bluebird&lt;/em&gt;&lt;/a&gt;. A dicembre 2025 X ha fatto causa a Operation Bluebird in Delaware, chiedendo un’ingiunzione preliminare per bloccare sul nascere il lancio di un servizio con il nome Twitter.&lt;/p&gt;

&lt;p&gt;Operation Bluebird, dal canto suo, non si è limitata a difendersi: ha depositato all’USPTO (l’ufficio marchi e brevetti americano) &lt;a href=&quot;https://cybernews.com/tech/key-takeaways-operation-bluebird-launches-twitter-now-amid-x-trademark-fight/&quot;&gt;una petizione di 105 pagine&lt;/a&gt; per far dichiarare decaduti i marchi “Twitter” e “Tweet”, facendo leva su una regola precisa del diritto americano dei marchi (il Lanham Act): tre anni consecutivi di non utilizzo, senza intenzione di riprenderlo, creano una presunzione di abbandono. Come prova principale citano le stesse parole di Musk del 2023, quando annunciò che avrebbe detto addio “al marchio Twitter e, gradualmente, a tutti gli uccellini”.&lt;/p&gt;

&lt;p&gt;A dare peso concreto alla tesi ci ha pensato, per ora solo a voce, il giudice distrettuale Colm Connolly: in un’udienza di aprile 2026 ha dichiarato dal banco che X “sembra aver abbandonato i diritti di proprietà intellettuale sulla parola ‘Tweet’, sul logo dell’uccellino, e forse anche sulla parola ‘Twitter’ stessa” — &lt;a href=&quot;https://www.engadget.com/2246550/theres-a-new-twitter-in-town-even-though-a-judge-has-yet-to-rule-on-a-trademark-injunction/&quot;&gt;una dichiarazione&lt;/a&gt; che &lt;a href=&quot;https://www.techrepublic.com/article/news-twitter-now-x-trademark-fight-2026/&quot;&gt;le testate tecnologiche&lt;/a&gt; hanno riportato ampiamente. È importante essere precisi però: si tratta di un’opinione preliminare espressa a voce durante l’udienza, non di una sentenza scritta — che a fine agosto 2026 non è ancora arrivata. Operation Bluebird ha comunque deciso che bastava come via libera informale per lanciare Twitter.now senza aspettare l’ultima parola del tribunale.&lt;/p&gt;

&lt;h2 id=&quot;un-pronostico-difficile-ma-stimolante&quot;&gt;Un pronostico difficile, ma stimolante&lt;/h2&gt;

&lt;p&gt;Non ho gli strumenti per dire come finirà la causa — il diritto dei marchi è un campo minato anche per chi lo mastica di mestiere, figurarsi da fuori. Ma qualche osservazione mi sento di farla.&lt;/p&gt;

&lt;p&gt;La prima è che la scommessa di Operation Bluebird è tanto più forte quanto più X somiglia oggi a una piattaforma diversa, con un altro nome, un altro logo, un altro proprietario che ha esplicitamente detto di volersi allontanare dal brand originale: è raro che chi fa causa per un marchio abbia lasciato in giro così tante proprie dichiarazioni contro se stesso. La seconda è che rifare “un altro Twitter” nel 2026 è molto più difficile che nel 2009: il pubblico è più stanco, più diviso su piattaforme diverse (Bluesky, Threads, Mastodon), e un nome familiare da solo non basta più a spostare milioni di persone — serve anche fiducia, cosa che né Musk né una startup appena nata hanno in abbondanza.&lt;/p&gt;

&lt;h2 id=&quot;il-mio-punto-di-vista-personale&quot;&gt;Il mio punto di vista. Personale.&lt;/h2&gt;

&lt;p&gt;C’è poi un pezzo più personale, ed è quello che mi tiene davvero legato a questa notizia. Mi sento orfano di Twitter da quando è finito nelle mani di Musk: da troppi anni ormai è diventato il megafono della mediocrità, più che una piazza. Ricordo un Twitter autorevole — l’unico media abbastanza veloce da mettere in comunicazione in tempo reale zone disagiate del pianeta, o intere zone di guerra, con tutti noi, spesso prima ancora che la notizia arrivasse ai telegiornali. Ricordo quando la spunta blu te la dava Twitter stesso, perché eri autorevole per quello che facevi o dicevi, non perché avevi una carta di credito: oggi te la compri come l’insalata al mercato, e infatti vale altrettanto.&lt;/p&gt;

&lt;p&gt;È anche per questo che continuerò a seguire Operation Bluebird con più di una semplice curiosità tecnica: non tanto per rivedere lo stesso nome tornare online, quanto nella speranza — flebile, lo so — che qualcuno, ripartendo da zero proprio con quel nome, provi davvero a restituirgli l’autorevolezza persa per strada. Un servizio che ho contribuito a tradurre da volontario, quando era ancora un progetto piccolo e un po’ ingenuo, che torna a esistere gestito da chi lo conosceva dall’interno prima che diventasse altro: difficile. Ma vale la pena sperarci.&lt;/p&gt;
</description>
        <pubDate>Sat, 29 Aug 2026 09:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/fuori-ufficio/twitter-torna-operation-bluebird/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/fuori-ufficio/twitter-torna-operation-bluebird/</guid>
        
        <category>twitter</category>
        
        <category>x</category>
        
        <category>social media</category>
        
        <category>attualità</category>
        
        
        <category>Fuori dall&apos;Ufficio</category>
        
      </item>
    
      <item>
        <title>Il redesign di Ipui2Ipei</title>
        <description>&lt;p&gt;Nel 2019 ho scritto &lt;a href=&quot;/it/blog/tools/ipui-to-ipei/&quot;&gt;un post&lt;/a&gt; su un problema piccolo e molto specifico: registrare un cordless Siemens su un centralino di un altro produttore richiede di convertire il suo codice IPUI in un codice IPEI standard. Per risolverlo avevo aggiustato &lt;a href=&quot;https://cristiancastellari.it/ipui2ipei/&quot;&gt;ipui2ipei&lt;/a&gt;, un tool trovato su GitHub, e me n’ero sostanzialmente dimenticato.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-rifare-un-tool-che-quasi-nessuno-nota&quot; id=&quot;markdown-toc-perché-rifare-un-tool-che-quasi-nessuno-nota&quot;&gt;Perché rifare un tool che quasi nessuno nota&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-cera-prima-e-perché-non-bastava-più&quot; id=&quot;markdown-toc-cosa-cera-prima-e-perché-non-bastava-più&quot;&gt;Cosa c’era prima, e perché non bastava più&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#stesso-schema-aggiustamenti-su-misura&quot; id=&quot;markdown-toc-stesso-schema-aggiustamenti-su-misura&quot;&gt;Stesso schema, aggiustamenti su misura&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#riscrivere-lalgoritmo-senza-cambiare-un-solo-bit-di-output&quot; id=&quot;markdown-toc-riscrivere-lalgoritmo-senza-cambiare-un-solo-bit-di-output&quot;&gt;Riscrivere l’algoritmo senza cambiare un solo bit di output&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#la-palette-dal-primo-tentativo-a-quello-buono&quot; id=&quot;markdown-toc-la-palette-dal-primo-tentativo-a-quello-buono&quot;&gt;La palette: dal primo tentativo a quello buono&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#una-nuova-pagina-contatti&quot; id=&quot;markdown-toc-una-nuova-pagina-contatti&quot;&gt;Una nuova pagina Contatti&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#un-bug-trovato-scrivendo-la-pagina-contatti&quot; id=&quot;markdown-toc-un-bug-trovato-scrivendo-la-pagina-contatti&quot;&gt;Un bug trovato scrivendo la pagina Contatti&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-mi-porto-a-casa&quot; id=&quot;markdown-toc-cosa-mi-porto-a-casa&quot;&gt;Cosa mi porto a casa&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;perché-rifare-un-tool-che-quasi-nessuno-nota&quot;&gt;Perché rifare un tool che quasi nessuno nota&lt;/h2&gt;

&lt;p&gt;Guardando Google Search Console per quel dominio, la cosa che salta all’occhio non sono i numeri assoluti — è uno strumento verticale per un problema di nicchia, il traffico è quello che ci si aspetta — ma &lt;strong&gt;cosa&lt;/strong&gt; cercano le persone per arrivarci. Quasi nessuno digita “ipui2ipei”, il nome del progetto: la query dominante è una variante diretta del problema, “ipui to ipei”, seguita da “ipei”, “ipei to ipui” e persino “ipui gigaset” — qualcuno che cerca partendo dalla marca del proprio telefono, non dal nome dello standard. Sono persone che non sanno che il mio tool esiste, sanno solo di avere in mano un cordless Siemens/Gigaset e un centralino che non lo riconosce.&lt;/p&gt;

&lt;p&gt;La posizione media per queste query non-branded è comunque bassa, vicina alla decima — eppure il CTR resta sopra il 12%, niente male per un risultato che in teoria è sepolto in fondo alla prima pagina. Chi arriva, insomma, sa esattamente cosa vuole trovare.&lt;/p&gt;

&lt;p&gt;C’è un altro modo di guardare la stessa cosa: fra tutte le pagine di destinazione da ricerca organica di &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cristiancastellari.it&lt;/code&gt;, quasi tre quarti dei click finiscono dritti su &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/ipui2ipei/&lt;/code&gt;. Il vecchio post del 2019 che lo racconta è il secondo — insieme, tool e articolo, si prendono quasi 9 click su 10 fra le pagine più cercate del sito. Uno strumento così merita di funzionare bene su schermi diversi da quello 2014 per cui era stato pensato, non di essere abbandonato solo perché nessuno se ne lamenta apertamente.&lt;/p&gt;

&lt;h2 id=&quot;cosa-cera-prima-e-perché-non-bastava-più&quot;&gt;Cosa c’era prima, e perché non bastava più&lt;/h2&gt;

&lt;p&gt;Il tool era un fork del 2014 di un progetto di Gorka Hernández, aggiustato da me negli anni fra il 2018 e il 2019: Bootstrap 3.2.0, jQuery, un CSS esterno ma niente di più. Funzionava — è ancora online da anni senza un solo bug segnalato — ma portava lo stesso tipo di debito tecnico da cui sono partito per &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/&quot;&gt;ricostruire il sito principale&lt;/a&gt; poche settimane fa: una libreria CSS ferma al 2014, uno script scritto con variabili globali mai dichiarate con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var&lt;/code&gt;, zero automazione — ogni modifica andava caricata a mano via FTP.&lt;/p&gt;

&lt;p&gt;Con lo schema Jekyll 4 + GitHub Actions appena collaudato e documentato sul sito principale, riapplicarlo qui è stato più veloce che lasciare le cose come stavano.&lt;/p&gt;

&lt;h2 id=&quot;stesso-schema-aggiustamenti-su-misura&quot;&gt;Stesso schema, aggiustamenti su misura&lt;/h2&gt;

&lt;p&gt;Non è stato un copia-incolla: ipui2ipei vive come &lt;em&gt;project page&lt;/em&gt; sotto lo stesso dominio custom del sito principale (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cristiancastellari.it/ipui2ipei/&lt;/code&gt;), quindi ha un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;baseurl&lt;/code&gt; diverso — ogni link e ogni asset passa dal filtro giusto di Jekyll per restare coerente sotto quel prefisso, cosa che sul sito principale (che vive alla radice del dominio) non serve.&lt;/p&gt;

&lt;p&gt;Il vincolo più importante era però lo stesso di allora: &lt;strong&gt;l’URL storico già indicizzato da anni doveva continuare a funzionare&lt;/strong&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://cristiancastellari.it/ipui2ipei/&lt;/code&gt; oggi fa da redirect verso la nuova pagina italiana — chi ha quel link salvato nei preferiti, o lo trova ancora citato da qualche parte, non nota nessuna differenza.&lt;/p&gt;

&lt;p&gt;Ho anche colto l’occasione per renderlo bilingue, con pagine italiane e inglesi separate invece di uno switcher lato client: la maggior parte del traffico arriva da ricerca organica, e uno switcher JavaScript è invisibile a un motore di ricerca quanto lo è a chi digita “ipei to ipui” in inglese dall’altra parte del mondo.&lt;/p&gt;

&lt;h2 id=&quot;riscrivere-lalgoritmo-senza-cambiare-un-solo-bit-di-output&quot;&gt;Riscrivere l’algoritmo senza cambiare un solo bit di output&lt;/h2&gt;

&lt;p&gt;La parte che mi metteva più a disagio era toccare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ipui2ipei.js&lt;/code&gt;: è il cuore del tool, e un bug introdotto lì produce un IPEI sbagliato senza che nessuno se ne accorga finché qualcuno non prova a registrare un telefono che non si aggancia. L’ho riscritto da zero — via jQuery, via le variabili globali implicite dell’originale — ma &lt;strong&gt;la matematica non doveva cambiare di una virgola&lt;/strong&gt;: stesso scarto del primo carattere, stessa suddivisione in due blocchi esadecimali, stesso zero-padding, stesso checksum secondo lo standard ETSI EN 300 175-6.&lt;/p&gt;

&lt;p&gt;Per essere sicuro di non aver rotto nulla ho scritto un piccolo harness che esegue in parallelo l’algoritmo originale (trascritto tale e quale) e quello nuovo su una batteria di IPUI di prova — compreso un caso limite trovato apposta per forza bruta, quello in cui il checksum dà esattamente 10 e va sostituito con un asterisco secondo lo standard. Output identico su tutta la batteria, poi riconfermato aprendo davvero il tool in un browser headless, compilando il campo e premendo Converti via script, prima di fidarmi della riscrittura.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/ipuitoipei/redesign-convertitore.jpg&quot; alt=&quot;Il nuovo convertitore ipui2ipei&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/ipuitoipei/redesign-convertitore-dark.jpg&quot; alt=&quot;Il nuovo convertitore ipui2ipei&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Un paio di dettagli che prima non c’erano: l’errore di validazione, che era un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alert()&lt;/code&gt; bloccante del browser, è diventato un messaggio inline con bordo colorato; e il bottone Converti è ora un vero &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type=&quot;submit&quot;&lt;/code&gt;, quindi premere Invio nel campo IPUI avvia la conversione da solo, senza dover cliccare — comportamento nativo del form, non uno script scritto apposta per intercettare il tasto.&lt;/p&gt;

&lt;h2 id=&quot;la-palette-dal-primo-tentativo-a-quello-buono&quot;&gt;La palette: dal primo tentativo a quello buono&lt;/h2&gt;

&lt;p&gt;La prima proposta puntava su un’identità visiva totalmente indipendente dal sito principale, in ambra. L’ho rivista dopo essermela guardata per un po’: il bottone Converti e il bordo di validazione dell’IPUI sono rimasti verdi in entrambi i temi — un richiamo diretto al vecchio &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;btn-success&lt;/code&gt; di Bootstrap, l’unica cosa della palette originale che valeva la pena conservare — mentre testo ed evidenziazioni sono tornati sul blu del sito principale nel tema chiaro, con un arancione (un omaggio al mondo Gigaset/DECT di cui parla lo strumento) nel tema scuro.&lt;/p&gt;

&lt;p&gt;Altri due dettagli minori ma fastidiosi da guardare, prima: nello switcher lingua e nel nav in alto, l’elemento su cui ti trovi già era discreto e quello cliccabile era colorato — esattamente il contrario di quello che ci si aspetta guardando una UI del genere. Ho invertito la gerarchia: ora “sei qui” è evidenziato con un pill pieno, il resto si accende solo al passaggio del mouse.&lt;/p&gt;

&lt;h2 id=&quot;una-nuova-pagina-contatti&quot;&gt;Una nuova pagina Contatti&lt;/h2&gt;

&lt;p&gt;Lo strumento non aveva alcun modo per chi lo usa di contattarmi, se non intuendo che il progetto fosse mio da un link GitHub in fondo alla pagina. Ho aggiunto una pagina Contatti che replica la struttura di quella del sito principale — stesso saluto personale come titolo (“Ciao, sono Cristian!”) al posto di un anonimo “Contatti”, stessa griglia di card per canale.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;post-image post-image--on-light&quot; src=&quot;/static/assets/img/blog/ipuitoipei/redesign-contatti.jpg&quot; alt=&quot;La nuova pagina Contatti di ipui2ipei&quot; /&gt;
&lt;img class=&quot;post-image post-image--on-dark&quot; src=&quot;/static/assets/img/blog/ipuitoipei/redesign-contatti-dark.jpg&quot; alt=&quot;La nuova pagina Contatti di ipui2ipei&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Le icone però sono disegnate a mano in SVG invece di venire da un font di icone: reintrodurne uno solo per sei icone in una pagina avrebbe vanificato il senso di essersi liberati di Bootstrap e di tutto il resto in un colpo solo.&lt;/p&gt;

&lt;h2 id=&quot;un-bug-trovato-scrivendo-la-pagina-contatti&quot;&gt;Un bug trovato scrivendo la pagina Contatti&lt;/h2&gt;

&lt;p&gt;Controllando l’HTML generato per la nuova pagina mi sono accorto che il loop che genera i tag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apple-touch-icon&lt;/code&gt; per le otto dimensioni richieste (57px, 72px, e così via) non funzionava come pensavo: lo split della stringa &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;57,72,114,...&quot;&lt;/code&gt; avveniva &lt;em&gt;dentro&lt;/em&gt; il ciclo invece che prima, quindi il loop girava una sola volta con l’intera stringa come valore unico, producendo un solo link con un nome file illeggibile invece di otto tag separati. Un bug presente fin dalla prima stesura del tema, mai notato perché nessuno controlla mai l’HTML generato per le icone del touch bar di iOS — finché non serve confrontarlo per una pagina nuova.&lt;/p&gt;

&lt;h2 id=&quot;cosa-mi-porto-a-casa&quot;&gt;Cosa mi porto a casa&lt;/h2&gt;

&lt;p&gt;Il sito principale aveva una scusa evidente per essere ricostruito: è il mio biglietto da visita, ci scrivo sopra, lo guardo ogni giorno. Ipui2ipei non ce l’aveva — è un tool che io stesso apro forse una volta all’anno. Eppure è probabilmente lo strumento che aiuta più sconosciuti, in silenzio, di qualsiasi altra cosa io abbia pubblicato: non genera conversazioni, non genera commenti, genera solo un telefono che finalmente si registra. Vale la pena curarlo con lo stesso standard delle cose che si vedono di più, non di meno — nessuno lo nota quando è fatto bene, ma qualcuno lo nota di sicuro quando è fatto male.&lt;/p&gt;
</description>
        <pubDate>Tue, 18 Aug 2026 12:30:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/il-redesign-di-ipui2ipei/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/il-redesign-di-ipui2ipei/</guid>
        
        <category>jekyll</category>
        
        <category>redesign</category>
        
        <category>claude-code</category>
        
        <category>ia</category>
        
        <category>javascript</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Potenziare la ricerca interna del blog</title>
        <description>&lt;p&gt;Il sito è online da qualche giorno, le rifiniture principali sono fatte, e stavo scorrendo le funzionalità una per una per vedere cosa meritasse ancora attenzione. Mi sono fermato sulla lente di ricerca in alto a destra: funziona, ma non ho mai controllato davvero &lt;em&gt;quanto bene&lt;/em&gt; funziona.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-faceva-prima&quot; id=&quot;markdown-toc-cosa-faceva-prima&quot;&gt;Cosa faceva, prima&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#un-confronto-con-un-altro-progetto&quot; id=&quot;markdown-toc-un-confronto-con-un-altro-progetto&quot;&gt;Un confronto con un altro progetto&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-ho-portato-e-come&quot; id=&quot;markdown-toc-cosa-ho-portato-e-come&quot;&gt;Cosa ho portato, e come&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#un-bug-silenzioso-trovato-per-strada&quot; id=&quot;markdown-toc-un-bug-silenzioso-trovato-per-strada&quot;&gt;Un bug silenzioso trovato per strada&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#verificare-uno-script-che-scarica-dati-e-aggiorna-il-dom-da-terminale&quot; id=&quot;markdown-toc-verificare-uno-script-che-scarica-dati-e-aggiorna-il-dom-da-terminale&quot;&gt;Verificare uno script che scarica dati e aggiorna il DOM, da terminale&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-mi-porto-a-casa&quot; id=&quot;markdown-toc-cosa-mi-porto-a-casa&quot;&gt;Cosa mi porto a casa&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cosa-faceva-prima&quot;&gt;Cosa faceva, prima&lt;/h2&gt;

&lt;p&gt;La ricerca di questo blog è tutta lato client: alla build, Jekyll genera un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search.json&lt;/code&gt; con titolo, URL e descrizione di ogni post; quando apri il pannello, un piccolo script scarica quel file una volta sola e, ad ogni tasto premuto, cerca la query come sottostringa dentro titolo e descrizione. Funzionale, ma con dei limiti concreti:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Le pagine statiche non c’erano.&lt;/strong&gt; Cercare “CV” non trovava la pagina CV, perché il JSON includeva solo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site.posts&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Solo titolo e descrizione&lt;/strong&gt;, mai tag o categorie — un post taggato “kace” ma senza quella parola nel titolo non usciva cercando “kace”.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Nessun match su più parole&lt;/strong&gt;: “claude code” veniva cercato come frase esatta, non come due parole da trovare entrambe, magari sparse fra campi diversi.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Nessun ranking&lt;/strong&gt;: i risultati uscivano nell’ordine cronologico dei post, non in base a quanto fossero pertinenti.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;un-confronto-con-un-altro-progetto&quot;&gt;Un confronto con un altro progetto&lt;/h2&gt;

&lt;p&gt;Ho anche un altro sito Jekyll che curo, quello del mio squadrone di &lt;strong&gt;Elite: Dangerous&lt;/strong&gt; (&lt;a href=&quot;https://flottastellare.it&quot;&gt;flottastellare.it&lt;/a&gt; — ne ho scritto &lt;a href=&quot;/it/blog/fuori-ufficio/flotta-stellare-quando-lapi-va-giu/&quot;&gt;qualche giorno fa&lt;/a&gt;, a proposito di un altro script rotto). Ha anche lui un motore di ricerca interno, decisamente più maturo: usa &lt;a href=&quot;https://github.com/christian-fei/Simple-Jekyll-Search&quot;&gt;Simple-Jekyll-Search&lt;/a&gt;, una libreria di terze parti con qualche anno sulle spalle, con un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search.json&lt;/code&gt; che include tag, categorie e data oltre a titolo e descrizione, e persino una tag cloud in fondo alla pagina di ricerca.&lt;/p&gt;

&lt;p&gt;Idee valide, ma non tutte da copiare così come sono. Questo sito ha una regola precisa, dichiarata anche nel README ora che il tema è open source: &lt;strong&gt;zero JavaScript di terze parti&lt;/strong&gt;. Aggiungere una libreria — per quanto piccola — solo per la ricerca avrebbe rotto quella regola per un guadagno che potevo ottenere anche scrivendo poche righe in più a mano.&lt;/p&gt;

&lt;h2 id=&quot;cosa-ho-portato-e-come&quot;&gt;Cosa ho portato, e come&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Più contenuto indicizzato.&lt;/strong&gt; Il &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search.json&lt;/code&gt; ora include, oltre ai post, tutte le pagine che usano un layout “di contenuto” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;page&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;category&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blog-index&lt;/code&gt;) — CV, Progetti, Contatti, le pagine legali, l’indice del blog e tutte le pagine categoria. Cercare “CV” ora trova la pagina CV.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ricerca su più campi.&lt;/strong&gt; Ogni voce del JSON porta con sé anche tag e categorie (uniti in una stringa), non solo titolo e descrizione. La funzione di match adesso guarda dentro tutti questi campi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query a più parole, in AND.&lt;/strong&gt; La query viene spezzata in parole; un elemento risulta un match solo se &lt;em&gt;ogni&lt;/em&gt; parola è presente da qualche parte (titolo, descrizione, tag o categoria) — non serve più che compaiano vicine o nell’ordine esatto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un ranking minimo, ma sensato.&lt;/strong&gt; Ogni parola trovata nel titolo vale 3 punti, ogni parola trovata altrove ne vale 1; i risultati sono ordinati per punteggio decrescente. Cercare “kace” fa uscire per prima la pagina categoria Kace e i post che hanno “Kace” nel titolo, poi quelli che ce l’hanno solo nei tag — invece dell’ordine cronologico casuale di prima.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;scoreItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;categories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inTitle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inRest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inTitle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;inRest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// AND: una parola assente esclude il risultato&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inTitle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Un estratto sotto ogni risultato&lt;/strong&gt;, non più solo il titolo nudo — così si capisce perché quel risultato è uscito, prima ancora di cliccarci sopra.&lt;/p&gt;

&lt;h2 id=&quot;un-bug-silenzioso-trovato-per-strada&quot;&gt;Un bug silenzioso trovato per strada&lt;/h2&gt;

&lt;p&gt;Scrivendo il nuovo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search.json&lt;/code&gt; ho notato che quello vecchio inseriva titolo e descrizione nel JSON senza nessun escaping:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;title&quot; : &quot;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&quot;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nessuno dei titoli attuali contiene virgolette doppie, quindi non si era mai rotto — ma sarebbe bastato un titolo con un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&lt;/code&gt; dentro per generare un JSON non valido e far fallire silenziosamente &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch(&apos;/search.json&apos;).then(r =&amp;gt; r.json())&lt;/code&gt; su tutto il sito, senza errori visibili se non aprendo la console. Corretto usando il filtro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jsonify&lt;/code&gt; di Liquid, che serializza il valore in JSON corretto invece di inserirlo come stringa grezza:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;title&quot; : &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;verificare-uno-script-che-scarica-dati-e-aggiorna-il-dom-da-terminale&quot;&gt;Verificare uno script che scarica dati e aggiorna il DOM, da terminale&lt;/h2&gt;

&lt;p&gt;L’ultima parte è stata la più scomoda, nel senso buono: come si verifica in automatico — senza aprire davvero un browser e digitare — che una ricerca javascript funzioni? Ho scritto una pagina di prova temporanea che apre il pannello, digita “kace” nel campo e scrive i risultati nel DOM, poi l’ho fotografata con Microsoft Edge headless.&lt;/p&gt;

&lt;p&gt;Primo tentativo, fallito: uno screenshot semplice cattura la pagina subito dopo il caricamento, senza aspettare che i &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setTimeout&lt;/code&gt; dello script di prova (e la vera chiamata &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt; al &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search.json&lt;/code&gt; reale) abbiano il tempo di completarsi — risultato, uno screenshot con la casella di ricerca ancora vuota. Secondo tentativo: alzare il “budget di tempo virtuale” di Chromium (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--virtual-time-budget&lt;/code&gt;) a un valore abbondante, cosa che permette al browser di far avanzare timer e rete anche in modalità headless prima di scattare la foto. Con margine sufficiente (6 secondi virtuali), lo script ha avuto tutto il tempo di aprire il pannello, scaricare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search.json&lt;/code&gt; davvero e mostrare i risultati — verificati poi anche con lo stile reale del sito, non solo con la logica nuda.&lt;/p&gt;

&lt;h2 id=&quot;cosa-mi-porto-a-casa&quot;&gt;Cosa mi porto a casa&lt;/h2&gt;

&lt;p&gt;Non è stato un lavoro nato da una segnalazione o da un bug in produzione — è nato da una domanda pigra (“come funziona quell’altro sito?”) che si è rivelata utile. A volte il modo più veloce per migliorare qualcosa non è inventarsi la soluzione da zero, ma guardare come l’ha già risolta un progetto simile, capire cosa vale la pena portare a casa e cosa no — e nel farlo, trovare pure un bug che dormiva lì da mesi senza che nessuno se ne accorgesse.&lt;/p&gt;
</description>
        <pubDate>Sun, 16 Aug 2026 11:32:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/potenziare-la-ricerca-interna-del-blog/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/potenziare-la-ricerca-interna-del-blog/</guid>
        
        <category>jekyll</category>
        
        <category>claude-code</category>
        
        <category>ia</category>
        
        <category>javascript</category>
        
        <category>css</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Il nuovo sito, parte 5: breadcrumb, metainfo riordinate e un post in vetrina</title>
        <description>&lt;p&gt;Le prime quattro parti di questa serie (&lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-2-i-bug-nascosti-in-un-sito-nuovo/&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-3-le-copertine-che-si-adattano-al-tema/&quot;&gt;3&lt;/a&gt;, &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-4-le-ultime-finiture/&quot;&gt;4&lt;/a&gt;) coprivano la ricostruzione, i bug, le copertine, le rifiniture prima del lancio. Il sito è online da un po’, ora — ed è arrivato il momento di lavorare su cose che si notano solo se funzionano bene: come ci si orienta dentro un articolo, cosa si legge per primo, e come si scopre un contenuto che altrimenti sarebbe sepolto in mezzo a tutti gli altri.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#un-breadcrumb-per-chi-legge-e-per-chi-indicizza&quot; id=&quot;markdown-toc-un-breadcrumb-per-chi-legge-e-per-chi-indicizza&quot;&gt;Un breadcrumb, per chi legge e per chi indicizza&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#la-riga-con-data-e-tempo-di-lettura-spostata-due-volte&quot; id=&quot;markdown-toc-la-riga-con-data-e-tempo-di-lettura-spostata-due-volte&quot;&gt;La riga con data e tempo di lettura, spostata due volte&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#un-post-in-vetrina-e-la-lezione-sulle-immagini-con-testo-dentro&quot; id=&quot;markdown-toc-un-post-in-vetrina-e-la-lezione-sulle-immagini-con-testo-dentro&quot;&gt;Un post in vetrina, e la lezione sulle immagini con testo dentro&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-questa-parte-conta&quot; id=&quot;markdown-toc-perché-questa-parte-conta&quot;&gt;Perché questa parte conta&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;un-breadcrumb-per-chi-legge-e-per-chi-indicizza&quot;&gt;Un breadcrumb, per chi legge e per chi indicizza&lt;/h2&gt;

&lt;p&gt;L’idea di partenza era semplice: aggiungere un breadcrumb (Home / Blog / Categoria / Titolo) sopra ogni articolo, per dare un punto di riferimento durante la lettura. Ma il breadcrumb visibile è solo metà del valore — l’altra metà è dati strutturati che Google usa per sostituire l’URL nudo con un percorso leggibile nei risultati di ricerca, a patto di marcarli con lo schema &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BreadcrumbList&lt;/code&gt; in JSON-LD.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll-seo-tag&lt;/code&gt;, il plugin che già genera buona parte dei meta tag del sito, non lo fa in autonomia — verificato leggendo l’HTML compilato: produce solo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BlogPosting&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WebSite&lt;/code&gt; e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt;. Serviva un secondo blocco &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application/ld+json&lt;/code&gt; indipendente (pratica comune: più blocchi JSON-LD sulla stessa pagina sono validi), generato dagli &lt;strong&gt;stessi identici dati&lt;/strong&gt; del breadcrumb visibile, per non rischiare che i due finiscano per raccontare percorsi diversi.&lt;/p&gt;

&lt;p&gt;Liquid, però, non ha letterali per gli hash — non puoi scrivere al volo un array di coppie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{etichetta, url}&lt;/code&gt;. La soluzione più pulita è stata usare due array paralleli, costruiti con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; (lo stesso filtro già usato altrove nel sito per i post correlati):&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_labels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_urls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_labels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_labels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;i18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nav_home&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;short_lang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_urls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_urls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;home_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_labels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_labels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;i18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;nav_blog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;short_lang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_urls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_urls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;blog_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;breadcrumb.html&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_labels&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;crumb_urls&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;e dentro l’include, un solo ciclo genera sia gli &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a&amp;gt;&lt;/code&gt; che gli &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;li&amp;gt;&lt;/code&gt; del JSON-LD, scorrendo gli stessi due array:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;@type&quot;: &quot;ListItem&quot;,
  &quot;position&quot;: &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;forloop.index&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;,
  &quot;name&quot;: &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;unless&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;forloop.last&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;,&quot;item&quot;: &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;absolute_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endunless&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il primo tentativo aveva però un difetto invisibile finché non lo si guarda affiancato al resto della pagina: il breadcrumb, inserito appena sopra &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;header class=&quot;post-header&quot;&amp;gt;&lt;/code&gt;, restava ancorato al bordo dell’intero container da 1360px — mentre il titolo sotto vive dentro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.post-header&lt;/code&gt;, che ha un suo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max-width: 760px&lt;/code&gt; centrato, la colonna di lettura usata in tutto il sito. Risultato: su schermi larghi il breadcrumb partiva più a sinistra del titolo che gli stava proprio sotto, uno sfasamento che si nota a colpo d’occhio ma che in isolamento — guardando solo il breadcrumb, o solo il titolo — è del tutto invisibile. Bastava spostare l’include dentro l’header per farlo ereditare la stessa colonna.&lt;/p&gt;

&lt;h2 id=&quot;la-riga-con-data-e-tempo-di-lettura-spostata-due-volte&quot;&gt;La riga con data e tempo di lettura, spostata due volte&lt;/h2&gt;

&lt;p&gt;Volevo che la riga con data, numero di parole e tempo di lettura stesse fra il titolo e il contenuto, non sopra al titolo com’era prima. Mi sono ispirato al layout di un altro blog Jekyll — titolo, poi immagine di copertina, poi metainfo, poi contenuto — e ho spostato tutto lì.&lt;/p&gt;

&lt;p&gt;Sbagliato: quel layout metteva le metainfo &lt;em&gt;dopo&lt;/em&gt; l’immagine, io le volevo &lt;em&gt;subito sotto&lt;/em&gt; il titolo. Un malinteso mio nell’interpretare il riferimento, corretto non appena fatto notare — la differenza è di poche righe di Liquid, ma cambia parecchio la gerarchia visiva della pagina.&lt;/p&gt;

&lt;p&gt;Sistemata la posizione, è rimasto un problema di affollamento: sotto al titolo si accalcavano tre elementi tutti “di servizio” — il breadcrumb appena sopra, la pill della categoria, la riga della meta-info — e la pill era ridondante: la stessa categoria è già visibile nel breadcrumb, una riga più in alto. L’ho spostata sotto la riga meta invece che sopra, e ho stretto lo spazio fra breadcrumb e titolo (24px → 8px, che da solo bastava a far sembrare tutto più affollato di quanto fosse davvero).&lt;/p&gt;

&lt;h2 id=&quot;un-post-in-vetrina-e-la-lezione-sulle-immagini-con-testo-dentro&quot;&gt;Un post in vetrina, e la lezione sulle immagini con testo dentro&lt;/h2&gt;

&lt;p&gt;L’ultima modifica: un post per lingua, marcato &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;featured: true&lt;/code&gt; nel front matter, compare come card grande in cima all’indice del blog — escluso dalla griglia normale sotto, per non mostrarlo due volte. Anche qui, ispirazione presa da come altri blog Jekyll gestiscono l’articolo in evidenza.&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;featured_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lang_posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;featured&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;grid_posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;lang_posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;unless&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;featured_post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;featured_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;grid_posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;grid_posts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endunless&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;La parte interessante è stata l’immagine di copertina. Primo tentativo: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object-fit: cover&lt;/code&gt; con un rapporto fisso 16:9, per dare alla card un’altezza prevedibile. Sembrava ragionevole finché non ho provato con la copertina del post su WireGuard: il ritaglio tagliava via la parte alta dell’immagine, portandosi via il titolo “WireGuard” disegnato dentro la grafica stessa. Ho stretto l’altezza pensando bastasse un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object-position: top&lt;/code&gt; — e ho provato con un’altra copertina, un banner panoramico (rapporto 4:1) per l’articolo sulla Flotta Stellare. Risultato anche peggiore: il testo “Alto Comando Flotta Stellare” tagliato ai lati, illeggibile.&lt;/p&gt;

&lt;p&gt;Il problema non era la posizione del ritaglio, era il ritaglio in sé. Le copertine di questo blog sono quasi sempre grafiche con testo integrato — non fotografie — a proporzioni imprevedibili e con il testo in posizioni diverse ogni volta. Nessun &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object-position&lt;/code&gt; fisso avrebbe funzionato per tutte. La soluzione è stata smettere di ritagliare: l’immagine mantiene le sue proporzioni naturali, esattamente come già succede per le copertine dentro ai singoli articoli. Card più alte o più basse a seconda del post, ma mai un titolo tagliato a metà.&lt;/p&gt;

&lt;p&gt;Un’ultima rifinitura, via feedback diretto: la badge “In evidenza” era troppo timida — sfondo tenue, poco riconoscibile — sostituita con uno sfondo pieno, colore d’accento, e un’iconcina a stella.&lt;/p&gt;

&lt;h2 id=&quot;perché-questa-parte-conta&quot;&gt;Perché questa parte conta&lt;/h2&gt;

&lt;p&gt;Con una manciata di articoli, breadcrumb e post in evidenza sono quasi decorazione. Non lo sono più a lungo andare: più il blog cresce, più contano i punti di riferimento — sapere dove si è, tornare indietro di un livello senza usare il tasto back del browser, scoprire un articolo importante senza doverlo cercare fra dieci schede uguali nella griglia. Sono le stesse rifiniture “poco glamour” della &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-4-le-ultime-finiture/&quot;&gt;parte 4&lt;/a&gt;, applicate un livello più in alto: non più “il sito funziona”, ma “il sito si lascia usare bene”.&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Aug 2026 22:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-5-breadcrumb-metainfo-e-un-post-in-vetrina/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-5-breadcrumb-metainfo-e-un-post-in-vetrina/</guid>
        
        <category>css</category>
        
        <category>claude-code</category>
        
        <category>ia</category>
        
        <category>ux</category>
        
        <category>seo</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Una VPN veloce e solida con WireGuard: da Windows Server 2022 a Windows 11</title>
        <description>&lt;p&gt;Sono certificato Fortinet NSE6 e le VPN le so costruire, ma quando serve qualcosa di pronto in pochi click — comprese le VPN onboard sui router Mikrotik, magari per raggiungere l’intera rete di un cliente — uso &lt;strong&gt;WireGuard&lt;/strong&gt;: meno configurazione, meno cose che si rompono, e una velocità che OpenVPN e IPsec non riescono ad avvicinare. Il protocollo è nato su Linux, ma il client ufficiale per Windows è maturo da tempo, e da quando esiste il driver &lt;strong&gt;WireGuardNT&lt;/strong&gt; (a livello kernel, non più il vecchio &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wireguard-go&lt;/code&gt; in user space) le prestazioni su Windows sono del tutto in linea con quelle su Linux.&lt;/p&gt;

&lt;p&gt;In questo articolo mettiamo in piedi uno scenario molto comune: un &lt;strong&gt;server WireGuard su Windows Server 2022&lt;/strong&gt; che fa da punto d’ingresso per uno o più &lt;strong&gt;client Windows 11&lt;/strong&gt;, con accesso alla rete locale dell’ufficio e, opzionalmente, un tunnel completo per instradare anche il traffico Internet. Userò nomi ed indirizzi IP generici — sostituiteli con i vostri prima di andare in produzione.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-wireguard-e-non-openvpn-o-ipsec&quot; id=&quot;markdown-toc-perché-wireguard-e-non-openvpn-o-ipsec&quot;&gt;Perché WireGuard e non OpenVPN o IPsec&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#prerequisiti&quot; id=&quot;markdown-toc-prerequisiti&quot;&gt;Prerequisiti&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#architettura-e-schema-di-indirizzamento&quot; id=&quot;markdown-toc-architettura-e-schema-di-indirizzamento&quot;&gt;Architettura e schema di indirizzamento&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#installazione-su-windows-server-2022&quot; id=&quot;markdown-toc-installazione-su-windows-server-2022&quot;&gt;Installazione su Windows Server 2022&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#generare-la-coppia-di-chiavi-del-server&quot; id=&quot;markdown-toc-generare-la-coppia-di-chiavi-del-server&quot;&gt;Generare la coppia di chiavi del server&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#il-file-di-configurazione-del-server&quot; id=&quot;markdown-toc-il-file-di-configurazione-del-server&quot;&gt;Il file di configurazione del server&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#attivare-il-tunnel-come-servizio-windows&quot; id=&quot;markdown-toc-attivare-il-tunnel-come-servizio-windows&quot;&gt;Attivare il tunnel come servizio Windows&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#aprire-la-porta-sul-firewall-e-sul-router&quot; id=&quot;markdown-toc-aprire-la-porta-sul-firewall-e-sul-router&quot;&gt;Aprire la porta sul firewall e sul router&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configurazione-del-client-windows-11&quot; id=&quot;markdown-toc-configurazione-del-client-windows-11&quot;&gt;Configurazione del client Windows 11&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#generare-la-coppia-di-chiavi-del-client&quot; id=&quot;markdown-toc-generare-la-coppia-di-chiavi-del-client&quot;&gt;Generare la coppia di chiavi del client&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#il-file-di-configurazione-del-client&quot; id=&quot;markdown-toc-il-file-di-configurazione-del-client&quot;&gt;Il file di configurazione del client&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#aggiungere-il-peer-sul-server&quot; id=&quot;markdown-toc-aggiungere-il-peer-sul-server&quot;&gt;Aggiungere il peer sul server&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#verificare-che-tutto-funzioni&quot; id=&quot;markdown-toc-verificare-che-tutto-funzioni&quot;&gt;Verificare che tutto funzioni&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#tunnel-completo-o-split-tunnel&quot; id=&quot;markdown-toc-tunnel-completo-o-split-tunnel&quot;&gt;Tunnel completo o split tunnel?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#qualche-accorgimento-di-sicurezza&quot; id=&quot;markdown-toc-qualche-accorgimento-di-sicurezza&quot;&gt;Qualche accorgimento di sicurezza&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-è-così-veloce&quot; id=&quot;markdown-toc-perché-è-così-veloce&quot;&gt;Perché è così veloce&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusioni&quot; id=&quot;markdown-toc-conclusioni&quot;&gt;Conclusioni&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#download&quot; id=&quot;markdown-toc-download&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#riferimenti&quot; id=&quot;markdown-toc-riferimenti&quot;&gt;Riferimenti&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;perché-wireguard-e-non-openvpn-o-ipsec&quot;&gt;Perché WireGuard e non OpenVPN o IPsec&lt;/h2&gt;

&lt;p&gt;Qualche numero e qualche motivo concreto, senza dilungarmi troppo:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Codice minuscolo&lt;/strong&gt;: il core di WireGuard è poche migliaia di righe, contro le centinaia di migliaia di OpenVPN. Meno codice vuol dire meno superficie d’attacco e una revisione di sicurezza molto più semplice.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Crittografia moderna e non negoziabile&lt;/strong&gt;: Curve25519, ChaCha20-Poly1305, BLAKE2s. Non ci sono liste di cifrari da configurare né vecchi algoritmi da disabilitare a mano: WireGuard usa una sola suite crittografica, aggiornata, punto.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Solo UDP&lt;/strong&gt;: niente “TCP-over-TCP meltdown” (il rallentamento tipico di OpenVPN in modalità TCP su reti con perdita di pacchetti).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Roaming istantaneo&lt;/strong&gt;: un client può cambiare rete — dal Wi-Fi di casa al 5G, da un hotel a un altro — senza rinegoziare il tunnel. Il server si accorge del nuovo IP sorgente al primo pacchetto valido e continua da lì.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Nessuna sessione, nessuno stato lato server&lt;/strong&gt; finché non arriva traffico: il server non tiene processi appesi per ogni client connesso.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il rovescio della medaglia: WireGuard &lt;strong&gt;non ha un concetto di utente/password&lt;/strong&gt;. L’identità di un peer è la sua chiave pubblica, punto. Se vi serve un’autenticazione utente-per-utente in stile “aziendale”, va costruita sopra (una coppia di chiavi per persona/dispositivo, mai condivisa) — ne parlo più avanti nella sezione sulla sicurezza.&lt;/p&gt;

&lt;h2 id=&quot;prerequisiti&quot;&gt;Prerequisiti&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Un &lt;strong&gt;Windows Server 2022&lt;/strong&gt; con un indirizzo IP pubblico, oppure dietro un router su cui potete fare port forwarding.&lt;/li&gt;
  &lt;li&gt;Uno o più &lt;strong&gt;Windows 11&lt;/strong&gt; da collegare in VPN.&lt;/li&gt;
  &lt;li&gt;Il client ufficiale &lt;strong&gt;WireGuard per Windows&lt;/strong&gt;, stesso installer su server e client (link nella sezione &lt;a href=&quot;#download&quot;&gt;Download&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;Accesso amministrativo su entrambe le macchine, e la possibilità di aprire una porta UDP sul firewall (e sul router, se il server è dietro NAT).&lt;/li&gt;
  &lt;li&gt;Se volete che i client raggiungano anche la LAN dell’ufficio (non solo il server), un minimo di pianificazione degli indirizzi: vedi lo schema qui sotto.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;architettura-e-schema-di-indirizzamento&quot;&gt;Architettura e schema di indirizzamento&lt;/h2&gt;

&lt;p&gt;Lo scenario che useremo come riferimento in tutto l’articolo:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/wireguard-windows/diagram-topology.png&quot; alt=&quot;Schema: client Windows 11, tunnel WireGuard cifrato attraverso Internet e un router con NAT/port-forward sulla porta UDP 51820, fino al server Windows Server 2022, che espone anche la LAN dell&apos;ufficio 192.168.1.0/24&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rete WireGuard: &lt;strong&gt;10.66.0.0/24&lt;/strong&gt; (una sottorete dedicata alla VPN, separata dalla LAN reale — così evitiamo conflitti di routing).&lt;/li&gt;
  &lt;li&gt;Server: indirizzo VPN &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.1&lt;/code&gt;, indirizzo LAN &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.10&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Client: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.3&lt;/code&gt;, ecc. — un indirizzo per dispositivo.&lt;/li&gt;
  &lt;li&gt;Porta di ascolto: &lt;strong&gt;UDP 51820&lt;/strong&gt; (quella di default; potete cambiarla, vedi la sezione sicurezza).&lt;/li&gt;
  &lt;li&gt;LAN dell’ufficio da raggiungere in VPN: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.0/24&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; se il server è dietro un router (es. connessione domestica o in un ufficio senza IP pubblico diretto), serve un &lt;strong&gt;port forward UDP 51820 → indirizzo LAN del server&lt;/strong&gt;. Se l’IP pubblico non è statico, aggiungete un servizio di DDNS (Dynamic DNS) e usate l’hostname invece dell’IP nell’&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Endpoint&lt;/code&gt; del client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;installazione-su-windows-server-2022&quot;&gt;Installazione su Windows Server 2022&lt;/h2&gt;

&lt;p&gt;Installate &lt;a href=&quot;https://www.wireguard.com/install/&quot; target=&quot;_blank&quot;&gt;WireGuard per Windows&lt;/a&gt; con l’MSI ufficiale (stesso installer del client). L’app si apre con un pannello di gestione tunnel: la useremo solo per generare le chiavi la prima volta, perché su un server la modalità corretta è avviarlo &lt;strong&gt;come servizio Windows&lt;/strong&gt;, non lasciarlo agganciato a una sessione utente.&lt;/p&gt;

&lt;h3 id=&quot;generare-la-coppia-di-chiavi-del-server&quot;&gt;Generare la coppia di chiavi del server&lt;/h3&gt;

&lt;p&gt;Dal pannello &lt;strong&gt;Gestisci tunnel&lt;/strong&gt; dell’app, scegliete &lt;strong&gt;Aggiungi tunnel &amp;gt; Aggiungi tunnel vuoto&lt;/strong&gt;: viene generata subito una nuova coppia di chiavi, con la chiave privata già inserita nell’editor di configurazione e la chiave pubblica corrispondente visibile nel pannello di dettaglio del tunnel. Copiatevi da qualche parte la chiave pubblica: dovrete inserirla nella configurazione di ogni client.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Attenzione:&lt;/strong&gt; la chiave privata è l’unica cosa che identifica il server (o un client) nella VPN — trattatela come una password. Non giriatela per email o chat, non mettetela in un repository, e se sospettate che sia stata esposta rigeneratela e aggiornate tutti i peer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;il-file-di-configurazione-del-server&quot;&gt;Il file di configurazione del server&lt;/h3&gt;

&lt;p&gt;Sostituite il contenuto dell’editor con qualcosa di questo tipo (adattando indirizzi e chiavi):&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Interface]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;chiave privata del server&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.1/24&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;ListenPort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;51820&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[Peer]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# laptop-01
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;chiave pubblica del client&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;AllowedIPs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.2/32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ogni client ha il suo blocco &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt;, con la propria chiave pubblica e un solo indirizzo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/32&lt;/code&gt; (il suo, non l’intera sottorete: è così che WireGuard sa a quale peer instradare i pacchetti diretti a quell’IP). Salvate il file come &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\WireGuard\wg0.conf&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;attivare-il-tunnel-come-servizio-windows&quot;&gt;Attivare il tunnel come servizio Windows&lt;/h3&gt;

&lt;p&gt;Su un server, evitate di usare il pulsante “Attiva” dell’app grafica: quel tunnel resta legato alla sessione dell’utente che l’ha avviato e si ferma al logout. La via corretta è installarlo come &lt;strong&gt;servizio&lt;/strong&gt;, così parte da solo al boot ed è indipendente da qualunque sessione:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/installtunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\WireGuard\wg0.conf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Per fermarlo o rimuoverlo in seguito:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/uninstalltunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wg0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verificate che il servizio sia attivo con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get-Service WireGuardTunnel*&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;aprire-la-porta-sul-firewall-e-sul-router&quot;&gt;Aprire la porta sul firewall e sul router&lt;/h3&gt;

&lt;p&gt;Sul firewall di Windows:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;New-NetFirewallRule&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-DisplayName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;WireGuard VPN&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Direction&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Inbound&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Protocol&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UDP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-LocalPort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;51820&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Allow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se il server è dietro un router, aggiungete anche lì il port forward UDP 51820 verso l’indirizzo LAN del server (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.10&lt;/code&gt; nel nostro schema).&lt;/p&gt;

&lt;h2 id=&quot;configurazione-del-client-windows-11&quot;&gt;Configurazione del client Windows 11&lt;/h2&gt;

&lt;h3 id=&quot;generare-la-coppia-di-chiavi-del-client&quot;&gt;Generare la coppia di chiavi del client&lt;/h3&gt;

&lt;p&gt;Stessa procedura vista per il server: installate &lt;a href=&quot;https://www.wireguard.com/install/&quot; target=&quot;_blank&quot;&gt;WireGuard per Windows&lt;/a&gt;, &lt;strong&gt;Aggiungi tunnel &amp;gt; Aggiungi tunnel vuoto&lt;/strong&gt;, copiate la chiave pubblica generata (vi servirà per il blocco &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; sul server).&lt;/p&gt;

&lt;h3 id=&quot;il-file-di-configurazione-del-client&quot;&gt;Il file di configurazione del client&lt;/h3&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Interface]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;chiave privata del client&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.2/24&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;DNS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;192.168.1.1&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[Peer]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;chiave pubblica del server&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vpn.tuodominio.it:51820&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;AllowedIPs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0, ::/0&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PersistentKeepalive&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Un paio di campi meritano una spiegazione:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Endpoint&lt;/code&gt;&lt;/strong&gt;: l’indirizzo pubblico (o l’hostname DDNS) e la porta a cui il client si connette. È l’unico posto dove serve un indirizzo “raggiungibile dall’esterno” — il server non ha bisogno di conoscere l’IP del client in anticipo.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PersistentKeepalive = 25&lt;/code&gt;&lt;/strong&gt;: manda un pacchetto vuoto ogni 25 secondi per tenere aperto il mapping NAT del router del client. Senza, se il client è dietro un NAT (quasi sempre, su reti domestiche o mobili), il tunnel può “addormentarsi” e il server non riesce più a raggiungerlo per primo.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs&lt;/code&gt;&lt;/strong&gt;: qui decidete se fare tunnel completo o split tunnel — se ne parla nella prossima sezione.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Importate il file da &lt;strong&gt;WireGuard &amp;gt; Importa tunnel da file…&lt;/strong&gt;, oppure incollate il contenuto direttamente nell’editor del tunnel vuoto creato prima.&lt;/p&gt;

&lt;h3 id=&quot;aggiungere-il-peer-sul-server&quot;&gt;Aggiungere il peer sul server&lt;/h3&gt;

&lt;p&gt;Tornate sul server e aggiungete un blocco &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; per questo client (o create il file con tutti i peer fin da subito, se sapete già quanti client collegherete):&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Peer]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# laptop-01
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;chiave pubblica del client&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;AllowedIPs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.2/32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Salvate e riavviate il servizio del tunnel perché rilegga la configurazione:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/uninstalltunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wg0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/installtunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\WireGuard\wg0.conf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;verificare-che-tutto-funzioni&quot;&gt;Verificare che tutto funzioni&lt;/h2&gt;

&lt;p&gt;Sul client, attivate il tunnel dall’app WireGuard e controllate nella finestra principale: dovreste vedere l’host del server, i contatori di traffico in salita/discesa che si muovono, e l’orario dell’&lt;strong&gt;ultimo handshake&lt;/strong&gt; aggiornarsi periodicamente. Se l’handshake non compare mai, il problema è quasi sempre a livello di rete (porta non raggiungibile, firewall, port forward mancante) prima ancora che di configurazione WireGuard.&lt;/p&gt;

&lt;p&gt;Poi, da riga di comando sul client:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;10.66.0.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se risponde, il tunnel funziona. Per verificare l’accesso alla LAN dell’ufficio (se l’avete abilitato, vedi sotto), provate a raggiungere una risorsa nota, ad esempio &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping 192.168.1.10&lt;/code&gt; o l’apertura di una condivisione file.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; se il ping verso &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.1&lt;/code&gt; funziona ma quello verso la LAN (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.x&lt;/code&gt;) no, il tunnel va bene: manca solo l’&lt;strong&gt;IP forwarding&lt;/strong&gt; sul server, descritto nella prossima sezione.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;tunnel-completo-o-split-tunnel&quot;&gt;Tunnel completo o split tunnel?&lt;/h2&gt;

&lt;p&gt;Il campo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs&lt;/code&gt; nel blocco &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; del client decide cosa passa dentro la VPN:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Tunnel completo&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs = 0.0.0.0/0, ::/0&lt;/code&gt;: tutto il traffico del client, incluso quello verso Internet, passa dal server. Utile se non vi fidate della rete su cui siete (Wi-Fi pubblico) o se volete che il client “esca” sempre con l’IP del server.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Split tunnel&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs = 10.66.0.0/24, 192.168.1.0/24&lt;/code&gt;: solo il traffico verso la VPN e la LAN dell’ufficio passa dal tunnel, il resto (Netflix, siti web, tutto il resto) esce direttamente dalla connessione del client. Più leggero per il server, e per l’utente spesso più veloce.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In entrambi i casi, se volete che i client raggiungano host della &lt;strong&gt;LAN reale&lt;/strong&gt; (non solo il server), Windows deve fare da router fra l’interfaccia WireGuard e quella di rete fisica. Per impostazione predefinita non lo fa: va abilitato l’&lt;strong&gt;IP forwarding&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Set-ItemProperty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;IPEnableRouter&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Restart-Computer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se invece volete il &lt;strong&gt;tunnel completo&lt;/strong&gt; (i client escono su Internet passando dal server), serve anche il &lt;strong&gt;NAT&lt;/strong&gt; in uscita sull’interfaccia fisica del server:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;New-NetNat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;WireGuardNAT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-InternalIPInterfaceAddressPrefix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;10.66.0.0/24&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Questo comando basta da solo, senza installare il ruolo Routing and Remote Access: il modulo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NetNat&lt;/code&gt; è incluso in Windows Server dal 2016 in poi.&lt;/p&gt;

&lt;h2 id=&quot;qualche-accorgimento-di-sicurezza&quot;&gt;Qualche accorgimento di sicurezza&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Una coppia di chiavi per dispositivo&lt;/strong&gt;, mai condivisa fra più persone: è l’unico modo per poter revocare l’accesso a &lt;em&gt;un&lt;/em&gt; dispositivo (basta togliere il suo blocco &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; dal server) senza dover ruotare le chiavi di tutti gli altri.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs&lt;/code&gt; il più stretto possibile&lt;/strong&gt; per ogni peer sul server: un client che si occupa solo di backup non ha bisogno di poter raggiungere l’intera LAN.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cambiare la porta di default&lt;/strong&gt; (51820) non è vera sicurezza — è &lt;em&gt;security through obscurity&lt;/em&gt; — ma riduce parecchio il rumore di fondo degli scanner automatici che bussano sulla porta standard nei log.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;WireGuard non sostituisce l’autenticazione applicativa&lt;/strong&gt;: dà accesso di rete, non identità utente. Se dietro la VPN c’è RDP, una share file o un pannello di amministrazione, tenete comunque password forti e, dove possibile, MFA su quei servizi — la VPN è un livello, non l’unico.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Backup delle chiavi private&lt;/strong&gt; in un posto cifrato (password manager, non un file di testo sul desktop): se perdete quella del server dovrete rigenerarla e riconfigurare ogni client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;perché-è-così-veloce&quot;&gt;Perché è così veloce&lt;/h2&gt;

&lt;p&gt;Vale la pena spendere due righe sul “veloce” del titolo. Su Windows, dalla versione che usa il driver &lt;strong&gt;WireGuardNT&lt;/strong&gt;, l’intero percorso dei pacchetti gira &lt;strong&gt;nel kernel&lt;/strong&gt;, non più in un processo user-space come nelle prime versioni (che si appoggiavano a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wireguard-go&lt;/code&gt; sopra l’adattatore Wintun). Questo, insieme al fatto che il protocollo non ha l’overhead di negoziazione di IPsec (niente IKE, niente rinegoziazioni continue) e non soffre del rallentamento tipico di OpenVPN in TCP, si traduce in throughput molto vicini al limite della connessione fisica e in una latenza aggiuntiva minima — nella pratica, spesso impercettibile rispetto al traffico non incapsulato.&lt;/p&gt;

&lt;h2 id=&quot;conclusioni&quot;&gt;Conclusioni&lt;/h2&gt;

&lt;p&gt;Messo in piedi una volta, un tunnel WireGuard fra Windows Server e Windows 11 richiede pochissima manutenzione: nessun certificato da rinnovare, nessuna lista di cifrari da tenere aggiornata, un file di configurazione per peer. I punti dove ci si incastra di solito sono sempre gli stessi tre: &lt;strong&gt;porta UDP non raggiungibile&lt;/strong&gt; (firewall o port forward mancante), &lt;strong&gt;IP forwarding disattivato&lt;/strong&gt; (niente accesso alla LAN) e &lt;strong&gt;NAT mancante&lt;/strong&gt; (niente Internet in tunnel completo) — se il tunnel si stabilisce (handshake visibile) ma qualcosa dopo non funziona, è quasi sempre uno di questi tre.&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/install/&quot;&gt;WireGuard per Windows&lt;/a&gt; — stesso installer per server e client, sito ufficiale&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/wireguard-windows/wg0-server.conf&quot;&gt;wg0-server.conf&lt;/a&gt; — template di configurazione per il server, con due peer di esempio&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/wireguard-windows/client.conf&quot;&gt;client.conf&lt;/a&gt; — template di configurazione per il client, con entrambe le varianti (tunnel completo / split tunnel) commentate&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;riferimenti&quot;&gt;Riferimenti&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/&quot;&gt;wireguard.com&lt;/a&gt; — sito ufficiale del protocollo&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/install/&quot; target=&quot;_blank&quot;&gt;Documentazione WireGuard per Windows&lt;/a&gt; — pagina di download e note sul client&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/module/netnat/new-netnat&quot; target=&quot;_blank&quot;&gt;New-NetNat (documentazione Microsoft)&lt;/a&gt; — dettagli sul cmdlet usato per il NAT in tunnel completo&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Aug 2026 00:15:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/infrastruttura/vpn-veloce-e-solida-con-wireguard/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/infrastruttura/vpn-veloce-e-solida-con-wireguard/</guid>
        
        <category>windows-server</category>
        
        <category>windows11</category>
        
        <category>vpn</category>
        
        <category>wireguard</category>
        
        <category>networking</category>
        
        <category>sicurezza</category>
        
        
        <category>Infrastruttura &amp; Sistemi</category>
        
      </item>
    
      <item>
        <title>A fast, solid VPN with WireGuard: from Windows Server 2022 to Windows 11</title>
        <description>&lt;p&gt;I’m Fortinet NSE6 certified and know how to build VPNs, but when I need something ready to go in a few clicks — including onboard VPNs on Mikrotik routers, to reach a client’s whole network — I reach for &lt;strong&gt;WireGuard&lt;/strong&gt;: less configuration, fewer things that break, and speed OpenVPN and IPsec can’t get close to. The protocol was born on Linux, but the official Windows client has been mature for a while, and since the &lt;strong&gt;WireGuardNT&lt;/strong&gt; driver (kernel-level, no longer the old user-space &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wireguard-go&lt;/code&gt;) Windows performance is fully in line with Linux.&lt;/p&gt;

&lt;p&gt;In this article we’ll set up a very common scenario: a &lt;strong&gt;WireGuard server on Windows Server 2022&lt;/strong&gt; acting as the entry point for one or more &lt;strong&gt;Windows 11 clients&lt;/strong&gt;, with access to the office LAN and, optionally, a full tunnel that also routes Internet traffic. I’ll use generic names and IP addresses — swap in your own before going to production.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#why-wireguard-over-openvpn-or-ipsec&quot; id=&quot;markdown-toc-why-wireguard-over-openvpn-or-ipsec&quot;&gt;Why WireGuard over OpenVPN or IPsec&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#prerequisites&quot; id=&quot;markdown-toc-prerequisites&quot;&gt;Prerequisites&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#architecture-and-addressing-scheme&quot; id=&quot;markdown-toc-architecture-and-addressing-scheme&quot;&gt;Architecture and addressing scheme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#setting-up-windows-server-2022&quot; id=&quot;markdown-toc-setting-up-windows-server-2022&quot;&gt;Setting up Windows Server 2022&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#generating-the-servers-keypair&quot; id=&quot;markdown-toc-generating-the-servers-keypair&quot;&gt;Generating the server’s keypair&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-servers-config-file&quot; id=&quot;markdown-toc-the-servers-config-file&quot;&gt;The server’s config file&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#running-the-tunnel-as-a-windows-service&quot; id=&quot;markdown-toc-running-the-tunnel-as-a-windows-service&quot;&gt;Running the tunnel as a Windows service&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#opening-the-port-on-the-firewall-and-the-router&quot; id=&quot;markdown-toc-opening-the-port-on-the-firewall-and-the-router&quot;&gt;Opening the port on the firewall and the router&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#setting-up-the-windows-11-client&quot; id=&quot;markdown-toc-setting-up-the-windows-11-client&quot;&gt;Setting up the Windows 11 client&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#generating-the-clients-keypair&quot; id=&quot;markdown-toc-generating-the-clients-keypair&quot;&gt;Generating the client’s keypair&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-clients-config-file&quot; id=&quot;markdown-toc-the-clients-config-file&quot;&gt;The client’s config file&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#adding-the-peer-on-the-server&quot; id=&quot;markdown-toc-adding-the-peer-on-the-server&quot;&gt;Adding the peer on the server&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#checking-that-everything-works&quot; id=&quot;markdown-toc-checking-that-everything-works&quot;&gt;Checking that everything works&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#full-tunnel-or-split-tunnel&quot; id=&quot;markdown-toc-full-tunnel-or-split-tunnel&quot;&gt;Full tunnel or split tunnel?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#a-few-security-notes&quot; id=&quot;markdown-toc-a-few-security-notes&quot;&gt;A few security notes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-its-this-fast&quot; id=&quot;markdown-toc-why-its-this-fast&quot;&gt;Why it’s this fast&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#wrapping-up&quot; id=&quot;markdown-toc-wrapping-up&quot;&gt;Wrapping up&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#download&quot; id=&quot;markdown-toc-download&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#references&quot; id=&quot;markdown-toc-references&quot;&gt;References&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-wireguard-over-openvpn-or-ipsec&quot;&gt;Why WireGuard over OpenVPN or IPsec&lt;/h2&gt;

&lt;p&gt;A few numbers and concrete reasons, without dragging this out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Tiny codebase&lt;/strong&gt;: WireGuard’s core is a few thousand lines, versus OpenVPN’s hundreds of thousands. Less code means a smaller attack surface and a much simpler security review.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Modern, non-negotiable cryptography&lt;/strong&gt;: Curve25519, ChaCha20-Poly1305, BLAKE2s. There’s no cipher list to configure and no legacy algorithms to disable by hand — WireGuard uses one up-to-date suite, full stop.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;UDP only&lt;/strong&gt;: no “TCP-over-TCP meltdown” (the slowdown typical of OpenVPN over TCP on lossy networks).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Instant roaming&lt;/strong&gt;: a client can switch networks — home Wi-Fi to mobile data, one hotel to another — without renegotiating the tunnel. The server notices the new source IP on the first valid packet and just continues.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No sessions, no server-side state&lt;/strong&gt; until traffic actually arrives: the server doesn’t keep a process hanging around per connected client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The flip side: WireGuard &lt;strong&gt;has no concept of user/password&lt;/strong&gt;. A peer’s identity is its public key, period. If you need per-user, “enterprise-style” authentication, you build it on top (one keypair per person/device, never shared) — more on that in the security section below.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;A &lt;strong&gt;Windows Server 2022&lt;/strong&gt; with a public IP address, or behind a router where you can set up port forwarding.&lt;/li&gt;
  &lt;li&gt;One or more &lt;strong&gt;Windows 11&lt;/strong&gt; machines to connect over VPN.&lt;/li&gt;
  &lt;li&gt;The official &lt;strong&gt;WireGuard for Windows&lt;/strong&gt; client, same installer on server and clients (link in the &lt;a href=&quot;#download&quot;&gt;Download&lt;/a&gt; section).&lt;/li&gt;
  &lt;li&gt;Administrative access on both machines, and the ability to open a UDP port on the firewall (and on the router, if the server sits behind NAT).&lt;/li&gt;
  &lt;li&gt;If you want clients to reach the office LAN too (not just the server), a bit of address planning — see the diagram below.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;architecture-and-addressing-scheme&quot;&gt;Architecture and addressing scheme&lt;/h2&gt;

&lt;p&gt;The reference scenario we’ll use throughout the article:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/wireguard-windows/diagram-topology.png&quot; alt=&quot;Diagram: a Windows 11 client, an encrypted WireGuard tunnel across the Internet and a router doing NAT/port-forward on UDP port 51820, to the Windows Server 2022 server, which also exposes the office LAN 192.168.1.0/24&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;WireGuard network: &lt;strong&gt;10.66.0.0/24&lt;/strong&gt; (a subnet dedicated to the VPN, separate from the real LAN — this avoids routing conflicts).&lt;/li&gt;
  &lt;li&gt;Server: VPN address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.1&lt;/code&gt;, LAN address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.10&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Clients: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.3&lt;/code&gt;, etc. — one address per device.&lt;/li&gt;
  &lt;li&gt;Listening port: &lt;strong&gt;UDP 51820&lt;/strong&gt; (the default; you can change it, see the security section).&lt;/li&gt;
  &lt;li&gt;Office LAN to reach over VPN: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.0/24&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; if the server sits behind a router (e.g. a home connection, or an office without a direct public IP), you need a &lt;strong&gt;UDP 51820 port forward → the server’s LAN address&lt;/strong&gt;. If the public IP isn’t static, add a DDNS (Dynamic DNS) service and use the hostname instead of the IP in the client’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Endpoint&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;setting-up-windows-server-2022&quot;&gt;Setting up Windows Server 2022&lt;/h2&gt;

&lt;p&gt;Install &lt;a href=&quot;https://www.wireguard.com/install/&quot; target=&quot;_blank&quot;&gt;WireGuard for Windows&lt;/a&gt; using the official MSI (the same installer as the client). The app opens with a tunnel management panel: we’ll only use it to generate keys the first time, because on a server the right approach is to run it &lt;strong&gt;as a Windows service&lt;/strong&gt;, not leave it tied to a logged-in session.&lt;/p&gt;

&lt;h3 id=&quot;generating-the-servers-keypair&quot;&gt;Generating the server’s keypair&lt;/h3&gt;

&lt;p&gt;From the app’s &lt;strong&gt;Manage tunnels&lt;/strong&gt; panel, choose &lt;strong&gt;Add Tunnel &amp;gt; Add empty tunnel…&lt;/strong&gt;: a fresh keypair is generated right away, with the private key already filled into the config editor and the matching public key shown in the tunnel’s detail panel. Copy the public key somewhere safe: you’ll need it in every client’s configuration.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; the private key is the only thing that identifies the server (or a client) on the VPN — treat it like a password. Don’t send it over email or chat, don’t put it in a repository, and if you suspect it’s been exposed, regenerate it and update every peer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-servers-config-file&quot;&gt;The server’s config file&lt;/h3&gt;

&lt;p&gt;Replace the editor’s content with something like this (adjusting addresses and keys):&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Interface]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;server private key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.1/24&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;ListenPort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;51820&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[Peer]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# laptop-01
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;client public key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;AllowedIPs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.2/32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each client gets its own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; block, with its own public key and a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/32&lt;/code&gt; address (its own, not the whole subnet — that’s how WireGuard knows which peer to route packets for that IP to). Save the file as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\WireGuard\wg0.conf&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;running-the-tunnel-as-a-windows-service&quot;&gt;Running the tunnel as a Windows service&lt;/h3&gt;

&lt;p&gt;On a server, avoid the GUI app’s “Activate” button: that tunnel stays tied to the session of whoever started it and stops at logoff. The correct way is to install it as a &lt;strong&gt;service&lt;/strong&gt;, so it starts on its own at boot and doesn’t depend on any session:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/installtunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\WireGuard\wg0.conf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To stop or remove it later:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/uninstalltunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wg0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check the service is running with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get-Service WireGuardTunnel*&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;opening-the-port-on-the-firewall-and-the-router&quot;&gt;Opening the port on the firewall and the router&lt;/h3&gt;

&lt;p&gt;On the Windows firewall:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;New-NetFirewallRule&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-DisplayName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;WireGuard VPN&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Direction&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Inbound&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Protocol&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UDP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-LocalPort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;51820&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Allow&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the server is behind a router, also add the UDP 51820 port forward there, pointing to the server’s LAN address (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.10&lt;/code&gt; in our diagram).&lt;/p&gt;

&lt;h2 id=&quot;setting-up-the-windows-11-client&quot;&gt;Setting up the Windows 11 client&lt;/h2&gt;

&lt;h3 id=&quot;generating-the-clients-keypair&quot;&gt;Generating the client’s keypair&lt;/h3&gt;

&lt;p&gt;Same process as the server: install &lt;a href=&quot;https://www.wireguard.com/install/&quot; target=&quot;_blank&quot;&gt;WireGuard for Windows&lt;/a&gt;, &lt;strong&gt;Add Tunnel &amp;gt; Add empty tunnel…&lt;/strong&gt;, copy the generated public key (you’ll need it for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; block on the server).&lt;/p&gt;

&lt;h3 id=&quot;the-clients-config-file&quot;&gt;The client’s config file&lt;/h3&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Interface]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;client private key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.2/24&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;DNS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;192.168.1.1&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[Peer]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;server public key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;Endpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;vpn.yourdomain.com:51820&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;AllowedIPs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0/0, ::/0&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;PersistentKeepalive&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A couple of fields worth explaining:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Endpoint&lt;/code&gt;&lt;/strong&gt;: the public address (or DDNS hostname) and port the client connects to. It’s the only place that needs an “externally reachable” address — the server doesn’t need to know the client’s IP in advance.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PersistentKeepalive = 25&lt;/code&gt;&lt;/strong&gt;: sends an empty packet every 25 seconds to keep the client’s router NAT mapping open. Without it, if the client sits behind NAT (almost always true on home or mobile networks), the tunnel can “fall asleep” and the server can no longer reach it first.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs&lt;/code&gt;&lt;/strong&gt;: this is where you decide between a full tunnel and a split tunnel — covered in the next section.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Import the file via &lt;strong&gt;WireGuard &amp;gt; Import tunnel(s) from file…&lt;/strong&gt;, or paste the content directly into the empty tunnel’s editor created earlier.&lt;/p&gt;

&lt;h3 id=&quot;adding-the-peer-on-the-server&quot;&gt;Adding the peer on the server&lt;/h3&gt;

&lt;p&gt;Back on the server, add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; block for this client (or create the file with all peers from the start, if you already know how many clients you’ll connect):&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[Peer]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# laptop-01
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;client public key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;AllowedIPs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.66.0.2/32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save and restart the tunnel service so it picks up the new config:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/uninstalltunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wg0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\WireGuard\wireguard.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/installtunnelservice&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\WireGuard\wg0.conf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;checking-that-everything-works&quot;&gt;Checking that everything works&lt;/h2&gt;

&lt;p&gt;On the client, activate the tunnel from the WireGuard app and check the main window: you should see the server’s host, the up/down traffic counters moving, and the &lt;strong&gt;latest handshake&lt;/strong&gt; timestamp updating periodically. If the handshake never shows up, the problem is almost always at the network level (unreachable port, firewall, missing port forward) rather than in the WireGuard config itself.&lt;/p&gt;

&lt;p&gt;Then, from the client’s command line:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;10.66.0.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If it replies, the tunnel works. To check access to the office LAN (if you’ve enabled it, see below), try reaching a known resource, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping 192.168.1.10&lt;/code&gt; or opening a file share.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; if pinging &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.66.0.1&lt;/code&gt; works but pinging the LAN (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.x&lt;/code&gt;) doesn’t, the tunnel itself is fine: you’re just missing &lt;strong&gt;IP forwarding&lt;/strong&gt; on the server, covered in the next section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;full-tunnel-or-split-tunnel&quot;&gt;Full tunnel or split tunnel?&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs&lt;/code&gt; field in the client’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; block decides what goes through the VPN:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Full tunnel&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs = 0.0.0.0/0, ::/0&lt;/code&gt;: all of the client’s traffic, including Internet traffic, goes through the server. Useful if you don’t trust the network you’re on (public Wi-Fi) or if you want the client to always exit with the server’s IP.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Split tunnel&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs = 10.66.0.0/24, 192.168.1.0/24&lt;/code&gt;: only traffic to the VPN and the office LAN goes through the tunnel, everything else (Netflix, websites, everything else) goes straight out from the client’s own connection. Lighter on the server, and often faster for the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way, if you want clients to reach hosts on the &lt;strong&gt;real LAN&lt;/strong&gt; (not just the server), Windows needs to route between the WireGuard interface and the physical network interface. By default it doesn’t: you need to enable &lt;strong&gt;IP forwarding&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Set-ItemProperty&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;IPEnableRouter&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Restart-Computer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want a &lt;strong&gt;full tunnel&lt;/strong&gt; instead (clients go out to the Internet through the server), you also need outbound &lt;strong&gt;NAT&lt;/strong&gt; on the server’s physical interface:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;New-NetNat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;WireGuardNAT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-InternalIPInterfaceAddressPrefix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;10.66.0.0/24&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That command is enough on its own, no need to install the Routing and Remote Access role: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NetNat&lt;/code&gt; module has shipped with Windows Server since 2016.&lt;/p&gt;

&lt;h2 id=&quot;a-few-security-notes&quot;&gt;A few security notes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;One keypair per device&lt;/strong&gt;, never shared between people: it’s the only way to revoke access for &lt;em&gt;one&lt;/em&gt; device (just remove its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[Peer]&lt;/code&gt; block on the server) without rotating everyone else’s keys.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keep &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AllowedIPs&lt;/code&gt; as narrow as possible&lt;/strong&gt; for every peer on the server: a client that only handles backups doesn’t need access to the whole LAN.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changing the default port&lt;/strong&gt; (51820) isn’t real security — it’s &lt;em&gt;security through obscurity&lt;/em&gt; — but it noticeably cuts down the background noise of automated scanners knocking on the standard port in your logs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;WireGuard doesn’t replace application-level authentication&lt;/strong&gt;: it grants network access, not user identity. If RDP, a file share or an admin panel sits behind the VPN, keep strong passwords and, where possible, MFA on those services too — the VPN is one layer, not the only one.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Back up private keys&lt;/strong&gt; somewhere encrypted (a password manager, not a text file on the desktop): lose the server’s and you’ll have to regenerate it and reconfigure every client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-its-this-fast&quot;&gt;Why it’s this fast&lt;/h2&gt;

&lt;p&gt;Worth spending a couple of lines on the “fast” in the title. On Windows, since the &lt;strong&gt;WireGuardNT&lt;/strong&gt; driver, the entire packet path runs &lt;strong&gt;in the kernel&lt;/strong&gt;, no longer in a user-space process like the early versions (which relied on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wireguard-go&lt;/code&gt; on top of the Wintun adapter). That, combined with the protocol having none of IPsec’s negotiation overhead (no IKE, no constant renegotiation) and none of OpenVPN’s typical TCP slowdown, translates into throughput very close to the physical connection’s limit and minimal added latency — in practice, often imperceptible compared to unencapsulated traffic.&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;Once set up, a WireGuard tunnel between Windows Server and Windows 11 needs very little maintenance: no certificates to renew, no cipher list to keep up to date, one config file per peer. The places people usually get stuck are always the same three: an &lt;strong&gt;unreachable UDP port&lt;/strong&gt; (firewall or missing port forward), &lt;strong&gt;IP forwarding disabled&lt;/strong&gt; (no LAN access), and &lt;strong&gt;missing NAT&lt;/strong&gt; (no Internet in full-tunnel mode) — if the tunnel comes up (handshake visible) but something downstream doesn’t work, it’s almost always one of these three.&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/install/&quot;&gt;WireGuard for Windows&lt;/a&gt; — same installer for server and client, official site&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/wireguard-windows/wg0-server.conf&quot;&gt;wg0-server.conf&lt;/a&gt; — server config template, with two example peers&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/wireguard-windows/client.conf&quot;&gt;client.conf&lt;/a&gt; — client config template, with both variants (full tunnel / split tunnel) commented&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/&quot;&gt;wireguard.com&lt;/a&gt; — the protocol’s official site&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.wireguard.com/install/&quot; target=&quot;_blank&quot;&gt;WireGuard for Windows documentation&lt;/a&gt; — download page and client notes&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/module/netnat/new-netnat&quot; target=&quot;_blank&quot;&gt;New-NetNat (Microsoft docs)&lt;/a&gt; — details on the cmdlet used for full-tunnel NAT&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 14 Aug 2026 00:15:00 +0200</pubDate>
        <link>https://cristiancastellari.it/en/blog/infrastructure/fast-solid-vpn-with-wireguard/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/en/blog/infrastructure/fast-solid-vpn-with-wireguard/</guid>
        
        <category>windows-server</category>
        
        <category>windows11</category>
        
        <category>vpn</category>
        
        <category>wireguard</category>
        
        <category>networking</category>
        
        <category>security</category>
        
        
        <category>Infrastructure &amp; Systems</category>
        
      </item>
    
      <item>
        <title>Il nuovo sito, parte 4: le ultime finiture</title>
        <description>&lt;p&gt;Le prime tre parti di questa serie (&lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-2-i-bug-nascosti-in-un-sito-nuovo/&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-3-le-copertine-che-si-adattano-al-tema/&quot;&gt;3&lt;/a&gt;) raccontano decisioni con un inizio, uno svolgimento e una fine chiara: ricostruire il sito, correggere i bug, inventarsi le copertine giuste. Questa parte è diversa. È la lista, un po’ meno epica ma altrettanto necessaria, delle rifiniture fatte passando in rassegna il sito pezzo per pezzo prima di considerarlo pronto per andare online.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#un-menu-che-non-portava-dove-prometteva&quot; id=&quot;markdown-toc-un-menu-che-non-portava-dove-prometteva&quot;&gt;Un menu che non portava dove prometteva&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#un-indice-per-un-blog-che-crescerà&quot; id=&quot;markdown-toc-un-indice-per-un-blog-che-crescerà&quot;&gt;Un indice per un blog che crescerà&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#i-bottoni-di-condivisione-erano-fermi-al-2016&quot; id=&quot;markdown-toc-i-bottoni-di-condivisione-erano-fermi-al-2016&quot;&gt;I bottoni di condivisione erano fermi al 2016&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-colore-dei-social-recuperato-dal-sito-vecchio&quot; id=&quot;markdown-toc-il-colore-dei-social-recuperato-dal-sito-vecchio&quot;&gt;Il colore dei social, recuperato dal sito vecchio&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-box-dei-commenti-che-si-rompeva-in-silenzio&quot; id=&quot;markdown-toc-il-box-dei-commenti-che-si-rompeva-in-silenzio&quot;&gt;Il box dei commenti che si rompeva in silenzio&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#tre-pixel-di-troppo&quot; id=&quot;markdown-toc-tre-pixel-di-troppo&quot;&gt;Tre pixel di troppo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-vuoto-fra-il-testo-e-la-foto-in-home&quot; id=&quot;markdown-toc-il-vuoto-fra-il-testo-e-la-foto-in-home&quot;&gt;Il vuoto fra il testo e la foto, in home&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#perché-questa-parte-conta&quot; id=&quot;markdown-toc-perché-questa-parte-conta&quot;&gt;Perché questa parte conta&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;un-menu-che-non-portava-dove-prometteva&quot;&gt;Un menu che non portava dove prometteva&lt;/h2&gt;

&lt;p&gt;La voce “Blog” nel menu principale apriva il sottomenu con le categorie — ma cliccandoci sopra direttamente, invece di finire su &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/blog/&lt;/code&gt; come ci si aspetterebbe, non succedeva nulla di utile. La voce del menu faceva doppio servizio: link e interruttore del sottomenu insieme, e il secondo comportamento vinceva sempre sul primo.&lt;/p&gt;

&lt;p&gt;La correzione è stata separare le due responsabilità in due elementi distinti — un link vero verso &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/blog/&lt;/code&gt; e, accanto, un piccolo pulsante a freccia dedicato solo ad aprire o chiudere il sottomenu delle categorie:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-item&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-link&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ &apos;/blog/&apos; | relative_url }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Blog&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;button&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-caret&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Mostra le categorie del blog&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-expanded=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;i&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fa fa-chevron-down&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-hidden=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ul&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-submenu&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    {% for category in site.data.blog %}
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ category.href | relative_url }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{{ category.name }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    {% endfor %}
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Un bug banale da leggere nel codice, ma invisibile finché non si prova davvero a cliccare “Blog” aspettandosi di arrivare al blog.&lt;/p&gt;

&lt;h2 id=&quot;un-indice-per-un-blog-che-crescerà&quot;&gt;Un indice per un blog che crescerà&lt;/h2&gt;

&lt;p&gt;Con dieci post il problema non si pone, ma prima o poi la lista cronologica in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/blog/&lt;/code&gt; smetterà di bastare da sola. Ho aggiunto due cose pensando in avanti: un indice di categorie navigabile in cima alla pagina, con il conteggio degli articoli per categoria, e la paginazione vera e propria — quindici post per pagina, tramite il plugin &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll-paginate&lt;/code&gt; (il cui comportamento tutt’altro che intuitivo, con quattro pagine che scrivevano tutte sullo stesso file, è raccontato nella &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-2-i-bug-nascosti-in-un-sito-nuovo/&quot;&gt;parte 2&lt;/a&gt;).&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;nav class=&quot;category-index&quot; aria-label=&quot;Categorie del blog&quot;&amp;gt;
  &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site.data.blog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
  &amp;lt;a class=&quot;category-pill&quot; href=&quot;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;relative_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&quot;&amp;gt;
    &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
    &amp;lt;span class=&quot;category-pill__count&quot;&amp;gt;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;categories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&amp;lt;/span&amp;gt;
  &amp;lt;/a&amp;gt;
  &lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ho scartato di proposito l’idea di una tag cloud: con una manciata di categorie ben curate, le pillole già rispondono alla domanda “cosa trovo qui dentro” in un colpo d’occhio — una nuvola di tag avrebbe aggiunto rumore visivo senza aggiungere informazione.&lt;/p&gt;

&lt;h2 id=&quot;i-bottoni-di-condivisione-erano-fermi-al-2016&quot;&gt;I bottoni di condivisione erano fermi al 2016&lt;/h2&gt;

&lt;p&gt;Ogni post ha dei bottoni per condividerlo sui social. Non li avevo toccati durante la ricostruzione — funzionavano, quindi perché guardarli? Guardandoli davvero, la lista dei problemi è stata più lunga del previsto:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Twitter era ancora Twitter.&lt;/strong&gt; Dominio e testo non aggiornati a X.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;LinkedIn usava un endpoint dismesso.&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shareArticle&lt;/code&gt; non è più quello raccomandato da anni: quello attuale è &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sharing/share-offsite&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reddit era in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://&lt;/code&gt;&lt;/strong&gt;, non &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&lt;/code&gt;, e non passava nemmeno il titolo del post.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Nessun parametro era codificato per l’URL.&lt;/strong&gt; Il titolo di un articolo con un apostrofo — capita spesso, in italiano — poteva rompere silenziosamente il link di condivisione.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Il testo dell’email era in inglese&lt;/strong&gt; (“Check out this site”) in mezzo a un sito interamente in italiano.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mancavano Bluesky, WhatsApp e Telegram&lt;/strong&gt;, probabilmente più rilevanti oggi di Reddit per un pubblico italiano.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La correzione più istruttiva è quella dell’URL encoding. Senza:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://x.com/intent/tweet?text=&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&amp;amp;url=&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Con un titolo tipo &lt;em&gt;“…dell’Infrastruttura IT…“&lt;/em&gt;, quell’apostrofo tipografico finiva letteralmente dentro l’URL, rompendo la struttura dei parametri. Basta un filtro:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://x.com/intent/tweet?text=&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;url_encode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&amp;amp;url=&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;url_encode&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Un dettaglio che non si nota mai, finché non capita il titolo sbagliato.&lt;/p&gt;

&lt;h2 id=&quot;il-colore-dei-social-recuperato-dal-sito-vecchio&quot;&gt;Il colore dei social, recuperato dal sito vecchio&lt;/h2&gt;

&lt;p&gt;Nella nuova versione i bottoni di condivisione erano tutti uguali: bordo neutro, colore d’accento del sito. Corretto, ma anonimo — nel sito vecchio ogni icona aveva il proprio colore di brand, e passandoci sopra con il mouse il bottone si riempiva di quel colore invertendo il testo in bianco. Un dettaglio che aiuta a riconoscere le icone a colpo d’occhio, prima ancora di leggerle.&lt;/p&gt;

&lt;p&gt;Il file che conteneva quei colori l’avevo cancellato durante la ricostruzione, ma la cronologia Git non dimentica:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git show master:_sass/_social.scss
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Da lì ho recuperato le tonalità esatte (Facebook &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#3b5998&lt;/code&gt;, LinkedIn &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#0077b5&lt;/code&gt;, Reddit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#ff5700&lt;/code&gt;…) e le ho riapplicate, aggiungendo i colori ufficiali delle tre piattaforme nuove. Per X, che oggi non usa più l’uccellino ma un logo che Font Awesome — fermo al 2016 — non conosce, ho dovuto disegnare l’icona a mano con un piccolo SVG inline, esattamente come già fatto per Bluesky nella parte 3.&lt;/p&gt;

&lt;h2 id=&quot;il-box-dei-commenti-che-si-rompeva-in-silenzio&quot;&gt;Il box dei commenti che si rompeva in silenzio&lt;/h2&gt;

&lt;p&gt;Questo è stato il bug più subdolo dell’intera rifinitura. Un lettore mi ha segnalato che, aprendo certi articoli, il box dei commenti restava vuoto — a meno di ricaricare la pagina, dopodiché funzionava perfettamente.&lt;/p&gt;

&lt;p&gt;La causa era in una singola riga, rimasta invariata probabilmente dal giorno in cui questo sito ha adottato Disqus:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;cookieValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)?&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;cookiebar=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;([^&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se il cookie &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cookiebar&lt;/code&gt; non esiste ancora — cioè alla primissima visita, prima che l’utente interagisca col banner dei cookie — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.cookie.match()&lt;/code&gt; restituisce &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;. E leggere &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[2]&lt;/code&gt; da &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; non restituisce &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt;: &lt;strong&gt;lancia un errore JavaScript&lt;/strong&gt; che interrompe l’intero script sul colpo. Disqus non veniva nemmeno provato a caricare. Solo un secondo caricamento della pagina, con il cookie ormai presente, faceva ripartire lo script da capo — da qui il “funziona solo dopo il reload”.&lt;/p&gt;

&lt;p&gt;La correzione è una manciata di righe più difensive:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cookieMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;(?:&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;^|; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;cookiebar=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;([^&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cookieValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cookieMatch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;decodeURIComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookieMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ne ho approfittato per allineare l’imbottitura di Disqus alla configurazione raccomandata attuale — URL e identificatore di pagina dichiarati esplicitamente invece di lasciarli dedurre automaticamente — e per passare da un URL “protocol-relative” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;//...disqus.com/embed.js&lt;/code&gt;) a un più corretto &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&lt;/code&gt; esplicito.&lt;/p&gt;

&lt;h2 id=&quot;tre-pixel-di-troppo&quot;&gt;Tre pixel di troppo&lt;/h2&gt;

&lt;p&gt;Ultima segnalazione, la più piccola: nella pagina Progetti, il numero di fork su GitHub andava a capo su una riga propria invece di stare accanto alle stelle, per pochi pixel. Corretto allargando leggermente il contenitore generale del sito (1280 → 1360px, un aumento del 6% impercettibile ovunque tranne che dove serviva) e riducendo il padding orizzontale delle pillole statistiche. Verificato sul caso peggiore — il repository di questo stesso sito, con oltre cento stelle — che ora sta comodamente su una riga sola.&lt;/p&gt;

&lt;h2 id=&quot;il-vuoto-fra-il-testo-e-la-foto-in-home&quot;&gt;Il vuoto fra il testo e la foto, in home&lt;/h2&gt;

&lt;p&gt;Ultima segnalazione, arrivata guardando la home su uno schermo largo: nella hero, il blocco di testo a sinistra e la mia foto a destra sembravano due isole scollegate, con un vuoto enorme in mezzo che restringere la finestra non risolveva. Colpa di &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;justify-content: space-between&lt;/code&gt; su un contenitore largo quanto l’intera pagina (1360px): spinge i due elementi ai bordi opposti a prescindere da quanto sia effettivamente largo il testo — più lo schermo è ampio, più cresce il vuoto.&lt;/p&gt;

&lt;p&gt;Primo tentativo, sbagliato: dare alla hero un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max-width&lt;/code&gt; proprio e centrarla, per tenere testo e foto più vicini. Il risultato sembrava due blocchi sovrapposti, perché le sezioni sotto (Progetti, Blog) restavano larghe quanto tutta la pagina e non più allineate al bordo sinistro della hero. Corretto tornando alla larghezza piena e sostituendo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;space-between&lt;/code&gt; con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;justify-content: flex-start&lt;/code&gt; più un gap fisso: testo e foto restano un gruppo compatto, ancorato allo stesso margine di tutto il resto della pagina.&lt;/p&gt;

&lt;p&gt;Ultimo dettaglio, puramente estetico, che ha richiesto tre tentativi: prima un alone sfumato nel colore d’accento dietro la foto (“sembro una divinità scesa dal cielo”, bocciato giustamente), poi un cerchio piatto più discreto nello stesso colore delle card sotto, e alla fine — con lo spazio ormai risolto dal fix di layout — nessun elemento decorativo. La foto non ne aveva più bisogno: il problema era il vuoto intorno, non l’assenza di uno sfondo.&lt;/p&gt;

&lt;h2 id=&quot;perché-questa-parte-conta&quot;&gt;Perché questa parte conta&lt;/h2&gt;

&lt;p&gt;Nessuna di queste correzioni cambia in modo visibile il sito per un visitatore distratto. Ma sommate, sono la differenza tra un sito che “sembra pronto” e uno che lo è davvero: link che portano dove devono, un box commenti che funziona sempre e non solo a volte, un dettaglio di colore che aiuta a riconoscere un’icona senza doverla leggere. Le parti divertenti di ricostruire un sito sono le prime tre. Questa è quella che, silenziosamente, fa la differenza quando qualcuno lo usa sul serio.&lt;/p&gt;
</description>
        <pubDate>Thu, 13 Aug 2026 21:15:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-4-le-ultime-finiture/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-4-le-ultime-finiture/</guid>
        
        <category>css</category>
        
        <category>claude-code</category>
        
        <category>ia</category>
        
        <category>debugging</category>
        
        <category>ux</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Il nuovo sito, parte 3: le copertine che si adattano al tema</title>
        <description>&lt;p&gt;Nelle prime due parti di questa serie (&lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-2-i-bug-nascosti-in-un-sito-nuovo/&quot;&gt;2&lt;/a&gt;) ho raccontato perché ho ricostruito questo sito e i bug scoperti subito dopo. Poi mi serviva un’ultima cosa, piccola: una copertina per quei due articoli. Ci è voluto più tempo di quanto vorrei ammettere, ed è successo letteralmente mentre scrivevo — quindi eccolo qui, come terzo capitolo.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#lidea-di-partenza&quot; id=&quot;markdown-toc-lidea-di-partenza&quot;&gt;L’idea di partenza&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#tre-tentativi-tre-problemi&quot; id=&quot;markdown-toc-tre-tentativi-tre-problemi&quot;&gt;Tre tentativi, tre problemi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#losservazione-che-ha-cambiato-tutto&quot; id=&quot;markdown-toc-losservazione-che-ha-cambiato-tutto&quot;&gt;L’osservazione che ha cambiato tutto&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#catturare-uno-screenshot-in-chiaro-quando-il-browser-insiste-per-lo-scuro&quot; id=&quot;markdown-toc-catturare-uno-screenshot-in-chiaro-quando-il-browser-insiste-per-lo-scuro&quot;&gt;Catturare uno screenshot “in chiaro” quando il browser insiste per lo scuro&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#limplementazione-finale-zero-javascript&quot; id=&quot;markdown-toc-limplementazione-finale-zero-javascript&quot;&gt;L’implementazione finale: zero JavaScript&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-mi-porto-a-casa-di-nuovo&quot; id=&quot;markdown-toc-cosa-mi-porto-a-casa-di-nuovo&quot;&gt;Cosa mi porto a casa, di nuovo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;lidea-di-partenza&quot;&gt;L’idea di partenza&lt;/h2&gt;

&lt;p&gt;Volevo una copertina che raccontasse visivamente il passaggio dal vecchio sito al nuovo: uno screenshot di ciascuna versione, uniti in un’unica immagine — due terzi vecchio e un terzo nuovo per la prima parte, il contrario per la seconda.&lt;/p&gt;

&lt;p&gt;Nessun tool di editing immagini a disposizione, solo la riga di comando. Ho installato Pillow (Python) per comporre le immagini, e usato Microsoft Edge in &lt;strong&gt;modalità headless&lt;/strong&gt; per catturare gli screenshot: sia del sito vecchio, ancora online in produzione, sia del nuovo, in locale.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;msedge --headless --disable-gpu --window-size=1600,1400 --screenshot=old.png https://cristiancastellari.it/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Fin qui, facile.&lt;/p&gt;

&lt;h2 id=&quot;tre-tentativi-tre-problemi&quot;&gt;Tre tentativi, tre problemi&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Primo tentativo&lt;/strong&gt;: dissolvenza larga tra le due immagini. Risultato: i testi delle due pagine, sovrapposti nella zona di sfumatura, diventavano illeggibili — un doppio esposizione confusa, non una transizione elegante.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secondo tentativo&lt;/strong&gt;: ho ristretto il ritaglio a soli 300 pixel in alto, giusto l’header. Risultato: si vedeva solo lo sfondo animato a particelle del vecchio sito, senza alcun indizio che si trattasse davvero di un sito diverso. Contenuto zero, contesto zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terzo tentativo&lt;/strong&gt;: ritaglio più alto (750px, header più una porzione di contenuto reale) e un taglio netto con una linea di accento blu invece della dissolvenza. Finalmente qualcosa di leggibile e riconoscibile — ma mancava ancora qualcosa.&lt;/p&gt;

&lt;h2 id=&quot;losservazione-che-ha-cambiato-tutto&quot;&gt;L’osservazione che ha cambiato tutto&lt;/h2&gt;

&lt;p&gt;A quel punto ho notato una cosa banale ma rivelatrice: la copertina, da sola, non “sapeva” se sarebbe stata vista su una pagina chiara o scura. Un’immagine con il nuovo sito fotografato in tema scuro, su una pagina già scura, si mimetizza — poco contrasto, poco impatto. La stessa immagine su una pagina chiara, invece, risalta.&lt;/p&gt;

&lt;p&gt;La soluzione ovvia, una volta vista: &lt;strong&gt;due copertine per articolo&lt;/strong&gt;, una con il nuovo sito fotografato in chiaro e una in scuro, mostrate in alternativa a seconda del tema con cui il lettore sta guardando il sito in quel momento — sempre quella che fa più contrasto.&lt;/p&gt;

&lt;h2 id=&quot;catturare-uno-screenshot-in-chiaro-quando-il-browser-insiste-per-lo-scuro&quot;&gt;Catturare uno screenshot “in chiaro” quando il browser insiste per lo scuro&lt;/h2&gt;

&lt;p&gt;Qui la parte tecnicamente più interessante. Il sistema su cui lavoro preferisce il tema scuro, quindi ogni screenshot headless usciva scuro di default. Ho provato i flag “giusti” di Chromium per forzare il tema chiaro:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;--force-prefers-color-scheme=light
--blink-settings=preferredColorScheme=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nessuno dei due ha funzionato con questa versione di Edge headless — lo screenshot continuava a uscire scuro, senza errori, semplicemente ignorando il flag.&lt;/p&gt;

&lt;p&gt;La soluzione che ha funzionato: &lt;strong&gt;imposta la preferenza attraverso il sito stesso, non attraverso il browser&lt;/strong&gt;. Il selettore di tema di questo sito salva la scelta in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt;. Ho creato una micro-pagina temporanea che imposta quel valore e reindirizza alla home:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;light&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…e l’ho aperta con Edge headless usando un &lt;strong&gt;profilo persistente&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--user-data-dir&lt;/code&gt;), in due passaggi separati: prima la micro-pagina (per scrivere il valore), poi lo screenshot della home vera e propria, riusando lo stesso profilo — così &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; sopravviveva tra i due lanci del browser. Non elegantissimo, ma affidabile al 100%.&lt;/p&gt;

&lt;h2 id=&quot;limplementazione-finale-zero-javascript&quot;&gt;L’implementazione finale: zero JavaScript&lt;/h2&gt;

&lt;p&gt;Con gli screenshot giusti in mano, l’ultimo pezzo è stato mostrare l’immagine corretta in base al tema — usando lo stesso meccanismo, puramente CSS, con cui questo sito gestisce già i tre stati automatico/chiaro/scuro:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.post-image--on-dark&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nd&quot;&gt;:root:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-theme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;light&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;.post-image--on-light&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;.post-image--on-dark&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-theme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;.post-image--on-light&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;.post-image--on-dark&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il markup diventa semplicemente due tag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, uno per variante:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post-image post-image--on-light&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cover.jpg&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post-image post-image--on-dark&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cover-dark.jpg&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il browser scarica entrambe le immagini (un piccolo costo, accettabile per due JPEG leggeri) ma ne mostra sempre una sola, decisa dal CSS in base al tema corrente — nessun JavaScript aggiuntivo, nessun flash, nessuna richiesta extra.&lt;/p&gt;

&lt;h2 id=&quot;cosa-mi-porto-a-casa-di-nuovo&quot;&gt;Cosa mi porto a casa, di nuovo&lt;/h2&gt;

&lt;p&gt;La versione finale delle copertine — quella che stai vedendo proprio ora, in cima a questo articolo — è il quinto tentativo, non il primo. Nessuno dei tentativi precedenti era “sbagliato” in senso tecnico: producevano tutti un’immagine valida. Erano semplicemente poco convincenti, ed è stato solo continuando a guardarli con occhio critico — mio, non del codice — che è emerso il problema vero: un’immagine statica non può sapere in che contesto verrà mostrata, a meno che non gliene costruisci due e lasci decidere il CSS.&lt;/p&gt;

&lt;p&gt;Anche questo, alla fine, è un dettaglio che nessuno noterà consciamente. Ma se il sito regge bene sia in chiaro che in scuro, è anche per questo pomeriggio passato a litigare con i flag di un browser headless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nella &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-4-le-ultime-finiture/&quot;&gt;parte 4&lt;/a&gt;&lt;/strong&gt;, l’ultima di questa serie: le rifiniture minori ma necessarie trovate ripassando il sito pezzo per pezzo.&lt;/p&gt;
</description>
        <pubDate>Thu, 13 Aug 2026 10:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-3-le-copertine-che-si-adattano-al-tema/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-3-le-copertine-che-si-adattano-al-tema/</guid>
        
        <category>css</category>
        
        <category>claude-code</category>
        
        <category>ia</category>
        
        <category>design</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Il nuovo sito, parte 2: i bug nascosti in un sito nuovo di zecca</title>
        <description>&lt;p&gt;Nella &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/&quot;&gt;prima parte&lt;/a&gt; ho raccontato perché ho deciso di ricostruire questo sito da zero, insieme a Claude Code, e come l’intero lavoro si sia concluso in un pomeriggio invece che in settimane. Quello che non ho detto è che, appena ho iniziato a usare il sito nuovo per davvero — cliccando in giro, cambiando tema, aggiungendo contenuti — sono saltati fuori bug che il codice, da solo, non avrebbe mai rivelato. Codice scritto quel pomeriggio stesso, non debito ereditato dal 2019.&lt;/p&gt;

&lt;p&gt;Qui ci sono i cinque più interessanti.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#1-il-bottone-sottolineato-che-non-voleva-saperne&quot; id=&quot;markdown-toc-1-il-bottone-sottolineato-che-non-voleva-saperne&quot;&gt;1. Il bottone sottolineato che non voleva saperne&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#2-il-tema-chiaro-che-si-perdeva-a-ogni-pagina&quot; id=&quot;markdown-toc-2-il-tema-chiaro-che-si-perdeva-a-ogni-pagina&quot;&gt;2. Il tema chiaro che si perdeva a ogni pagina&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#3-le-stelline-di-github-sparite&quot; id=&quot;markdown-toc-3-le-stelline-di-github-sparite&quot;&gt;3. Le stelline di GitHub sparite&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#4-le-pillole-delle-categorie-che-puntavano-nel-vuoto&quot; id=&quot;markdown-toc-4-le-pillole-delle-categorie-che-puntavano-nel-vuoto&quot;&gt;4. Le pillole delle categorie che puntavano nel vuoto&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#5-quattro-pagine-un-solo-file-quello-di-jekyll&quot; id=&quot;markdown-toc-5-quattro-pagine-un-solo-file-quello-di-jekyll&quot;&gt;5. Quattro pagine, un solo file (quello di Jekyll)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-mi-porto-a-casa&quot; id=&quot;markdown-toc-cosa-mi-porto-a-casa&quot;&gt;Cosa mi porto a casa&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1-il-bottone-sottolineato-che-non-voleva-saperne&quot;&gt;1. Il bottone sottolineato che non voleva saperne&lt;/h2&gt;

&lt;p&gt;Il primo problema evidente: il bottone “Scarica il CV in PDF” con il testo sottolineato, bruttissimo, in mezzo a un componente che doveva essere un bottone pieno. Stessa cosa era già successa sulle card della pagina Contatti.&lt;/p&gt;

&lt;p&gt;La causa: le pagine come CV e Contatti avvolgono il loro contenuto in un contenitore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.prose&lt;/code&gt;, pensato per lo stile tipografico degli articoli del blog (dove i link &lt;em&gt;devono&lt;/em&gt; essere sottolineati, per leggibilità e accessibilità). La regola CSS era:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.prose&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;underline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il problema è che un bottone (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a class=&quot;btn&quot;&amp;gt;&lt;/code&gt;) o una card cliccabile (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a class=&quot;card&quot;&amp;gt;&lt;/code&gt;), quando si trovano dentro un paragrafo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; dentro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.prose&lt;/code&gt; — come capita spesso, per semplice comodità di markup — vengono presi in pieno da questa regola, che ha più specificità della classe del componente stesso. Ho ristretto la regola al vero testo discorsivo:&lt;/p&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.prose&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.btn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.pill&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.prose&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.btn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.pill&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.prose&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;blockquote&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.btn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.pill&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;underline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Non basta scrivere “solo i link nei paragrafi vanno sottolineati” — bisogna anche escludere esplicitamente i componenti che, per comodità di impaginazione, finiscono dentro un paragrafo pur non essendo testo.&lt;/p&gt;

&lt;h2 id=&quot;2-il-tema-chiaro-che-si-perdeva-a-ogni-pagina&quot;&gt;2. Il tema chiaro che si perdeva a ogni pagina&lt;/h2&gt;

&lt;p&gt;Avevo costruito un selettore Automatico/Chiaro/Scuro con uno script che, nell’&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, legge la preferenza salvata e la applica &lt;em&gt;prima&lt;/em&gt; del disegno della pagina, per evitare il classico “flash” del tema sbagliato. Cliccavo “Chiaro”, funzionava. Cambiavo pagina, tornava scuro.&lt;/p&gt;

&lt;p&gt;Il colpevole: &lt;strong&gt;Brave&lt;/strong&gt;. Il browser blocca, per motivi di privacy, gli script inline (come quello nell’&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt;) in certe condizioni, pur lasciando passare i file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.js&lt;/code&gt; esterni. Il pulsante mostrava comunque “Chiaro” come selezionato — perché quello stato veniva letto correttamente da &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; da un file esterno — ma nessuno riapplicava davvero l’attributo sulla pagina.&lt;/p&gt;

&lt;p&gt;La correzione: non fidarsi che lo script inline avesse fatto il suo lavoro. Il file esterno ora riapplica sempre l’attributo all’avvio, invece di limitarsi ad aggiornare l’aspetto del pulsante:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initialChoice&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentChoice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;applyAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initialChoice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// non solo updateButtons(initialChoice)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;updateButtons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initialChoice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ridondante, se lo script inline funziona. Indispensabile, quando (per qualsiasi motivo, non solo Brave) non funziona.&lt;/p&gt;

&lt;h2 id=&quot;3-le-stelline-di-github-sparite&quot;&gt;3. Le stelline di GitHub sparite&lt;/h2&gt;

&lt;p&gt;Le card dei miei progetti mostrano stelle e fork presi in diretta dalla GitHub API. Funzionava, poi improvvisamente tutti i numeri sono spariti, sostituiti da un trattino.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;L’API pubblica di GitHub, senza autenticazione, concede &lt;strong&gt;60 richieste all’ora per indirizzo IP&lt;/strong&gt;. Tra i miei test e i ricaricamenti della pagina durante lo sviluppo, l’avevamo esaurita — e con 5 repository interrogati a ogni caricamento della pagina Progetti, in produzione sarebbero bastate 12 visite in un’ora per rifare lo stesso danno.&lt;/p&gt;

&lt;p&gt;Non un bug da correggere, ma un limite strutturale da progettare meglio: ho aggiunto una cache lato client di 6 ore in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt;, così visite ripetute non richiamano l’API ogni volta.&lt;/p&gt;

&lt;h2 id=&quot;4-le-pillole-delle-categorie-che-puntavano-nel-vuoto&quot;&gt;4. Le pillole delle categorie che puntavano nel vuoto&lt;/h2&gt;

&lt;p&gt;Dopo aver rinominato le categorie del blog — “Kace” è rimasta “Kace”, ma “Tools” è diventata “Strumenti”, “ITIL” è diventata “Filosofia di Lavoro” — le pillole colorate sulle card dei post (quelle che mostrano la categoria e ci si clicca sopra) hanno iniziato a puntare a URL che non esistevano. Il codice faceva semplicemente:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;a class=&quot;pill&quot; href=&quot;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;relative_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Funzionava per puro caso quando il nome della categoria coincideva con lo slug dell’URL (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kace&lt;/code&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/kace/&lt;/code&gt;). Rinominando “Strumenti” (URL reale: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tools/&lt;/code&gt;), quella logica ha iniziato a generare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/strumenti&lt;/code&gt;, una pagina inesistente. La correzione: cercare davvero l’URL configurato, invece di indovinarlo:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cat_data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;blog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&amp;lt;a class=&quot;pill&quot; href=&quot;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cat_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;relative_url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una riga di scorciatoia che ha funzionato per caso finché i nomi non sono cambiati — il tipo di bug che resta invisibile finché qualcuno, mesi dopo, decide di rinominare qualcosa.&lt;/p&gt;

&lt;h2 id=&quot;5-quattro-pagine-un-solo-file-quello-di-jekyll&quot;&gt;5. Quattro pagine, un solo file (quello di Jekyll)&lt;/h2&gt;

&lt;p&gt;Questo è stato il più tosto. Volevo la paginazione degli articoli del blog (utile quando supereranno i 15 post), e ho aggiunto il plugin &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll-paginate&lt;/code&gt;. Impostato tutto, build pulita, nessun errore — ma abbassando temporaneamente il numero di post per pagina per testare davvero il meccanismo, Jekyll ha iniziato ad avvisarmi:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Conflict: The following destination is shared by multiple files.
  C:/.../blog/index.html
   - blog/index.html
   - blog/index.html
   - blog/index.html
   - blog/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Quattro pagine diverse (pagina 1, 2, 3, 4), tutte scritte nello stesso identico file. Ho dovuto leggere il codice sorgente di Jekyll stesso per capire perché: la mia pagina &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blog/index.html&lt;/code&gt; aveva un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;permalink: /blog/&lt;/code&gt; esplicito nel front matter. Il plugin di paginazione crea copie della pagina per ogni “numero” (assegnando a ciascuna una diversa sotto-cartella), ma se il file ha un permalink fisso nel front matter, &lt;strong&gt;quel valore vince sempre&lt;/strong&gt;, a prescindere dalla cartella assegnata alla copia — perché nel codice di Jekyll, il metodo che calcola l’URL di una pagina dà priorità assoluta al permalink esplicito:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# jekyll/page.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;permalink&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;permalink&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tutte e quattro le copie, leggendo lo stesso file sorgente, ereditavano lo stesso permalink fisso. La correzione, una volta capita la causa, è stata la più semplice di tutte: &lt;strong&gt;togliere&lt;/strong&gt; il permalink esplicito. Il file si chiama &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; e vive nella cartella &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blog/&lt;/code&gt; — Jekyll gli assegna già &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/blog/&lt;/code&gt; da solo, senza bisogno di forzarlo, e a quel punto le pagine successive (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/blog/pagina2/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/blog/pagina3/&lt;/code&gt;…) si generano correttamente.&lt;/p&gt;

&lt;h2 id=&quot;cosa-mi-porto-a-casa&quot;&gt;Cosa mi porto a casa&lt;/h2&gt;

&lt;p&gt;Nessuno di questi cinque bug era debito tecnico ereditato — erano tutti in codice scritto quello stesso pomeriggio. La lezione non è “l’IA scrive codice pieno di bug”: è che &lt;strong&gt;qualsiasi codice, scritto da chiunque, va usato davvero per scoprire dove si rompe&lt;/strong&gt;. La differenza, lavorando con Claude Code, è stata la velocità con cui ogni bug — dalla sottolineatura CSS al codice sorgente di Jekyll — è stato isolato, spiegato e corretto, quasi sempre nell’arco della stessa conversazione in cui l’ho segnalato.&lt;/p&gt;

&lt;p&gt;Un sito nuovo, costruito in un pomeriggio, con la stessa attenzione ai dettagli che normalmente richiederebbe settimane. Continuo a scoprirne di nuovi, probabilmente — ma per ora, il sito che vedete è quello.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C’è anche una &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-3-le-copertine-che-si-adattano-al-tema/&quot;&gt;parte 3&lt;/a&gt;&lt;/strong&gt;, più leggera: la storia di come sono nate le copertine di questi stessi articoli.&lt;/p&gt;
</description>
        <pubDate>Wed, 12 Aug 2026 18:20:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-2-i-bug-nascosti-in-un-sito-nuovo/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-2-i-bug-nascosti-in-un-sito-nuovo/</guid>
        
        <category>jekyll</category>
        
        <category>debugging</category>
        
        <category>claude-code</category>
        
        <category>css</category>
        
        <category>ia</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Il nuovo sito, parte 1: perché ho deciso di ripartire da zero</title>
        <description>&lt;p&gt;Stamattina ho raccontato &lt;a href=&quot;/it/blog/progetti-personali/manutenzione-sito-2026/&quot;&gt;come ho passato il pomeriggio a rimettere in sesto le dipendenze di questo sito&lt;/a&gt; — Ruby, npm, Bootstrap, Chart.js, tutta roba ferma al 2019 che nessuno aveva mai più toccato davvero. Quel lavoro doveva essere la fine della giornata: sito sistemato, dipendenze pulite, tutti contenti.&lt;/p&gt;

&lt;p&gt;Invece, mentre sistemavo l’ennesimo bug dormiente, mi sono fatto una domanda scomoda: &lt;em&gt;sto davvero curando questo sito, o sto solo rallentando la sua fine?&lt;/em&gt;&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#il-momento-in-cui-ho-smesso-di-fare-manutenzione&quot; id=&quot;markdown-toc-il-momento-in-cui-ho-smesso-di-fare-manutenzione&quot;&gt;Il momento in cui ho smesso di fare manutenzione&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-vincolo-che-non-potevo-permettermi-di-rompere&quot; id=&quot;markdown-toc-il-vincolo-che-non-potevo-permettermi-di-rompere&quot;&gt;Il vincolo che non potevo permettermi di rompere&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#le-decisioni-prese-insieme&quot; id=&quot;markdown-toc-le-decisioni-prese-insieme&quot;&gt;Le decisioni prese insieme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#la-velocità-non-è-un-compromesso-sulla-qualità&quot; id=&quot;markdown-toc-la-velocità-non-è-un-compromesso-sulla-qualità&quot;&gt;La velocità non è un compromesso sulla qualità&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;il-momento-in-cui-ho-smesso-di-fare-manutenzione&quot;&gt;Il momento in cui ho smesso di fare manutenzione&lt;/h2&gt;

&lt;p&gt;Il tema di partenza (derivato da &lt;a href=&quot;https://github.com/jarrekk/Jalpc&quot;&gt;Jalpc&lt;/a&gt;) era una landing page one-page con Bootstrap 3, jQuery e una manciata di plugin che oggi nessuno sceglierebbe più: un carosello hero, un menu mobile basato su Bootstrap 3 collapse, un grafico radar via Chart.js per le competenze. Funzionale, nel 2016. Nel 2026, con Bootstrap 3 a fine vita (due XSS moderate mai patchate, per dirne una) e un intero pomeriggio già speso a scoprire quanto debito tecnico si fosse accumulato, la domanda vera non era più “come lo sistemo” ma “vale ancora la pena sistemarlo?”.&lt;/p&gt;

&lt;p&gt;La risposta che mi sono dato: no. Non perché il contenuto non valesse — dieci anni di post tecnici, un CV, i miei progetti — ma perché il &lt;em&gt;contenitore&lt;/em&gt; aveva fatto il suo tempo. Ho deciso di ricostruire il sito da zero, con un tema nuovo, scritto apposta per quello che sono oggi: un Infrastructure &amp;amp; Service Management Manager con un blog tecnico, non più un laptop pieno di plugin jQuery.&lt;/p&gt;

&lt;h2 id=&quot;il-vincolo-che-non-potevo-permettermi-di-rompere&quot;&gt;Il vincolo che non potevo permettermi di rompere&lt;/h2&gt;

&lt;p&gt;C’era però una condizione non negoziabile: &lt;strong&gt;ogni URL dei post del blog doveva restare esattamente identico&lt;/strong&gt;. Dieci anni di articoli tecnici, alcuni ancora citati o linkati da terzi, indicizzati da Google — un redesign che rompe i permalink è un redesign che butta via anni di posizionamento. Qualunque cosa avessi ricostruito, doveva convivere con questo vincolo dall’inizio, non come ripensamento finale.&lt;/p&gt;

&lt;p&gt;Ho lavorato con &lt;strong&gt;Claude Code&lt;/strong&gt;, l’assistente IA da riga di comando di Anthropic, che avevo già usato per il lavoro di manutenzione della mattina. La differenza, per un progetto di questa portata, è stata partire con un piano scritto invece che con il codice: prima abbiamo mappato cosa del sito esistente fosse contenuto reale (i miei progetti GitHub, il CV, gli articoli) e cosa fosse invece morto — funzionalità mai attivate (multilingua), sezioni con dati segnaposto mai personalizzati, categorie di blog senza un solo articolo. Poi abbiamo scritto un documento di piano vero e proprio: architettura delle pagine, sistema di design, quali file sarebbero spariti e quali sarebbero rimasti, come verificare che i permalink non si rompessero. Solo dopo, il codice.&lt;/p&gt;

&lt;p&gt;Questo approccio — pianificare prima di scrivere, invece di scrivere e sistemare — è probabilmente il motivo per cui un lavoro che immaginavo su più giorni si è concluso in un pomeriggio.&lt;/p&gt;

&lt;h2 id=&quot;le-decisioni-prese-insieme&quot;&gt;Le decisioni prese insieme&lt;/h2&gt;

&lt;p&gt;Alcune scelte le ho fatte io, altre le ho delegate, altre ancora le abbiamo discusse:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Stack&lt;/strong&gt;: restare su Jekyll. GitHub Pages lo supporta nativamente, la pipeline di build l’avevo appena sistemata la mattina stessa, e cambiare generatore avrebbe voluto dire rifare da capo anche quello.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Stile visivo&lt;/strong&gt;: minimal e professionale. Niente più carosello, niente animazioni jQuery — palette neutra, molto spazio bianco, il blu &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#3385FF&lt;/code&gt; mantenuto come accento (è già nel favicon, nei profili social collegati, non aveva senso perderlo).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Struttura&lt;/strong&gt;: da one-page a sito multi-pagina — Home, CV, Progetti, Blog, Contatti — più adatta a un sito che deve funzionare sia come portfolio professionale sia come blog tecnico.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cosa buttare&lt;/strong&gt;: Bootstrap 3, jQuery e tutti i suoi plugin, l’intera pipeline npm di build (sostituita dalla compilazione Sass nativa di Jekyll, zero dipendenze front-end), il grafico radar Chart.js (sostituito da una lista di competenze raggruppate per area, più leggibile e coerente con lo stile minimal).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il CV e le competenze, tra l’altro, li avevamo già aggiornati con i dati reali dal mio profilo LinkedIn qualche ora prima — quel lavoro è confluito direttamente nella nuova pagina &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/cv/&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;la-velocità-non-è-un-compromesso-sulla-qualità&quot;&gt;La velocità non è un compromesso sulla qualità&lt;/h2&gt;

&lt;p&gt;La parte che mi ha sorpreso di più non è stata la velocità in sé, ma il fatto che non sia arrivata a scapito della verifica. Ogni singolo pezzo — ogni layout, ogni pagina, ogni categoria del blog — è stato ricompilato e controllato prima di passare al successivo. Prima di considerare il lavoro finito, abbiamo fatto una cosa che a mano non avrei mai avuto la pazienza di fare: un confronto automatico, URL per URL, tra il sito vecchio e quello nuovo, per garantire zero permalink rotti. Il risultato: tre nuove pagine aggiunte (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/cv/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/progetti/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/contatti/&lt;/code&gt;), &lt;strong&gt;zero URL persi&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Quello che un redesign di questo genere avrebbe richiesto in settimane — se fatto da solo, nei ritagli di tempo serali — si è concluso in un pomeriggio. Non perché il lavoro fosse meno, ma perché avere un collega che scrive codice, lo verifica, e lo ricontrolla in loop, senza stancarsi e senza saltare passaggi, cambia radicalmente i tempi.&lt;/p&gt;

&lt;p&gt;Detto questo — e lo scoprirete nella prossima puntata — “veloce” non ha voluto dire “senza intoppi”. Anche un sito costruito da zero, in poche ore, ha nascosto una manciata di bug genuinamente interessanti: uno di questi mi ha fatto finire a leggere il codice sorgente di Jekyll stesso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Continua nella &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-2-i-bug-nascosti-in-un-sito-nuovo/&quot;&gt;parte 2&lt;/a&gt;: i bug nascosti in un sito nuovo di zecca.&lt;/strong&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 12 Aug 2026 18:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/</guid>
        
        <category>jekyll</category>
        
        <category>redesign</category>
        
        <category>claude-code</category>
        
        <category>ia</category>
        
        <category>design</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Manutenzione straordinaria: quando aggiornare le dipendenze rompe tutto</title>
        <description>&lt;p&gt;Questo sito gira su Jekyll da anni, con un tema (Jalpc) basato su Bootstrap, jQuery e qualche libreria JS per grafici e animazioni. Come capita spesso ai side-project personali, negli ultimi anni l’ho tenuto vivo solo dal punto di vista dei contenuti, lasciando che Dependabot aprisse pull request di aggiornamento dipendenze in automatico, quasi sempre mergiate a scatola chiusa perché “tanto sono solo numeri di versione”.&lt;/p&gt;

&lt;p&gt;Oggi ho deciso di fare pulizia sul serio: verificare gemme Ruby e pacchetti npm, aggiornarli davvero, e assicurarmi che il sito continuasse a funzionare. Quello che è successo è stata una piccola lezione di archeologia informatica.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#il-problema-di-partenza-una-build-congelata-dal-2019&quot; id=&quot;markdown-toc-il-problema-di-partenza-una-build-congelata-dal-2019&quot;&gt;Il problema di partenza: una build congelata dal 2019&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#aggiornare-ruby-e-jekyll&quot; id=&quot;markdown-toc-aggiornare-ruby-e-jekyll&quot;&gt;Aggiornare Ruby e Jekyll&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ripulire-npm-25-vulnerabilità-quasi-tutte-fantasma&quot; id=&quot;markdown-toc-ripulire-npm-25-vulnerabilità-quasi-tutte-fantasma&quot;&gt;Ripulire npm: 25 vulnerabilità, quasi tutte fantasma&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-sito-si-rompe-colpa-di-bootstrap-5-che-in-realtà-non-esiste&quot; id=&quot;markdown-toc-il-sito-si-rompe-colpa-di-bootstrap-5-che-in-realtà-non-esiste&quot;&gt;Il sito si rompe: colpa di Bootstrap 5 (che in realtà non esiste)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-grafico-delle-competenze-sparito-chartjs-v2-vs-v4&quot; id=&quot;markdown-toc-il-grafico-delle-competenze-sparito-chartjs-v2-vs-v4&quot;&gt;Il grafico delle competenze sparito: Chart.js v2 vs v4&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-bundle-javascript-da-228-byte&quot; id=&quot;markdown-toc-il-bundle-javascript-da-228-byte&quot;&gt;Il bundle JavaScript da 228 byte&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#le-icone-sparite-font-awesome-4-vs-5&quot; id=&quot;markdown-toc-le-icone-sparite-font-awesome-4-vs-5&quot;&gt;Le icone sparite: Font Awesome 4 vs 5&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-filo-conduttore&quot; id=&quot;markdown-toc-il-filo-conduttore&quot;&gt;Il filo conduttore&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#e-adesso&quot; id=&quot;markdown-toc-e-adesso&quot;&gt;E adesso?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;il-problema-di-partenza-una-build-congelata-dal-2019&quot;&gt;Il problema di partenza: una build congelata dal 2019&lt;/h2&gt;

&lt;p&gt;La prima cosa che ho scoperto è che gli asset compilati del sito (i file CSS/JS minificati in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static/assets/&lt;/code&gt;) portavano tutti la data &lt;strong&gt;20190620&lt;/strong&gt; nel nome. Sei anni di pull request di Dependabot avevano alzato pian piano i numeri di versione dichiarati in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; — Bootstrap da 3 a 4 a 5, Chart.js da 2 a 4, Font Awesome da 4 a 5 — ma &lt;strong&gt;nessuno aveva mai più eseguito &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run build&lt;/code&gt;&lt;/strong&gt; da allora.&lt;/p&gt;

&lt;p&gt;Il sito pubblicato, quindi, funzionava benissimo per un motivo molto semplice: stava ancora servendo i bundle compilati nel 2019, con le librerie di allora. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; diceva una cosa, i file effettivamente in produzione ne dicevano un’altra. Due sistemi paralleli che non si parlavano da anni.&lt;/p&gt;

&lt;p&gt;Appena ho lanciato una build vera, questa finzione è crollata tutta insieme.&lt;/p&gt;

&lt;h2 id=&quot;aggiornare-ruby-e-jekyll&quot;&gt;Aggiornare Ruby e Jekyll&lt;/h2&gt;

&lt;p&gt;Prima ostacolo: Jekyll 4.2.2 (versione dichiarata nel &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt;) non partiva più su Ruby 3.4, l’unica installata sulla macchina. Ruby 3.4 ha smesso di includere &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;csv&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logger&lt;/code&gt; e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigdecimal&lt;/code&gt; come gemme di default, e Jekyll 4.2.2 le usava senza dichiararle esplicitamente. Anche risolto quello, Liquid 4.0.3 (il motore dei template di Jekyll) chiamava &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String#tainted?&lt;/code&gt;, un metodo rimosso da Ruby 3.2 in poi.&lt;/p&gt;

&lt;p&gt;Soluzione: aggiornare Jekyll a 4.4.1, che dichiara correttamente le sue dipendenze, e Liquid alla patch 4.0.4 che risolve il problema di &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tainted?&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;ripulire-npm-25-vulnerabilità-quasi-tutte-fantasma&quot;&gt;Ripulire npm: 25 vulnerabilità, quasi tutte fantasma&lt;/h2&gt;

&lt;p&gt;Sul lato npm la sorpresa è stata diversa. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm audit&lt;/code&gt; segnalava &lt;strong&gt;25 vulnerabilità&lt;/strong&gt;, ma scavando ho trovato che il &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; conteneva pacchetti come &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@npmcli/arborist&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hosted-git-info&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jsprim&lt;/code&gt; e persino &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; stesso&lt;/strong&gt;, elencato come dipendenza del progetto. Nessuno di questi veniva mai usato da una riga di codice del sito: erano tutti finiti lì, probabilmente, come effetto collaterale di vecchi &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm audit fix&lt;/code&gt; che avevano promosso dipendenze transitive a dipendenze dirette invece di usare gli &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overrides&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Rimuovendo i pacchetti morti (verificato con una ricerca sull’intero repository, zero riferimenti):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;25 vulnerabilità → 0
431 pacchetti installati → 40
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nessun codice toccato, solo pulizia.&lt;/p&gt;

&lt;h2 id=&quot;il-sito-si-rompe-colpa-di-bootstrap-5-che-in-realtà-non-esiste&quot;&gt;Il sito si rompe: colpa di Bootstrap 5 (che in realtà non esiste)&lt;/h2&gt;

&lt;p&gt;Con le dipendenze pulite e aggiornate, ho lanciato per la prima volta in anni una vera ricompilazione degli asset. Il sito è diventato illeggibile: menu di navigazione non collassato, sezioni impilate senza layout, icone sparite.&lt;/p&gt;

&lt;p&gt;La causa: il bundle CSS compilato nel 2019 conteneva &lt;strong&gt;Bootstrap v3.4.1&lt;/strong&gt;. Tutto il markup del tema — layout, header, sezioni della landing page — è scritto con classi Bootstrap 3 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navbar-default&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navbar-fixed-top&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;col-lg-*&lt;/code&gt;…). Ma &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; dichiarava da tempo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bootstrap: &quot;^5.0.0&quot;&lt;/code&gt;, mai realmente compilato contro quel markup. Il numero “5” in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; era, di fatto, una bugia mai verificata.&lt;/p&gt;

&lt;p&gt;Ho riportato Bootstrap a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;^3.4.1&lt;/code&gt;, l’unica versione compatibile con il tema così com’è. Bootstrap 3 è però a fine vita: porta con sé due vulnerabilità XSS moderate in popover/tooltip mai patchate. Rischio pratico basso (il sito non inserisce mai contenuto utente in quei componenti), ma è un compromesso consapevole, in attesa di una revisione più profonda del tema.&lt;/p&gt;

&lt;h2 id=&quot;il-grafico-delle-competenze-sparito-chartjs-v2-vs-v4&quot;&gt;Il grafico delle competenze sparito: Chart.js v2 vs v4&lt;/h2&gt;

&lt;p&gt;Nella sezione “Cosa so fare” della home c’è un grafico radar (Chart.js) con le mie competenze. Dopo la ricompilazione, spazio vuoto. Il codice della pagina configurava il grafico così:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ticks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pointLabels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fontSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sintassi di &lt;strong&gt;Chart.js 2&lt;/strong&gt;. Nella versione 4 (quella effettivamente installata da tempo) la configurazione è cambiata: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scale&lt;/code&gt; è diventato &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scales.r&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;legend&lt;/code&gt; è finito dentro &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugins&lt;/code&gt;. La vecchia sintassi non generava errori, semplicemente veniva ignorata in silenzio, e il grafico non si disegnava.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;responsive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;scales&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pointLabels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cinque righe, e il radar è tornato visibile.&lt;/p&gt;

&lt;h2 id=&quot;il-bundle-javascript-da-228-byte&quot;&gt;Il bundle JavaScript da 228 byte&lt;/h2&gt;

&lt;p&gt;Questo è stato il bug più subdolo. Lo script di build (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build/build.js&lt;/code&gt;) passa a &lt;strong&gt;UglifyJS&lt;/strong&gt; l’elenco dei percorsi dei file da minificare e concatenare — jQuery, Bootstrap, Chart.js, eccetera. Dopo la ricompilazione, il file JS finale pesava &lt;strong&gt;228 byte&lt;/strong&gt; invece delle solite centinaia di KB, e conteneva, testuale:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;node_modules,jquery,dist,jquery.js,static,js,bs3,typeahead.js,node_modules,bootstrap,...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I percorsi dei file, non il loro contenuto. UglifyJS li aveva interpretati come &lt;em&gt;codice sorgente&lt;/em&gt; — ogni &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; letto come operatore di divisione tra variabili — invece di leggerli da disco. Il motivo: &lt;strong&gt;UglifyJS 3.x ha cambiato API&lt;/strong&gt;. Nella versione 2 (quella in uso nel 2019), passare un array di percorsi significava “leggi questi file da disco”. Nella 3.x, un array di stringhe viene trattato come codice sorgente letterale: bisogna leggere i file esplicitamente e passare un oggetto &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{nomefile: contenuto}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Il bug è nato probabilmente anni fa, quando &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uglify-js&lt;/code&gt; è stato aggiornato alla v3 da una PR di Dependabot — ma è rimasto dormiente, invisibile, perché nessuno rieseguiva più la build. Corretto leggendo i file esplicitamente prima di passarli a UglifyJS, e aggiunto un controllo sugli errori che prima veniva ignorato silenziosamente.&lt;/p&gt;

&lt;h2 id=&quot;le-icone-sparite-font-awesome-4-vs-5&quot;&gt;Le icone sparite: Font Awesome 4 vs 5&lt;/h2&gt;

&lt;p&gt;Ultimo tassello: le iconcine sui bottoni (Linux, Apple, i social nella sezione “Chi sono”) erano vuote. Lo script di build puntava a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_modules/components-font-awesome/css/font-awesome.css&lt;/code&gt; — file che &lt;strong&gt;non esiste più&lt;/strong&gt; in Font Awesome 5, che ha riorganizzato i CSS in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all.css&lt;/code&gt; più uno shim (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v4-shims.css&lt;/code&gt;) per la retrocompatibilità con i vecchi nomi di classe (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fa fa-linux&lt;/code&gt;, usati ovunque nel tema). Il riferimento al file mancante veniva ignorato silenziosamente dal compressore CSS, proprio come il resto.&lt;/p&gt;

&lt;p&gt;Corretto puntando ai file giusti e copiando i webfont di Font Awesome 5 nella cartella corretta (un dettaglio non banale: i percorsi relativi dentro il CSS compilato si risolvono rispetto alla posizione del bundle finale, non rispetto alla posizione originale dei file sorgente — un’altra assunzione implicita che nessuno aveva mai verificato).&lt;/p&gt;

&lt;h2 id=&quot;il-filo-conduttore&quot;&gt;Il filo conduttore&lt;/h2&gt;

&lt;p&gt;Quattro bug, quattro cause diverse, ma un solo colpevole comune: &lt;strong&gt;una pipeline di build che nessuno eseguiva più&lt;/strong&gt;. Dependabot aggiornava fedelmente i numeri di versione, il sito pubblicato continuava a funzionare (perché serviva asset compilati anni prima), e il divario fra “quello che il progetto dichiara” e “quello che il progetto effettivamente pubblica” cresceva silenzioso, versione dopo versione.&lt;/p&gt;

&lt;p&gt;Nessuno di questi bug avrebbe causato danni se qualcuno avesse rilanciato una build ogni tanto. La lezione, come spesso capita, non è tecnica ma di processo: gli aggiornamenti automatici delle dipendenze sono utili, ma senza una build (e magari un deploy) che li verifichi regolarmente, si accumula un debito invisibile che esplode tutto insieme, il giorno che qualcuno ricomincia a guardare.&lt;/p&gt;

&lt;h2 id=&quot;e-adesso&quot;&gt;E adesso?&lt;/h2&gt;

&lt;p&gt;Il sito torna a funzionare com’era prima, con le dipendenze aggiornate dove ha senso farlo (Ruby, Jekyll, npm) e bloccate dove non lo ha, come Bootstrap 3, in attesa di qualcosa di più definitivo: sto lavorando a una versione completamente nuova di questo sito, con un aspetto più moderno, mantenendo però intatti tutti i permalink degli articoli del blog per non perdere l’indicizzazione su Google. Ne parlerò presto.&lt;/p&gt;
</description>
        <pubDate>Wed, 12 Aug 2026 14:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/progetti-personali/manutenzione-sito-2026/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/progetti-personali/manutenzione-sito-2026/</guid>
        
        <category>jekyll</category>
        
        <category>npm</category>
        
        <category>ruby</category>
        
        <category>bootstrap</category>
        
        <category>chart.js</category>
        
        <category>dependabot</category>
        
        <category>manutenzione</category>
        
        <category>debugging</category>
        
        
        <category>Progetti Personali</category>
        
      </item>
    
      <item>
        <title>Quando l&apos;API va giù: tenere aggiornata la mappa di flottastellare.it</title>
        <description>&lt;p&gt;Fuori dall’orario di lavoro (e fuori dal &lt;a href=&quot;/it/blog/progetti-personali/il-nuovo-sito-parte-1-perche-ripartire-da-zero/&quot;&gt;restyling di questo sito&lt;/a&gt;, di cui potete leggere qui) gioco a &lt;strong&gt;Elite: Dangerous&lt;/strong&gt;, e faccio parte dell’&lt;strong&gt;Alto Comando Flotta Stellare (ACFS)&lt;/strong&gt;, uno squadrone di comandanti indipendenti radunati attorno a una minor faction chiamata, per l’appunto, Flotta Stellare. Il sito dello squadrone, &lt;a href=&quot;https://flottastellare.it&quot;&gt;flottastellare.it&lt;/a&gt;, è anche lui un sito Jekyll — e anche lui, come questo, ha bisogno ogni tanto di un po’ di manutenzione.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#una-tabella-che-si-aggiorna-da-sola-di-solito&quot; id=&quot;markdown-toc-una-tabella-che-si-aggiorna-da-sola-di-solito&quot;&gt;Una tabella che si aggiorna da sola (di solito)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-candidato-scartato&quot; id=&quot;markdown-toc-il-candidato-scartato&quot;&gt;Il candidato scartato&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-sostituto-spansh&quot; id=&quot;markdown-toc-il-sostituto-spansh&quot;&gt;Il sostituto: Spansh&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#i-bug-trovati-passando-i-dati-al-setaccio&quot; id=&quot;markdown-toc-i-bug-trovati-passando-i-dati-al-setaccio&quot;&gt;I bug trovati passando i dati al setaccio&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-risultato&quot; id=&quot;markdown-toc-il-risultato&quot;&gt;Il risultato&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;una-tabella-che-si-aggiorna-da-sola-di-solito&quot;&gt;Una tabella che si aggiorna da sola (di solito)&lt;/h2&gt;

&lt;p&gt;Nella pagina &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;about/index.md&lt;/code&gt; del sito teniamo una tabella con tutti i sistemi stellari presidiati dalla fazione: governo, popolazione, alleanza, stato di controllo. Sono dati che si muovono di continuo — il &lt;strong&gt;BGS&lt;/strong&gt; (Background Simulation) di Elite Dangerous fa evolvere ogni sistema in base a cosa i giocatori ci fanno dentro, giorno per giorno — quindi tenerla aggiornata a mano non è pensabile. Da qui uno script Python, lanciato ogni tanto dalla root del repository, che va a interrogare un’API pubblica e riscrive la tabella.&lt;/p&gt;

&lt;p&gt;Il problema: lo script puntava a &lt;strong&gt;EDSM&lt;/strong&gt;, e l’API di EDSM ha smesso di rispondere.&lt;/p&gt;

&lt;h2 id=&quot;il-candidato-scartato&quot;&gt;Il candidato scartato&lt;/h2&gt;

&lt;p&gt;Prima di cercare un sostituto ho controllato se l’&lt;strong&gt;API di Inara&lt;/strong&gt; — un altro grande database comunitario per Elite Dangerous — potesse coprire lo stesso bisogno. Risposta breve: no. È un’API pensata per essere &lt;strong&gt;push-only&lt;/strong&gt; dal punto di vista di un comandante: riceve eventi come &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addCommanderReputation&lt;/code&gt; o &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setCommanderRankPilot&lt;/code&gt; dal client di gioco, non espone un modo per interrogare popolazione o fazioni presenti in un sistema arbitrario. Gli unici endpoint di lettura sono legati al profilo di un singolo comandante o alle community goal recenti — niente che aiuti a ricostruire lo stato di un sistema. Scartata.&lt;/p&gt;

&lt;h2 id=&quot;il-sostituto-spansh&quot;&gt;Il sostituto: Spansh&lt;/h2&gt;

&lt;p&gt;La soluzione arriva da &lt;a href=&quot;https://spansh.co.uk/&quot;&gt;Spansh&lt;/a&gt;, un altro strumento molto popolare nella comunità di Elite Dangerous (più noto per il suo route planner), che espone un’&lt;strong&gt;API pubblica non ufficiale&lt;/strong&gt; senza bisogno di alcuna chiave: una richiesta &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET&lt;/code&gt; con il nome del sistema restituisce governo, popolazione, alleanza, stato di sicurezza e perfino le fazioni minori presenti con la relativa influenza.&lt;/p&gt;

&lt;p&gt;Il nuovo script, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spansh_sync.py&lt;/code&gt;, non si limita a rimpiazzare quello vecchio funzione per funzione — fa qualcosa in più:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Aggiunge le righe mancanti&lt;/strong&gt;, in ordine alfabetico, per i sistemi che lo squadrone ha conquistato ma che non erano ancora in tabella.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Aggiorna i campi esistenti&lt;/strong&gt; solo quando il dato live di Spansh è effettivamente diverso da quello in tabella (un sistema passa da Controllato a Non Controllato, cambia governo, l’alleanza si sposta) — e stampa un log riga per riga di cosa è cambiato.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lascia invariate le righe non trovate&lt;/strong&gt; su Spansh (es. sistemi visitati ma non ancora presenti nei dump pubblici), invece di cancellarle per errore.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Aggiorna da solo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;last_modified_at&lt;/code&gt;&lt;/strong&gt; nel front matter a ogni esecuzione.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ricalcola il totale&lt;/strong&gt; nella frase “Governiamo su &lt;strong&gt;N&lt;/strong&gt; abitanti”, sommando la popolazione di tutti i sistemi con stato Controllato.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;i-bug-trovati-passando-i-dati-al-setaccio&quot;&gt;I bug trovati passando i dati al setaccio&lt;/h2&gt;

&lt;p&gt;Il primo lancio contro dati reali, come spesso succede, ha fatto emergere problemi che uno script “silenzioso” aveva lasciato indisturbati per chissà quanto:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Una riga con il nome &lt;strong&gt;duplicato per errore&lt;/strong&gt;: “V0502 V0502 Ophiuchii” invece di “V0502 Ophiuchii” — non abbastanza simile al nome vero perché Spansh la riconoscesse, quindi mai più aggiornata da anni.&lt;/li&gt;
  &lt;li&gt;Una &lt;strong&gt;riga doppia per lo stesso sistema&lt;/strong&gt; (“Misir”), con due valori di Governo diversi tra loro — probabilmente il risultato di due inserimenti manuali in momenti diversi.&lt;/li&gt;
  &lt;li&gt;Un file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__pycache__/edsm_fetcher.cpython-314.pyc&lt;/code&gt; &lt;strong&gt;finito nel repository per sbaglio&lt;/strong&gt; in un commit precedente — rimosso, e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__pycache__&lt;/code&gt; aggiunto finalmente a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;il-risultato&quot;&gt;Il risultato&lt;/h2&gt;

&lt;p&gt;Il primo run completo con Spansh ha aggiunto &lt;strong&gt;98 sistemi&lt;/strong&gt; che mancavano del tutto dalla tabella e ha aggiornato popolazione, governo, alleanza e stato su gran parte delle righe esistenti. Lo script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;edsm_fetcher.py&lt;/code&gt; originale resta nel repository come fallback, per il giorno — se mai arriverà — in cui l’API di EDSM tornerà raggiungibile.&lt;/p&gt;

&lt;p&gt;Niente di paragonabile, per portata, al lavoro fatto in questi giorni su questo sito. Ma è lo stesso tipo di soddisfazione: uno script che si era rotto in silenzio, un’alternativa scartata a ragion veduta, una sostituzione che funziona meglio dell’originale. Anche gestire una flotta virtuale di comandanti spaziali, alla fine, è una questione di infrastruttura da tenere in ordine.&lt;/p&gt;
</description>
        <pubDate>Tue, 11 Aug 2026 18:30:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/fuori-ufficio/flotta-stellare-quando-lapi-va-giu/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/fuori-ufficio/flotta-stellare-quando-lapi-va-giu/</guid>
        
        <category>elite dangerous</category>
        
        <category>games</category>
        
        <category>jekyll</category>
        
        <category>python</category>
        
        <category>api</category>
        
        <category>dati aperti</category>
        
        
        <category>Fuori dall&apos;Ufficio</category>
        
      </item>
    
      <item>
        <title>Regole operative per la gestione dell’Infrastruttura IT: Il Black Cat Agreement</title>
        <description>&lt;h2 id=&quot;regole-operative-per-la-gestione-dellinfrastruttura-it&quot;&gt;Regole operative per la gestione dell’Infrastruttura IT&lt;/h2&gt;

&lt;h3 id=&quot;agreements&quot;&gt;Agreements&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Non si effettuano attività sull’infrastruttura IT di produzione &lt;u&gt;il Venerdì, il Sabato e la Domenica/Festivi&lt;/u&gt; (ed anche il Giovedì dopo le 16:00)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Prima di effettuare &lt;strong&gt;qualsiasi attività&lt;/strong&gt; sull’infrastruttura IT di test o produzione va inviata &lt;u&gt;una email a tutti i colleghi&lt;/u&gt; per informare dell’intervento, &lt;strong&gt;con almeno 24h di anticipo&lt;/strong&gt;.&lt;br /&gt;
Il preavviso di 24h si può ridurre al minimo indispensabile in caso di attività da effettuare per risolvere problemi urgenti (la comunicazione va comunque inviata sempre).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Nel caso l’attività abbia un impatto sugli utenti, &lt;strong&gt;il preavviso sale ad almeno 72h&lt;/strong&gt; per la comunicazione, che va inviata anche a tutti gli utenti coinvolti.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Per ciascuna attività va inviata a tutti i soggetti coinvolti (interni ed esterni) una email di inizio attività ed una email di fine attività.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Riunioni interne di allineamento che coinvolgono tutto il gruppo:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Almeno &lt;strong&gt;una a settimana&lt;/strong&gt; il Venerdì mattina.&lt;/li&gt;
      &lt;li&gt;Non più di &lt;strong&gt;3 a settimana&lt;/strong&gt;.&lt;/li&gt;
      &lt;li&gt;Durata MAX di ciascuna riunione &lt;strong&gt;90 minuti&lt;/strong&gt;&lt;br /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;La gestione dei progetti prevede:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;La condivisione degli obiettivi e delle scadenze con la Direzione IT e con il gruppo di progetto, che prevede la &lt;strong&gt;presenza fissa dei responsabili delle varie BU interessate&lt;/strong&gt;.&lt;/li&gt;
      &lt;li&gt;L’approvazione del progetto da parte della Direzione IT, prima di iniziare a lavorare sul progetto stesso.&lt;/li&gt;
      &lt;li&gt;La redazione della documentazione di progetto &lt;strong&gt;prima di iniziare il progetto&lt;/strong&gt;.&lt;/li&gt;
      &lt;li&gt;Che gli eventuali acquisti vanno &lt;strong&gt;sempre correlati&lt;/strong&gt; ad un progetto approvato&lt;br /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Le decisioni prese in riunione possono essere modificate &lt;strong&gt;solo a seguito di una nuova riunione&lt;/strong&gt; che allinei tutti i partecipanti.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Thu, 26 Jan 2023 10:00:00 +0100</pubDate>
        <link>https://cristiancastellari.it/it/blog/itil/black-cat-agreement/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/itil/black-cat-agreement/</guid>
        
        <category>it</category>
        
        <category>service management</category>
        
        <category>gestione</category>
        
        <category>itil</category>
        
        
        <category>Filosofia di Lavoro</category>
        
        <category>Fuori dall&apos;Ufficio</category>
        
      </item>
    
      <item>
        <title>Distribuire un aggiornamento di build di Windows 10 via Kace</title>
        <description>&lt;p&gt;Gli aggiornamenti di build di Windows 10 non sono &lt;em&gt;patch&lt;/em&gt; tradizionali, &lt;em&gt;rollup&lt;/em&gt; o &lt;em&gt;service pack&lt;/em&gt;. Dal punto di vista della distribuzione, sono progettati e si comportano come un aggiornamento del sistema operativo &lt;em&gt;“in-place”&lt;/em&gt;, cioè senza toccare alcun dato od impostazione preesistente, rimuovere la versione di SO precedente e senza salvare nessun dato. Pertanto richiedono maggiore pianificazione e test rispetto alle patch tradizionali, oltre a maggiori risorse (capacità del disco per server e client, larghezza di banda, tempo di installazione, ecc.).&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;b&gt;NOTA: questa guida utilizza la versione 1903 di Ottobre 2019 come esempio di build nei passaggi seguenti&lt;/b&gt;&lt;/small&gt;&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#prima-fase-creare-il-pacchetto-di-installazione&quot; id=&quot;markdown-toc-prima-fase-creare-il-pacchetto-di-installazione&quot;&gt;Prima fase: Creare il pacchetto di installazione&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#otteniamo-una-iso-ufficiale-di-windows-10&quot; id=&quot;markdown-toc-otteniamo-una-iso-ufficiale-di-windows-10&quot;&gt;Otteniamo una ISO ufficiale di Windows 10&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#montare-la-iso-e-creare-un-file-zip&quot; id=&quot;markdown-toc-montare-la-iso-e-creare-un-file-zip&quot;&gt;Montare la ISO e creare un file .zip&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#seconda-fase-upload-del-pacchetto-su-kace-sma&quot; id=&quot;markdown-toc-seconda-fase-upload-del-pacchetto-su-kace-sma&quot;&gt;Seconda fase: Upload del pacchetto su KACE SMA&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#verifica-dellattivazione-di-samba&quot; id=&quot;markdown-toc-verifica-dellattivazione-di-samba&quot;&gt;Verifica dell’attivazione di Samba&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#caricamento-dellarchivio-nella-clientdrop&quot; id=&quot;markdown-toc-caricamento-dellarchivio-nella-clientdrop&quot;&gt;Caricamento dell’archivio nella clientdrop&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#terza-fase-mappare-il-pacchetto-di-installazione-allinterno-dellinventario&quot; id=&quot;markdown-toc-terza-fase-mappare-il-pacchetto-di-installazione-allinterno-dellinventario&quot;&gt;Terza Fase: Mappare il pacchetto di installazione all’interno dell’Inventario&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#scenario-operativo&quot; id=&quot;markdown-toc-scenario-operativo&quot;&gt;Scenario Operativo&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#creazione-di-un-custom-software&quot; id=&quot;markdown-toc-creazione-di-un-custom-software&quot;&gt;Creazione di un Custom Software&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#quarta-fase-creazione-e-deploy-del-processo-di-aggiornamento&quot; id=&quot;markdown-toc-quarta-fase-creazione-e-deploy-del-processo-di-aggiornamento&quot;&gt;Quarta Fase: Creazione e deploy del processo di aggiornamento&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#creazione-della-managed-install&quot; id=&quot;markdown-toc-creazione-della-managed-install&quot;&gt;Creazione della Managed Install&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#deploy&quot; id=&quot;markdown-toc-deploy&quot;&gt;Deploy&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;prima-fase-creare-il-pacchetto-di-installazione&quot;&gt;Prima fase: Creare il pacchetto di installazione&lt;/h2&gt;

&lt;p&gt;Gli aggiornamenti di build devono essere ottenuti direttamente da Microsoft. Sono distribuiti in formato ISO e devono essere estratti e ripacchettizzati per la distribuzione tramite prodotti di terze parti come KACE Systems Management Appliance.&lt;/p&gt;

&lt;h3 id=&quot;otteniamo-una-iso-ufficiale-di-windows-10&quot;&gt;Otteniamo una ISO ufficiale di Windows 10&lt;/h3&gt;

&lt;p&gt;Per ottenere la ISO di Windows 10 useremo &lt;strong&gt;uno quasiasi&lt;/strong&gt; di questi &lt;strong&gt;tre metodi&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Scaricare la ISO dalla propria &lt;strong&gt;MSDN Library&lt;/strong&gt; (richiede una licenza MSDN attiva), assicurandosi di ottenere la versione appropriata per il proprio scopo (es: Home, Professional, eccetera)&lt;/li&gt;
  &lt;li&gt;Usare il &lt;strong&gt;Media Creation Tool&lt;/strong&gt; di Microsoft, scaricandolo da questo indirizzo: &lt;a href=&quot;https://www.microsoft.com/en-us/software-download/windows10&quot;&gt;https://www.microsoft.com/en-us/software-download/windows10&lt;/a&gt;
    &lt;ol&gt;
      &lt;li&gt;Una volta scaricato, esegui il Tool&lt;/li&gt;
      &lt;li&gt;Quando ti viene richiesto cosa vuoi fare, scegli “&lt;strong&gt;Crea supporti di installazione (unità flash USB, DVD o file ISO) per un altro PC&lt;/strong&gt;” e clicca &lt;strong&gt;Avanti&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Verifica che le opzioni raccomandate (linuga, versione ed architettura) siano corrette in base alle tue necessità e prosegui&lt;/li&gt;
      &lt;li&gt;In “Scegli il supporto da usare” seleziona &lt;strong&gt;File ISO&lt;/strong&gt; e clicca &lt;strong&gt;Avanti&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Scegli un nome per il file (esempio: &lt;strong&gt;Windows10_1903_Italiano_x64.iso&lt;/strong&gt;) e prosegui. Inizierà il download della ISO configurata così come hai deciso nelle opzioni precedenti&lt;/li&gt;
      &lt;li&gt;Clicca &lt;strong&gt;Fine&lt;/strong&gt; al completamento per chiudere il wizard&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Metodo Alternativo: Scaricare la ISO direttamente da &lt;a href=&quot;https://www.microsoft.com/en-us/software-download/windows10ISO&quot;&gt;https://www.microsoft.com/en-us/software-download/windows10ISO&lt;/a&gt;, visitando la URL con un computer &lt;strong&gt;non Windows&lt;/strong&gt; (OS X o Linux)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;montare-la-iso-e-creare-un-file-zip&quot;&gt;Montare la ISO e creare un file .zip&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;In Windows 10, la ISO può essere montata facendo clic con il pulsante destro del mouse sul file e scegliendo “&lt;strong&gt;Monta&lt;/strong&gt;” o semplicemente facendo doppio clic sul file ISO.&lt;/li&gt;
  &lt;li&gt;Assicurati di avere 7-Zip installato. 7-Zip è una condizione essenziale, quindi se non lo hai procuratelo da &lt;a href=&quot;https://www.7-zip.org/a/7z1604.exe&quot;&gt;qui&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Una volta montata, &lt;strong&gt;seleziona tutti i file all’interno della ISO&lt;/strong&gt; (non la directory / unità, ma i file al suo interno), quindi fai clic con il pulsante destro del mouse e scegli “&lt;strong&gt;7-Zip &amp;gt; Aggiungi all’archivio…&lt;/strong&gt;”
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_AddToArchive.png&quot; alt=&quot;Screenshot 1 - 7-Zip &amp;gt; Add to Archive...&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Nota:&lt;/strong&gt; visto che la ISO è ovviamente read-only, scegli un percorso di salvataggio dello zip esterno, da qualche parte sul tuo hard disk&lt;/li&gt;
  &lt;li&gt;Al termine della compressione, il file zip verrà creato nella posizione selezionata con un nome generato automaticamente (esempio: setup.zip). Rinomina in modo appropriato (esempio: &lt;strong&gt;Windows10_1903_Italiano_x64.zip&lt;/strong&gt;). Questo file verrà utilizzato nei seguenti passaggi&lt;/li&gt;
  &lt;li&gt;Smonta la ISO (fai clic con il pulsante destro del mouse sull’unità ed espelli) e fanne ciò che preferisci. Per questa guida non ne avremo più bisogno&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;seconda-fase-upload-del-pacchetto-su-kace-sma&quot;&gt;Seconda fase: Upload del pacchetto su KACE SMA&lt;/h2&gt;

&lt;p&gt;Per via delle limitazioni sulla massima dimensione uploadabile di un file via interfaccia web, il pacchetto zip deve essere caricato su Kace tramite la &lt;strong&gt;condivisione samba &lt;em&gt;clientdrop&lt;/em&gt;&lt;/strong&gt;. La dimensione massima del file per il caricamento tramite l’interfaccia utente Web di SMA è infatti di 2GB (versione 8.0 e precedenti) o 4 GB (versione 8.1 e successive), mentre gli aggiornamenti di build tendono ad essere leggermente troppo grandi rispetto a questi limiti. Il metodo Samba evita del tutto il limite di upload e, opinione personale, è anche più veloce e pratico.&lt;/p&gt;

&lt;h3 id=&quot;verifica-dellattivazione-di-samba&quot;&gt;Verifica dell’attivazione di Samba&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Se Samba non è abilitato, abilitalo (Samba può essere disabilitato dopo che il pacchetto è stato caricato e importato al passaggio 3). Le impostazioni di abilitazione / disabilitazione di Samba si trovano in &lt;strong&gt;Settings &amp;gt; Security Settings&lt;/strong&gt; all’interno dell’interfaccia di Amministrazione. Per abilitare Samba, verifica che sia abilitato il checkbox “&lt;strong&gt;Enable organization file shares&lt;/strong&gt;”
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_samba.png&quot; alt=&quot;Screenshot 2 - Samba Shares&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Assicurati anche che la condivisione sia abilitata anche a &lt;strong&gt;livello di organizzazione&lt;/strong&gt; e di conoscere le credenziali per la share &lt;strong&gt;clientdrop&lt;/strong&gt;. In caso contrario la password può essere reimpostata nella pagina &lt;strong&gt;Settings &amp;gt; General Settings&lt;/strong&gt; nell’interfaccia utente di amministrazione (specifica dell’organizzazione per sistemi multi-org)
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_samba_share.png&quot; alt=&quot;Screenshot 3 - Samba Shares&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;caricamento-dellarchivio-nella-clientdrop&quot;&gt;Caricamento dell’archivio nella clientdrop&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Collegati alla share &lt;strong&gt;clientdrop&lt;/strong&gt;. Puoi farlo facilmente aprendo un Esplora Risorse e digitando l’indirizzo UNC della tua appliance Kace (esempio: &lt;strong&gt;\\KBOX\clientdrop&lt;/strong&gt;, sostituendo &lt;em&gt;KBOX&lt;/em&gt; con il nome del tuo host SMA).
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_clientdrop.png&quot; alt=&quot;Screenshot 4 - KACE SMA clientdrop&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Trascina all’interno della &lt;strong&gt;clientdrop&lt;/strong&gt; il file .zip creato nella &lt;a href=&quot;#prima-fase-creare-il-pacchetto-di-installazione&quot;&gt;Fase Uno&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_clientdrop_copy.png&quot; alt=&quot;Screenshot 5 - KACE SMA clientdrop&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Concluso con successo il caricamento, passiamo allo Step 3&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;terza-fase-mappare-il-pacchetto-di-installazione-allinterno-dellinventario&quot;&gt;Terza Fase: Mappare il pacchetto di installazione all’interno dell’Inventario&lt;/h2&gt;

&lt;h3 id=&quot;scenario-operativo&quot;&gt;Scenario Operativo&lt;/h3&gt;

&lt;p&gt;Per ragioni di performance, andremo a creare un &lt;strong&gt;Custom Software Title&lt;/strong&gt; all’interno del &lt;strong&gt;Software Inventory&lt;/strong&gt;, tralasciando invece il &lt;strong&gt;Software Catalog&lt;/strong&gt;. Questa scelta è dovuta alla grande quantità di versioni e revisioni di Windows 10 all’interno del Catalog che andrebbero ad impattare fortemente sulle prestazioni del database di Kace ad ogni interrogazione dello stesso da parte della Managed Install che andremo a vedere verso la fine di questa guida.&lt;/p&gt;

&lt;p&gt;Creeremo anche una apposita &lt;strong&gt;regola di inventario personalizzato&lt;/strong&gt; (“&lt;em&gt;Custom Inventory Rule&lt;/em&gt;”), in modo da avere un controllo più preciso e granulare sul processo di aggiornamento attuale e futuro.&lt;/p&gt;

&lt;h3 id=&quot;creazione-di-un-custom-software&quot;&gt;Creazione di un Custom Software&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Nell’interfaccia di amministraizone, vai su &lt;strong&gt;Inventory &amp;gt; Software&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Dal menu &lt;strong&gt;Choose Action&lt;/strong&gt; seleziona &lt;strong&gt;New&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Compila i campi &lt;strong&gt;Name&lt;/strong&gt;, &lt;strong&gt;Version&lt;/strong&gt;, &lt;strong&gt;Publisher&lt;/strong&gt; e &lt;strong&gt;Notes&lt;/strong&gt; come preferisci. Ad esempio:
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/customsoftware.png&quot; alt=&quot;Screenshot 6 - Custom Inventory Title&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Nel campo &lt;strong&gt;Custom Inventory Rule&lt;/strong&gt; inseriamo una regola che andrà a verificare una precisa chiave di registro, situata in &lt;strong&gt;HKLM\Software\Microsoft\Windows NT\CurrentVersion&lt;/strong&gt;, per identificare la verisone della build.
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/cir1903.png&quot; alt=&quot;Screenshot 7 - Custom Inventory Rule&quot; /&gt;&lt;br /&gt;
Codice:
    &lt;div class=&quot;language-dosbatch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;RegistryValueEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;HKLM64&lt;/span&gt;\SOFTWARE\Microsoft\Windows &lt;span class=&quot;kd&quot;&gt;NT&lt;/span&gt;\CurrentVersion&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;ReleaseId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1903&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Associamo finalmente il nostro file zip, creato in precendenza e copiato nella condivisione clientdrop, al Custom Software. Per far ciò andremo a scegliere dal menu a tendina &lt;strong&gt;Upload and Associate Client Drop File&lt;/strong&gt; il file, salvando al termine.
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mediaupload.png&quot; alt=&quot;Screenshot 8 - Media Upload&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;quarta-fase-creazione-e-deploy-del-processo-di-aggiornamento&quot;&gt;Quarta Fase: Creazione e deploy del processo di aggiornamento&lt;/h2&gt;

&lt;p&gt;Ora che abbiamo tutti i pezzi del puzzle, dobbiamo solo unirli nel passaggio finale. Andremo a creare una &lt;strong&gt;Installazione Gestita&lt;/strong&gt; (&lt;em&gt;Managed Install&lt;/em&gt;) che avrà il compito di distrubire la nuova build di Windows 10 nei PC target.&lt;br /&gt;
Andremo a dettagliare i passaggi uno per uno ma chi avesse già esperienza con le Managed Install può saltare direttamente alla fine.&lt;/p&gt;

&lt;h3 id=&quot;creazione-della-managed-install&quot;&gt;Creazione della Managed Install&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Nel pannello di Amministrazione, vai in &lt;strong&gt;Distribution &amp;gt; Managed Installations&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Seleziona &lt;strong&gt;Choose Action &amp;gt; **New&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Digita un nome per la Managed Install, ad esempio “Windows 10 1903 Upgrade”&lt;/li&gt;
  &lt;li&gt;Imposta l’opzione di esecuzione desiderata in base ai tuoi requisiti. Se tieni il campo con valore “Disabled” la MI non verrà eseguita&lt;/li&gt;
  &lt;li&gt;In &lt;strong&gt;Inventory&lt;/strong&gt; scegli &lt;strong&gt;Software&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Nel menu a discesa &lt;strong&gt;Software&lt;/strong&gt;, scegli il software title che hai creato nello &lt;a href=&quot;#terza-fase-mappare-il-pacchetto-di-installazione-allinterno-dellinventario&quot;&gt;Step 3&lt;/a&gt; ed a cui hai associato lo zip&lt;/li&gt;
  &lt;li&gt;Verifica che sia selezionato &lt;strong&gt;Use associated file&lt;/strong&gt; e che il fle mostrato sia il tuo zip
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-general.png&quot; alt=&quot;Screenshot 9 - Managed Install - General Settings&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Se lo desideri (consigliato), seleziona &lt;strong&gt;Delete downloaded files&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Installation Options:&lt;/strong&gt;
    &lt;ol&gt;
      &lt;li&gt;Seleziona &lt;strong&gt;Override default installation&lt;/strong&gt; ed inserisci questa stringa&lt;br /&gt;
  Codice:
        &lt;div class=&quot;language-dosbatch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;kd&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.exe&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/auto &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;upgrade&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/DynamicUpdate disable&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/showoobe &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;Verifica che sia spuntato il selettore &lt;strong&gt;Don’t prepend msiexec.exe&lt;/strong&gt;&lt;br /&gt;
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-options.png&quot; alt=&quot;Screenshot 10 - Managed Install - Opzioni&quot; /&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Nota&lt;/strong&gt;: Puoi trovare una spiegazione esaustiva riguardo tutti i parametri di &lt;strong&gt;setup.exe&lt;/strong&gt; tu technet di Microsoft, qui: &lt;a href=&quot;https://blogs.technet.microsoft.com/home_is_where_i_lay_my_head/2015/09/14/windows-10-setup-command-line-switches/&quot;&gt;https://blogs.technet.microsoft.com/home_is_where_i_lay_my_head/2015/09/14/windows-10-setup-command-line-switches/&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;deploy&quot;&gt;Deploy&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Nella sezione &lt;strong&gt;Deploy&lt;/strong&gt;, assegna alla Managed Install le &lt;strong&gt;Labels&lt;/strong&gt; che ritieni più opportune (&lt;em&gt;è &lt;strong&gt;fortemente raccomandato&lt;/strong&gt; eseguire uno o più test preliminari su un numero ristretto di computer&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;Configura la sezione di &lt;strong&gt;Notifica&lt;/strong&gt;, inserendo un messaggio chiaro e che rispecchi le tue necessità
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-schedule.png&quot; alt=&quot;Screenshot 11 - Managed Install - Schedule&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Configura la &lt;strong&gt;Schedulazione&lt;/strong&gt;, anche qui in base alle tue specifiche (&lt;em&gt;Nota: non è consigliabile regolare la finestra di distribuzione, poiché le MIs vengono eseguite solo durante l’intervallo di inventario. Se la finestra è configurata in modo tale da non essere aperta abbastanza a lungo da consentire a tutti i sistemi di eseguire un intervallo di inventario, i sistemi interessati non riceveranno mai l’aggiornamento.&lt;/em&gt;)
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-deployment.png&quot; alt=&quot;Screenshot 12 - Managed Install - Schedule&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Premi &lt;strong&gt;Save&lt;/strong&gt; per completare il lavoro. Abbiamo finito, buon aggiornamento di build&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(ricorda: &lt;strong&gt;PROVA SEMPRE PRIMA DI DISTRIBUIRE!!!&lt;/strong&gt;)&lt;/p&gt;
</description>
        <pubDate>Mon, 16 Dec 2019 11:30:00 +0100</pubDate>
        <link>https://cristiancastellari.it/it/blog/kace/aggiornare-windows10-k1000/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/kace/aggiornare-windows10-k1000/</guid>
        
        <category>kace</category>
        
        <category>k1000</category>
        
        <category>script</category>
        
        <category>batch</category>
        
        <category>kscript</category>
        
        <category>windows</category>
        
        <category>windows 10</category>
        
        <category>upgrade</category>
        
        <category>aggiornamento</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>Deploying a Windows 10 feature update via Kace</title>
        <description>&lt;p&gt;Windows 10 feature updates aren’t traditional &lt;em&gt;patches&lt;/em&gt;, &lt;em&gt;rollups&lt;/em&gt; or &lt;em&gt;service packs&lt;/em&gt;. From a deployment standpoint, they’re designed and behave like an &lt;em&gt;“in-place”&lt;/em&gt; OS upgrade — meaning they don’t touch any existing data or settings, remove the previous OS version, or save any data. Because of this, they require more planning and testing than traditional patches, along with more resources (disk space on both server and clients, bandwidth, installation time, and so on).&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;b&gt;NOTE: this guide uses the October 2019 1903 build as an example throughout the following steps&lt;/b&gt;&lt;/small&gt;&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#first-step-create-the-installation-package&quot; id=&quot;markdown-toc-first-step-create-the-installation-package&quot;&gt;First step: Create the installation package&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#get-an-official-windows-10-iso&quot; id=&quot;markdown-toc-get-an-official-windows-10-iso&quot;&gt;Get an official Windows 10 ISO&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#mount-the-iso-and-create-a-zip-file&quot; id=&quot;markdown-toc-mount-the-iso-and-create-a-zip-file&quot;&gt;Mount the ISO and create a .zip file&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#second-step-upload-the-package-to-kace-sma&quot; id=&quot;markdown-toc-second-step-upload-the-package-to-kace-sma&quot;&gt;Second step: Upload the package to KACE SMA&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#check-that-samba-is-enabled&quot; id=&quot;markdown-toc-check-that-samba-is-enabled&quot;&gt;Check that Samba is enabled&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#upload-the-archive-to-clientdrop&quot; id=&quot;markdown-toc-upload-the-archive-to-clientdrop&quot;&gt;Upload the archive to clientdrop&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#third-step-map-the-installation-package-inside-inventory&quot; id=&quot;markdown-toc-third-step-map-the-installation-package-inside-inventory&quot;&gt;Third step: Map the installation package inside Inventory&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#operational-scenario&quot; id=&quot;markdown-toc-operational-scenario&quot;&gt;Operational scenario&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#create-a-custom-software-title&quot; id=&quot;markdown-toc-create-a-custom-software-title&quot;&gt;Create a Custom Software title&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#fourth-step-create-and-deploy-the-upgrade-process&quot; id=&quot;markdown-toc-fourth-step-create-and-deploy-the-upgrade-process&quot;&gt;Fourth step: Create and deploy the upgrade process&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#create-the-managed-install&quot; id=&quot;markdown-toc-create-the-managed-install&quot;&gt;Create the Managed Install&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#deploy&quot; id=&quot;markdown-toc-deploy&quot;&gt;Deploy&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;first-step-create-the-installation-package&quot;&gt;First step: Create the installation package&lt;/h2&gt;

&lt;p&gt;Feature updates must be obtained directly from Microsoft. They’re distributed as an ISO and need to be extracted and repackaged for deployment through third-party products like the KACE Systems Management Appliance.&lt;/p&gt;

&lt;h3 id=&quot;get-an-official-windows-10-iso&quot;&gt;Get an official Windows 10 ISO&lt;/h3&gt;

&lt;p&gt;To get the Windows 10 ISO we’ll use &lt;strong&gt;any one&lt;/strong&gt; of these &lt;strong&gt;three methods&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Download the ISO from your own &lt;strong&gt;MSDN Library&lt;/strong&gt; (requires an active MSDN license), making sure to grab the edition appropriate for your purpose (e.g. Home, Professional, etc.)&lt;/li&gt;
  &lt;li&gt;Use Microsoft’s &lt;strong&gt;Media Creation Tool&lt;/strong&gt;, downloadable from this address: &lt;a href=&quot;https://www.microsoft.com/en-us/software-download/windows10&quot;&gt;https://www.microsoft.com/en-us/software-download/windows10&lt;/a&gt;
    &lt;ol&gt;
      &lt;li&gt;Once downloaded, run the Tool&lt;/li&gt;
      &lt;li&gt;When asked what you want to do, choose “&lt;strong&gt;Create installation media (USB flash drive, DVD, or ISO file) for another PC&lt;/strong&gt;” and click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Verify that the recommended options (language, edition, and architecture) match your needs and proceed&lt;/li&gt;
      &lt;li&gt;On “Choose which media to use”, select &lt;strong&gt;ISO file&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Choose a name for the file (example: &lt;strong&gt;Windows10_1903_English_x64.iso&lt;/strong&gt;) and continue. The download will start for the ISO configured with the options you chose above&lt;/li&gt;
      &lt;li&gt;Click &lt;strong&gt;Finish&lt;/strong&gt; once it completes to close the wizard&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Alternative method: download the ISO directly from &lt;a href=&quot;https://www.microsoft.com/en-us/software-download/windows10ISO&quot;&gt;https://www.microsoft.com/en-us/software-download/windows10ISO&lt;/a&gt;, visiting the URL from a &lt;strong&gt;non-Windows&lt;/strong&gt; computer (macOS or Linux)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;mount-the-iso-and-create-a-zip-file&quot;&gt;Mount the ISO and create a .zip file&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;On Windows 10, the ISO can be mounted by right-clicking the file and choosing “&lt;strong&gt;Mount&lt;/strong&gt;”, or simply double-clicking the ISO file.&lt;/li&gt;
  &lt;li&gt;Make sure 7-Zip is installed. 7-Zip is a hard requirement, so if you don’t have it, grab it from &lt;a href=&quot;https://www.7-zip.org/a/7z1604.exe&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Once mounted, &lt;strong&gt;select all the files inside the ISO&lt;/strong&gt; (not the directory/drive itself, but the files inside it), then right-click and choose “&lt;strong&gt;7-Zip &amp;gt; Add to archive…&lt;/strong&gt;”
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_AddToArchive.png&quot; alt=&quot;Screenshot 1 - 7-Zip &amp;gt; Add to Archive...&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Note:&lt;/strong&gt; since the ISO is obviously read-only, choose an external save path for the zip, somewhere on your hard disk&lt;/li&gt;
  &lt;li&gt;Once compression finishes, the zip file will be created at the chosen location with an auto-generated name (example: setup.zip). Rename it appropriately (example: &lt;strong&gt;Windows10_1903_English_x64.zip&lt;/strong&gt;). This file will be used in the following steps&lt;/li&gt;
  &lt;li&gt;Unmount the ISO (right-click the drive and eject) and do whatever you like with it — we won’t need it again for this guide&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;second-step-upload-the-package-to-kace-sma&quot;&gt;Second step: Upload the package to KACE SMA&lt;/h2&gt;

&lt;p&gt;Because of the limit on the maximum uploadable file size through the web interface, the zip package needs to be uploaded to Kace via the &lt;strong&gt;clientdrop Samba share&lt;/strong&gt;. The maximum file size for upload through the SMA Web UI is in fact 2GB (version 8.0 and earlier) or 4GB (version 8.1 and later), while feature update packages tend to run slightly over these limits. The Samba method sidesteps the upload limit entirely and, in my opinion, is also faster and more convenient.&lt;/p&gt;

&lt;h3 id=&quot;check-that-samba-is-enabled&quot;&gt;Check that Samba is enabled&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;If Samba isn’t enabled, enable it (Samba can be disabled again once the package has been uploaded and imported in step 3). The Samba enable/disable settings live under &lt;strong&gt;Settings &amp;gt; Security Settings&lt;/strong&gt; in the Admin interface. To enable Samba, make sure the “&lt;strong&gt;Enable organization file shares&lt;/strong&gt;” checkbox is checked
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_samba.png&quot; alt=&quot;Screenshot 2 - Samba Shares&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Also make sure the share is enabled at the &lt;strong&gt;organization level&lt;/strong&gt; too, and that you know the credentials for the &lt;strong&gt;clientdrop&lt;/strong&gt; share. If not, the password can be reset from the &lt;strong&gt;Settings &amp;gt; General Settings&lt;/strong&gt; page in the Admin UI (organization-specific on multi-org systems)
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_samba_share.png&quot; alt=&quot;Screenshot 3 - Samba Shares&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;upload-the-archive-to-clientdrop&quot;&gt;Upload the archive to clientdrop&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Connect to the &lt;strong&gt;clientdrop&lt;/strong&gt; share. You can do this easily by opening File Explorer and typing the UNC address of your Kace appliance (example: &lt;strong&gt;\\KBOX\clientdrop&lt;/strong&gt;, replacing &lt;em&gt;KBOX&lt;/em&gt; with your SMA host’s name).
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_clientdrop.png&quot; alt=&quot;Screenshot 4 - KACE SMA clientdrop&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Drag the .zip file created in &lt;a href=&quot;#first-step-create-the-installation-package&quot;&gt;Step One&lt;/a&gt; into the &lt;strong&gt;clientdrop&lt;/strong&gt; share&lt;/li&gt;
  &lt;li&gt;&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/KB_1-547AMZ0_w10bu_clientdrop_copy.png&quot; alt=&quot;Screenshot 5 - KACE SMA clientdrop&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Once the upload completes successfully, move on to Step 3&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;third-step-map-the-installation-package-inside-inventory&quot;&gt;Third step: Map the installation package inside Inventory&lt;/h2&gt;

&lt;h3 id=&quot;operational-scenario&quot;&gt;Operational scenario&lt;/h3&gt;

&lt;p&gt;For performance reasons, we’ll create a &lt;strong&gt;Custom Software Title&lt;/strong&gt; inside the &lt;strong&gt;Software Inventory&lt;/strong&gt;, rather than using the &lt;strong&gt;Software Catalog&lt;/strong&gt;. This choice is due to the sheer number of Windows 10 versions and revisions in the Catalog, which would heavily impact Kace’s database performance on every query issued by the Managed Install we’ll set up toward the end of this guide.&lt;/p&gt;

&lt;p&gt;We’ll also create a dedicated &lt;strong&gt;Custom Inventory Rule&lt;/strong&gt;, to have more precise and granular control over the current and future upgrade process.&lt;/p&gt;

&lt;h3 id=&quot;create-a-custom-software-title&quot;&gt;Create a Custom Software title&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;In the Admin interface, go to &lt;strong&gt;Inventory &amp;gt; Software&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;From the &lt;strong&gt;Choose Action&lt;/strong&gt; menu, select &lt;strong&gt;New&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Fill in the &lt;strong&gt;Name&lt;/strong&gt;, &lt;strong&gt;Version&lt;/strong&gt;, &lt;strong&gt;Publisher&lt;/strong&gt; and &lt;strong&gt;Notes&lt;/strong&gt; fields as you prefer. For example:
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/customsoftware.png&quot; alt=&quot;Screenshot 6 - Custom Inventory Title&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;In the &lt;strong&gt;Custom Inventory Rule&lt;/strong&gt; field, enter a rule that checks a specific registry key, located at &lt;strong&gt;HKLM\Software\Microsoft\Windows NT\CurrentVersion&lt;/strong&gt;, to identify the build version.
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/cir1903.png&quot; alt=&quot;Screenshot 7 - Custom Inventory Rule&quot; /&gt;&lt;br /&gt;
Code:
    &lt;div class=&quot;language-dosbatch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;RegistryValueEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;HKLM64&lt;/span&gt;\SOFTWARE\Microsoft\Windows &lt;span class=&quot;kd&quot;&gt;NT&lt;/span&gt;\CurrentVersion&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;ReleaseId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1903&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Finally, associate our zip file — created earlier and copied to the clientdrop share — with the Custom Software. To do this, choose the file from the &lt;strong&gt;Upload and Associate Client Drop File&lt;/strong&gt; dropdown, then save.
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mediaupload.png&quot; alt=&quot;Screenshot 8 - Media Upload&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;fourth-step-create-and-deploy-the-upgrade-process&quot;&gt;Fourth step: Create and deploy the upgrade process&lt;/h2&gt;

&lt;p&gt;Now that we have all the pieces of the puzzle, we just need to put them together in this final step. We’ll create a &lt;strong&gt;Managed Install&lt;/strong&gt; whose job will be to deploy the new Windows 10 build to the target PCs.&lt;br /&gt;
We’ll go through each step one by one, but if you’re already familiar with Managed Installs, feel free to skip straight to the end.&lt;/p&gt;

&lt;h3 id=&quot;create-the-managed-install&quot;&gt;Create the Managed Install&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;In the Admin panel, go to &lt;strong&gt;Distribution &amp;gt; Managed Installations&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Choose Action &amp;gt; New&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Type a name for the Managed Install, for example “Windows 10 1903 Upgrade”&lt;/li&gt;
  &lt;li&gt;Set the desired run option based on your requirements. If you leave the field set to “Disabled” the MI won’t run&lt;/li&gt;
  &lt;li&gt;Under &lt;strong&gt;Inventory&lt;/strong&gt;, choose &lt;strong&gt;Software&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;In the &lt;strong&gt;Software&lt;/strong&gt; dropdown, choose the software title you created in &lt;a href=&quot;#third-step-map-the-installation-package-inside-inventory&quot;&gt;Step 3&lt;/a&gt; and associated the zip with&lt;/li&gt;
  &lt;li&gt;Verify that &lt;strong&gt;Use associated file&lt;/strong&gt; is selected and that the file shown is your zip
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-general.png&quot; alt=&quot;Screenshot 9 - Managed Install - General Settings&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;If you’d like (recommended), select &lt;strong&gt;Delete downloaded files&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Installation Options:&lt;/strong&gt;
    &lt;ol&gt;
      &lt;li&gt;Select &lt;strong&gt;Override default installation&lt;/strong&gt; and enter this string&lt;br /&gt;
  Code:
        &lt;div class=&quot;language-dosbatch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;kd&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.exe&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/auto &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;upgrade&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/DynamicUpdate disable&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/showoobe &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;Verify that the &lt;strong&gt;Don’t prepend msiexec.exe&lt;/strong&gt; checkbox is checked&lt;br /&gt;
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-options.png&quot; alt=&quot;Screenshot 10 - Managed Install - Options&quot; /&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can find an exhaustive explanation of all &lt;strong&gt;setup.exe&lt;/strong&gt; parameters on Microsoft’s technet, here: &lt;a href=&quot;https://blogs.technet.microsoft.com/home_is_where_i_lay_my_head/2015/09/14/windows-10-setup-command-line-switches/&quot;&gt;https://blogs.technet.microsoft.com/home_is_where_i_lay_my_head/2015/09/14/windows-10-setup-command-line-switches/&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;deploy&quot;&gt;Deploy&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;In the &lt;strong&gt;Deploy&lt;/strong&gt; section, assign the &lt;strong&gt;Labels&lt;/strong&gt; you find most appropriate to the Managed Install (&lt;em&gt;running one or more preliminary tests on a small number of computers is &lt;strong&gt;strongly recommended&lt;/strong&gt;&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;Configure the &lt;strong&gt;Notification&lt;/strong&gt; section, entering a clear message that fits your needs
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-schedule.png&quot; alt=&quot;Screenshot 11 - Managed Install - Schedule&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Configure the &lt;strong&gt;Schedule&lt;/strong&gt;, again based on your specific needs (&lt;em&gt;Note: adjusting the deployment window isn’t recommended, since MIs only run during the inventory interval. If the window is configured so that it isn’t open long enough for all systems to complete an inventory cycle, affected systems will never receive the update.&lt;/em&gt;)
&lt;img src=&quot;/static/assets/img/blog/kace/w10upgrade/mi-deployment.png&quot; alt=&quot;Screenshot 12 - Managed Install - Schedule&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt; to finish the job. That’s it, happy build upgrading!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(remember: &lt;strong&gt;ALWAYS TEST BEFORE DEPLOYING!!!&lt;/strong&gt;)&lt;/p&gt;
</description>
        <pubDate>Mon, 16 Dec 2019 11:30:00 +0100</pubDate>
        <link>https://cristiancastellari.it/en/blog/kace/windows10-feature-update-k1000/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/en/blog/kace/windows10-feature-update-k1000/</guid>
        
        <category>kace</category>
        
        <category>k1000</category>
        
        <category>script</category>
        
        <category>batch</category>
        
        <category>kscript</category>
        
        <category>windows</category>
        
        <category>windows 10</category>
        
        <category>upgrade</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>IPUI to IPEI</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Aggiornamento (agosto 2026):&lt;/strong&gt; lo strumento è stato ricostruito da zero — via Bootstrap e jQuery, dentro Jekyll 4, ora bilingue italiano/inglese. Il link qui sotto resta lo stesso di sempre; ho raccontato il dietro le quinte del redesign in &lt;a href=&quot;/it/blog/progetti-personali/il-redesign-di-ipui2ipei/&quot;&gt;un nuovo post&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Talvolta (prendiamo ad esempio le installazioni aziendali) diventa necessario registrare sull’impianto DECT un telefono di produttore diverso da quello del centralino, vuoi per mancanza di disponibilità del prodotto voluto, vuoi per pure e semplici ragioni di budget (un cordless &lt;strong&gt;Siemes&lt;/strong&gt; costa mediamente fra i 50 e i 70€, mentre un terminale ad esempio &lt;strong&gt;Spectralink&lt;/strong&gt; o &lt;strong&gt;Avaya&lt;/strong&gt; supera allegramente i 250€).&lt;/p&gt;

&lt;p&gt;L’operazione è comunque veloce ed indolore per ogni tipo di terminale che supporti ovviamente lo standard DECT/GAP in quanto, come detto, è sufficiente l’IPEI del telefono che intendiamo registrare ed il gioco è fatto.
Con Siemens, invece, ci servirà un passaggio in più, proprio per via della loro scelta di utilizzare IPUI, che ha una diversa notazione ed una diversa struttura e non viene quindi riconosciuto come IPEI valido.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-è-il-codice-ipei&quot; id=&quot;markdown-toc-cosa-è-il-codice-ipei&quot;&gt;Cosa è il codice IPEI?&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#come-convertire-il-codice-ipei-in-ipui&quot; id=&quot;markdown-toc-come-convertire-il-codice-ipei-in-ipui&quot;&gt;Come convertire il codice IPEI in IPUI?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ipui2ipei-strumento-di-conversione-da-ipui-a-ipei&quot; id=&quot;markdown-toc-ipui2ipei-strumento-di-conversione-da-ipui-a-ipei&quot;&gt;Ipui2Ipei: strumento di conversione da IPUI a IPEI&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#informazioni-aggiuntive&quot; id=&quot;markdown-toc-informazioni-aggiuntive&quot;&gt;Informazioni aggiuntive&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#come-trovare-il-codice-ipui-di-un-telefono-siemens&quot; id=&quot;markdown-toc-come-trovare-il-codice-ipui-di-un-telefono-siemens&quot;&gt;Come trovare il codice IPUI di un telefono SIEMENS?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#note-ulteriori&quot; id=&quot;markdown-toc-note-ulteriori&quot;&gt;Note ulteriori&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#riferimenti&quot; id=&quot;markdown-toc-riferimenti&quot;&gt;Riferimenti&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cosa-è-il-codice-ipei&quot;&gt;Cosa è il codice IPEI?&lt;/h2&gt;

&lt;p&gt;L’IPEI è un codice numerico di identificazione, simile al codice IMEI dei telefoni cellulari, dei telefoni DECT/GAP (i cosiddetti “telefoni cordless”).&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;IPEI è un codice numerico di 13 caratteri&lt;/li&gt;
  &lt;li&gt;IPUI è un codice alfanumerico di 10 caratteri&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;come-convertire-il-codice-ipei-in-ipui&quot;&gt;Come convertire il codice IPEI in IPUI?&lt;/h3&gt;

&lt;p&gt;La conversione consiste nella divisione di IPUI in due parti separate, con conseguente trasformazione dei due valori esadecimanli in decimali e qualche altro calcolo più o meno divertente. 
Oppure, molto più semplicemente, si può utilizzare un tool di conversione come quello indicato di seguito.&lt;/p&gt;

&lt;h2 id=&quot;ipui2ipei-strumento-di-conversione-da-ipui-a-ipei&quot;&gt;Ipui2Ipei: strumento di conversione da IPUI a IPEI&lt;/h2&gt;
&lt;p&gt;Al questo link è possibile trovare &lt;a href=&quot;https://cristiancastellari.it/ipui2ipei/&quot;&gt;ipui2ipei&lt;/a&gt;, un tool web che ho trovato su GitHub e che ho poi provveduto a modificare e migliorare (si spera) secondo le mie esigenze.&lt;/p&gt;

&lt;p&gt;Il suo utilizzo è semplicissimo: è sufficiente scrivere nel primo campo il codice IPUI estratto dal telefono Siemens seguendo la procedura indicata successivamente e premere il bottone Converti.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/ipuitoipei/conversione.jpg&quot; alt=&quot;Conversione da IPUI a IPEI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Un attimo dopo avrete il risultato desiderato, con tanto di spiegazione sul tipo di calcoli che sono stati eseguiti.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/ipuitoipei/calcolo.jpg&quot; alt=&quot;Calcolo IPUI a IPEI&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;informazioni-aggiuntive&quot;&gt;Informazioni aggiuntive&lt;/h2&gt;

&lt;h3 id=&quot;come-trovare-il-codice-ipui-di-un-telefono-siemens&quot;&gt;Come trovare il codice IPUI di un telefono SIEMENS?&lt;/h3&gt;

&lt;p&gt;col portatile in stand-by, bisogna premere la seguente sequenza di tasti:
  &lt;strong&gt;Tasto MENU’&lt;/strong&gt; seguito da [*] [#] 0 6 [#]&lt;/p&gt;

&lt;h3 id=&quot;note-ulteriori&quot;&gt;Note ulteriori&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Il codice IPEI è composto da 13 caratteri: i primi 12 sono il codice vero e proprio, mentre il tredicesimo è un carattere di controllo.&lt;/li&gt;
  &lt;li&gt;A seconda del produttore del vostro centralino o server DECT dovrete inserirli tutti o solo i primi 12.&lt;/li&gt;
  &lt;li&gt;La conversione potrebbe portare ad un risultato con &lt;em&gt;quattordici&lt;/em&gt; numeri in quanto il codice di controllo può essere un qualsiasi numero compreso fra 1 e 10. In questo caso trasfromate il 10 in “asterisco” [*]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;riferimenti&quot;&gt;Riferimenti&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://cristiancastellari.it/ipui2ipei/&quot;&gt;Strumento di conversione Ipui 2 Ipei&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/skyflash/ipui2ipei&quot;&gt;Pagina GitHub del progetto&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/StrongeLeeroy/ipui2ipei&quot;&gt;Progetto originale&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 24 Jan 2019 12:30:00 +0100</pubDate>
        <link>https://cristiancastellari.it/it/blog/tools/ipui-to-ipei/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/tools/ipui-to-ipei/</guid>
        
        <category>telefonia</category>
        
        <category>cordless</category>
        
        <category>siemens</category>
        
        <category>dect</category>
        
        <category>ipui</category>
        
        <category>ipei</category>
        
        <category>conversione</category>
        
        
        <category>Strumenti</category>
        
      </item>
    
      <item>
        <title>Come importare un certificato CA su PC Windows con Kace 1000</title>
        <description>&lt;p&gt;Per installare certificati in maniera trasparente possiamo usare diversi metodi, il più ovvio e diffuso dei quali è senza dubbio una &lt;em&gt;GPO&lt;/em&gt;. Ma non è sempre detto che la GPO sia la soluzione più veloce (ad esempio: chi deve eseguire il deploy dei certificati non ha i permessi per lavorare sulle GPO, o il certificato deve essere installato su un numero esiguo di PC per cui chi dovrebbe fare la GPO non considera questa un’attività primaria da svolgere ed i tempi rischiano di allungarsi)&lt;/p&gt;

&lt;p&gt;Andiamo quindi a vedere come &lt;strong&gt;distribuire un certificato CA self-signed tramite KACE 1000&lt;/strong&gt;. In seguito andremo anche ad installare sui client, con la stessa procedura, un secondo certificato intermedio, validato a sua volta dalla CA.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#lutility-certutilexe&quot; id=&quot;markdown-toc-lutility-certutilexe&quot;&gt;L’utility certutil.exe&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#definizioni&quot; id=&quot;markdown-toc-definizioni&quot;&gt;Definizioni&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#importazione-della-ca-tramite-certutilexe&quot; id=&quot;markdown-toc-importazione-della-ca-tramite-certutilexe&quot;&gt;Importazione della CA tramite certutil.exe&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#importazione-del-certificato-intermedio&quot; id=&quot;markdown-toc-importazione-del-certificato-intermedio&quot;&gt;Importazione del certificato intermedio&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#lo-script-kscript-su-kace&quot; id=&quot;markdown-toc-lo-script-kscript-su-kace&quot;&gt;Lo script (kscript) su Kace&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#riassunto-delle-attività&quot; id=&quot;markdown-toc-riassunto-delle-attività&quot;&gt;Riassunto delle Attività&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#realizzazione&quot; id=&quot;markdown-toc-realizzazione&quot;&gt;Realizzazione&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#schedulazione&quot; id=&quot;markdown-toc-schedulazione&quot;&gt;Schedulazione&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;lutility-certutilexe&quot;&gt;L’utility certutil.exe&lt;/h1&gt;
&lt;p&gt;certutil.exe è una utility, presente in tutti i sistemi Windows, che ci permette di fare tante cose con i certificati.
Possiamo convertirli in caso il formato che abbiamo non sia quello che ci serve, possiamo importarli ed esportarli (sia in locale che su Active Directory), verificare la validità delle coppie di chiavi privata/pubblica eccetera.&lt;/p&gt;

&lt;p&gt;Si usa da riga di comando e per la sintassi completa vi rimando &lt;a href=&quot;https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil&quot; target=&quot;_blank&quot;&gt;all’apposita pagina&lt;/a&gt; sulla Knowledge Base Micorosoft&lt;/p&gt;

&lt;h2 id=&quot;definizioni&quot;&gt;Definizioni&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Entrambi i nostri certificati sono già in formato .cer&lt;/li&gt;
  &lt;li&gt;Il certificato CA si chiama NGIRootCA01&lt;/li&gt;
  &lt;li&gt;Il certificato intermedio si chiama NGISSLCA01.cer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;importazione-della-ca-tramite-certutilexe&quot;&gt;Importazione della CA tramite certutil.exe&lt;/h2&gt;

&lt;p&gt;Il comando di importazione, che possiamo eseguire da un qualsiasi prompt amministrativo, è il seguente&lt;/p&gt;

&lt;div class=&quot;language-batch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/wait &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;certutil&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-addstore &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;NGIRootCA01&lt;/span&gt;.cer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il parametro -addstore deifnisce il datastore in cui andremo ad installare il certificato. &lt;em&gt;Root&lt;/em&gt; corrisponde a “&lt;strong&gt;Autorità di certificazione radice attendibili&lt;/strong&gt;”&lt;/p&gt;

&lt;h2 id=&quot;importazione-del-certificato-intermedio&quot;&gt;Importazione del certificato intermedio&lt;/h2&gt;

&lt;p&gt;Il certificato intermedio (che chiameremo SSLCA01.cer) a cui si faceva riferimento poco fa si importerà nella stessa maniera, cambiando solamente il datastore di riferimento:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-msdos&quot;&gt;start /wait certutil -addstore CA NGISSLCA01.cer
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;lo-script-kscript-su-kace&quot;&gt;Lo script (kscript) su Kace&lt;/h1&gt;

&lt;p&gt;Andiamo ora a riportare tutto su Kace 1000, creando uno script apposito.&lt;/p&gt;

&lt;h2 id=&quot;riassunto-delle-attività&quot;&gt;Riassunto delle Attività&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Impostazione dei parametri di base&lt;/li&gt;
  &lt;li&gt;Importazione dei due certificati come dipendenze&lt;/li&gt;
  &lt;li&gt;Creazione di un batch file&lt;/li&gt;
  &lt;li&gt;Schedulazione&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;realizzazione&quot;&gt;Realizzazione&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Portiamoci in &lt;em&gt;Scripting / Scripts&lt;/em&gt; e creiamo un novo kscript (“&lt;em&gt;New&lt;/em&gt;”)&lt;/li&gt;
  &lt;li&gt;Una volta definito un nome, portiamoci nella sezione &lt;em&gt;Operating Systems&lt;/em&gt; e clicchiamo su &lt;strong&gt;Manage Operating Systems&lt;/strong&gt;, andando a selezionare tutti i SO Windows (e, visto che andremo a creare un batch file, &lt;strong&gt;solo&lt;/strong&gt; quelli Windows) di nostro interesse&lt;br /&gt;
  &lt;img src=&quot;/static/assets/img/blog/kace/cassl/oss.png&quot; alt=&quot;kscript Manage Operating Systems&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Verifichiamo che &lt;em&gt;Windows Run As&lt;/em&gt; sia impostato a &lt;strong&gt;Local System&lt;/strong&gt;&lt;br /&gt;
  &lt;img src=&quot;/static/assets/img/blog/kace/cassl/runas.png&quot; alt=&quot;kscript Run As&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Nella sezione &lt;strong&gt;Dependencies&lt;/strong&gt; facciamo click su &lt;strong&gt;+New Dependency…&lt;/strong&gt; e carichiamo:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Il certificato Root&lt;/li&gt;
      &lt;li&gt;Il certificato Intermedio&lt;br /&gt;
&lt;img src=&quot;/static/assets/img/blog/kace/cassl/certificati.png&quot; alt=&quot;kscript Certificati&quot; /&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Facciamo ora click su &lt;strong&gt;+Task…&lt;/strong&gt; nella sezione sottostante ed impostiamo i seguenti passi:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;&lt;strong&gt;Verify&lt;/strong&gt; that the file “C:\Tools\certificato.ok” exists (questo ci servirà per fare un check sull’applicazione del certificato, che andremo a schedulare e qundi ad eseguire ad intervalli regolari; se il file &lt;em&gt;certificato.ok&lt;/em&gt; esiste allora lo script si interrompe). Per riferimento sul percorso scelto, potete vedere &lt;a href=&quot;/it/blog/kace/k1000-report-pc-with-smartcard-reader/&quot; target=&quot;_blank&quot;&gt;questo mio precedente articolo&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;On Success&lt;/strong&gt;: nulla&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Remediation&lt;/strong&gt;: Run the batch file “&lt;em&gt;Install_CA_Root_Script&lt;/em&gt;” with params “”&lt;br /&gt;
 &lt;img src=&quot;/static/assets/img/blog/kace/cassl/script.png&quot; alt=&quot;kscript Script&quot; /&gt;&lt;br /&gt;
 Codice:
        &lt;div class=&quot;language-dosbatch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; @echo &lt;span class=&quot;na&quot;&gt;off&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/wait &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;certutil&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-addstore &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;NGIRootCA01&lt;/span&gt;.cer
 &lt;span class=&quot;nb&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/wait &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;certutil&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-addstore &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;CA&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;NGISSLCA01&lt;/span&gt;.cer
 &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;On Remediaton Success&lt;/strong&gt; Launch a program…&lt;br /&gt;
 &lt;img src=&quot;/static/assets/img/blog/kace/cassl/remediation.png&quot; alt=&quot;kscript Remediation&quot; /&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Clicchiamo su &lt;strong&gt;Save&lt;/strong&gt; per completare la creazione del kscript&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;schedulazione&quot;&gt;Schedulazione&lt;/h2&gt;

&lt;p&gt;In caso volessimo schedulare lo script, sarà sufficiente, una volta tornati in editing, andare ad impostare la finestra desiderata.
Ad esempio, nel nostro caso lo script verrà eseguito sui devices desiderati ogni tre ore secondo questa logica:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Lo script viene eseguito la prima volta&lt;/li&gt;
  &lt;li&gt;Al termine, lascia un file “&lt;em&gt;certificato.ok&lt;/em&gt;”, di dimensione 0 bytes, in C:\Tools\&lt;/li&gt;
  &lt;li&gt;Dalla volta seguente (ogni tre ore…) se trova il file “certificato.ok” allora si interrompe e non fa null’altro&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/cassl/schedule.png&quot; alt=&quot;kscript Schedule&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 17 Sep 2018 11:30:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/kace/distribuzione-certificati_ca-kace1000/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/kace/distribuzione-certificati_ca-kace1000/</guid>
        
        <category>kace</category>
        
        <category>k1000</category>
        
        <category>script</category>
        
        <category>batch</category>
        
        <category>kscript</category>
        
        <category>ssl</category>
        
        <category>ca root</category>
        
        <category>certificate</category>
        
        <category>security</category>
        
        <category>sicurezza</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>How to import a CA certificate on Windows PCs with Kace 1000</title>
        <description>&lt;p&gt;There are several methods for installing certificates transparently, the most obvious and common of which is undoubtedly a &lt;em&gt;GPO&lt;/em&gt;. But a GPO isn’t always the fastest solution (for example: whoever needs to deploy the certificates might not have permissions to work on GPOs, or the certificate might only need to be installed on a handful of PCs, so whoever should create the GPO doesn’t treat it as a priority and the timeline risks stretching out)&lt;/p&gt;

&lt;p&gt;So let’s see how to &lt;strong&gt;deploy a self-signed CA certificate via KACE 1000&lt;/strong&gt;. Afterwards, using the same procedure, we’ll also install a second, intermediate certificate on the clients, itself validated by the CA.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#the-certutilexe-utility&quot; id=&quot;markdown-toc-the-certutilexe-utility&quot;&gt;The certutil.exe utility&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#definitions&quot; id=&quot;markdown-toc-definitions&quot;&gt;Definitions&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#importing-the-ca-via-certutilexe&quot; id=&quot;markdown-toc-importing-the-ca-via-certutilexe&quot;&gt;Importing the CA via certutil.exe&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#importing-the-intermediate-certificate&quot; id=&quot;markdown-toc-importing-the-intermediate-certificate&quot;&gt;Importing the intermediate certificate&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-kscript-on-kace&quot; id=&quot;markdown-toc-the-kscript-on-kace&quot;&gt;The kscript on Kace&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#task-summary&quot; id=&quot;markdown-toc-task-summary&quot;&gt;Task summary&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#building-it&quot; id=&quot;markdown-toc-building-it&quot;&gt;Building it&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#scheduling&quot; id=&quot;markdown-toc-scheduling&quot;&gt;Scheduling&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;the-certutilexe-utility&quot;&gt;The certutil.exe utility&lt;/h1&gt;
&lt;p&gt;certutil.exe is a utility, present on every Windows system, that lets us do a lot of things with certificates.
We can convert them if the format we have isn’t the one we need, import and export them (both locally and to Active Directory), verify the validity of private/public key pairs, and so on.&lt;/p&gt;

&lt;p&gt;It’s used from the command line, and for the full syntax I’ll point you to the &lt;a href=&quot;https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil&quot; target=&quot;_blank&quot;&gt;dedicated page&lt;/a&gt; on the Microsoft Knowledge Base&lt;/p&gt;

&lt;h2 id=&quot;definitions&quot;&gt;Definitions&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Both of our certificates are already in .cer format&lt;/li&gt;
  &lt;li&gt;The CA certificate is called NGIRootCA01&lt;/li&gt;
  &lt;li&gt;The intermediate certificate is called NGISSLCA01.cer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;importing-the-ca-via-certutilexe&quot;&gt;Importing the CA via certutil.exe&lt;/h2&gt;

&lt;p&gt;The import command, which we can run from any administrative prompt, is the following&lt;/p&gt;

&lt;div class=&quot;language-batch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/wait &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;certutil&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-addstore &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;NGIRootCA01&lt;/span&gt;.cer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The -addstore parameter defines the datastore where we’ll install the certificate. &lt;em&gt;Root&lt;/em&gt; corresponds to “&lt;strong&gt;Trusted Root Certification Authorities&lt;/strong&gt;”&lt;/p&gt;

&lt;h2 id=&quot;importing-the-intermediate-certificate&quot;&gt;Importing the intermediate certificate&lt;/h2&gt;

&lt;p&gt;The intermediate certificate (which we’ll call SSLCA01.cer) referenced earlier is imported the same way, only changing the target datastore:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-msdos&quot;&gt;start /wait certutil -addstore CA NGISSLCA01.cer
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;the-kscript-on-kace&quot;&gt;The kscript on Kace&lt;/h1&gt;

&lt;p&gt;Now let’s bring all of this over to Kace 1000, by creating a dedicated script.&lt;/p&gt;

&lt;h2 id=&quot;task-summary&quot;&gt;Task summary&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Setting up the basic parameters&lt;/li&gt;
  &lt;li&gt;Importing the two certificates as dependencies&lt;/li&gt;
  &lt;li&gt;Creating a batch file&lt;/li&gt;
  &lt;li&gt;Scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;building-it&quot;&gt;Building it&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Go to &lt;em&gt;Scripting / Scripts&lt;/em&gt; and create a new kscript (“&lt;em&gt;New&lt;/em&gt;”)&lt;/li&gt;
  &lt;li&gt;Once you’ve given it a name, go to the &lt;em&gt;Operating Systems&lt;/em&gt; section and click &lt;strong&gt;Manage Operating Systems&lt;/strong&gt;, selecting all the Windows OSs (and, since we’re going to create a batch file, &lt;strong&gt;only&lt;/strong&gt; the Windows ones) you’re interested in&lt;br /&gt;
  &lt;img src=&quot;/static/assets/img/blog/kace/cassl/oss.png&quot; alt=&quot;kscript Manage Operating Systems&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Verify that &lt;em&gt;Windows Run As&lt;/em&gt; is set to &lt;strong&gt;Local System&lt;/strong&gt;&lt;br /&gt;
  &lt;img src=&quot;/static/assets/img/blog/kace/cassl/runas.png&quot; alt=&quot;kscript Run As&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In the &lt;strong&gt;Dependencies&lt;/strong&gt; section, click &lt;strong&gt;+New Dependency…&lt;/strong&gt; and upload:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;The Root certificate&lt;/li&gt;
      &lt;li&gt;The Intermediate certificate&lt;br /&gt;
&lt;img src=&quot;/static/assets/img/blog/kace/cassl/certificati.png&quot; alt=&quot;kscript Certificates&quot; /&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now click &lt;strong&gt;+Task…&lt;/strong&gt; in the section below and set up the following steps:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;&lt;strong&gt;Verify&lt;/strong&gt; that the file “C:\Tools\certificato.ok” exists (this will let us check whether the certificate has already been applied — since we’ll schedule this script to run at regular intervals, if the &lt;em&gt;certificato.ok&lt;/em&gt; file exists the script stops right there). For reference on why this path was chosen, see &lt;a href=&quot;/en/blog/kace/k1000-report-pc-with-smartcard-reader/&quot; target=&quot;_blank&quot;&gt;this earlier article of mine&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;On Success&lt;/strong&gt;: nothing&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Remediation&lt;/strong&gt;: Run the batch file “&lt;em&gt;Install_CA_Root_Script&lt;/em&gt;” with params “”&lt;br /&gt;
 &lt;img src=&quot;/static/assets/img/blog/kace/cassl/script.png&quot; alt=&quot;kscript Script&quot; /&gt;&lt;br /&gt;
 Code:
        &lt;div class=&quot;language-dosbatch highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; @echo &lt;span class=&quot;na&quot;&gt;off&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/wait &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;certutil&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-addstore &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;NGIRootCA01&lt;/span&gt;.cer
 &lt;span class=&quot;nb&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;/wait &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;certutil&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-addstore &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;CA&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;NGISSLCA01&lt;/span&gt;.cer
 &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;On Remediation Success&lt;/strong&gt; Launch a program…&lt;br /&gt;
 &lt;img src=&quot;/static/assets/img/blog/kace/cassl/remediation.png&quot; alt=&quot;kscript Remediation&quot; /&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt; to finish creating the kscript&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;scheduling&quot;&gt;Scheduling&lt;/h2&gt;

&lt;p&gt;If we want to schedule the script, once back in editing mode it’s enough to set the desired window.
For example, in our case the script will run on the target devices every three hours, following this logic:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The script runs for the first time&lt;/li&gt;
  &lt;li&gt;When it finishes, it leaves a “&lt;em&gt;certificato.ok&lt;/em&gt;” file, 0 bytes in size, in C:\Tools\&lt;/li&gt;
  &lt;li&gt;From the next run onward (every three hours…) if it finds the “certificato.ok” file, it stops and does nothing else&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/cassl/schedule.png&quot; alt=&quot;kscript Schedule&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 17 Sep 2018 11:30:00 +0200</pubDate>
        <link>https://cristiancastellari.it/en/blog/kace/deploying-ca-certificates-kace1000/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/en/blog/kace/deploying-ca-certificates-kace1000/</guid>
        
        <category>kace</category>
        
        <category>k1000</category>
        
        <category>script</category>
        
        <category>batch</category>
        
        <category>kscript</category>
        
        <category>ssl</category>
        
        <category>ca root</category>
        
        <category>certificate</category>
        
        <category>security</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>Blocco delle applicazioni con K1000</title>
        <description>&lt;p&gt;In questa veloce guida si dà per scontato che il lettore abbia già una buona conoscenza dell’ambiente KACE 1000 (KACE SMA) e del funzionamento delle etichette (&lt;em&gt;Labels&lt;/em&gt;)&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#il-blocco-delle-applicazioni-tramite-k1000-passo-per-passo&quot; id=&quot;markdown-toc-il-blocco-delle-applicazioni-tramite-k1000-passo-per-passo&quot;&gt;Il blocco delle applicazioni tramite K1000, passo per passo&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#la-label&quot; id=&quot;markdown-toc-la-label&quot;&gt;La Label&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#significato-della-label&quot; id=&quot;markdown-toc-significato-della-label&quot;&gt;Significato della Label&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#i-software-da-bloccare&quot; id=&quot;markdown-toc-i-software-da-bloccare&quot;&gt;I software da bloccare&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#i-pc-e-la-loro-label&quot; id=&quot;markdown-toc-i-pc-e-la-loro-label&quot;&gt;I PC e la loro Label&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#linventario&quot; id=&quot;markdown-toc-linventario&quot;&gt;L’Inventario&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#il-risultato-finale&quot; id=&quot;markdown-toc-il-risultato-finale&quot;&gt;Il risultato finale&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;il-blocco-delle-applicazioni-tramite-k1000-passo-per-passo&quot;&gt;Il blocco delle applicazioni tramite K1000, passo per passo&lt;/h1&gt;

&lt;p&gt;Il blocco delle applicazioni su Kace funziona con una doppia logica:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Una &lt;a href=&quot;#la-label&quot;&gt;&lt;em&gt;Label&lt;/em&gt;&lt;/a&gt;, in grado di controllare e bloccare i software&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Uno o più Software marcati come &lt;a href=&quot;#i-software-da-bloccare&quot;&gt;&lt;em&gt;Not Allowed&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Per funzionare, è necessario che &lt;strong&gt;entrambe&lt;/strong&gt; le condizioni siano &lt;strong&gt;vere&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;la-label&quot;&gt;La Label&lt;/h2&gt;

&lt;p&gt;Kace mette a disposizione una Label già preconfezionata, secondo i parametri di default.&lt;/p&gt;

&lt;p&gt;Useremo quindi questa per illustrare il funzionamento del blocco applicativo.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/K1000_appcontrol.png&quot; alt=&quot;Screenshot 1 - Default Application Control Label&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Portarsi in &lt;em&gt;Home&lt;/em&gt; ed in seguito in &lt;em&gt;Label Management&lt;/em&gt; e selezionare &lt;strong&gt;Labels&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;La nostra etichetta di default si chiama &lt;strong&gt;“ApplicationControlDevices”&lt;/strong&gt; ed è così impostata:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/appcontrol_label.png&quot; alt=&quot;Screenshot 2 - Appcontrol Label Details&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;significato-della-label&quot;&gt;Significato della Label&lt;/h3&gt;

&lt;p&gt;Illustriamo velocemente il significato dei vari flag:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Device Inventory: La Label si applica all’Inventario dei devices (quindi: ai PC)
    &lt;ul&gt;
      &lt;li&gt;Allow Application Control:    La Label è in grado di controllare l’avvio delle applicazioni&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Resources (Processes, Services, Startup Items):   La Label si può applicare anche a processi e servizi&lt;/li&gt;
  &lt;li&gt;Catalog:  La Label si applica al Catalogo Software (&lt;strong&gt;essenziale&lt;/strong&gt; per il buon funzionamento del meccanismo di blocco!)&lt;/li&gt;
  &lt;li&gt;Software: Come sopra, ma per i software non normalizzati dal catalogo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;i-software-da-bloccare&quot;&gt;I software da bloccare&lt;/h2&gt;

&lt;p&gt;Portiamoci nel &lt;strong&gt;Software Catalog&lt;/strong&gt; (&lt;em&gt;Inventory&lt;/em&gt; -&amp;gt; &lt;em&gt;Software Catalog&lt;/em&gt;), che racchiude e “normalizza” tutti i software rilevati e riconosciuti, all’interno di un unico nome. Più chiaramente, avremo ad esempio “iTunes 12.x”, che contiene tutte le revisioni di iTunes 12 (es: 12.1, 12.4, 12.74 e via dicendo).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lavorare nel Catalogo Software ci consente di bloccare o monitorare un software indipendentemente dagli aggiornamenti che riceve.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/appcontrol_software_catalog.png&quot; alt=&quot;Screenshot 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Una volta trovato il nostro software (prendiamo sempre ad esempio iTunes 12.x), selezioniamo il checkbox e da menu “Choose Action” scegliamo “&lt;strong&gt;Mark Not Allowed&lt;/strong&gt;”&lt;/p&gt;

&lt;p&gt;In alternativa, è possibile spuntare “&lt;strong&gt;Not Allowed&lt;/strong&gt;” all’interno della pagina del software di nostro interesse. L’autosave è automatico nel momento del cambio di selezione del checkbox.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/software_catalog_detail.png&quot; alt=&quot;Screenshot 4&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;i-pc-e-la-loro-label&quot;&gt;I PC e la loro Label&lt;/h2&gt;

&lt;p&gt;Ora che abbiamo capito come funzionano sia la Label (che si applica ai PC) che il marker di blocco (relativo ai software), possiamo metterli insieme ed ottenere l’effetto desiderato.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTA BENE:&lt;/strong&gt; &lt;em&gt;il meccanismo di Kace bloccherà l’apertura dei programmi SOLO sui PC a cui abbiamo applicato la Label “ApplicationControlDevice” e solo a loro&lt;/em&gt;. In questa fase sperimentale l’etichetta NON VIENE applicata di default a tutti i device in inventario e l’operazione dovrà quindi essere fatta manualmente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un pc che ha un software bloccato ma che non appartiene alla Label continuerà ad eseguire quel software come se nulla fosse, senza alcun problema.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 Otteniamo il nome od i nomi dei PC che ci interessa inserire nella Label. Per fare ciò abbiamo diverse strade, tutte corrette:
    &lt;ol&gt;
      &lt;li&gt;Lo conosciamo e lo troviamo direttamente in Inventario&lt;/li&gt;
      &lt;li&gt;Dal Catalogo Software clicchiamo sul numero nella colonna “Installed”, che ci porterà alla lista di tutti i PC con quel software&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/appcontrol_locked.png&quot; alt=&quot;Screenshot 5&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2 Selezioniamo il checkbox a fianco del o dei PC di nostro interesse e da menu “&lt;strong&gt;Choose Action&lt;/strong&gt;” facciamo click su “&lt;strong&gt;Apply Labels&lt;/strong&gt;”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/apply_label.png&quot; alt=&quot;Screenshot 6&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;3 Scegliamo quindi la Label “&lt;strong&gt;ApplicationControlDevices&lt;/strong&gt;” e trasciniamola nella parte destra della finestra, cliccando poi su “&lt;strong&gt;Apply Labels&lt;/strong&gt;”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/labels_list.png&quot; alt=&quot;Screenshot 7&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;linventario&quot;&gt;L’Inventario&lt;/h2&gt;

&lt;p&gt;Abbiamo fatto le regole. Abbiamo applicato le Labels. Manca ancora qualcosa?&lt;/p&gt;

&lt;p&gt;Uno dei punti di forza dell’agente Kace è quello di lavorare sostanzialmente offline: si collega una volta al server, riceve tutte le regole, le informazioni e gli script e poi continua a lavorare, da solo, senza più bisogno di dialogare col server. E continua a fare tutto ciò che gli è stato detto di fare fino a prova contraria. Questo meccanismo si chiama “Inventario” e nel nostro caso viene eseguito una volta ogni 8 ore (almeno) da ogni singolo computer inventariato.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Le nuove regole di blocco, quindi, verranno applicate al PC al suo prossimo contatto (cioè al prossimo inventario)&lt;/em&gt;. Le potremo forzare manualmente (click sul box del device e poi “&lt;strong&gt;Force Inventory&lt;/strong&gt;”) ma, in condizioni normali, il frutto delle nostre regole si vedrà solo dopo alcune ore (ed un riavvio del PC) e non in tempo reale.&lt;/p&gt;

&lt;h1 id=&quot;il-risultato-finale&quot;&gt;Il risultato finale&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/final_result.png&quot; alt=&quot;Screenshot 8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In seguito alla creazione della lista di blocco (alcuni software in questa immagine sono solo puramente a scopo di esempio e non si intende veramente bloccarli), all’assegnazione della Label al PC ed al propagarsi delle regole tramite inventario, una volta che l’utente tenterà di aprire il software riceverà un alert nell’angolo basso a destra dello schermo, come questo:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/messagebox.png&quot; alt=&quot;Screenshot 9&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 11 Jul 2018 00:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/kace/blocco-applicativo-k1000/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/kace/blocco-applicativo-k1000/</guid>
        
        <category>kace</category>
        
        <category>script</category>
        
        <category>vbs</category>
        
        <category>smartcard</category>
        
        <category>security</category>
        
        <category>encryption</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>Application Control with K1000</title>
        <description>&lt;p&gt;This quick guide assumes the reader already has a good understanding of the KACE 1000 (KACE SMA) environment and of how Labels work&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#application-control-with-k1000-step-by-step&quot; id=&quot;markdown-toc-application-control-with-k1000-step-by-step&quot;&gt;Application control with K1000, step by step&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-label&quot; id=&quot;markdown-toc-the-label&quot;&gt;The Label&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#what-the-label-means&quot; id=&quot;markdown-toc-what-the-label-means&quot;&gt;What the Label means&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-software-to-block&quot; id=&quot;markdown-toc-the-software-to-block&quot;&gt;The software to block&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-pcs-and-their-label&quot; id=&quot;markdown-toc-the-pcs-and-their-label&quot;&gt;The PCs and their Label&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#inventory&quot; id=&quot;markdown-toc-inventory&quot;&gt;Inventory&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-end-result&quot; id=&quot;markdown-toc-the-end-result&quot;&gt;The end result&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;application-control-with-k1000-step-by-step&quot;&gt;Application control with K1000, step by step&lt;/h1&gt;

&lt;p&gt;Application control on Kace works with a two-part logic:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;A &lt;a href=&quot;#the-label&quot;&gt;&lt;em&gt;Label&lt;/em&gt;&lt;/a&gt;, able to control and block software&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;One or more Software titles marked as &lt;a href=&quot;#the-software-to-block&quot;&gt;&lt;em&gt;Not Allowed&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For it to work, &lt;strong&gt;both&lt;/strong&gt; conditions must be &lt;strong&gt;true&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-label&quot;&gt;The Label&lt;/h2&gt;

&lt;p&gt;Kace ships with a Label already pre-configured, using default parameters.&lt;/p&gt;

&lt;p&gt;We’ll use this one to walk through how application control works.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/K1000_appcontrol.png&quot; alt=&quot;Screenshot 1 - Default Application Control Label&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Go to &lt;em&gt;Home&lt;/em&gt;, then to &lt;em&gt;Label Management&lt;/em&gt;, and select &lt;strong&gt;Labels&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Our default label is called &lt;strong&gt;“ApplicationControlDevices”&lt;/strong&gt; and is configured like this:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/appcontrol_label.png&quot; alt=&quot;Screenshot 2 - Appcontrol Label Details&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;what-the-label-means&quot;&gt;What the Label means&lt;/h3&gt;

&lt;p&gt;Let’s quickly go over what each flag means:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Device Inventory: The Label applies to the device Inventory (i.e. PCs)
    &lt;ul&gt;
      &lt;li&gt;Allow Application Control: The Label is able to control the launching of applications&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Resources (Processes, Services, Startup Items): The Label can also be applied to processes and services&lt;/li&gt;
  &lt;li&gt;Catalog: The Label applies to the Software Catalog (&lt;strong&gt;essential&lt;/strong&gt; for the blocking mechanism to work properly!)&lt;/li&gt;
  &lt;li&gt;Software: Same as above, but for software not normalized by the catalog&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-software-to-block&quot;&gt;The software to block&lt;/h2&gt;

&lt;p&gt;Let’s head to the &lt;strong&gt;Software Catalog&lt;/strong&gt; (&lt;em&gt;Inventory&lt;/em&gt; -&amp;gt; &lt;em&gt;Software Catalog&lt;/em&gt;), which groups and “normalizes” all detected and recognized software under a single name. To be clearer, we’ll have for example “iTunes 12.x”, which covers every revision of iTunes 12 (e.g. 12.1, 12.4, 12.74, and so on).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Working from the Software Catalog lets us block or monitor a piece of software regardless of the updates it receives.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/appcontrol_software_catalog.png&quot; alt=&quot;Screenshot 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once we’ve found our software (let’s keep using iTunes 12.x as an example), select the checkbox and from the “Choose Action” menu pick “&lt;strong&gt;Mark Not Allowed&lt;/strong&gt;”&lt;/p&gt;

&lt;p&gt;Alternatively, you can check “&lt;strong&gt;Not Allowed&lt;/strong&gt;” directly on the software’s own detail page. Autosave kicks in as soon as you change the checkbox selection.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/software_catalog_detail.png&quot; alt=&quot;Screenshot 4&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-pcs-and-their-label&quot;&gt;The PCs and their Label&lt;/h2&gt;

&lt;p&gt;Now that we understand how both the Label (which applies to PCs) and the block marker (which applies to software) work, we can put them together to get the effect we want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; &lt;em&gt;Kace’s mechanism will only block programs from opening on PCs that have the “ApplicationControlDevice” Label applied — and only on those&lt;/em&gt;. During this experimental phase, the label is NOT applied by default to every inventoried device, so this has to be done manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A PC that has a blocked piece of software but doesn’t belong to the Label will keep running that software as if nothing happened, with no issues at all.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 Get the name(s) of the PC(s) you want to add to the Label. There are several ways to do this, all equally valid:
    &lt;ol&gt;
      &lt;li&gt;You already know it and can find it directly in Inventory&lt;/li&gt;
      &lt;li&gt;From the Software Catalog, click the number in the “Installed” column, which will take you to the list of all PCs with that software&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/appcontrol_locked.png&quot; alt=&quot;Screenshot 5&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2 Check the checkbox next to the PC(s) you’re interested in and from the “&lt;strong&gt;Choose Action&lt;/strong&gt;” menu click “&lt;strong&gt;Apply Labels&lt;/strong&gt;”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/apply_label.png&quot; alt=&quot;Screenshot 6&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;3 Choose the “&lt;strong&gt;ApplicationControlDevices&lt;/strong&gt;” Label and drag it into the right-hand side of the window, then click “&lt;strong&gt;Apply Labels&lt;/strong&gt;”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/labels_list.png&quot; alt=&quot;Screenshot 7&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;inventory&quot;&gt;Inventory&lt;/h2&gt;

&lt;p&gt;We’ve made the rules. We’ve applied the Labels. Is anything still missing?&lt;/p&gt;

&lt;p&gt;One of the strengths of the Kace agent is that it works essentially offline: it connects to the server once, receives all the rules, information, and scripts, and then keeps working on its own, without needing to talk to the server again. And it keeps doing everything it’s been told to do until told otherwise. This mechanism is called “Inventory”, and in our case it runs once every 8 hours (at minimum) on every single inventoried computer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;New blocking rules, then, will be applied to the PC the next time it checks in (i.e. at its next inventory)&lt;/em&gt;. We can force this manually (click the device’s box, then “&lt;strong&gt;Force Inventory&lt;/strong&gt;”), but under normal conditions the results of our rules will only show up after a few hours (and a PC restart), not in real time.&lt;/p&gt;

&lt;h1 id=&quot;the-end-result&quot;&gt;The end result&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/final_result.png&quot; alt=&quot;Screenshot 8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once the blocklist has been created (some software titles in this image are purely for illustration purposes and aren’t actually meant to be blocked), the Label assigned to the PC, and the rules propagated through inventory, once the user tries to open the software they’ll get an alert in the bottom-right corner of the screen, like this one:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/appcontrol/messagebox.png&quot; alt=&quot;Screenshot 9&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 11 Jul 2018 00:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/en/blog/kace/application-control-k1000/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/en/blog/kace/application-control-k1000/</guid>
        
        <category>kace</category>
        
        <category>script</category>
        
        <category>vbs</category>
        
        <category>smartcard</category>
        
        <category>security</category>
        
        <category>encryption</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>Come bloccare l&apos;hotlinking delle immagini con .htaccess</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#lhotlinking-danneggia-il-tuo-sito&quot; id=&quot;markdown-toc-lhotlinking-danneggia-il-tuo-sito&quot;&gt;L’hotlinking danneggia il tuo sito&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#come-proteggersi-da-hotlinking&quot; id=&quot;markdown-toc-come-proteggersi-da-hotlinking&quot;&gt;Come proteggersi da hotlinking&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#spiegazione&quot; id=&quot;markdown-toc-spiegazione&quot;&gt;Spiegazione&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#concedere-laccesso-a-bing-yahoo-e-facebook&quot; id=&quot;markdown-toc-concedere-laccesso-a-bing-yahoo-e-facebook&quot;&gt;Concedere l’accesso a Bing, Yahoo e Facebook&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#conclusioni&quot; id=&quot;markdown-toc-conclusioni&quot;&gt;Conclusioni&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;lhotlinking-danneggia-il-tuo-sito&quot;&gt;L’hotlinking danneggia il tuo sito&lt;/h1&gt;

&lt;p&gt;Facciamo un esempio. L’immagine che vedete qui sotto è stata caricata direttamente su un mio sito (ospite originale di questo articolo). Il server web non fa altro che andare a caricare una risorsa interna (l’immagine stessa) e mostrarla al visitatore, senza alcun carico elaborativo extra.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/hotlinking-htaccess/kenny-mccormick.jpg&quot; alt=&quot;Kenny&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Il codice dell’immagine è il seguente:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/wp-content/uploads/2012/05/kenny-mccormick.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;border=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;320&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;240&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Un sito esterno, invece di effettuare il download dell’immagine e di caricarla sul proprio server (cosa che, oltretutto, è sempre consigliabile in quanto migliora moltissimo i tempi di caricamento e le prestazioni generali del sito), ottiene il collegamento diretto alla risorsa e lo include all’interno di una propria pagina.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://www.skyflash.it/wp-content/uploads/2012/05/kenny-mccormick.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;border=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;320&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;240&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il risultato sarà che il secondo sito, eterno al nostro, sfrutta l’immagine senza però sacrificare il proprio spazio web, mentre il nostro sito, su cui risiede l’immagine, sarà sottoposto ad un carico eccessivo (ed assolutamente non richiesto nè tanto meno gradito) sia per quanto riguarda la propria banda passante che per le risorse del server.&lt;/p&gt;

&lt;h1 id=&quot;come-proteggersi-da-hotlinking&quot;&gt;Come proteggersi da hotlinking&lt;/h1&gt;

&lt;p&gt;Sono sufficienti alcune righe da aggiungere al proprio, immancabile, file &lt;em&gt;.htaccess&lt;/em&gt; e, se volete, un’immagine preparata per lo scopo, che servirà per sbattere in faccia al webmaster antipatico la dura verità :smile:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ATTENZIONE:&lt;/strong&gt; la modifica di .htaccess, se fatta da persone non esperte e non particolarmente attente, può generare molti problemi al vostro sito WordPress, fino a &lt;strong&gt;renderlo inaccessibile&lt;/strong&gt;. Assicuratevi quindi di fare &lt;strong&gt;SEMPRE una copia di sicurezza&lt;/strong&gt; di .htaccess prima di cominciare le modifiche&lt;/p&gt;

&lt;p&gt;Aprite in un qualsaisi editor di testo il vostro file &lt;em&gt;.htaccess&lt;/em&gt; ed in coda (se usate WordPress, dovrebbero esserci solo le direttive di default) e &lt;strong&gt;dopo ### END WordPress ###&lt;/strong&gt; aggiungete il seguente codice:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;### Blocca l&apos;hotlinking - Webmaster cattivo pussa via&lt;/span&gt;
RewriteEngineon
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^$
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^http(s)?://(www\.)?skyflash.it[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^http(s)?://(www\.)?google.com[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_USER_AGENT}!googlebot[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{REQUEST_URI}!^hotlink.jpg$
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt;\.(jpg|jpeg|png|gif|js|swf)$hotlink.jpg[NC,R,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;spiegazione&quot;&gt;Spiegazione&lt;/h2&gt;

&lt;p&gt;Alla &lt;strong&gt;terza riga&lt;/strong&gt; trovate:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Questa istruzione consente ai referrer che non forniscono informazioni di accedere al contenuto. Ciò è reso necessario in quanto molti visitatori (soprattutto quelli che si connettono dall’ufficio e si trovano dietro a proxy) sono protetti da un firewall che non rilascia informazioni al server per ragioni di sicurezza.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Righe 4, 5 e 6:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^http(s)?://(www\.)?skyflash.it[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^http(s)?://(www\.)?google.com[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_USER_AGENT}!googlebot[NC]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;specificano i domini che invece possono effettuare l’hotlinking e quindi visualizzare le immagini presenti sul server. Quindi scriveremo il nostro dominio internet e, in caso vogliate farvi indicizzare le immagini, Google. Tutti gli altri sono fuori.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Riga 7:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{REQUEST_URI}!^hotlink.jpg$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Se proprio qualcuno vuole rubare qualche immagine, allora lasciamogli prendere quella che abbiamo preparato per segnalare gli hotlinker. Questa riga ci assicura che l’immagine sia sempre visualizzabile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Riga 8:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt;\.(jpg|jpeg|png|gif|js|swf)$hotlink.jpg[NC,R,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Specifica il formato dei file protetti dall’hotlinking (non solo le immagini ne sono soggette) e sostituisce le richieste con un’immagine statica.&lt;/p&gt;

&lt;p&gt;Ovviamente potrete specificare l’immagine che desiderate, ma assicuratevi che questa non sia protetta (vedi riga precedente) oppure, ancora meglio, caricatela su una risorsa esterna come &lt;a href=&quot;http://db.tt/wgafhzXi&quot;&gt;Dropbox&lt;/a&gt;, o il risultato sarà un loop infinito!&lt;/p&gt;

&lt;h3 id=&quot;concedere-laccesso-a-bing-yahoo-e-facebook&quot;&gt;Concedere l’accesso a Bing, Yahoo e Facebook&lt;/h3&gt;

&lt;p&gt;Esattamente come abbiamo fatto per Google, aggiungiamo al nostro &lt;em&gt;.htaccess&lt;/em&gt; le seguenti direttive:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!msn.[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!yahoo.[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^https?://(www\.)?facebook\.com[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_USER_AGENT}!facebookplatform[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_USER_AGENT}!msnbot[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;${HTTP_USER_AGENT}!slurp[NC]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusioni&quot;&gt;Conclusioni&lt;/h2&gt;

&lt;p&gt;Il risultato finale sarà quindi il seguente:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;### Blocca l&apos;hotlinking - Webmaster cattivo pussa via&lt;/span&gt;

RewriteEngineon
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^$
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^http(s)?://(www\.)?skyflash.it[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^http(s)?://(www\.)?google.com[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!msn.[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!yahoo.[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_REFERER}!^https?://(www\.)?facebook\.com[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_USER_AGENT}!googlebot[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_USER_AGENT}!facebookplatform[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{HTTP_USER_AGENT}!msnbot[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;${HTTP_USER_AGENT}!slurp[NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt;%{REQUEST_URI}!^hotlink.jpg$
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt;\.(jpg|jpeg|png|gif|js|swf)$hotlink.jpg[NC,R,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Chi dovesse linkare ad una risorsa con estensione jpg, jpeg, png, gif, js o swf sul sito così “trattato”, come unico risultato avrà la visualizzazione della seguente immagine&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/hotlinking-htaccess/hotlink.jpg&quot; alt=&quot;Hotlink-Monkey&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Se guardando le statistiche del vostro spazio web avete notato un consumo eccessivo di banda in uscita o se state rilevando una grande quantità di accessi esterni alle stesse immagini, probabilmente siete hotlinkati da qualcuno. Ora sapete come difendervi.&lt;/p&gt;

&lt;p&gt;Risorse esterne:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20200202030302/http://www.istanto.net:80/stop-bandwith-thief-using-rewrite-rules-on-htaccess-file.html&quot;&gt;Stop bandwith thief using rewrite rules on .htaccess file&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.hongkiat.com/blog/smarter-way-to-prevent-image-hotlinking-with-htaccess/&quot;&gt;Smarter Way To Prevent Image Hotlinking With .Htaccess&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gabrieleromanato.com/2012/03/impedire-hotlinking-immagini-htaccess/&quot;&gt;Impedire l’hotlinking delle immagini con il file .htaccess&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20121013044719/http://vincenzodibiaggio.it:80/2008/07/11/proteggiamoci-dallhotlinking-con-un-semplice-file-htaccess/&quot;&gt;Proteggiamoci dall’hotlinking con un semplice file&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.thewebsqueeze.com/web-design-tutorials/stop-image-hotlinking-with-htaccess.html&quot;&gt;Stop image hotlinking with .htaccess&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 13 Apr 2018 00:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/infrastruttura/bloccare-hotlinking-immagini/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/infrastruttura/bloccare-hotlinking-immagini/</guid>
        
        <category>htaccess</category>
        
        <category>apache</category>
        
        <category>wordpress</category>
        
        
        <category>Infrastruttura &amp; Sistemi</category>
        
      </item>
    
      <item>
        <title>La traduzione in italiano di Morrowind, Tribunal e Bloodmoon</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;Il 2002, ludicamente parlando, verrà ricordato per l’uscita dell’attesissimo terzo capitolo della saga The Elder Scrolls.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#la-traduzione-di-morrowind-un-po-di-storia&quot; id=&quot;markdown-toc-la-traduzione-di-morrowind-un-po-di-storia&quot;&gt;La Traduzione di Morrowind: Un po’ di storia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#si-comincia-a-tradurre&quot; id=&quot;markdown-toc-si-comincia-a-tradurre&quot;&gt;Si comincia a tradurre!&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#i-primi-problemi&quot; id=&quot;markdown-toc-i-primi-problemi&quot;&gt;I primi problemi…&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#la-quest-aggiuntiva&quot; id=&quot;markdown-toc-la-quest-aggiuntiva&quot;&gt;La quest aggiuntiva&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#la-traduzione-in-italiano-di-morrowind---download&quot; id=&quot;markdown-toc-la-traduzione-in-italiano-di-morrowind---download&quot;&gt;La traduzione in Italiano di Morrowind - Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#morrowind-itp---staff&quot; id=&quot;markdown-toc-morrowind-itp---staff&quot;&gt;Morrowind ITP - Staff&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#responsabile-del-gruppo&quot; id=&quot;markdown-toc-responsabile-del-gruppo&quot;&gt;Responsabile del gruppo&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#responsabili-della-traduzione&quot; id=&quot;markdown-toc-responsabili-della-traduzione&quot;&gt;Responsabili della traduzione&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#responsabile-tecnico&quot; id=&quot;markdown-toc-responsabile-tecnico&quot;&gt;Responsabile tecnico&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#traduttori-itp---morrowind&quot; id=&quot;markdown-toc-traduttori-itp---morrowind&quot;&gt;Traduttori ITP - Morrowind&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#traduttori-itp---tribunal&quot; id=&quot;markdown-toc-traduttori-itp---tribunal&quot;&gt;Traduttori ITP - Tribunal&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;la-traduzione-di-morrowind-un-po-di-storia&quot;&gt;La Traduzione di Morrowind: Un po’ di storia&lt;/h2&gt;

&lt;p&gt;Atteso dai fan di mezzo mondo, il lancio Italiano di &lt;strong&gt;The Elder Scrolls 3: Morrowind&lt;/strong&gt; destò molte polemiche. Il gioco infatti, inspiegabilmente, non venne tradotto in nessuna sua parte. Ricordo ancora le parole di un P.R. Ubisoft, il quale dichiarò, più o meno, che la traduzione non era stata fatta per motivi di tempo. In pratica, ci disse che non lo avevano tradotto perchè altrimenti ci sarebbe stato un pesante ritardo nella distribuzione, e che quindi avevano preso questa decisione per fare un favore a noi giocatori. Che avesse forse dinanzi un roseo futuro da umorista? O forse da politico? Fatto sta che Morrowind venne distribuito in rigorosa lingua Inglese. Il CRPG che avrebbe rivoluzionato il genere, il gioco con la più grande mole di testi mai realizzato, sarebbe stato di difficile comprensione per molti giocatori Italiani.&lt;/p&gt;

&lt;p&gt;Qualche mese prima della sua uscita nel nostro mercato, io ed altri appassionati ci eravamo già procurati la versione USA del gioco (per quanto mi riguarda, fu la Collector’s Edition), scoprendo così le fantastiche potenzialità dell’editor integrato. Tramite il TES Construction Set era possibile realizzare praticamente di tutto; si potevano creare nuovi oggetti, nuove quest, nuovi NPC. Si poteva addirittura realizzare un plugin che cambiasse ogni singola parola del gioco. Come? Era possibile cambiare i testi a video? Allora era anche possibile tradurre Morrowind!&lt;/p&gt;

&lt;h2 id=&quot;si-comincia-a-tradurre&quot;&gt;Si comincia a tradurre!&lt;/h2&gt;

&lt;p&gt;Quasi per gioco, iniziai a tradurre il tutorial e le prime fasi di gioco. In quegli anni uno dei miei contatti era Lello Sarti, responsabile della testata online &lt;a href=&quot;http://forum.multiplayer.it/showthread.php?209954-RPGPLAYER&quot;&gt;RPGPlayer.it&lt;/a&gt;, poi assorbita da Multiplayer. Lello era a sua volta in contatti con alcuni membri dell’ITP, a cui riferì che io avevo iniziato a tradurre il gioco. Il prossimo passo fu ovvio: stava per prendere il via la traduzione in Italiano più lunga e complessa mai realizzata a livello amatoriale!&lt;/p&gt;

&lt;p&gt;Proprio su questo stesso dominio istituimmo una mailing list con cui tenerci in contatto, e creai anche un piccolo forum (utilizzando lo storico &lt;a href=&quot;http://forum.snitz.com/&quot;&gt;Snitz! forum&lt;/a&gt;, un ormai obsoleto forum in .ASP). Nel frattempo il gioco era già uscito in Italia e, come c’era da immaginarsi, venne accolto con molta timidezza, proprio a causa della sua gigantesca mole di testo in Inglese. Non ci volle molto perchè la voce secondo cui l’ITP, già noto per il meraviglioso lavoro svolto con &lt;a href=&quot;/it/blog/fuori-ufficio/la-traduzione-in-italiano-di-planescape-torment-del-team-itp/&quot;&gt;Planescape: Torment&lt;/a&gt;, si era messo al lavoro su Morrowind si diffondesse. Nel giro di alcuni mesi si parlava di noi su Usenet, sui forum, e persino su qualche rivista.&lt;/p&gt;

&lt;h2 id=&quot;i-primi-problemi&quot;&gt;I primi problemi…&lt;/h2&gt;

&lt;p&gt;L’opera di traduzione stava procedendo bene, quando ci rendemmo conto di una cosa: la particolare struttura del Journal – il Diario – di Morrowind era basata sul linguaggio HTML, ed alcuni termini erano veri e propri hyperlinks che riconducevano ad altre sezioni, le quali erano a loro volta collegate con altre ancora. Non aver verificato quali fossero questi particolari termini (indicati col prefisso @, es: @elves) significava una sola cosa: era tutto da rifare!&lt;/p&gt;

&lt;p&gt;Per nostra grande fortuna, uno dei componenti il team era un buon programmatore e riuscì a realizzare un software il cui compito era quello di analizzare il Journal ed estrarre tutti i termini ‘speciali’. Questo comportò ovviamente un ritardo sui tempi, ma ci permise di non buttare tutto quanto fino a quel momento realizzato, limitandoci a ricontrollare solo le parti indicate dal nostro programma specifico.&lt;/p&gt;

&lt;p&gt;I mesi passavano ed il lavoro proseguiva. Le richieste da parte degli utenti fioccavano da più parti, tutti impazienti di poter finalmente giocare a Morrowind senza essere costretti a tenere un vocabolario sulle gambe. Ci fu anche qualche problema con Ubisoft, il publisher, di cui non vi parlerò neppure sotto tortura perchè sono comunque argomenti riservati.&lt;/p&gt;

&lt;h2 id=&quot;la-quest-aggiuntiva&quot;&gt;La quest aggiuntiva&lt;/h2&gt;

&lt;p&gt;Ci mancava ancora qualcosa, però. In fin dei conti si stava lavorando sul TES, e si stava realizzando un plugin, no? E allora, perchè non distribuire nello stesso pacchetto anche una quest aggiuntiva, la quale avrebbe ricompensato il giocatore con un libro il cui contenuto si riferiva a “Coloro i quali fecero l’impresa”? In quel libro avremmo riportato una breve ‘Bio’ di ciascuno di noi, con uno stile ovviamente conforme all’ambientazione di gioco. Dopo tanto lavoro, un minimo di auto referenzialità mi pare dovuta, no?&lt;/p&gt;

&lt;p&gt;Purtroppo, a causa di mancanza di tempo, io fui costretto a sospendere la mia collaborazione con l’ITP anzitempo, ma questo non comportò alcun problema per i ragazzi, che non mi stancherò mai di ringraziare per avermi comunque incluso sia fra i ringraziamenti nelle note di rilascio che nella quest aggiuntiva, malgrado la mia defezione anticipata.&lt;/p&gt;

&lt;p&gt;Ebbene, se siete arrivati fin qui a leggere e state cercando disperatamente la traduzione di Morrowind, direi che è giunto il momento di ricompensarvi per il tempo dedicato ai miei ricordi.&lt;/p&gt;

&lt;h2 id=&quot;la-traduzione-in-italiano-di-morrowind---download&quot;&gt;La traduzione in Italiano di Morrowind - Download&lt;/h2&gt;

&lt;p&gt;Questa è la versione definitiva della traduzione, che non necessita di preventivi aggiornamenti del gioco. Si basa sull’edizione Game of the Year di Morrowind e contiene sia la traduzione del gioco base che della sua espansione “vampiresca”, BloodMoon. E’ un unico file auto installante che, se necessario, aggiornerà la vostra copia di The Elder Scrolls III: Morrowind alla più recente versione 1.6&lt;/p&gt;

&lt;p&gt;Si consiglia di non applicare la traduzione ad una partita già in corso; ciò potrebbe corrompere i salvataggi. E’ comunque possibile “trattare” i propri salvataggi con il programma Savepatcher, distribuito nell’archivio Tribunal.zip&lt;/p&gt;

&lt;p&gt;Installare la traduzione di Tribunal solo dopo aver installato il file precedente, relativo alle traduzioni di Morrowind e Bloodmoon&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Skyflash/morrowind-italian-itp-translation/blob/master/assets/morrowind/Morrowind_Bloodmoon_ITA_WinXP2K.zip?raw=true&quot;&gt;Traduzione di Morrowind e BloodMoon&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Skyflash/morrowind-italian-itp-translation/blob/master/assets/tribunal/Tribunal.zip?raw=true&quot;&gt;Traduzione di Tribunal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Articolo originariamente pubblicato sullo Skyblog nel 2010.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;morrowind-itp---staff&quot;&gt;Morrowind ITP - Staff&lt;/h2&gt;

&lt;h3 id=&quot;responsabile-del-gruppo&quot;&gt;Responsabile del gruppo&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Daniele ‘Falcocadarn’ Falcone&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;responsabili-della-traduzione&quot;&gt;Responsabili della traduzione&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Daniele ‘Falcocadarn’ Falcone&lt;/li&gt;
  &lt;li&gt;Anahid ‘Misty’ Mazkedian&lt;/li&gt;
  &lt;li&gt;Maurizio ‘Dester’ Focareta&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;responsabile-tecnico&quot;&gt;Responsabile tecnico&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Maurizio ‘Dester’ Focareta&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;traduttori-itp---morrowind&quot;&gt;Traduttori ITP - Morrowind&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Daniele ‘Falcocadarn’ Falcone&lt;/li&gt;
  &lt;li&gt;Anahid ‘Misty’ Mazkedian&lt;/li&gt;
  &lt;li&gt;Maurizio ‘Dester’ Focareta&lt;/li&gt;
  &lt;li&gt;Erica ‘Crysania’ Masserano&lt;/li&gt;
  &lt;li&gt;Elena ‘Rowena’ Ricci&lt;/li&gt;
  &lt;li&gt;Jacopo ‘Jacoposki’ Sacerdote&lt;/li&gt;
  &lt;li&gt;Paolo ‘Judas’ De Sandre&lt;/li&gt;
  &lt;li&gt;Lorenzo ‘il Somax’ Soma&lt;/li&gt;
  &lt;li&gt;Luca ‘Darkness’ Liberati&lt;/li&gt;
  &lt;li&gt;Fabio ‘Gimli81’ Poli&lt;/li&gt;
  &lt;li&gt;Leonardo ‘Spaziotempo6’ Picchi&lt;/li&gt;
  &lt;li&gt;Paolo “Tasslehoff” Regonesi&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://cristiancastellari.it&quot;&gt;Cristian ‘Skyflash’ Castellari&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Francesco ‘Hammer’ Penta&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Un sentito grazie al nostro ‘padre spirituale’ Lello ‘wolverine’ Sarti, a Davide ‘Magi System’ Martelli per il supporto web e a Gianluca ‘Eldar’ Barca per l’aiuto nella realizzazione della statua.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;traduttori-itp---tribunal&quot;&gt;Traduttori ITP - Tribunal&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Daniele ‘Falcocadarn’ Falcone&lt;/li&gt;
  &lt;li&gt;Anahid ‘Misty’ Mazkedian&lt;/li&gt;
  &lt;li&gt;Maurizio ‘Dester’ Focareta&lt;/li&gt;
  &lt;li&gt;Erica ‘Crysania’ Masserano&lt;/li&gt;
  &lt;li&gt;Elena ‘Rowena’ Ricci&lt;/li&gt;
  &lt;li&gt;Jacopo ‘Jacoposki’ Sacerdote&lt;/li&gt;
  &lt;li&gt;Paolo ‘Judas’ De Sandre&lt;/li&gt;
  &lt;li&gt;Lorenzo ‘il Somax’ Soma&lt;/li&gt;
  &lt;li&gt;Michele “Medioman” Lai Chin&lt;/li&gt;
  &lt;li&gt;Luca ‘Darkness’ Liberati&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Un sentito grazie al nostro ‘padre spirituale’ Lello ‘wolverine’ Sarti, a Davide ‘Magi System’ Martelli per il supporto web, a Luca “Fusion” Piombino per il largo aiuto durante lo sviluppo del programma di conversione dei salvataggi, a Jim Adam per le informazioni tratte dal suo sito sul formato dei file di Morrowind, e a Tanilo e Pec dalle terre del Canada per la compagnia “telematica” e per un buon numero di aiuti tecnici.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Thu, 05 Apr 2018 00:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/fuori-ufficio/la-traduzione-di-morrowind-e-bloodmoon/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/fuori-ufficio/la-traduzione-di-morrowind-e-bloodmoon/</guid>
        
        <category>morrowind</category>
        
        <category>itp</category>
        
        <category>games</category>
        
        
        <category>Fuori dall&apos;Ufficio</category>
        
      </item>
    
      <item>
        <title>[K1000 Scripting] Find and report all PCs with a smartcard reader installed</title>
        <description>&lt;h1 id=&quot;target&quot;&gt;Target&lt;/h1&gt;

&lt;p&gt;Find and report all managed Windows PCs in K1000 Inventory with a smartcard reader installed.&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;The script does not depend on K1000, so if you don’t have KACE SMA in your environment don’t worry: the script is still useful!&lt;/p&gt;

&lt;h2 id=&quot;components&quot;&gt;Components&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;[The Query] This script&lt;/li&gt;
  &lt;li&gt;[The Automation] Kace Systems Management Appliance (AKA ‘K1000’)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;The vbs script executes a WMI query over the target device(s) and saves an output file named &lt;em&gt;smartcard.txt&lt;/em&gt; (see below in the &lt;a href=&quot;#setup&quot;&gt;Setup section&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;The vbs script is scheduled and deployed to the target device(s) via K1000 &lt;a href=&quot;#the-kscript-smarcardvbs&quot;&gt;&lt;em&gt;Online KScript&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A K1000 &lt;a href=&quot;#the-custom-inventory-rule&quot;&gt;&lt;em&gt;Custom Inventory Rule&lt;/em&gt;&lt;/a&gt; reads the output file for every inventoried device and stores the information in the database&lt;/li&gt;
  &lt;li&gt;A scheduled &lt;a href=&quot;#the-report&quot;&gt;Report&lt;/a&gt; (choose your favorite format between HTML, CSV, PDF or Excel) returns only PCs with a smart card reader installed&lt;/li&gt;
  &lt;li&gt;Done!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;h3 id=&quot;the-kscript-smarcardvbs&quot;&gt;The KScript (smarcard.vbs)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Download &lt;a href=&quot;/static/assets/files/blog/kace-smartcard/smartcard.vbs&quot;&gt;the script&lt;/a&gt; or copy &amp;amp; paste the following code:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;strComputer &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;
Dim log
Set log &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Wscript.CreateObject&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Scripting.Filesystemobject&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Set f &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; log.CreateTextFile&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\T&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;martcard.txt&quot;&lt;/span&gt;, 2&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Set objWMIService &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; GetObject&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;winmgmts:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp; strComputer &amp;amp; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;oot&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\C&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;IMV2&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
Set colItems &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; objWMIService.ExecQuery&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; _
    &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM Win32_PnPSignedDriver Where DeviceClass = &apos;SMARTCARDREADER&apos;&quot;&lt;/span&gt;,,48&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
For Each objItem &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;colItems 
    f.WriteLine &lt;span class=&quot;s2&quot;&gt;&quot;DeviceClass: &quot;&lt;/span&gt; &amp;amp; objItem.DeviceClass
Next
f.Close
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Edit &lt;strong&gt;line 4&lt;/strong&gt; with the path where you want to save the output file. In our environment every PC has a &lt;em&gt;“C:\Tools”&lt;/em&gt; directory for service purpose, so I decided to save the output there.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Set f = log.CreateTextFile(&quot;C:\Tools\smartcard.txt&quot;, 2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Go to your &lt;em&gt;K1000 Dashboard&lt;/em&gt;, then go to &lt;em&gt;Scripting&lt;/em&gt; and create a &lt;strong&gt;New Script&lt;/strong&gt; (&lt;em&gt;Choose Action / New&lt;/em&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Name the script as your wish (for example: Check Smart Card Reader) and follow these steps:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;script-basic-settings&quot;&gt;Script Basic Settings&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Type: &lt;strong&gt;Online KScript&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Enabled: &lt;strong&gt;Yes&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Deploy: one or some devices, all devices or to a Device Label, according to your needs in your environment&lt;/li&gt;
  &lt;li&gt;Windows Run As: &lt;strong&gt;Local System&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Upload the smartcard.vbs as &lt;strong&gt;New Dependecy&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tasks&quot;&gt;Tasks&lt;/h4&gt;

&lt;p&gt;We want the script to run once in every PC, so we’ll use a “checkmark” (the smartcard.txt) to verify that…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Verify: &lt;strong&gt;Verify a file exists…&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;C:\Tools\smartcard.txt&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Remediation: &lt;strong&gt;Launch a program…&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Directory: &lt;strong&gt;$(KACE_SYS_DIR)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;File: &lt;strong&gt;cscript.exe $(KACE_DEPENDENCY_DIR)\smartcard.vbs&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Wait for completion: &lt;strong&gt;Yes&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;On Remediation Success: &lt;strong&gt;Upload a file…&lt;/strong&gt; (note: this step is not necessary and only for archiving purpose)
    &lt;ul&gt;
      &lt;li&gt;Directory: &lt;strong&gt;C:\Tools&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;File: &lt;strong&gt;smartcard.txt&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and &lt;strong&gt;Save&lt;/strong&gt; your brand new script.&lt;/p&gt;

&lt;p&gt;The Task and its steps are summarized in the following image. When you’re ready, let’s jump to the &lt;a href=&quot;#the-custom-inventory-rule&quot;&gt;Step 4!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/screenshot1.png&quot; alt=&quot;Screenshot 1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-custom-inventory-rule&quot;&gt;The Custom Inventory Rule&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;In the K100 Dashboard, now go to &lt;em&gt;Inventory&lt;/em&gt; section, then go to &lt;em&gt;Software&lt;/em&gt; and create a &lt;strong&gt;new Software entry&lt;/strong&gt; (&lt;em&gt;Choose Action / New&lt;/em&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Name the rule as your wish (for example: IT Dep — Check Smart Card Reader) and follow these steps:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;Publisher: &lt;strong&gt;IT Department&lt;/strong&gt; (it’s useful for further searches into the &lt;em&gt;Software Inventory&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;Supported Operating Systems: &lt;strong&gt;All the Windows OSs in your Inventory&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Custom Inventory Rule: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ShellCommandTextReturn(cmd /c type C:\Tools\smartcard.txt)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and &lt;strong&gt;Save&lt;/strong&gt; your new Custom Inventory Rule.&lt;/p&gt;

&lt;p&gt;Here’s the summary image&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/screenshot2.png&quot; alt=&quot;Screenshot 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now we need all our devices complete their inventory. The new Custom Inventory Rule creates a new entry in every device record managed by the K1000.&lt;/p&gt;

&lt;p&gt;If a smart card reader has been discovered we’ll have at least one “DeviceClass: SMARTCARDREADER” text iside the &lt;strong&gt;Custom Inventory Fields&lt;/strong&gt; section into every device record in &lt;em&gt;Inventory / Devices&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/screenshot3.png&quot; alt=&quot;Screenhot 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, if a smart card reader has not been discovered, we’ll have no text&lt;/p&gt;

&lt;p&gt;When all your devices has been inventoried and you’re ready, jump to the &lt;a href=&quot;#the-report&quot;&gt;next section&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-report&quot;&gt;The Report&lt;/h3&gt;

&lt;p&gt;In the K100 Dashboard, now go to &lt;em&gt;Reporting&lt;/em&gt; section, then in &lt;em&gt;Reports&lt;/em&gt; and create a new Report (&lt;em&gt;Choose Action / New&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Name the Report as your wish (for example: PCs with Smart Card Reader) and follow these steps:&lt;/p&gt;

&lt;h4 id=&quot;title-and-topic&quot;&gt;Title and Topic&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Category: &lt;strong&gt;Inventory&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Topic: &lt;strong&gt;Device&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;fields-to-display&quot;&gt;Fields to Display&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Device: &lt;strong&gt;System Name&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Operating System Info: &lt;strong&gt;Name&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;User Information: &lt;strong&gt;User Name&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Manufacturer and BIOS: &lt;strong&gt;System Model&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to add and modify any other field, according to your needings.&lt;/p&gt;

&lt;h4 id=&quot;filters&quot;&gt;Filters&lt;/h4&gt;

&lt;p&gt;Delete the default filter and create this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/report1.png&quot; alt=&quot;Filter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save&lt;/strong&gt; your new report and try it.&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Feb 2018 00:00:00 +0100</pubDate>
        <link>https://cristiancastellari.it/it/blog/kace/k1000-report-pc-with-smartcard-reader/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/kace/k1000-report-pc-with-smartcard-reader/</guid>
        
        <category>kace</category>
        
        <category>script</category>
        
        <category>vbs</category>
        
        <category>smartcard</category>
        
        <category>security</category>
        
        <category>encryption</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>[K1000 Scripting] Find and report all PCs with a smartcard reader installed</title>
        <description>&lt;h1 id=&quot;target&quot;&gt;Target&lt;/h1&gt;

&lt;p&gt;Find and report all managed Windows PCs in K1000 Inventory with a smartcard reader installed.&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;The script does not depend on K1000, so if you don’t have KACE SMA in your environment don’t worry: the script is still useful!&lt;/p&gt;

&lt;h2 id=&quot;components&quot;&gt;Components&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;[The Query] This script&lt;/li&gt;
  &lt;li&gt;[The Automation] Kace Systems Management Appliance (AKA ‘K1000’)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;The vbs script executes a WMI query over the target device(s) and saves an output file named &lt;em&gt;smartcard.txt&lt;/em&gt; (see below in the &lt;a href=&quot;#setup&quot;&gt;Setup section&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;The vbs script is scheduled and deployed to the target device(s) via K1000 &lt;a href=&quot;#the-kscript-smarcardvbs&quot;&gt;&lt;em&gt;Online KScript&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A K1000 &lt;a href=&quot;#the-custom-inventory-rule&quot;&gt;&lt;em&gt;Custom Inventory Rule&lt;/em&gt;&lt;/a&gt; reads the output file for every inventoried device and stores the information in the database&lt;/li&gt;
  &lt;li&gt;A scheduled &lt;a href=&quot;#the-report&quot;&gt;Report&lt;/a&gt; (choose your favorite format between HTML, CSV, PDF or Excel) returns only PCs with a smart card reader installed&lt;/li&gt;
  &lt;li&gt;Done!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;h3 id=&quot;the-kscript-smarcardvbs&quot;&gt;The KScript (smarcard.vbs)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Download &lt;a href=&quot;/static/assets/files/blog/kace-smartcard/smartcard.vbs&quot;&gt;the script&lt;/a&gt; or copy &amp;amp; paste the following code:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;strComputer &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;
Dim log
Set log &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Wscript.CreateObject&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Scripting.Filesystemobject&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Set f &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; log.CreateTextFile&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\T&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ools&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;martcard.txt&quot;&lt;/span&gt;, 2&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Set objWMIService &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; GetObject&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;winmgmts:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp; strComputer &amp;amp; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;oot&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\C&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;IMV2&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
Set colItems &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; objWMIService.ExecQuery&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; _
    &lt;span class=&quot;s2&quot;&gt;&quot;SELECT * FROM Win32_PnPSignedDriver Where DeviceClass = &apos;SMARTCARDREADER&apos;&quot;&lt;/span&gt;,,48&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
For Each objItem &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;colItems 
    f.WriteLine &lt;span class=&quot;s2&quot;&gt;&quot;DeviceClass: &quot;&lt;/span&gt; &amp;amp; objItem.DeviceClass
Next
f.Close
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Edit &lt;strong&gt;line 4&lt;/strong&gt; with the path where you want to save the output file. In our environment every PC has a &lt;em&gt;“C:\Tools”&lt;/em&gt; directory for service purpose, so I decided to save the output there.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Set f = log.CreateTextFile(&quot;C:\Tools\smartcard.txt&quot;, 2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Go to your &lt;em&gt;K1000 Dashboard&lt;/em&gt;, then go to &lt;em&gt;Scripting&lt;/em&gt; and create a &lt;strong&gt;New Script&lt;/strong&gt; (&lt;em&gt;Choose Action / New&lt;/em&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Name the script as your wish (for example: Check Smart Card Reader) and follow these steps:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;script-basic-settings&quot;&gt;Script Basic Settings&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Type: &lt;strong&gt;Online KScript&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Enabled: &lt;strong&gt;Yes&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Deploy: one or some devices, all devices or to a Device Label, according to your needs in your environment&lt;/li&gt;
  &lt;li&gt;Windows Run As: &lt;strong&gt;Local System&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Upload the smartcard.vbs as &lt;strong&gt;New Dependecy&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tasks&quot;&gt;Tasks&lt;/h4&gt;

&lt;p&gt;We want the script to run once in every PC, so we’ll use a “checkmark” (the smartcard.txt) to verify that…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Verify: &lt;strong&gt;Verify a file exists…&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;C:\Tools\smartcard.txt&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Remediation: &lt;strong&gt;Launch a program…&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Directory: &lt;strong&gt;$(KACE_SYS_DIR)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;File: &lt;strong&gt;cscript.exe $(KACE_DEPENDENCY_DIR)\smartcard.vbs&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Wait for completion: &lt;strong&gt;Yes&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;On Remediation Success: &lt;strong&gt;Upload a file…&lt;/strong&gt; (note: this step is not necessary and only for archiving purpose)
    &lt;ul&gt;
      &lt;li&gt;Directory: &lt;strong&gt;C:\Tools&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;File: &lt;strong&gt;smartcard.txt&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and &lt;strong&gt;Save&lt;/strong&gt; your brand new script.&lt;/p&gt;

&lt;p&gt;The Task and its steps are summarized in the following image. When you’re ready, let’s jump to the &lt;a href=&quot;#the-custom-inventory-rule&quot;&gt;Step 4!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/screenshot1.png&quot; alt=&quot;Screenshot 1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-custom-inventory-rule&quot;&gt;The Custom Inventory Rule&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;In the K100 Dashboard, now go to &lt;em&gt;Inventory&lt;/em&gt; section, then go to &lt;em&gt;Software&lt;/em&gt; and create a &lt;strong&gt;new Software entry&lt;/strong&gt; (&lt;em&gt;Choose Action / New&lt;/em&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Name the rule as your wish (for example: IT Dep — Check Smart Card Reader) and follow these steps:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;Publisher: &lt;strong&gt;IT Department&lt;/strong&gt; (it’s useful for further searches into the &lt;em&gt;Software Inventory&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;Supported Operating Systems: &lt;strong&gt;All the Windows OSs in your Inventory&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Custom Inventory Rule: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ShellCommandTextReturn(cmd /c type C:\Tools\smartcard.txt)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and &lt;strong&gt;Save&lt;/strong&gt; your new Custom Inventory Rule.&lt;/p&gt;

&lt;p&gt;Here’s the summary image&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/screenshot2.png&quot; alt=&quot;Screenshot 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now we need all our devices complete their inventory. The new Custom Inventory Rule creates a new entry in every device record managed by the K1000.&lt;/p&gt;

&lt;p&gt;If a smart card reader has been discovered we’ll have at least one “DeviceClass: SMARTCARDREADER” text iside the &lt;strong&gt;Custom Inventory Fields&lt;/strong&gt; section into every device record in &lt;em&gt;Inventory / Devices&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/screenshot3.png&quot; alt=&quot;Screenhot 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, if a smart card reader has not been discovered, we’ll have no text&lt;/p&gt;

&lt;p&gt;When all your devices has been inventoried and you’re ready, jump to the &lt;a href=&quot;#the-report&quot;&gt;next section&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-report&quot;&gt;The Report&lt;/h3&gt;

&lt;p&gt;In the K100 Dashboard, now go to &lt;em&gt;Reporting&lt;/em&gt; section, then in &lt;em&gt;Reports&lt;/em&gt; and create a new Report (&lt;em&gt;Choose Action / New&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;Name the Report as your wish (for example: PCs with Smart Card Reader) and follow these steps:&lt;/p&gt;

&lt;h4 id=&quot;title-and-topic&quot;&gt;Title and Topic&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Category: &lt;strong&gt;Inventory&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Topic: &lt;strong&gt;Device&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;fields-to-display&quot;&gt;Fields to Display&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Device: &lt;strong&gt;System Name&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Operating System Info: &lt;strong&gt;Name&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;User Information: &lt;strong&gt;User Name&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Manufacturer and BIOS: &lt;strong&gt;System Model&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to add and modify any other field, according to your needings.&lt;/p&gt;

&lt;h4 id=&quot;filters&quot;&gt;Filters&lt;/h4&gt;

&lt;p&gt;Delete the default filter and create this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/kace/smartcard/report1.png&quot; alt=&quot;Filter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save&lt;/strong&gt; your new report and try it.&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Feb 2018 00:00:00 +0100</pubDate>
        <link>https://cristiancastellari.it/en/blog/kace/k1000-report-pc-with-smartcard-reader/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/en/blog/kace/k1000-report-pc-with-smartcard-reader/</guid>
        
        <category>kace</category>
        
        <category>script</category>
        
        <category>vbs</category>
        
        <category>smartcard</category>
        
        <category>security</category>
        
        <category>encryption</category>
        
        
        <category>Kace</category>
        
      </item>
    
      <item>
        <title>Identificare e terminare processi zombie su Linux</title>
        <description>&lt;p&gt;Recentemente ho avuto necessità di eseguire un pò di tuning su un server Linux, spesso infestato da processi zombie che lo portavano a raggiungere elevatissimi carichi operativi. Dalla descrizione di Wikipedia:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Nei sistemi operativi Unix e Unix-like, un processo zombie o processo defunto è un processo informatico che, nonostante abbia terminato la propria esecuzione, possiede ancora un PID ed un process control block, necessario per permettere al proprio processo padre di leggerne il valore di uscita.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Girovagando alla ricerca di informazioni utili, mi sono imbattuto in un ottimo script, da copiare sui vostri server, in grado di identificare e killare lo zombie, oltre a lasciarne una traccia un un file di log apposito.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#lo-script-kill-zombie&quot; id=&quot;markdown-toc-lo-script-kill-zombie&quot;&gt;Lo script kill-zombie&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#funzionamento&quot; id=&quot;markdown-toc-funzionamento&quot;&gt;Funzionamento&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#inserimento-nel-crontab&quot; id=&quot;markdown-toc-inserimento-nel-crontab&quot;&gt;Inserimento nel crontab&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;lo-script-kill-zombie&quot;&gt;Lo script kill-zombie&lt;/h2&gt;

&lt;p&gt;Come prima cosa, creeremo (con i permessi di root) un nuovo file kill-zombie&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /root
vi kill-zombies
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In seguito copiamo il seguente codice ed incolliamolo nel file appena creato&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#! /bin/bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Zombie processes killing script. Must be run under root.&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;ps ax | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PID&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 ps lOp | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UID&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print&quot;pid: &quot;$3&quot; *** parent_pid: &quot;$4&quot; *** status: &quot;$10&quot; *** process: &quot;$13}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;: Z&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 0&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;then
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;zombie processes found:
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
ps ax | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PID&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 ps lOp | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UID&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print&quot;pid: &quot;$3&quot; *** parent_pid: &quot;$4&quot; *** status: &quot;$10&quot; *** process: &quot;$13}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;: Z&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Kill zombies? [y/n]: &quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;keyb
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$keyb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;y&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;then
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;killing zombies..
ps ax | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PID&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 ps lOp | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UID&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print$4&quot; status:&quot;$10}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;status:Z&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-9&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi
else
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;no zombies found!
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--cron&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;ps ax | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PID&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 ps lOp | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UID&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print&quot;pid: &quot;$3&quot; *** parent_pid: &quot;$4&quot; *** status: &quot;$10&quot; *** process: &quot;$13}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;: Z&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 0&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;then
&lt;/span&gt;ps ax | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PID&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 ps lOp | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UID&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print$4&quot; status:&quot;$10}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;status:Z&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-9&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;: killed some zombie processes!&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/zombies.log
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;usage: kill-zombies {--cron|--admin}&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ora rendiamolo eseguibile e spostiamolo in /usr/bin&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x kill-zombies
&lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;kill-zombies /usr/bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;funzionamento&quot;&gt;Funzionamento&lt;/h3&gt;

&lt;p&gt;Lo script viene evocato con un flag, che ne modifica il comportamento:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kill-zombies &lt;span class=&quot;nt&quot;&gt;--admin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;L’esecuzione avviene manualmente. Se vengono rilevati processi zombie l’utente ne viene informato e deve confermare l’operazione&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kill-zombies &lt;span class=&quot;nt&quot;&gt;--cron&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lo script &lt;strong&gt;dovrà essere inserito nel crontab&lt;/strong&gt; e richiamato quindi a cadenze regolari dal server. I processi vengono killati in automatico e ne viene lasciata traccia in /var/log/zombies.log&lt;/p&gt;

&lt;h3 id=&quot;inserimento-nel-crontab&quot;&gt;Inserimento nel crontab&lt;/h3&gt;

&lt;p&gt;Richiamiamo l’editor del crontab con questo comando&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crontab &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ed inseriamo questa riga:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;/10 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /usr/bin/kill-zombies &lt;span class=&quot;nt&quot;&gt;--cron&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In questo modo, lo script verrà eseguito ogni 10 minuti a cadenza regolare. In caso si voglia modificare l’intervallo di tempo, sarà sufficiente cambiare ad esempio */10 in */5 (per 5 minuti)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/assets/files/blog/process-zombie-linux/kill-zombies.sh&quot;&gt;Download kill-zombies.sh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Riferimenti:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://help.ubuntu.com/community/CronHowto&quot;&gt;CronHowTo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 25 Oct 2015 00:00:00 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/infrastruttura/idenficare-processi-zombie-linux/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/infrastruttura/idenficare-processi-zombie-linux/</guid>
        
        <category>linux</category>
        
        <category>scripting</category>
        
        <category>process</category>
        
        <category>sicurezza</category>
        
        <category>script</category>
        
        
        <category>Infrastruttura &amp; Sistemi</category>
        
      </item>
    
      <item>
        <title>Backup automatizzato dei database su SQL Express</title>
        <description>&lt;p&gt;SQL Server Express viene distribuito senza SQL Agent, quindi non è possibile schedulare internamente a SQL Server processi automatizzati, come ad esempio il backup. E’ però possibile aggirare questo ostacolo creando uno script ad-hoc, che si occupi dei vari job che vogliamo eseguire.&lt;/p&gt;

&lt;p&gt;In questo articolo vedremo come creare uno &lt;strong&gt;script per il backup di SQL Express&lt;/strong&gt; e, subito dopo, uno per la manutenzione dei backup, il cui compito sarà quello di tenere sempre le tre copie più recenti, cancellando di volta in volta quelli più vecchi.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Tutto il lavoro verrà svolto all’interno della cartella &lt;strong&gt;C:\SQL_Backup&lt;/strong&gt; e delle due sotto cartelle &lt;strong&gt;scripts&lt;/strong&gt; e &lt;strong&gt;Logs&lt;/strong&gt;, che dovranno quindi essere create preventivamente.&lt;/p&gt;

  &lt;p&gt;Alla fine dell’articolo trovate i tre script già pronti da scaricare, con la stessa struttura di cartelle descritta qui sopra.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#lo-script-per-il-backup-di-sql-express&quot; id=&quot;markdown-toc-lo-script-per-il-backup-di-sql-express&quot;&gt;Lo script per il backup di SQL Express&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cancellazione-dei-backup-vecchi&quot; id=&quot;markdown-toc-cancellazione-dei-backup-vecchi&quot;&gt;Cancellazione dei backup vecchi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#i-file-di-log&quot; id=&quot;markdown-toc-i-file-di-log&quot;&gt;I file di log&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#esecuzione&quot; id=&quot;markdown-toc-esecuzione&quot;&gt;Esecuzione&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#schedulazione&quot; id=&quot;markdown-toc-schedulazione&quot;&gt;Schedulazione&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#aggiornamento-2026-la-versione-moderna-con-powershell&quot; id=&quot;markdown-toc-aggiornamento-2026-la-versione-moderna-con-powershell&quot;&gt;Aggiornamento (2026): la versione moderna con PowerShell&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#download&quot; id=&quot;markdown-toc-download&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;lo-script-per-il-backup-di-sql-express&quot;&gt;Lo script per il backup di SQL Express&lt;/h2&gt;

&lt;p&gt;Portiamoci all’interno di &lt;strong&gt;C:\SQL_Backup\scripts&lt;/strong&gt; e creiamo un file che chiameremo &lt;strong&gt;backupDB.sql&lt;/strong&gt; con questo contenuto:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateString&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dayStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--month variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MONTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthSTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MONTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthSTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MONTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--day variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;daySTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;daySTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--hour variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--minute variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--name variable based on time stamp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;YEAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dayStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--=================================================================&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBNAME&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATABASES&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NAME&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;TEMPDB&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WHILE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NAME&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATABASES&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/*Change disk location here as required*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SQL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;BACKUP DATABASE &apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBNAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; TO DISK = &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\S&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;QL_Backup&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+@DBNAME+&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_db_&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; + @dateString +&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BAK&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INIT&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;
EXEC (@SQL)
SELECT @IDENT=MIN(database_id) FROM SYS.DATABASES WHERE [database_id] &amp;gt; 0 AND database_id&amp;gt;@IDENT AND NAME NOT IN (&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TEMPDB&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;)
END
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lo script andrà a creare tanti backup quanti sono i database all’interno di SQL Express, ed aggiungerà una sorta di “timestamp” alla fine del nome, in modo da identificarli nel tempo, prima di terminare con l’estensione &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.BAK&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;cancellazione-dei-backup-vecchi&quot;&gt;Cancellazione dei backup vecchi&lt;/h2&gt;

&lt;p&gt;Sempre all’interno di &lt;strong&gt;C:\SQL_Backup\scripts&lt;/strong&gt; creiamo un file &lt;strong&gt;deleteoldsqlbaks.vbs&lt;/strong&gt; in cui scriveremo:&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;On&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Resume&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Next&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Dim&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sFolderTarget&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Scripting.FileSystemObject&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;location of the database backup files&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;C:\SQL_Backup\&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Files&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;used for writing to textfile - generate report on database backups deleted&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Const&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ForAppending&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;you need to create a folder named &quot;scripts&quot; for ease of file management &amp;amp;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;a file inside it named &quot;LOG.txt&quot; for delete activity logging&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenTextFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\Logs\cleanuplog.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ForAppending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;================================================================&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; DATABASE BACKUP FILE REPORT &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; DATE: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FormatDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; TIME: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FormatDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;================================================================&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;iterate thru each of the files in the database backup folder&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;For&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Each&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemFiles&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;In&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;retrieve complete path of file for the DeleteFile method and to extract&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;file extension using the GetExtensionName method&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;retrieve file extension&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetExtensionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;check if the file extension is BAK&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BAK&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Then&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;check if the database backups are older than 3 days&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateDiff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itemFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Then&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;Delete any old BACKUP files to cleanup folder&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeleteFile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteLine&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;BACKUP FILE DELETED: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;End&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;If&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;End&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;If&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Next&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteLine&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;================================================================&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il compito di questo script sarà quello di posizionarsi nella cartella in cui abbiamo salvato i backup, “contare” i backup presenti e tenere i tre più recenti, cancellando gli altri. Inoltre, scrive un log delle operazioni eseguite.&lt;/p&gt;

&lt;h2 id=&quot;i-file-di-log&quot;&gt;I file di log&lt;/h2&gt;

&lt;p&gt;Portiamoci in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\SQL_Backup\Logs&lt;/code&gt; e creiamo due file vuoti, che chiameremo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backuplog.txt&lt;/code&gt; e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanuplog.txt&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;esecuzione&quot;&gt;Esecuzione&lt;/h2&gt;

&lt;p&gt;Creiamo il file &lt;strong&gt;databasebackup.cmd&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-batchfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@echo &lt;span class=&quot;na&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;REM Lancio il backup e scrivo il log&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;sqlcmd&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-S &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;SERVER&lt;/span&gt;\SQLEXPRESS &lt;span class=&quot;na&quot;&gt;-E -i &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;C&lt;/span&gt;:\SQL_Backup\scripts\backupDB.sql &lt;span class=&quot;na&quot;&gt;-o &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;C&lt;/span&gt;:\SQL_Backup\Logs\backuplog.txt

&lt;span class=&quot;c&quot;&gt;REM Lancio lo script di pulizia e aggiorno cleanuplog.txt&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;C&lt;/span&gt;:\SQL_Backup\scripts\deleteoldsqlbaks.vbs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;L’unica modifica da fare sarà l’inserimento del nome e della istanza del vostro server, sostituendo &lt;strong&gt;SERVER\SQLEXPRESS&lt;/strong&gt; con i vostri dati corretti (presumibilmente, l’istanza si chiama sempre &lt;strong&gt;SQLEXPRESS&lt;/strong&gt; ma cambia il nome del server)&lt;/p&gt;

&lt;h2 id=&quot;schedulazione&quot;&gt;Schedulazione&lt;/h2&gt;

&lt;p&gt;Non resta che creare una &lt;strong&gt;Operazione pianificata&lt;/strong&gt; dal &lt;strong&gt;Pannello di controllo di Windows&lt;/strong&gt;, il cui semplice compito consisterà nell’esecuzione di &lt;strong&gt;C:\SQL_Backup\scripts\databasebackup.cmd&lt;/strong&gt;, impostando un orario ed una ricorrenza secondo le proprie necessità.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/backup-sql-express/sqlbackup.jpg&quot; alt=&quot;Configurazione dell&apos;operazione pianificata in Windows, con il percorso di databasebackup.cmd impostato come azione da eseguire&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;aggiornamento-2026-la-versione-moderna-con-powershell&quot;&gt;Aggiornamento (2026): la versione moderna con PowerShell&lt;/h2&gt;

&lt;p&gt;A 14 anni di distanza, l’approccio di fondo (uno script esterno più un’operazione pianificata, visto che SQL Express non ha mai guadagnato SQL Agent) resta valido, e lo script &lt;strong&gt;backupDB.sql&lt;/strong&gt; funziona ancora così com’è. Quello che è invecchiato è il resto della catena: Microsoft ha avviato la &lt;strong&gt;deprecazione di VBScript&lt;/strong&gt;, con rimozione pianificata da Windows nelle prossime versioni, quindi non ha più senso scrivere nuovo codice che ci si appoggia.&lt;/p&gt;

&lt;p&gt;Al posto di &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deleteoldsqlbaks.vbs&lt;/code&gt; e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;databasebackup.cmd&lt;/code&gt; basta un unico script &lt;strong&gt;PowerShell&lt;/strong&gt;, che richiama &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backupDB.sql&lt;/code&gt; tramite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlcmd&lt;/code&gt; (nessuna dipendenza aggiuntiva da installare) e si occupa della pulizia mantenendo solo le ultime copie più recenti &lt;em&gt;per ciascun database&lt;/em&gt;, invece del semplice “cancella tutto ciò che ha più di 3 giorni” dello script originale:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# backup.ps1 - versione moderna, sostituisce deleteoldsqlbaks.vbs + databasebackup.cmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Richiede solo sqlcmd (incluso in SQL Server / SQL Server Express, oppure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# installabile a parte tramite gli &quot;SQL Server Command Line Utilities&quot;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SqlInstance&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SERVER\SQLEXPRESS&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\SQL_Backup&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$KeepCopies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 1. Backup: richiama lo script T-SQL backupDB.sql, invariato&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqlcmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SqlInstance&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\scripts\backupDB.sql&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Logs\backuplog.txt&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 2. Pulizia: tiene solo le ultime $KeepCopies copie per ciascun database&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-ChildItem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*.BAK&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Group-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-replace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;_db_\d{12}\.BAK$&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForEach-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sort-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LastWriteTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Descending&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Select-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Skip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$KeepCopies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForEach-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Remove-Item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-Date&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Format&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BACKUP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DELETED:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add-Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Logs\cleanuplog.txt&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nell’Operazione pianificata, l’azione da eseguire diventa quindi &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;powershell.exe -ExecutionPolicy Bypass -File C:\SQL_Backup\scripts\backup.ps1&lt;/code&gt; al posto del vecchio &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;databasebackup.cmd&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/backupDB.sql&quot;&gt;backupDB.sql&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/backup.ps1&quot;&gt;backup.ps1&lt;/a&gt; — versione consigliata (2026)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/deleteoldsqlbaks.vbs&quot;&gt;deleteoldsqlbaks.vbs&lt;/a&gt; — script originale del 2012&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/databasebackup.cmd&quot;&gt;databasebackup.cmd&lt;/a&gt; — script originale del 2012&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Via&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://www.mssqltips.com/sqlservertip/1486/automate-sql-server-express-backups-and-deletion-of-older-backup-files/&quot;&gt;mssqltips.com&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Thu, 24 May 2012 17:03:37 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/infrastruttura/backup-automatizzato-dei-database-su-sql-express/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/infrastruttura/backup-automatizzato-dei-database-su-sql-express/</guid>
        
        <category>database</category>
        
        <category>sql</category>
        
        <category>backup</category>
        
        
        <category>Infrastruttura &amp; Sistemi</category>
        
      </item>
    
      <item>
        <title>Automated SQL Express database backups</title>
        <description>&lt;p&gt;SQL Server Express ships without SQL Agent, so it isn’t possible to schedule automated processes — like backups — from within SQL Server itself. It is however possible to work around this by creating an ad-hoc script that takes care of the various jobs we want to run.&lt;/p&gt;

&lt;p&gt;In this article we’ll see how to create a &lt;strong&gt;script to back up SQL Express&lt;/strong&gt; and, right after, one to maintain the backups, whose job will be to always keep the three most recent copies, deleting the older ones as we go.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;All the work will happen inside the &lt;strong&gt;C:\SQL_Backup&lt;/strong&gt; folder and its two subfolders &lt;strong&gt;scripts&lt;/strong&gt; and &lt;strong&gt;Logs&lt;/strong&gt;, which need to be created beforehand.&lt;/p&gt;

  &lt;p&gt;At the end of the article you’ll find all three scripts ready to download, following the same folder structure described above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#the-sql-express-backup-script&quot; id=&quot;markdown-toc-the-sql-express-backup-script&quot;&gt;The SQL Express backup script&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#deleting-old-backups&quot; id=&quot;markdown-toc-deleting-old-backups&quot;&gt;Deleting old backups&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-log-files&quot; id=&quot;markdown-toc-the-log-files&quot;&gt;The log files&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#running-it&quot; id=&quot;markdown-toc-running-it&quot;&gt;Running it&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#scheduling&quot; id=&quot;markdown-toc-scheduling&quot;&gt;Scheduling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#update-2026-the-modern-powershell-version&quot; id=&quot;markdown-toc-update-2026-the-modern-powershell-version&quot;&gt;Update (2026): the modern PowerShell version&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#download&quot; id=&quot;markdown-toc-download&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-sql-express-backup-script&quot;&gt;The SQL Express backup script&lt;/h2&gt;

&lt;p&gt;Let’s go into &lt;strong&gt;C:\SQL_Backup\scripts&lt;/strong&gt; and create a file called &lt;strong&gt;backupDB.sql&lt;/strong&gt; with this content:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateString&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dayStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--month variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MONTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthSTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MONTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthSTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MONTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--day variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;daySTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;daySTR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--hour variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--minute variable&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATEPART&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--name variable based on time stamp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dateString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;CAST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;YEAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GETDATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;CHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monthStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dayStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hourStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minStr&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;--=================================================================&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBNAME&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;MIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATABASES&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NAME&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;TEMPDB&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WHILE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBNAME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NAME&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATABASES&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDENT&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/*Change disk location here as required*/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SQL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;BACKUP DATABASE &apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DBNAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; TO DISK = &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\S&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;QL_Backup&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;+@DBNAME+&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_db_&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; + @dateString +&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BAK&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INIT&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;
EXEC (@SQL)
SELECT @IDENT=MIN(database_id) FROM SYS.DATABASES WHERE [database_id] &amp;gt; 0 AND database_id&amp;gt;@IDENT AND NAME NOT IN (&apos;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TEMPDB&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;)
END
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The script will create one backup for each database inside SQL Express, appending a kind of “timestamp” to the end of the name to identify them over time, before finishing with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.BAK&lt;/code&gt; extension.&lt;/p&gt;

&lt;h2 id=&quot;deleting-old-backups&quot;&gt;Deleting old backups&lt;/h2&gt;

&lt;p&gt;Still inside &lt;strong&gt;C:\SQL_Backup\scripts&lt;/strong&gt;, let’s create a file &lt;strong&gt;deleteoldsqlbaks.vbs&lt;/strong&gt; containing:&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;On&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Resume&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Next&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Dim&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sFolderTarget&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Scripting.FileSystemObject&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;location of the database backup files&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;C:\SQL_Backup\&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Files&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;used for writing to textfile - generate report on database backups deleted&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Const&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ForAppending&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;you need to create a folder named &quot;scripts&quot; for ease of file management &amp;amp;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;a file inside it named &quot;LOG.txt&quot; for delete activity logging&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenTextFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\Logs\cleanuplog.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ForAppending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;================================================================&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; DATABASE BACKUP FILE REPORT &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; DATE: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FormatDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; TIME: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FormatDateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;================================================================&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;iterate thru each of the files in the database backup folder&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;For&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Each&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemFiles&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;In&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;retrieve complete path of file for the DeleteFile method and to extract&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;file extension using the GetExtensionName method&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sFolder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;itemFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;retrieve file extension&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetExtensionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;&apos;check if the file extension is BAK&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;BAK&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Then&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;check if the database backups are older than 3 days&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateDiff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itemFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Then&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;&apos;Delete any old BACKUP files to cleanup folder&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeleteFile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteLine&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;BACKUP FILE DELETED: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;End&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;If&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;End&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;If&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Next&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WriteLine&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;================================================================&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VBCRLF&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This script’s job is to go into the folder where we’ve saved the backups, “count” the backups present, and keep the three most recent, deleting the rest. It also writes a log of the operations it performs.&lt;/p&gt;

&lt;h2 id=&quot;the-log-files&quot;&gt;The log files&lt;/h2&gt;

&lt;p&gt;Let’s go into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\SQL_Backup\Logs&lt;/code&gt; and create two empty files, which we’ll call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backuplog.txt&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanuplog.txt&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;running-it&quot;&gt;Running it&lt;/h2&gt;

&lt;p&gt;Let’s create the &lt;strong&gt;databasebackup.cmd&lt;/strong&gt; file&lt;/p&gt;

&lt;div class=&quot;language-batchfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@echo &lt;span class=&quot;na&quot;&gt;off&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;REM Run the backup and write the log&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;sqlcmd&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;-S &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;SERVER&lt;/span&gt;\SQLEXPRESS &lt;span class=&quot;na&quot;&gt;-E -i &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;C&lt;/span&gt;:\SQL_Backup\scripts\backupDB.sql &lt;span class=&quot;na&quot;&gt;-o &lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;C&lt;/span&gt;:\SQL_Backup\Logs\backuplog.txt

&lt;span class=&quot;c&quot;&gt;REM Run the cleanup script and update cleanuplog.txt&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;C&lt;/span&gt;:\SQL_Backup\scripts\deleteoldsqlbaks.vbs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only change needed is entering your server’s name and instance, replacing &lt;strong&gt;SERVER\SQLEXPRESS&lt;/strong&gt; with your own values (presumably the instance is always called &lt;strong&gt;SQLEXPRESS&lt;/strong&gt;, but the server name changes)&lt;/p&gt;

&lt;h2 id=&quot;scheduling&quot;&gt;Scheduling&lt;/h2&gt;

&lt;p&gt;All that’s left is to create a &lt;strong&gt;Scheduled Task&lt;/strong&gt; from the &lt;strong&gt;Windows Control Panel&lt;/strong&gt;, whose only job will be to run &lt;strong&gt;C:\SQL_Backup\scripts\databasebackup.cmd&lt;/strong&gt;, setting a time and recurrence to fit your needs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/backup-sql-express/sqlbackup.jpg&quot; alt=&quot;Scheduled Task configuration in Windows, with the path to databasebackup.cmd set as the action to run (original Italian-language screenshot from 2012)&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;update-2026-the-modern-powershell-version&quot;&gt;Update (2026): the modern PowerShell version&lt;/h2&gt;

&lt;p&gt;14 years on, the underlying approach (an external script plus a scheduled task, since SQL Express never gained SQL Agent) still holds up, and the &lt;strong&gt;backupDB.sql&lt;/strong&gt; script still works exactly as it is. What’s aged is the rest of the chain: Microsoft has started &lt;strong&gt;deprecating VBScript&lt;/strong&gt;, with planned removal from Windows in upcoming versions, so it no longer makes sense to write new code that depends on it.&lt;/p&gt;

&lt;p&gt;Instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deleteoldsqlbaks.vbs&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;databasebackup.cmd&lt;/code&gt;, a single &lt;strong&gt;PowerShell&lt;/strong&gt; script is enough — it calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backupDB.sql&lt;/code&gt; via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlcmd&lt;/code&gt; (no extra dependency to install) and handles cleanup by keeping only the most recent copies &lt;em&gt;per database&lt;/em&gt;, instead of the original script’s simple “delete anything older than 3 days”:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# backup.ps1 - modern version, replaces deleteoldsqlbaks.vbs + databasebackup.cmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Only requires sqlcmd (included with SQL Server / SQL Server Express, or&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# installable separately via the &quot;SQL Server Command Line Utilities&quot;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SqlInstance&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SERVER\SQLEXPRESS&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\SQL_Backup&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$KeepCopies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 1. Backup: calls the T-SQL script backupDB.sql, unchanged&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqlcmd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SqlInstance&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\scripts\backupDB.sql&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Logs\backuplog.txt&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# 2. Cleanup: keeps only the latest $KeepCopies copies per database&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-ChildItem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Filter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*.BAK&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Group-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-replace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;_db_\d{12}\.BAK$&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForEach-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sort-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LastWriteTime&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Descending&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Select-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Skip&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$KeepCopies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForEach-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Remove-Item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-Date&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Format&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BACKUP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DELETED:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FullName&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add-Content&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BackupPath&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;\Logs\cleanuplog.txt&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the Scheduled Task, the action to run becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;powershell.exe -ExecutionPolicy Bypass -File C:\SQL_Backup\scripts\backup.ps1&lt;/code&gt; instead of the old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;databasebackup.cmd&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/backupDB.sql&quot;&gt;backupDB.sql&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/backup.ps1&quot;&gt;backup.ps1&lt;/a&gt; — recommended version (2026)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/deleteoldsqlbaks.vbs&quot;&gt;deleteoldsqlbaks.vbs&lt;/a&gt; — original 2012 script&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/backup-sql-express/databasebackup.cmd&quot;&gt;databasebackup.cmd&lt;/a&gt; — original 2012 script&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Via&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://www.mssqltips.com/sqlservertip/1486/automate-sql-server-express-backups-and-deletion-of-older-backup-files/&quot;&gt;mssqltips.com&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Thu, 24 May 2012 17:03:37 +0200</pubDate>
        <link>https://cristiancastellari.it/en/blog/infrastructure/automated-sql-express-backup/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/en/blog/infrastructure/automated-sql-express-backup/</guid>
        
        <category>database</category>
        
        <category>sql</category>
        
        <category>backup</category>
        
        
        <category>Infrastructure &amp; Systems</category>
        
      </item>
    
      <item>
        <title>Come proteggere un sito dagli attacchi - Parte 2 - Il file .htaccess</title>
        <description>&lt;p&gt;Ieri abbiamo illustrato un ottimo e “furbo” metodo per &lt;a href=&quot;/it/blog/infrastruttura/come-proteggere-il-proprio-sito-dagli-attacchi-dei-bad-crawlers-parte-1/&quot;&gt;ingabbiare i bad crawlers&lt;/a&gt;. La tecnica della “trappola” illustrata è molto efficiente, ma potrebbe non bastare a garantire una protezione di alto livello. Esistono infatti anche bot maligni il cui scopo principale non è quello di effettuare il download indiscriminato di un intero sito web, ma che vanno alla ricerca di exploit, di falle di sicurezza e che magari tentano di eseguire script e simili.&lt;/p&gt;

&lt;p&gt;In questi casi, diventa di fondamentale importanza dotarsi di un file &lt;em&gt;.htaccess&lt;/em&gt; customizzato quel tanto che serve a bloccare la stragrande maggioranza degli attacchi. Negli ultimi giorni ho provato diverse modifiche al mio &lt;em&gt;.htaccess&lt;/em&gt; e ritengo di aver trovato, grazie all’ottimo blog &lt;a href=&quot;https://perishablepress.com&quot;&gt;Perishable Press&lt;/a&gt;, quella che ritengo una delle migliori soluzioni possibili.&lt;/p&gt;

&lt;p&gt;Inoltre, particolare non di poco conto per quanto mi riguarda, l’autore della blacklist ha un blog basato su WordPress e ne testa personalmente il funzionamento proprio su quella piattaforma, garantendone in pratica il funzionamento.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#htaccess-5g-blacklist-2012&quot; id=&quot;markdown-toc-htaccess-5g-blacklist-2012&quot;&gt;.htaccess 5G Blacklist 2012&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cosa-fa-la-blacklist&quot; id=&quot;markdown-toc-cosa-fa-la-blacklist&quot;&gt;Cosa fa la Blacklist&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#come-si-usa&quot; id=&quot;markdown-toc-come-si-usa&quot;&gt;Come si usa&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#alcune-note&quot; id=&quot;markdown-toc-alcune-note&quot;&gt;Alcune note&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;htaccess-5g-blacklist-2012&quot;&gt;.htaccess 5G Blacklist 2012&lt;/h2&gt;

&lt;p&gt;L’autore è giunto al quinto rilascio della sua blacklist, che prende quindi il nome attuale di “5G”. E’ già in fase avanzata di sviluppo la futura versione &lt;strong&gt;6G&lt;/strong&gt;, che comprenderà ulteriori modifiche derivate dai suggerimenti degli utenti e dalla propria esperienza&lt;/p&gt;

&lt;h2 id=&quot;cosa-fa-la-blacklist&quot;&gt;Cosa fa la Blacklist&lt;/h2&gt;

&lt;p&gt;La 5G è un set semplice e flessibile di direttive &lt;em&gt;.htaccess&lt;/em&gt; che verificano tutte le richieste verso il web server e si occupano delle eventuali contromisure immediate, senza appesantire il carico del web server stesso, permettendoci quindi di risparmiare una grande quantità di banda e di risorse.&lt;/p&gt;

&lt;h2 id=&quot;come-si-usa&quot;&gt;Come si usa&lt;/h2&gt;

&lt;p&gt;Installare la blacklist è relativamente semplice. Sarà sufficiente aggiungere il codice necessario in fondo al proprio file &lt;em&gt;.htaccess&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Attenzione:&lt;/strong&gt; la modifica di .htaccess, se fatta da persone non esperte e non particolarmente attente, può generare molti problemi al vostro sito WordPress, fino a &lt;strong&gt;renderlo inaccessibile&lt;/strong&gt;. Assicuratevi quindi di fare &lt;strong&gt;sempre una copia di sicurezza&lt;/strong&gt; di .htaccess prima di cominciare le modifiche&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Aprite il vostro file &lt;em&gt;.htaccess&lt;/em&gt; ed in coda (se usate WordPress, dovrebbero esserci solo le direttive di default) e &lt;strong&gt;dopo &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;### END WordPress ###&lt;/code&gt;&lt;/strong&gt; aggiungete il seguente codice:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 5G BLACKLIST/FIREWALL&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# @ http://perishablepress.com/5g-blacklist/&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 5G:[QUERY STRINGS]&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;RewriteEngine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;On&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteBase&lt;/span&gt; /
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (environ|localhost|mosconfig|scanner) [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} (menu|mod|path|tag)\=\.?/? [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} boot\.ini [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} echo.*kae [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} etc/passwd [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} \=\\%27$ [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} \=\\\&apos;$ [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} \.\./ [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} \? [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} \: [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} \[ [NC,OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} \] [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; .* - [F]

&lt;span class=&quot;c&quot;&gt;# 5G:[USER AGENTS]&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;SetEnvIfNoCase&lt;/span&gt; User-Agent ^$ keep_out
&lt;span class=&quot;nc&quot;&gt;SetEnvIfNoCase&lt;/span&gt; User-Agent (casper|cmsworldmap|diavol|dotbot) keep_out
&lt;span class=&quot;nc&quot;&gt;SetEnvIfNoCase&lt;/span&gt; User-Agent (flicky|ia_archiver|jakarta|kmccrew) keep_out
&lt;span class=&quot;nc&quot;&gt;SetEnvIfNoCase&lt;/span&gt; User-Agent (libwww|planetwork|pycurl|skygrid) keep_out
&lt;span class=&quot;nc&quot;&gt;SetEnvIfNoCase&lt;/span&gt; User-Agent (purebot|comodo|feedfinder|turnit) keep_out
&lt;span class=&quot;nc&quot;&gt;SetEnvIfNoCase&lt;/span&gt; User-Agent (zmeu|nutch|vikspider|binlar|sucker) keep_out

&lt;span class=&quot;nc&quot;&gt;Order&lt;/span&gt; Allow,Deny
&lt;span class=&quot;nc&quot;&gt;Allow&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Deny&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;from&lt;/span&gt; env=keep_out

&lt;span class=&quot;c&quot;&gt;# 5G:[REQUEST STRINGS]&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 (https?|ftp|php)\://
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 /(cgi|https?|ima|ucp)/
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 /(Permanent|Better)$
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 (\=\\\&apos;|\=\\%27|/\\\&apos;/?|\)\.css\()$
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 (\,|//|\)\+|/\,/|\{0\}|\(/\(|\.\.\.|\+\+\+|\||\\\&quot;\\\&quot;)
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 \.(cgi|asp|aspx|cfg|dll|exe|jsp|mdb|sql|ini|rar)$
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 /(contac|fpw|install|pingserver|register)\.php$
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 (base64|crossdomain|localhost|wwwroot|e107\_)
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 (eval\(|\_vti\_|\(null\)|echo.*kae|config\.xml)
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 \.well\-known/host\-meta
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 /function\.array\-rand
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 \)\;\$\(this\)\.html\(
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 proc/self/environ
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 msnbot\.htm\)\.\_
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 /ref\.outcontrol
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 com\_cropimage
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 indonesia\.htm
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 \{\$itemURL\}
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 function\(\)
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 labels\.rdf
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 /playing.php
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 muieblackcat

&lt;span class=&quot;c&quot;&gt;# 5G:[BAD IPS]&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;Order&lt;/span&gt; Allow,Deny
&lt;span class=&quot;nc&quot;&gt;Allow&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# uncomment/edit/repeat next line to block IPs&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Deny from 123.456.789&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;alcune-note&quot;&gt;Alcune note&lt;/h2&gt;

&lt;p&gt;Se sul vostro sito ospitate dei download, fate molta attenzione a questa riga:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 \.(cgi|asp|aspx|cfg|dll|exe|jsp|mdb|sql|ini|rar)$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;che contiene un filtro contro, fra gli altri, i files .rar&lt;/p&gt;

&lt;p&gt;In caso quindi alcuni dei vostri downloads siano compressi con questo formato, rimuovete semplicemente l’eccezione “rar” dalla regola:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 403 \.(cgi|asp|aspx|cfg|dll|exe|jsp|mdb|sql|ini)$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Via&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://perishablepress.com/5g-blacklist-2012/&quot;&gt;5G Blacklist 2012&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Tue, 08 May 2012 16:05:48 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/infrastruttura/protezione-di-un-sito-parte-2-htaccess/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/infrastruttura/protezione-di-un-sito-parte-2-htaccess/</guid>
        
        <category>sicurezza</category>
        
        <category>htaccess</category>
        
        <category>bad crawler</category>
        
        <category>bot</category>
        
        <category>php</category>
        
        
        <category>Infrastruttura &amp; Sistemi</category>
        
      </item>
    
      <item>
        <title>Come proteggere un sito dagli attacchi - Parte 1 - I bad crawler</title>
        <description>&lt;p&gt;Questo blog da alcuni giorni si trova sotto attacco dei cosiddetti “bad crawler”, cioè di bot il cui scopo consiste nel download indiscriminato di ogni singolo elemento che componga il sito web.&lt;/p&gt;

&lt;p&gt;Questo genere di attacco provoca un enorme spreco di banda passante (nel nostro caso, si sono viste punte anche di &lt;strong&gt;2Gb/sec di upload verso Internet per diverse ore&lt;/strong&gt;, reiterati più volte nell’arco della giornata) ed un impegno della CPU del server che molto spesso raggiunge il 100%, arrivando a bloccare così non solo il sito colpito, ma tutti quanti si trovano ospitati all’interno della stessa macchina.&lt;/p&gt;

&lt;p&gt;Infatti, negli ultimi 4 giorni la media delle visite su www.skyflash.it si è quasi dimezzata, arrivando a punte minime davvero non abituali per questo blog, proprio a causa dei frequenti blocchi del server, incapace di soddisfare tutte le richieste a cui era sottoposto. Nei giorni passati sono state ovviamente prese alcune contromisure, la cui bontà è ancora in fase di testing. Alcune di queste sembrano più efficaci di altre che sono quindi state scartate, o che devono ancora ricevere il necessario tuning.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Questo è il primo articolo dedicato alla protezione di un sito Internet basato su WordPress, a cui ne seguirà &lt;a href=&quot;/it/blog/infrastruttura/protezione-di-un-sito-parte-2-htaccess/&quot;&gt;un altro&lt;/a&gt;, basato sulla nostra esperienza diretta e su una discreta quantità di informazioni recepita in rete in questi giorni.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#una-trappola-per-i-bots-maligni&quot; id=&quot;markdown-toc-una-trappola-per-i-bots-maligni&quot;&gt;Una trappola per i bots maligni&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#creiamo-la-trappola&quot; id=&quot;markdown-toc-creiamo-la-trappola&quot;&gt;Creiamo la trappola&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#alcune-personalizzazioni&quot; id=&quot;markdown-toc-alcune-personalizzazioni&quot;&gt;Alcune personalizzazioni&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#whitelisting-dei-bot&quot; id=&quot;markdown-toc-whitelisting-dei-bot&quot;&gt;Whitelisting dei bot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;una-trappola-per-i-bots-maligni&quot;&gt;Una trappola per i bots maligni&lt;/h2&gt;

&lt;p&gt;Il primo passo da compiere consiste nel &lt;strong&gt;realizzare una trappola in grado di ingabbiare i crawlers&lt;/strong&gt;. Un web crawler non fa altro che collegarsi tramite una richiesta HTTP al sito da colpire, iniziando a scaricare tutto quanto trovi in quella pagina, in modo ricorsivo seguendo ogni singolo link che viene rilevato all’interno della stessa.&lt;/p&gt;

&lt;p&gt;L’idea, quindi, consiste nel fargli trovare un link invisibile ad un normale visitatore, che lo faccia puntare ad una specifica directory, che contiene la trappola stessa. La trappola altro non è che uno script php in grado di bannare l’IP del crawler, impedendogli di conseguenza ogni possibile accesso in futuro.&lt;/p&gt;

&lt;p&gt;Un crawler normale (come ad esempio googlebot, bing eccetera) non sarà interessato a seguire il link nascosto, e non accederà mai alla trappola in quanto la stessa viene negata all’interno del file robots.txt (file che viene normalmente letto ed interpretato da qualsiasi bot “legale” ed ovviamente ignorato da chi ha intenzioni malevole)&lt;/p&gt;

&lt;h2 id=&quot;creiamo-la-trappola&quot;&gt;Creiamo la trappola&lt;/h2&gt;

&lt;p&gt;Per la creazione della trappola, dopo aver provato un paio di script, ho deciso di adottare Blackhole di PerishablePress.&lt;/p&gt;

&lt;p&gt;In realtà ci sarebbe anche l’ottimo ZB Block, ma devo ancora finire di approfondirne la conoscenza, e quando l’ho installato e provato per 60 minuti circa, il risultato è stato il blocco anche di visitatori normali, tutti provenienti da ADSL Telecom Italia con IP dinamico. Quindi, per il momento, ZB Block non verrà ulteriormente citato.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Innanzi tutto, procediamo a scaricare l’ultima versione dello script dalla &lt;a href=&quot;https://perishablepress.com/blackhole-bad-bots/&quot;&gt;pagina ufficiale di Blackhole su Perishable Press&lt;/a&gt; e scompattiamo lo zip da qualche parte sul nostro computer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Carichiamo quindi via FTP la directory &lt;em&gt;/blackhole/&lt;/em&gt; nella root del nostro sito&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Verifichiamo che il file &lt;em&gt;/blackhole/&lt;/em&gt; sia regolarmente scrivibile (impostare i permessi a 755)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Inserire il seguente codice in cima ad ogni pagina del sito. Nel caso di WordPress, la soluzione migliore è l’inserimento in prima posizione all’interno del file &lt;em&gt;wp-load.php&lt;/em&gt;, &lt;strong&gt;ricordandosi però di andare a rieseguire la modifica ad ogni aggiornamento&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;DOCUMENT_ROOT&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/blackhole/blackhole.php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lo script &lt;em&gt;blackhole.php&lt;/em&gt; controlla l’IP richiedente all’interno della blacklist contenuta nel file &lt;em&gt;blackhole.dat&lt;/em&gt;. Se viene trovata una corrispondenza, la richiesta viene bloccata e viene mostrato un breve messaggio (personalizzabile).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; La trappola vera e propria! Aggiungere al &lt;em&gt;footer.php&lt;/em&gt; del proprio template, questo codice:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;display: none;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://example.com/blackhole/&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nofollow&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Do NOT follow this link or you will be banned from the site!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Questo è il link reso invisibile tramite CSS che i bad crawlers seguiranno diligentemente come tanti topolini seguono il Pifferaio Magico. Essendo reso invisibile, nessun visitatore lo vedrà mai e quindi non esiste il pericolo che possa venire cliccato inavvertitamente.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sostituite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://example.com&lt;/code&gt; con l’indirizzo del vostro sito&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; Passo fondamentale, da eseguire assolutamente.
Apriamo il nostro &lt;strong&gt;robots.txt&lt;/strong&gt; ed aggiungiamo:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;User-agent: *
Disallow: /blackhole/*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Come detto, i bot “legali” interpretano correttamente il file &lt;strong&gt;robots.txt&lt;/strong&gt; quindi, grazie a questa regola, saranno direttamente esclusi dalla directory contenente la trappola e non correranno quindi il rischio di venire bloccati per errore.&lt;/p&gt;

&lt;h2 id=&quot;alcune-personalizzazioni&quot;&gt;Alcune personalizzazioni&lt;/h2&gt;

&lt;p&gt;Il file &lt;strong&gt;index.php&lt;/strong&gt; richiede un minimo di configurazione. Aprire &lt;strong&gt;/blackhole/index.php&lt;/strong&gt; ed eseguire le seguenti modifiche:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Riga #54:&lt;/strong&gt; Il percorso del nostro file robots.txt&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Riga #56:&lt;/strong&gt; Il percorso della nostra pagina per i contatti&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Righe #140/141:&lt;/strong&gt; L’indirizzo email del mittente e del destinatario della notifica che riceveremo per ogni nuovo bot blacklistato da Blackhole&lt;/li&gt;
  &lt;li&gt;Nel file &lt;strong&gt;blackhole/blackhole.php&lt;/strong&gt; modificare la &lt;strong&gt;Riga #53&lt;/strong&gt; con le nostre informazioni di contatto esterne al sito&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;whitelisting-dei-bot&quot;&gt;Whitelisting dei bot&lt;/h2&gt;

&lt;p&gt;Lo script Blackhole si preoccupa, a scanso di errori, di eseguire il whitelisting dei principali bot di indicizzazione. In questa versione, la whitelist comprende:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;googlebot (Google)&lt;/li&gt;
  &lt;li&gt;msnbot (MSN/Bing)&lt;/li&gt;
  &lt;li&gt;yandex (Yandex)&lt;/li&gt;
  &lt;li&gt;teoma (Ask)&lt;/li&gt;
  &lt;li&gt;slurp (Yahoo)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E’ possibile eventualmente inserirne altri, modificando manualmente la &lt;strong&gt;riga #40&lt;/strong&gt; all’interno del file &lt;strong&gt;blackhole.php&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Aggiornamento:&lt;/strong&gt; se installato su un CMS, come Joomla o WordPress, potreste ricevere un errore del tipo “Error opening file…”. Per risolvere il problema, aprite il file &lt;strong&gt;blackhole.php&lt;/strong&gt; e modificate la riga evidenziata, immettendo il percorso assoluto del file &lt;strong&gt;blackhole.dat&lt;/strong&gt; all’interno del vostro server:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www/example/web/blackhole/blackhole.dat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// scan to prevent duplicates&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$fp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;die&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Error opening file...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/(googlebot|slurp|msnbot|teoma|yandex)/i&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;explode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;REMOTE_ADDR&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$badbot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Via&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://perishablepress.com/blackhole-bad-bots/&quot;&gt;Perishable Press&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Mon, 07 May 2012 11:36:11 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/infrastruttura/come-proteggere-il-proprio-sito-dagli-attacchi-dei-bad-crawlers-parte-1/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/infrastruttura/come-proteggere-il-proprio-sito-dagli-attacchi-dei-bad-crawlers-parte-1/</guid>
        
        <category>sicurezza</category>
        
        <category>bot</category>
        
        <category>bad crawler</category>
        
        <category>server</category>
        
        <category>php</category>
        
        <category>blackhole</category>
        
        
        <category>Infrastruttura &amp; Sistemi</category>
        
      </item>
    
      <item>
        <title>Ciao, Steve</title>
        <description>&lt;p&gt;Oggi il blog si ferma, per ricordare il più grande genio creativo dei nostri tempi, nei cui confronti abbiamo - tutti - un enorme debito. Alla visionarietà di Steve Jobs dobbiamo la nascita di device quali iPod, iPhone od iPad. Dispositivi divenuti non solo standard, ma che sono stati capaci di creare un vero e proprio segmento commerciale che fino a quel momento non esisteva.&lt;/p&gt;

&lt;p&gt;Mi risulta molto difficile trovare le parole adeguate con cui esprimere, in questo momento, quel senso di smarrimento, di vuoto e di profonda tristezza che sto provando. Steve Jobs, più di ogni altro, ha lasciato un’impronta indelebile nella storia dell’home computing e non potrà mai essere dimenticato.&lt;/p&gt;

&lt;p&gt;A Steve Jobs deve andare tutta la nostra riconoscenza.&lt;/p&gt;

&lt;p&gt;Questo blog ha deciso di salutare Steve pubblicando quello che forse è il video più famoso fra quelli che lo riguardano. Si tratta del discorso che tenne nel 2005 dinanzi ai laureandi dell’Università di Stanford.&lt;/p&gt;

&lt;div style=&quot;max-width: 760px; margin: 0 auto var(--space-4);&quot;&gt;
&lt;iframe width=&quot;100%&quot; style=&quot;aspect-ratio: 16/9; display:block; border:0;&quot; src=&quot;https://www.youtube.com/embed/oObxNDYyZPs&quot; title=&quot;Steve Jobs - 2005 Stanford Commencement Address&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Nel 2011 Apple attivò temporaneamente un indirizzo email attraverso il quale chiunque poteva esprimere il proprio cordoglio per la scomparsa di Steve Jobs: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rememberingsteve@apple.com&lt;/code&gt;. A distanza di tanti anni quell’indirizzo non è più attivo, ma lo riportiamo qui come testimonianza di quei giorni.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;small&gt;Foto di copertina: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Steve_Jobs_(cropped).jpg&quot;&gt;Joi Ito&lt;/a&gt;, &lt;a href=&quot;https://creativecommons.org/licenses/by/2.0/deed.it&quot;&gt;CC BY 2.0&lt;/a&gt;, ritagliata e convertita in bianco e nero.&lt;/small&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 06 Oct 2011 08:27:12 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/fuori-ufficio/ciao-steve/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/fuori-ufficio/ciao-steve/</guid>
        
        <category>apple</category>
        
        <category>steve jobs</category>
        
        
        <category>Fuori dall&apos;Ufficio</category>
        
      </item>
    
      <item>
        <title>La traduzione in Italiano di Planescape: Torment realizzata dal team ITP</title>
        <description>&lt;p&gt;Correva l’anno 1999 quando, in pieno boom dei &lt;abbr title=&quot;Computer Role Playing Game&quot;&gt;CRPG&lt;/abbr&gt;, &lt;strong&gt;Black Isle Studios&lt;/strong&gt; rilasciò il suo attesissimo &lt;strong&gt;Planescape: Torment&lt;/strong&gt;. Il gioco, basato sulle regole di Advanced Dungeons and Dragons, era ambientato nell’universo parallelo di Planescape, e si contraddistinse da subito per l’enorme mole di testo e per un gameplay molto orientato al dialogo ed agli effetti delle proprie scelte piuttosto che alla forza bruta.&lt;/p&gt;

&lt;p&gt;I temi affrontati si contraddistinguevano per i loro colori forti ed adulti, andando a toccare la religione, il confine fra la vita e la morte e teorie esistenziali.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/assets/img/blog/planescape-torment/the-nameless-one.webp&quot; alt=&quot;The Nameless One&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Il protagonista, il &lt;strong&gt;&lt;a href=&quot;http://images.google.it/search?hl=it&amp;amp;biw=1230&amp;amp;bih=1029&amp;amp;gbv=2&amp;amp;tbm=isch&amp;amp;sa=1&amp;amp;q=planescape+torment+nameless+one&amp;amp;aq=f&amp;amp;aqi=&amp;amp;aql=&amp;amp;oq=&quot;&gt;Nameless One&lt;/a&gt;&lt;/strong&gt; (il Senza Nome), si risveglia in un mondo abitato da morti e, in compagnia del simpatico ed a tratti logorroico “&lt;strong&gt;Morte&lt;/strong&gt;” - un teschio fluttuante - inizierà la sua missione con lo scopo di risolvere due fondamentali quesiti: conoscere il proprio passato e tornare mortale.&lt;/p&gt;

&lt;p&gt;Il gioco, malgrado la sua indiscutibile qualità (ancora oggi viene definito da molti come il &lt;strong&gt;miglior CRPG mai prodotto&lt;/strong&gt; per computer) ha avuto una scarsa diffusione nel nostro Paese, anche a causa della scelta operata dal distributore, CTO di Bologna, di non tradurlo. Scelta comprensibile vista la mole di testo a video, ma molto svantaggiosa per i giocatori non ferratissimi in Inglese, visto che i dialoghi sono ricchi di slang e modi di dire, e quindi terribilmente complessi da leggere e comprendere per i più.&lt;/p&gt;

&lt;p&gt;Alcuni mesi dopo l’uscita del gioco nei negozi un gruppo di ragazzi decise di riunirsi, e di mettersi al lavoro su quella che passerà alla storia come la &lt;strong&gt;traduzione amatoriale più grande, complessa e di qualità che sia mai stata realizzata in Italia&lt;/strong&gt;. Quel gruppo prese il nome di &lt;strong&gt;Italian Translation Project&lt;/strong&gt; o, in breve, &lt;strong&gt;ITP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Fu un lavoro enorme, vista la ferma intenzione dei ragazzi di mantenere quanto più possibile lo stile dei dialoghi in slang, portandoli in una lingua comprensibile per i propri connazionali. Non si fecero remore nel tradurre parole anche non politically-correct, quelle parolacce ed imprecazioni che 10 anni fa nessun editore si sarebbe mai sognato di inserire nelle proprie traduzioni. Ci vollero più di due anni ma alla fine, in pieno 2002, il lavoro venne rilasciato.&lt;/p&gt;

&lt;p&gt;Da allora l’ITP si è dedicato ad alcune delle traduzioni più importanti:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Arcanum&lt;/li&gt;
  &lt;li&gt;Divine Divinity&lt;/li&gt;
  &lt;li&gt;Morrowind: Bloodmoon&lt;/li&gt;
  &lt;li&gt;Morrowind: Tribunal&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;/it/blog/fuori-ufficio/la-traduzione-di-morrowind-e-bloodmoon/&quot;&gt;The Elder Scrolls: Morrowind&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Per un breve periodo della mia vita, in occasione della conversione di Morrowind, io stesso ho fatto parte di quel gruppo. Un esempio del mio lavoro è tutta la fase iniziale del tutorial di Morrowind, ed una parte dei dialoghi di combattimento dello stesso gioco.&lt;/p&gt;

&lt;p&gt;Veniamo ora alla ragione per cui, a distanza di 7 anni, ho deciso di rendere disponibile al download la traduzione di Planescape: Torment. E’ molto semplice: gli anni sono passati, il sito del team ITP ormai non è più attivo, così come non esiste nemmeno più il dominio e, pure cercando su Google, risulta piuttosto difficile reperire questo file che io reputo così importante.&lt;/p&gt;

&lt;p&gt;Lo Skyblog esiste da quasi 5 anni; prima di essere un blog, questo dominio ospitò il forum e la mailing list da cui partì la traduzione di The Elder Scrolls: Morrowind. Mi sembra quindi giusto, non avendo io intenzione di sospendere questo dominio ancora per un bel pezzo, permettere a chi sta cercando - o cercherà in futuro - la conversione in Italiano di Torment, di poterne fruire tranquillamente e di poter gustare al meglio il miglior CRPG di sempre, con la migliore traduzione amatoriale Italiana di tutti i tempi.&lt;/p&gt;

&lt;p&gt;Per concludere, manca solo il link. Il file è contenuto in un archivio rar.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/static/assets/files/blog/torment-ita/TormentITP.rar&quot;&gt;Scarica la traduzione in Italiano di Planescape: Torment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 14 Oct 2009 13:04:50 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/fuori-ufficio/la-traduzione-in-italiano-di-planescape-torment-del-team-itp/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/fuori-ufficio/la-traduzione-in-italiano-di-planescape-torment-del-team-itp/</guid>
        
        <category>planescape torment</category>
        
        <category>traduzione</category>
        
        <category>itp</category>
        
        <category>dungeons and dragons</category>
        
        <category>videogiochi</category>
        
        
        <category>Fuori dall&apos;Ufficio</category>
        
      </item>
    
      <item>
        <title>Twittero in Italiano anche io</title>
        <description>&lt;p&gt;Da qualche giorno il team di Twitter ha selezionato una ristretta cerchia di utenti Francesi, Italiani, Tedeschi e Spagnoli a cui ha chiesto se erano interessati a tradurre il proprio portale nelle loro rispettive lingue. Io appartengo a questo gruppo di persone ed ho ovviamente subito accettato di partecipare alla traduzione.&lt;/p&gt;

&lt;p&gt;Spesso molte persone mi chiedono “perche’ lo fai gratis?”. Beh, la risposta è piuttosto semplice, e cioè “&lt;strong&gt;perchè mi piace farlo, e perchè Twitter è e resta gratuito per tutti&lt;/strong&gt;”. In fin dei conti, ho avuto il piacere e l’onore di avviare - e partecipare - ad uno dei progetti di traduzione per un Videogioco più enormi che io ricordi, quel &lt;strong&gt;The Elder Scrolls III: Morrowind&lt;/strong&gt; che ogni giocatore che si rispetti non può non aver giocato. Quel progetto partì grazie a me e all’allora responsabile del sito rpgplayer.it, ora “inglobato” da &lt;a href=&quot;http://multiplayer.it/&quot;&gt;Multiplayer.it&lt;/a&gt;, fino ad assumere tratti quasi commerciali. Poi, per cause che non starò ad illustrare, tornò (fortunatamente - dico io -) ad essere amatoriale e la traduzione venne rilasciata dall’ITP dopo oltre due anni di lavoro, ed una mole di testo scritto paragonabile al tomo di Guerra e Pace.&lt;/p&gt;

&lt;p&gt;Quindi, se abbiamo &lt;a href=&quot;/it/blog/fuori-ufficio/la-traduzione-di-morrowind-e-bloodmoon/&quot;&gt;tradotto Morrowind&lt;/a&gt; gratis (per correttezza, va detto che dovetti mio malgrado abbandonare il team ITP dopo i primi sei mesi di lavoro per cause personali, ma che la mia persona viene comunque citata dalla quest aggiuntiva legata alla traduzione), perché non tradurre quei pochi caratteri che compongono tutto il sito di Twitter?&lt;/p&gt;

&lt;p&gt;Inoltre il lavoro che abbiamo accettato di svolgere è indirizzato a tutti gli utenti Italiani, per dare modo a tutti, anche a chi non sa una mezza parola di Inglese, di fruire di uno dei servizi più in voga dell’ultimo periodo, a tal punto da essere integrato addirittura in console come &lt;strong&gt;XBox 360&lt;/strong&gt; e videogiochi come &lt;strong&gt;Uncharted 2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20110102210321/http://www.napolux.com/2009/tradurre-twitter-gratis-perch.html&quot;&gt;Napolux&lt;/a&gt;, un altro traduttore selezionato, la vede un po’ al mio stesso modo, e sono convinto che questo sia più o meno il pensiero di tutti gli utenti che si sono buttati nell’impresa.&lt;/p&gt;
</description>
        <pubDate>Tue, 13 Oct 2009 08:50:08 +0200</pubDate>
        <link>https://cristiancastellari.it/it/blog/fuori-ufficio/twittero-in-italiano-anche-io/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/fuori-ufficio/twittero-in-italiano-anche-io/</guid>
        
        <category>twitter</category>
        
        <category>morrowind</category>
        
        <category>traduzione</category>
        
        
        <category>Fuori dall&apos;Ufficio</category>
        
      </item>
    
      <item>
        <title>Script bash per il backup di un database MySQL</title>
        <description>&lt;p&gt;In passato ho avuto bisogno di uno script che, in modo automatico, mi facesse una serie di backup del database MySQL che gira sul nostro server in azienda.&lt;/p&gt;

&lt;p&gt;Così mi sono messo un paio d’ore a studiare il linguaggio bash, che non conoscevo, ed ho “partorito” questo script:&lt;/p&gt;

&lt;p&gt;Dopo alcune impostazioni iniziali, non fa altro che eseguire un dump del database e ne mette una copia nella home dell’utente, col nome &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[database]_DAILY_[data_di_oggi]&lt;/code&gt; e, nel caso ci sia, rinomina quello del giorno prima in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[database]_OLD&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Infine genera un ulteriore dump e lo piazza in una altra directory, che sul mio server corrisponde a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dati&lt;/code&gt; e lo nomina &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Backup_db_[database]_[weekday]&lt;/code&gt;, dove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[weekday]&lt;/code&gt; è il giorno della settimana. In questo modo avremo sempre due copie del db nella nostra home (quella odierna e quella di ieri) e ben 7 copie, una per ogni giorno della settimana, in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dati&lt;/code&gt; (o altra directory).&lt;/p&gt;

&lt;p&gt;E’ molto grezzo, e chi volesse contribuire a migliorarlo è ben accetto 😉&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Impostazioni dello script&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Qui comincia la sezione delle varibili utente da impostare&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Regolare questi valori secondo le proprie necessita&apos;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# INIZIO SEZIONE IMPOSTAZIONE&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# DIR -&amp;gt; Nome della directory home dell&apos;utente.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ATTENZIONE: DEVE TERMINARE CON &quot;/&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# BACKUP -&amp;gt; Nome della directory dove andranno i backup.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ATTENZIONE: DEVE TERMINARE CON &quot;/&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# DATABASE -&amp;gt; Nome del database MySQL&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# NAME -&amp;gt; Nome che verra&apos; usato per generare il backup.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ATTENZIONE: MANTENERE IL SUFFISSO &quot;_DAILY_&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# OLD -&amp;gt; Nome che verra&apos; usato per copiare il database&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# del giorno prima. ATTENZIONE: MANTENERE IL SUFFISSO &quot;_OLD&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/home/utente/&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;BACKUP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/directory/&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DATABASE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;NOME DATABASE&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;NOME_DAILY_&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OLD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;NOME_OLD&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# FINE DELLA SEZIONE DI IMPOSTAZIONE&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Queste sono le variabili di data e di ricerca&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# in base al nome ricavato dalle variabili precedenti&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +%d-%m-%Y&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;IF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$DIR&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NAME&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DAY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +%w_%a&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Cambio il nome del file trovato&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$DIR$IF&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$DIR$OLD&lt;/span&gt;.sql
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Eseguo il dump del database definito dalle variabili&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ALTRE IMPOSTAZIONI&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Cambiare [nome] con il nome utente e [password] con la password&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Cambiare [group] con il gruppo a cui appartiene l&apos;utente (solitamente, uguale a [nome])&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
mysqldump &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=[&lt;/span&gt;nome] &lt;span class=&quot;nt&quot;&gt;--password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=[&lt;/span&gt;password] &lt;span class=&quot;nv&quot;&gt;$DATABASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$DIR$NAME$OF&lt;/span&gt;.sql
mysqldump &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=[&lt;/span&gt;nome] &lt;span class=&quot;nt&quot;&gt;--password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=[&lt;/span&gt;password] &lt;span class=&quot;nv&quot;&gt;$DATABASE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$BACKUP&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Backup_db_&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DATABASE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_0&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DAY&lt;/span&gt;.sql
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Cambio i permessi del dump nella mia home&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# perche&apos; sono fighetto e li voglio gia&apos; accessibili al mio utente&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nome].[group] &lt;span class=&quot;nv&quot;&gt;$DIR$NAME$OF&lt;/span&gt;.sql
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;Backup del database &lt;span class=&quot;nv&quot;&gt;$DATABASE&lt;/span&gt; eseguito!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/static/assets/files/blog/backup-database-mysql/backup-mysql.sh&quot;&gt;Download backup-mysql.sh&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; passare la password con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--password=&lt;/code&gt; la rende visibile a chiunque lanci &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps&lt;/code&gt; sulla stessa macchina, oltre che nella cronologia della shell. Oggi è preferibile salvare le credenziali in un file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.my.cnf&lt;/code&gt; (con permessi &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 600&lt;/code&gt;) e lanciare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqldump&lt;/code&gt; senza specificarle sulla riga di comando.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;pianificazione-automatica&quot;&gt;Pianificazione automatica&lt;/h3&gt;

&lt;p&gt;Per eseguire il backup ogni notte senza doversene ricordare, basta aggiungerlo al crontab:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crontab &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;ed inserire una riga come questa, che lo esegue ogni giorno all’1:00 di notte:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0 1 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /home/utente/backup-mysql.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 15 Jan 2005 15:08:20 +0100</pubDate>
        <link>https://cristiancastellari.it/it/blog/infrastruttura/script-bash-per-il-backup-di-un-database-mysql/</link>
        <guid isPermaLink="true">https://cristiancastellari.it/it/blog/infrastruttura/script-bash-per-il-backup-di-un-database-mysql/</guid>
        
        <category>bash</category>
        
        <category>mysql</category>
        
        <category>backup</category>
        
        <category>linux</category>
        
        <category>script</category>
        
        <category>database</category>
        
        
        <category>Infrastruttura &amp; Sistemi</category>
        
      </item>
    
  </channel>
</rss>
