Artigos

Construindo um Agente Seguro para Distribuir Wallpapers no Windows

Como usei PowerShell, Microsoft Intune, separação de privilégios e validação de conteúdo para gerenciar o wallpaper e a tela de bloqueio

13 min de leitura
Segurança de Endpoints Agendado
Microsoft IntunePowerShellWindowsSegurança de EndpointsSHA-256Tarefas Agendadas

Distribuir um wallpaper corporativo pode parecer uma tarefa simples.

Em um único computador, basta selecionar uma imagem e aplicá-la. Em um ambiente gerenciado, porém, a solução precisa funcionar para vários usuários, sobreviver a reinicializações, atualizar o conteúdo sem reconstruir o pacote, operar com privilégios mínimos e continuar segura quando a rede ou a publicação falhar.

Além disso, as imagens são conteúdo remoto. Mesmo quando vêm de uma origem controlada pela organização, o endpoint não deveria confiar nelas sem validação.

Para resolver esse problema, criei o WallpaperAgent, um agente em PowerShell para distribuir, validar e aplicar wallpapers de desktop e imagens de tela de bloqueio em dispositivos Windows gerenciados.

O projeto está disponível no GitHub:

O problema

Eu precisava de uma solução que permitisse atualizar o conteúdo visual dos dispositivos sem depender de uma nova implantação completa no Microsoft Intune para cada alteração.

Ao analisar o fluxo, percebi que o problema envolvia responsabilidades diferentes:

  1. Publicar novas imagens.
  2. Entregar o agente aos dispositivos.
  3. Baixar e validar o conteúdo com segurança.
  4. Aplicar as imagens no perfil do usuário conectado.
  5. Registrar estado suficiente para diagnóstico, detecção e recuperação.

Executar tudo em um único script ou contexto criaria riscos e limitações.

Um processo executado como SYSTEM pode administrar arquivos protegidos e tarefas da máquina, mas não representa corretamente o perfil interativo de cada usuário. Por outro lado, um processo comum do usuário não deveria controlar downloads, binários ou o estado global da instalação.

O projeto precisava responder a algumas perguntas importantes:

  • Como atualizar imagens sem republicar o pacote Win32?
  • Como impedir que credenciais de publicação cheguem aos endpoints?
  • Como validar que o arquivo baixado é exatamente o arquivo esperado?
  • Como evitar a ativação de uma versão parcialmente publicada?
  • Como aplicar configurações que pertencem ao contexto do usuário?
  • Como manter a versão atual funcionando quando uma atualização falha?

A decisão central de arquitetura tornou-se:

Separar publicação, validação em nível de máquina e aplicação no contexto do usuário.

Arquitetura do projeto

O WallpaperAgent separa o código do agente do conteúdo distribuído.

O repositório do projeto contém:

  • scripts de instalação e desinstalação;
  • os processos de atualização e aplicação;
  • configuração do agente;
  • lógica de detecção para o Intune;
  • empacotamento Win32;
  • validação, testes e diagnóstico;
  • documentação operacional e de segurança.

As imagens e o manifest.json ficam em um repositório de conteúdo ou origem HTTPS separados.

Servidor de publicação / Portal
          |
          | credencial de escrita
          v
Repositório de conteúdo ou origem HTTPS
          |
          | leitura anônima ou controlada por HTTPS
          v
Atualizador do endpoint (SYSTEM)
          |
          | arquivos validados e estado da máquina
          v
Aplicador do endpoint (usuário interativo)
          |
          +--> Wallpaper do desktop
          `--> Tela de bloqueio

Essa separação permite atualizar uma campanha visual sem alterar o código do agente ou gerar um novo .intunewin, desde que o contrato do manifesto continue compatível.

Separando código e conteúdo

Wallpapers são conteúdo operacional, não código do agente.

Misturar os dois no mesmo pacote teria várias consequências:

  • cada nova imagem exigiria uma reconstrução do pacote;
  • o Intune precisaria redistribuir arquivos que não alteram a lógica do agente;
  • o ciclo de publicação ficaria acoplado ao ciclo de software;
  • credenciais usadas para publicar conteúdo poderiam acabar próximas demais dos endpoints.

No WallpaperAgent, o processo de publicação possui acesso de escrita ao repositório de conteúdo. Os endpoints possuem apenas acesso de leitura ao manifesto e às imagens.

Quando o conteúdo não é sensível, ele pode ser entregue por um repositório público e lido anonimamente. Para conteúdo privado, a recomendação é usar um serviço HTTPS, proxy, armazenamento de objetos ou CDN com controles de leitura apropriados.

O ponto importante é que uma credencial de escrita nunca deve ser incluída:

  • no pacote do Intune;
  • no arquivo de configuração;
  • no manifesto;
  • nos URLs das imagens;
  • no ambiente do endpoint;
  • nos logs do agente.

Duas tarefas agendadas, dois contextos de segurança

Uma das principais decisões do projeto foi usar duas tarefas agendadas.

Tarefa de atualização

A tarefa de atualização executa como SYSTEM.

Ela é responsável por:

  • baixar o manifesto;
  • validar sua estrutura e tamanho;
  • baixar as imagens para arquivos temporários;
  • validar extensão, tamanho, hash e decodificação;
  • promover uma nova versão somente após a validação completa;
  • atualizar o estado da máquina;
  • iniciar a tarefa de aplicação quando o conteúdo muda.

Esse processo precisa de privilégios de máquina porque grava em ProgramData, administra o estado compartilhado e trabalha com a instalação protegida.

Tarefa de aplicação

A tarefa de aplicação executa no contexto do grupo interno INTERACTIVE, com privilégios limitados.

Ela é responsável por:

  • ler o estado validado da máquina;
  • comparar a configuração desejada com o perfil atual;
  • aplicar o wallpaper por meio de interfaces do Windows;
  • aplicar a tela de bloqueio por meio das APIs do Windows Runtime;
  • registrar o estado específico do usuário em %LOCALAPPDATA%;
  • reaplicar a configuração quando detectar uma alteração manual.

Esse processo não precisa modificar o agente, baixar arquivos ou controlar o estado global.

SYSTEM
  |
  +-- Downloads HTTPS
  +-- Validação de conteúdo
  +-- Assets e estado em ProgramData
  +-- Registro HKLM
  `-- Definições das tarefas

Usuário interativo
  |
  +-- Leitura dos assets validados
  +-- Configuração HKCU
  +-- API da tela de bloqueio
  `-- Estado e logs em LocalAppData

A árvore de instalação concede controle total apenas a SYSTEM e Administradores. Usuários padrão recebem somente leitura e execução.

Isso permite que o processo interativo consuma os arquivos validados sem poder substituir o atualizador, os scripts ou os assets gerenciados.

O manifesto como contrato de publicação

O manifest.json funciona como o contrato entre o processo de publicação e os endpoints.

Ele descreve uma versão combinada contendo um wallpaper de desktop e uma imagem para a tela de bloqueio.

Exemplo simplificado:

{
  "schemaVersion": 1,
  "release": "2026.07.30.1",
  "publishedAt": "2026-07-30T10:00:00Z",
  "desktop": {
    "version": "2026.07.30.1",
    "fileName": "wallpaper_desktop_20260730.jpg",
    "url": "https://example.invalid/windows/desktop/wallpaper.jpg",
    "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  },
  "lockScreen": {
    "version": "2026.07.30.1",
    "fileName": "lockscreen_20260730.jpg",
    "url": "https://example.invalid/windows/lockscreen/lockscreen.jpg",
    "sha256": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
  }
}

O agente atualmente aceita o esquema de versão 1.

Cada asset precisa declarar:

  • uma versão;
  • um nome de arquivo simples;
  • um URL HTTPS absoluto;
  • o SHA-256 do arquivo publicado.

O campo fileName não pode conter caminhos. Valores como ../wallpaper.jpg, C:\Temp\wallpaper.jpg ou windows/desktop/image.jpg são rejeitados.

Essa regra evita que um manifesto controle arbitrariamente onde o agente grava arquivos.

Validando conteúdo remoto como entrada não confiável

O WallpaperAgent trata o manifesto e as imagens como entrada não confiável até que todas as validações sejam concluídas.

Para cada nova versão, o atualizador verifica:

  • se o manifesto está dentro do limite de tamanho;
  • se o JSON é válido;
  • se a versão do esquema é suportada;
  • se a versão da publicação não está vazia;
  • se as definições de desktop e tela de bloqueio existem;
  • se os URLs são HTTPS absolutos;
  • se os nomes não contêm caminhos;
  • se as extensões são .jpg, .jpeg ou .png;
  • se os hashes têm o formato SHA-256 esperado;
  • se os arquivos estão dentro do limite configurado;
  • se o hash calculado corresponde ao manifesto;
  • se o Windows consegue decodificar o conteúdo como uma imagem válida;
  • se largura e altura são maiores que zero.

HTTPS protege o transporte, mas não define qual arquivo era esperado.

A validação por SHA-256 vincula a entrada do manifesto aos bytes exatos que o publicador pretendia entregar.

Um arquivo com extensão de imagem e hash correspondente ainda precisa passar pela decodificação. Isso evita aceitar conteúdo que tem o nome correto, mas não pode ser interpretado como uma imagem válida.

Arquivos temporários e promoção atômica

O agente não baixa uma nova imagem diretamente sobre o arquivo ativo.

Cada download é realizado em um arquivo temporário com nome único. Somente depois das verificações de tamanho, hash e imagem o arquivo é movido para seu destino definitivo.

O estado principal também é tratado cuidadosamente.

O arquivo current.json representa a versão atualmente validada. Ele só é atualizado depois que tanto o wallpaper quanto a tela de bloqueio estão disponíveis e aprovados.

Os arquivos JSON são gravados de forma atômica, reduzindo a possibilidade de outro processo ler conteúdo parcial durante uma atualização.

O fluxo é:

Baixar manifesto
      |
Validar esquema e campos
      |
Baixar os dois assets em arquivos temporários
      |
Validar tamanho, SHA-256 e decodificação
      |
Promover os assets
      |
Gravar current.json de forma atômica
      |
Acionar a tarefa do usuário

Esse comportamento evita uma versão híbrida em que o desktop pertence a uma publicação e a tela de bloqueio pertence a outra.

Falhar sem destruir a versão funcional

Um princípio importante do projeto é que uma falha de atualização não deve remover a versão atual.

Se ocorrer qualquer um destes problemas:

  • indisponibilidade de rede;
  • manifesto malformado;
  • esquema incompatível;
  • URL inseguro;
  • arquivo grande demais;
  • hash incorreto;
  • imagem corrompida;
  • asset ausente;

…a nova candidata é rejeitada e a versão validada anterior continua disponível.

O erro é registrado no status.json e no update.log, mas o agente não limpa deliberadamente o current.json nem os assets funcionais.

Esse modelo transforma a atualização em uma promoção controlada, em vez de uma substituição otimista.

Aplicando no perfil do usuário

Depois que a máquina possui uma versão validada, a tarefa interativa compara o estado desejado com o estado do usuário.

Para o desktop, o agente utiliza:

  • as configurações em HKCU\Control Panel\Desktop;
  • a função SystemParametersInfo da API Win32.

Para a tela de bloqueio, utiliza as APIs do Windows Runtime.

O agente verifica tanto os hashes registrados quanto a configuração real do Windows. Portanto, se o usuário alterar manualmente o wallpaper ou a tela de bloqueio, a próxima execução pode detectar a divergência e restaurar a versão gerenciada.

As duas operações são isoladas.

Se a aplicação do wallpaper funcionar, mas a tela de bloqueio falhar, o resultado do desktop é preservado e o erro da tela de bloqueio é registrado. O processo não desfaz automaticamente uma operação válida apenas porque a outra falhou.

Estado de máquina e estado por usuário

O projeto mantém dados separados para a máquina e para cada usuário.

Exemplo do estado da máquina:

C:\ProgramData\{Organizacao}\WallpaperAgent\
|-- Agent\
|-- Assets\
|   |-- Desktop\
|   `-- LockScreen\
|-- State\
|   |-- current.json
|   |-- manifest.json
|   `-- status.json
`-- Logs\
    |-- install.log
    `-- update.log

Estado por usuário:

%LOCALAPPDATA%\{Organizacao}\WallpaperAgent\
|-- current.json
`-- Logs\apply.log

O estado da máquina descreve a versão validada e os caminhos dos assets. O estado do usuário registra os hashes e caminhos aplicados, o URI da tela de bloqueio e a data da aplicação.

Essa divisão permite que diferentes usuários do mesmo computador sejam avaliados de forma independente.

Controle de concorrência

Tarefas agendadas podem ser acionadas por diferentes eventos, inclusive durante inicialização, logon, repetição periódica ou uma atualização recém-publicada.

Para evitar execuções simultâneas, o projeto utiliza duas camadas de controle:

  • MultipleInstances IgnoreNew nas configurações das tarefas;
  • mutexes nomeados no código.

O atualizador utiliza um mutex global específico da organização. O aplicador utiliza um mutex específico da organização e do SID do usuário atual.

Isso reduz o risco de duas execuções modificarem o mesmo estado ou aplicarem o mesmo conteúdo ao mesmo perfil simultaneamente.

Instalação e empacotamento com Microsoft Intune

O instalador prepara a estrutura completa no endpoint.

Ele:

  • confirma que está executando em PowerShell de 64 bits;
  • exige privilégios administrativos;
  • valida os arquivos obrigatórios do pacote;
  • cria os diretórios da máquina;
  • copia scripts, módulo auxiliar e configuração;
  • aplica ACLs restritivas;
  • cria a pasta das tarefas agendadas;
  • registra as tarefas de atualização e aplicação;
  • grava valores de detecção em HKLM;
  • tenta executar a primeira atualização de conteúdo.

O projeto também contém um script de build para gerar uma área limpa de empacotamento, o script de detecção com a versão esperada e, opcionalmente, o arquivo .intunewin.

Configuração recomendada no Intune:

CampoValor
Comando de instalaçãoInstall.cmd
Comando de desinstalaçãoUninstall.cmd
Comportamento de instalaçãoSystem
ReinicializaçãoNenhuma ação específica
Arquitetura64 bits
Regra de detecçãoScript personalizado
Executar como 32 bitsNão

Detecção no Intune

A detecção não se limita à existência de uma pasta.

O script verifica:

  • ProductCode = WallpaperAgent no registro;
  • a versão esperada do pacote;
  • o caminho de instalação registrado;
  • a presença dos scripts, configuração e módulo auxiliar;
  • a existência das duas tarefas agendadas;
  • se as tarefas estão habilitadas.

O script descobre a organização e os caminhos específicos por meio do registro, enquanto mantém a versão esperada incorporada ao artefato de detecção gerado no build.

Essa abordagem diferencia o estado de instalação do estado de conteúdo.

O Intune confirma que o agente correto está instalado. O status.json e os diagnósticos informam se a última tentativa de conteúdo foi bem-sucedida.

Diagnóstico e observabilidade

O projeto inclui uma ferramenta de diagnóstico para reunir informações operacionais sem exigir uma análise manual de cada arquivo.

Ela pode mostrar:

  • a configuração resolvida;
  • os valores de detecção no registro;
  • o estado das tarefas agendadas;
  • a versão atual do agente;
  • a publicação atual;
  • o último resultado de atualização;
  • os caminhos dos assets;
  • os logs relevantes.

Os logs do atualizador e do aplicador possuem rotação baseada em tamanho.

Isso evita crescimento indefinido e mantém informação suficiente para investigar falhas de download, validação ou aplicação.

Decisões de segurança

Os principais controles do projeto incluem:

  • credencial de publicação isolada dos endpoints;
  • downloads somente por HTTPS;
  • validação de nomes contra travessia de diretórios;
  • lista restrita de extensões;
  • limites de tamanho para manifesto e imagens;
  • verificação SHA-256;
  • decodificação real das imagens;
  • downloads em arquivos temporários;
  • promoção somente após validação completa;
  • escrita atômica do estado;
  • separação entre privilégios de máquina e usuário;
  • ACLs restritivas;
  • mutexes para controle de concorrência;
  • preservação da última versão funcional;
  • implantação em anéis de piloto e expansão.

A solução não presume que a rede interna, um repositório ou um arquivo com extensão .jpg seja automaticamente confiável.

Desinstalação e preservação dos assets

Por padrão, o desinstalador remove o agente, suas tarefas e sua configuração operacional, mas preserva os assets atualmente utilizados.

Essa decisão é deliberada.

O Windows pode continuar apontando o perfil do usuário para um arquivo de imagem depois que o agente é removido. Excluir imediatamente o arquivo poderia deixar o perfil referenciando um caminho inexistente.

Quando uma remoção completa é necessária, o parâmetro -RemoveAssets permite apagar também as imagens baixadas.

O desinstalador possui suporte a -WhatIf, permitindo revisar o impacto antes da execução.

Limitações e considerações operacionais

O comportamento da tela de bloqueio pode variar de acordo com:

  • edição e versão do Windows;
  • estado do dispositivo;
  • políticas organizacionais existentes;
  • disponibilidade das APIs do Windows Runtime;
  • restrições de personalização aplicadas ao endpoint.

Por isso, a validação deve incluir dispositivos representativos e múltiplos perfis de usuário.

Também é importante diferenciar três estados:

  1. O agente está instalado.
  2. Uma versão de conteúdo foi validada pela máquina.
  3. Essa versão foi aplicada ao usuário atual.

Esses estados podem mudar em momentos diferentes e devem ser observados separadamente.

Estratégia de implantação

A implantação recomendada utiliza anéis progressivos:

Validação local
      |
Dispositivos piloto
      |
Equipe de TI e suporte
      |
Grupo representativo de usuários
      |
Implantação geral

Antes da implantação geral, considero importante testar:

  • instalação e atualização pelo Intune;
  • contexto de segurança de ambas as tarefas;
  • múltiplos usuários no mesmo dispositivo;
  • logon, reinicialização e atualização periódica;
  • manifesto malformado;
  • hash incorreto;
  • imagem inválida;
  • falha de rede;
  • preservação da publicação anterior;
  • alteração manual do wallpaper;
  • upgrade e desinstalação do agente.

O que aprendi

Este projeto reforçou que automação de endpoints não é apenas executar uma ação em muitos computadores.

O desafio é definir quais componentes podem confiar uns nos outros, qual privilégio cada componente realmente precisa e como o sistema deve se comportar quando recebe dados incompletos ou inválidos.

Também aprendi que conteúdo e software possuem ciclos de vida diferentes.

O agente deve mudar quando sua lógica ou seu contrato muda. As imagens devem mudar por meio de uma publicação de conteúdo independente. Separar esses ciclos reduz trabalho operacional e limita a distribuição de privilégios.

A principal lição foi:

Uma atualização segura não substitui o estado atual até provar que a nova versão está completa e válida.

Próximos passos

Possíveis melhorias futuras incluem:

  • assinatura dos scripts PowerShell;
  • testes automatizados para manifesto, validação e promoção de estado;
  • métricas para plataformas de monitoramento;
  • notificações de falha persistente;
  • suporte opcional a uma origem privada autenticada com credenciais de leitura específicas para dispositivos;
  • melhor telemetria de aplicação por usuário;
  • pipeline automatizado para validar e publicar novas versões de conteúdo;
  • documentação adicional para diferentes edições do Windows;
  • evidências sanitizadas de implantação piloto.

O WallpaperAgent já fornece uma base estruturada para distribuir conteúdo visual sem transformar uma tarefa aparentemente simples em um processo com privilégios excessivos, credenciais expostas ou atualizações frágeis.

Código-fonte

O projeto completo, os scripts de instalação, a documentação e as instruções de empacotamento estão disponíveis no GitHub: