Página principal » cómo » Cómo usar un archivo por lotes para hacer que los scripts de PowerShell sean más fáciles de ejecutar

    Cómo usar un archivo por lotes para hacer que los scripts de PowerShell sean más fáciles de ejecutar

    Por varias razones, en su mayoría relacionadas con la seguridad, los scripts de PowerShell no son tan fácilmente portátiles y utilizables como lo pueden ser los scripts por lotes. Sin embargo, podemos agrupar un script por lotes con nuestros scripts de PowerShell para solucionar estos problemas. Aquí, le mostraremos algunas de esas áreas problemáticas y cómo crear un script por lotes para sortearlas.

    ¿Por qué no puedo simplemente copiar mi archivo .PS1 a otra computadora y ejecutarlo??

    A menos que el sistema de destino haya sido preconfigurado para permitir la ejecución de scripts arbitrarios, con los privilegios necesarios y el uso de la configuración correcta, es probable que surja algunos problemas cuando intente hacer esto..

    1. PowerShell no está asociado a la extensión de archivo .PS1 por defecto.
      Lo mencionamos inicialmente en nuestra serie de PowerShell Geek School. Windows asocia los archivos .PS1 al Bloc de notas de forma predeterminada, en lugar de enviarlos al intérprete de comandos de PowerShell. Esto es para evitar la ejecución accidental de scripts maliciosos simplemente haciendo doble clic en ellos. Hay formas en que puede cambiar este comportamiento, pero es probable que no sea algo que quiera hacer en todas las computadoras a las que está llevando sus scripts, especialmente si algunas de esas computadoras no son suyas..
    2. PowerShell no permite la ejecución de scripts externos de forma predeterminada.
      La configuración de ExecutionPolicy en PowerShell evita la ejecución de scripts externos de forma predeterminada en todas las versiones de Windows. En algunas versiones de Windows, el valor predeterminado no permite la ejecución de secuencias de comandos en absoluto. Le mostramos cómo cambiar esta configuración en Cómo permitir la ejecución de scripts de PowerShell en Windows 7. Sin embargo, esto también es algo que no desea hacer en cualquier computadora.
    3. Algunos scripts de PowerShell no funcionarán sin los permisos de administrador.
      Incluso si se ejecuta con una cuenta de nivel de administrador, debe pasar por el Control de cuentas de usuario (UAC) para realizar ciertas acciones. No queremos deshabilitar esto, pero sigue siendo bueno cuando podemos hacerlo un poco más fácil de manejar.
    4. Algunos usuarios pueden tener entornos PowerShell personalizados.
      Probablemente no se encontrará con esto a menudo, pero cuando lo hace, puede hacer que la ejecución y la solución de problemas de sus scripts sean un poco frustrantes. Afortunadamente, podemos solucionar esto sin hacer cambios permanentes también.

    Paso 1: Haga doble clic para ejecutar.

    Comencemos por abordar el primer problema: las asociaciones de archivos .PS1. No puede hacer doble clic para ejecutar archivos .PS1, pero puede ejecutar un archivo .BAT de esa manera. Por lo tanto, escribiremos un archivo por lotes para llamar al script de PowerShell desde la línea de comandos para nosotros.

    Por lo tanto, no tenemos que volver a escribir el archivo por lotes para cada secuencia de comandos, o cada vez que movamos una secuencia de comandos, utilizará una variable de referencia automática para crear la ruta de acceso del archivo para la secuencia de comandos de PowerShell. Para que esto funcione, el archivo por lotes deberá colocarse en la misma carpeta que su script de PowerShell y tener el mismo nombre de archivo. Por lo tanto, si su secuencia de comandos de PowerShell se llama "MyScript.ps1", querrá nombrar su archivo por lotes "MyScript.bat" y asegurarse de que esté en la misma carpeta. Luego, ponga estas líneas en el script por lotes:

    @ECHO OFF PowerShell.exe -Comando "& '% ~ dpn0.ps1'" PAUSE

    Si no fuera por las otras restricciones de seguridad establecidas, eso sería todo lo necesario para ejecutar un script de PowerShell desde un archivo por lotes. De hecho, la primera y la última líneas son principalmente una cuestión de preferencia: es la segunda línea la que realmente hace el trabajo. Aquí está el desglose:

    @ECHO OFF desactiva el comando haciendo eco. Esto solo evita que sus otros comandos se muestren en pantalla cuando se ejecuta el archivo por lotes. Esta línea está oculta por el uso del símbolo at (@) delante de ella.

    PowerShell.exe: comando "& '% ~ dpn0.ps1'” en realidad ejecuta el script de PowerShell. PowerShell.exe, por supuesto, puede invocarse desde cualquier ventana de CMD o archivo de proceso por lotes para iniciar PowerShell en una consola simple como de costumbre. También puede usarlo para ejecutar comandos directamente desde un archivo por lotes, incluyendo el parámetro -Command y los argumentos apropiados. La forma en que se usa para apuntar a nuestro archivo .PS1 es con la variable especial% ~ dpn0. Ejecutado desde un archivo por lotes,% ~ dpn0 evalúa la letra de la unidad, la ruta de la carpeta y el nombre del archivo (sin extensión) del archivo por lotes. Dado que el archivo por lotes y el script de PowerShell estarán en la misma carpeta y tendrán el mismo nombre,% ~ dpn0.ps1 se traducirá a la ruta completa del archivo del script de PowerShell.

    PAUSA simplemente detiene la ejecución por lotes y espera la entrada del usuario. Esto generalmente es útil para tener al final de sus archivos por lotes, de modo que tenga la oportunidad de revisar cualquier salida de comando antes de que desaparezca la ventana. A medida que pasamos por la prueba de cada paso, la utilidad de esto se volverá más obvia..

    Por lo tanto, el archivo de proceso por lotes básico está configurado. Para fines de demostración, este archivo se guarda como "D: \ Script Lab \ MyScript.bat" y hay un "MyScript.ps1" en la misma carpeta. Veamos qué sucede cuando hacemos doble clic en MyScript.bat.

    Obviamente, el script de PowerShell no se ejecutó, pero es de esperar. Después de todo, solo hemos abordado el primero de nuestros cuatro problemas. Sin embargo, hay algunos puntos importantes demostrados aquí:

    1. El título de la ventana muestra que el script por lotes inició PowerShell con éxito..
    2. La primera línea de salida muestra que un perfil de PowerShell personalizado está en uso. Este es el problema potencial # 4, mencionado anteriormente.
    3. El mensaje de error muestra las restricciones de ExecutionPolicy vigentes. Ese es nuestro problema # 2.
    4. La parte subrayada del mensaje de error (que se realiza de forma nativa mediante la salida de error de PowerShell) muestra que el script por lotes estaba apuntando correctamente al script de PowerShell deseado (D: \ Script Lab \ MyScript.ps1). Así que al menos sabemos que mucho está funcionando correctamente..

    El perfil, en este caso, es un simple script de una línea que se utiliza para que esta demostración genere resultados siempre que el perfil esté activo. También puede personalizar su propio perfil de PowerShell para hacer esto, si desea probar estos scripts usted mismo. Simplemente agregue la siguiente línea a su script de perfil:

    Escritura-salida '¡Perfil PowerShell personalizado en efecto!'

    El ExecutionPolicy en el sistema de prueba aquí se establece en RemoteSigned. Esto permite la ejecución de scripts creados localmente (como el script de perfil), mientras que bloquea scripts de fuentes externas a menos que estén firmados por una autoridad confiable. Para fines de demostración, el siguiente comando se usó para marcar MyScript.ps1 como proveniente de una fuente externa:

    Add-Content -Path 'D: \ Script Lab \ MyScript.ps1' -Value "[ZoneTransfer] 'nZoneId = 3" -Stream' Zone.Identifier '

    Eso establece el flujo de datos alternativos Zone.Identifier en MyScript.ps1 para que Windows piense que el archivo proviene de Internet. Se puede revertir fácilmente con el siguiente comando:

    Clear-Content -Path 'D: \ Script Lab \ MyScript.ps1' -Stream 'Zone.Identifier'

    Paso 2: Moverse por ExecutionPolicy.

    Moverse por la configuración de ExecutionPolicy, desde CMD o un script por lotes, es bastante sencillo. Simplemente modificamos la segunda línea del script para agregar un parámetro más al comando PowerShell.exe.

    PowerShell.exe -ExecutionPolicy Bypass -Command "& '% ~ dpn0.ps1'"

    El parámetro -ExecutionPolicy se puede usar para modificar la ExecutionPolicy que se usa cuando genera una nueva sesión de PowerShell. Esto no persistirá más allá de esa sesión, por lo que podemos ejecutar PowerShell de esta manera siempre que sea necesario sin debilitar la postura de seguridad general del sistema. Ahora que hemos arreglado eso, vamos a intentarlo de nuevo:

    Ahora que el script se ha ejecutado correctamente, podemos ver lo que realmente hace. Nos está informando que estamos ejecutando el script como usuario limitado. De hecho, el script está siendo ejecutado por una cuenta con permisos de administrador, pero el Control de cuentas de usuario se está interponiendo. Si bien los detalles de cómo el script está verificando el acceso del Administrador están más allá del alcance de este artículo, aquí está el código que se está utilizando para la demostración:

    if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity] :: GetCurrent ()). IsInRole ([Security.Principal.WindowsBuiltInRole] "Administrator")) Write-Output 'Running as Administrator!' else Write-Output 'Running Limited!' Pausa

    También notará que ahora hay dos operaciones de "Pausa" en la salida del script: una del script de PowerShell y otra del archivo por lotes. La razón de esto será más evidente en el siguiente paso..

    Paso 3: Obtener acceso de administrador.

    Si su secuencia de comandos no ejecuta ningún comando que requiera elevación, y está bastante seguro de que no tendrá que preocuparse por que los perfiles personalizados de nadie se interpongan, puede omitir el resto de esto. Sin embargo, si está ejecutando algunos cmdlets de nivel de administrador, necesitará esta pieza.

    Desafortunadamente, no hay manera de desencadenar UAC para la elevación desde un archivo por lotes o una sesión CMD. Sin embargo, PowerShell nos permite hacer esto con Start-Process. Cuando se usa con "-Verb RunAs" en sus argumentos, Start-Process intentará iniciar una aplicación con permisos de administrador. Si la sesión de PowerShell aún no está elevada, esto activará un indicador de UAC. Para usar esto desde el archivo de proceso por lotes para iniciar nuestro script, terminaremos generando dos procesos de PowerShell: uno para iniciar Start-Process y otro, iniciado por Start-Process, para ejecutar el script. La segunda línea del archivo por lotes debe cambiarse a esto:

    PowerShell.exe -Command "& Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File" "% ~ dpn0.ps1" "-Verb RunAs"

    Cuando se ejecuta el archivo por lotes, la primera línea de salida que veremos es del script de perfil de PowerShell. Luego, habrá una solicitud de UAC cuando el Proceso de inicio intente iniciar MyScript.ps1.

    Después de hacer clic en el indicador de UAC, se generará una nueva instancia de PowerShell. Como se trata de una nueva instancia, por supuesto, volveremos a ver el aviso del script del perfil. Luego, MyScript.ps1 se ejecuta y vemos que de hecho estamos en una sesión elevada.

    Y ahí está la razón por la que también tenemos dos pausas aquí. Si no fuera por el script de PowerShell, nunca veríamos la salida del script: la ventana de PowerShell simplemente aparecerá y desaparecerá tan pronto como el script termine de ejecutarse. Y sin la pausa en el archivo por lotes, no podríamos ver si hubo algún error al iniciar PowerShell en primer lugar.

    Paso 4: Moverse por los perfiles personalizados de PowerShell.

    Vamos a deshacernos de ese desagradable aviso de perfil personalizado ahora, ¿vale? En este caso, es apenas una molestia, pero si el perfil de PowerShell de un usuario cambia la configuración, las variables o las funciones predeterminadas de una forma que quizás no haya anticipado con su secuencia de comandos, pueden ser realmente problemáticas. Es mucho más sencillo ejecutar el script sin el perfil por completo, por lo que no tiene que preocuparse por esto. Para hacer eso, solo necesitamos cambiar la segunda línea del archivo por lotes una vez más:

    PowerShell.exe -NoProfile -Command "& Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File" "% ~ dpn0.ps1" "-Verb RunAs"

    Agregar el parámetro -NoProfile a las dos instancias de PowerShell que se inician con el script significa que el script de perfil del usuario se omitirá por completo en ambos pasos y nuestro script de PowerShell se ejecutará en un entorno predeterminado bastante predecible. Aquí puede ver que no hay ningún aviso de perfil personalizado en ninguna de las conchas generadas.

    Si no necesita derechos de administrador en su secuencia de comandos de PowerShell y omitió el Paso 3, puede prescindir de la segunda instancia de PowerShell y la segunda línea de su archivo por lotes debería tener este aspecto:

    PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '% ~ dpn0.ps1'"

    La salida se verá así:

    (Por supuesto, en el caso de los scripts que no son de administrador, podría hacerlo sin una pausa de final de script en su script de PowerShell en este punto, ya que todo se captura en la misma ventana de la consola y se mantendrá allí por la pausa al final de el archivo por lotes de todos modos.)

    Archivos por lotes completos.

    Dependiendo de si necesita o no permisos de administrador para su secuencia de comandos de PowerShell (y realmente no debería solicitarlos si no los necesita), el archivo de lote final debe verse como uno de los siguientes.

    Sin acceso de administrador:

    @ECHO OFF PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '% ~ dpn0.ps1'" PAUSE

    Con acceso de administrador:

    @ECHO OFF PowerShell.exe -NoProfile -Command "& Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File" "% ~ dpn0.ps1" "' -Verb RunAs" PAUSE

    Recuerde colocar el archivo por lotes en la misma carpeta que el script de PowerShell para el que desea usarlo, y asígnele el mismo nombre. Entonces, sin importar a qué sistema lleve esos archivos, podrá ejecutar su secuencia de comandos de PowerShell sin tener que jugar con ninguna de las configuraciones de seguridad del sistema. Ciertamente, puedes hacer esos cambios manualmente cada vez, pero esto te ahorra ese problema y no tendrás que preocuparte por revertir los cambios más adelante.


    Referencias:

    • Ejecutando scripts de PowerShell desde un archivo por lotes - Blog de programación de Daniel Schroeder
    • Comprobando los permisos de administrador en PowerShell - ¡Hola, Scripting Guy! Blog