{"id":14064555,"url":"https://github.com/majkinetor/posher","last_synced_at":"2025-05-15T13:31:07.793Z","repository":{"id":30335206,"uuid":"33887560","full_name":"majkinetor/posher","owner":"majkinetor","description":"Windows image  build system via POwerSHell + packER","archived":true,"fork":false,"pushed_at":"2016-06-12T16:23:40.000Z","size":135,"stargazers_count":36,"open_issues_count":1,"forks_count":5,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-13T12:05:15.882Z","etag":null,"topics":["automation","iso","powershell","windows"],"latest_commit_sha":null,"homepage":"","language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/majkinetor.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-13T18:57:39.000Z","updated_at":"2024-12-31T17:58:46.000Z","dependencies_parsed_at":"2022-07-31T08:48:01.279Z","dependency_job_id":null,"html_url":"https://github.com/majkinetor/posher","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/majkinetor%2Fposher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/majkinetor%2Fposher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/majkinetor%2Fposher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/majkinetor%2Fposher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/majkinetor","download_url":"https://codeload.github.com/majkinetor/posher/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254349270,"owners_count":22056309,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","iso","powershell","windows"],"created_at":"2024-08-13T07:03:55.715Z","updated_at":"2025-05-15T13:31:06.946Z","avatar_url":"https://github.com/majkinetor.png","language":"PowerShell","funding_links":[],"categories":["PowerShell"],"sub_categories":[],"readme":"Posher\n======\n\n.. contents::\n   :local:\n\nPosher is a build system that generates images for Windows 2012 family of operating systems - all variants of Windows Server 2012 and Windows 8. Machines are defined using Powershell scripts and built using `Packer \u003chttps://www.packer.io/\u003e`__.\n\nThe main features of the system are:\n\n- Hierarchical machine definition - machine can inherit from another one which serves as a base system and then it can add or tweak options, features and provisioning elements on top of those already defined in the parent machines. The system is made so that all the different types of machines used for specific project can be described and created in this manner while keeping the entire process `DRY \u003chttp://en.wikipedia.org/wiki/Don't_repeat_yourself\u003e`__.\n- Strict usage of the Powershell scripting rather then outdated cmd.exe shell.\n- Extensive auditing of installed options so that one can understand what is inside the machine just by looking in the log file of the build system.\n- Support for multiple virtualization platforms via Packer. Currently, the machines are built for vmWare and VirtualBox providers with addition of Vagrant box. Other providers that Packer supports can easily by added if required.\n\nPosher can be used for:\n\n- Creation of referent machines for which developers program desired features. Usage of referent machines solve the *it works on my computer* problem as functionality is considered done if it is successfully deployed and tested on the referent machine(s).\n- Using single code base for setting up machines for all types of environments in a service life cycle.\n- Creation of immutable infrastructure which is defined and versioned as a source code.\n\n\nPrerequisites\n-------------\n\n- `Windows Management Framework 4.0 \u003chttp://www.microsoft.com/en-us/download/details.aspx?id=40855\u003e`_ or newer.\n- `Packer \u003chttps://www.packer.io/\u003e`__\n- `VirtualBox \u003chttps://www.virtualbox.org\u003e`__  (if the build type includes VirtualBox output)\n- `vmWare Workstation \u003chttp://www.vmware.com/products/workstation\u003e`__ (if the build type includes vmware output)\n- `Vagrant \u003chttps://www.vagrantup.com/\u003e`__ (to test virtualbox boxes)\n\nThe easiest way to install all open source prerequisites is via `Chocolatey \u003chttps://chocolatey.org\u003e`__ repository::\n\n    choco install packer virtualbox vagrant\n\n\nCreating machine\n----------------\n\nMachines are placed in the ``machines`` directory and described in Powershell syntax. The only input for the machine apart from assets required for provisioning of vendor tools is the ISO image of the desired OS. ISO files can be linked from the Internet, SMB share or locally by placing them into ``iso`` directory (using symbolic link is also an option via ``iso\\New-SymLink.ps1`` function).\n\nTo start defining a machine in a Powershell, first check `machines\\_default.ps1 \u003chttps://github.com/majkinetor/posher/blob/master/machines/_default.ps1\u003e`__ which contains all variables supported by the build system and their default values. This file should not be edited - a new Powershell file should be created for each machine which sources aforementioned defaults.\n\nAs an example, lets say we want all servers for the service to have some common foundation on which we can further specialise for different roles. We can create ``base-server.ps1`` to describe this configuration::\n\n    . \"$PSScriptRoot/_default.ps1\"\n\n    $OS_ISO_NAME     = 'SW_DVD5_Windows_Svr_Std_and_DataCtr_2012_R2_64Bit_English_Core_MLF_X19-05182'\n    $OS_ISO_CHECKSUM = '6823c34a84d22886baea88f60e08b73001c31bc8'\n    $OS_TYPE         = @{vmWare = 'windows8srv-64'; virtualbox = 'Windows2012_64'}\n    $OS_ANSWER_FILE  = '2012_r2'\n\n    $WINDOWS_UPDATE                  = $true\n    $WINDOWS_UPDATE_CATEGORIES_LIST += 'CriticalUpdates', 'SecurityUpdates'\n    #$WINDOWS_UPDATE_KB_LIST        += 'KB2939087'\n\n    $WINDOWS_TWEAKS                  = $true\n    $WINDOWS_TWEAKS_SCRIPT = {\n        Explorer-Feature -ShowHidden -ShowSupperHidden -ShowFileExtensions -ShowRun -ShowAdminTools -PSOpenHere\n        CLI-Feature      -EnableQuickEdit\n        System-Feature   -NoUAC -NoHibernation -NoShutDownTracker -NoAutoUpdate\n    }\n\n    $WINDOWS_FEATURE = $true\n    $WINDOWS_FEATURE_LIST = @(\n        \"PowerShell-ISE\"\n    )\n\nThis will define the ``base-server`` so that:\n\n- It will use specified ISO image and answer file with the given name ( ``OS_ISO_NAME`` and ``OS_ANSWER_FILE`` variables ).\n- The build option ``WINDOWS_UPDATE`` is enabled which means that during OS setup the specified windows updates will be installed. In this example only critical and security updates are installed (variable ``WINDOWS_UPDATE_CATEGORIES_LIST``). The commented option ``WINDOWS_UPDATE_KB_LIST`` is used for deterministic updates as defining updates via category list will produce non-deterministic operating system on which updates are installed as soon as they are available which can potentially create a problem with some applications.\n- The build option ``WINDOWS_TWEAKS`` is enabled which is integrated list of small Windows customizations which are self describing in above case. The option accepts single script block which calls 3 functions that tweak OS installation.\n- At the end, there is one Windows features that will be installed on the base server - Powershell-ISE.\n\nLater we can either build this base server or create another machine based on it. If, for instance, we need IIS web server on top of the base server definition, we can define the machine ``server-web.ps1`` such as::\n\n    . \"$PSScriptRoot/base-server.ps1\"\n\n    $CPU    = 4\n    $MEMORY = 4GB\n    $DISK   = 60GB\n\n    $WINDOWS_FEATURE_LIST += @(\n    # Web server modules\n        \"Web-Common-Http\",\n        \"Web-Security\",\n    # \"Web-App-Dev\"\n        \"Web-CGI\",\n        \"Web-ISAPI-Ext\",\n        \"Web-ISAPI-Filter\",\n        \"Web-Includes\",\n    # Web Management Tools\n        \"Web-Mgmt-Console\",\n        \"Web-Scripting-Tools\",\n        \"Web-Mgmt-Service\",\n    # Dot.Net 4.5\n        \"NET-Framework-45-ASPNET\"\n        \"NET-Framework-45-Features\"\n    )\n\n    # Vagrant settings\n    $BOX_DESCRIPTION = \"IIS web server\"\n    $BOX_VERSION     = 1.1\n    $BOX_STORE       = \"file:////itshare.mycompany.com/_images/projectX/projectx-server-web\"\n\nIn the above example the new server is defined so that it:\n\n- uses specified number of CPUs (default is 1) and desired memory and disk size.\n- adds new Windows features to the ``WINDOWS_FEATURE_LIST`` of the already specified features in the base server (hence ``+=``).\n- defines few Vagrant related variables - ``BOX_XXX`` -  which may be needed for the development environments with the machine.\n\nDepending on the parameter, the machine can either inherit the parameter value from the parent machine, redefine it, or add it to the existing list. Machines can be defined this way to the arbitrary depth and any machine in the hierarchy can be built by specifying its name as an argument of the build script.\n\nHost and guest provision\n------------------------\n\nThere is an option to specify provision scriptblock on either the host (the one that builds the image, before or after the image build process is started) or the machine that is being built.\n\nThe following machine ``server-web-extra`` inherits from the ``server-web`` and during the build it requires credentials for the share, exports the credentials temporarily to copy and use them within the context of the new machine in order to install the application from the share. At the end of the build it deletes temporary file on the host::\n\n    . \"$PSScriptRoot/server-web.ps1\"\n\n    #Executes on host\n    $BUILD_START_LIST += {\n        $err = export_credential $args.Credential -Store './machines' -AskMsg 'Enter credentials for the administrative share:'\n        if ($err) { \"Credential export failed - $err\"; return $false }\n    }\n\n    #Executes on host\n    $BUILD_END_LIST += {\n        \"Deleting temporary files on host\"\n        rm \"./machines/*.sss\" -ea ignore\n    }\n\n    #Executes on guest\n    $PROVISION_LIST  += {\n        \"Loading credentials\"\n        $f = gi \"*.sss\"\n        $Credential = load_credential $f\n        if (!$Credential) { throw \"Can't load credentials.\" }\n        rm $f\n\n        New-PSDrive -Name adminshare -PSProvider FileSystem -Root \\\\itshare.mycompany.com\\install -Credential $Credential\n        $installer = \"adminshare:\\ToolXYZ\\toolxyz.msi\"\n        start -Wait msiexec -ArgumentList \"/quiet\", \"ADDLOCAL=ALL\", \"/i $installer\"\n        if (Test-Path 'c:\\program files\\toolxyz\\toolxyz.exe) { \"Install OK\" } else { throw \"Install failed\" }\n    }\n\n    function load_credential($File) {\n        if (!$File) { return }\n        $u = $File.BaseName.Replace('-', '\\')\n        $p = ConvertTo-SecureString (gc $File) -Key (1..16)\n        New-Object -Type PSCredential -ArgumentList $u, $p\n    }\n\n    function export_credential($Credential, $Store, $AskMsg){\n        gi $Store -ErrorVariable err -ea 0 | out-null\n        if ($err) { return $err }\n\n        if (!$Credential -or $Credential.gettype() -ne [PSCredential]) {\n            $Credential = Get-Credential $Credential -Message $AskMsg\n            if (!$Credential) { Write-Error \"Credential input canceled.\" -ev err -ea 0; return $err }\n        }\n\n        try {\n            $fp = \"{0}/{1}.sss\" -f $Store, $Credential.UserName.Replace('\\', '-')\n            rm $fp -ea ignore\n            ConvertFrom-SecureString -SecureString $Credential.Password -Key (1..16) | out-file $fp\n        } catch { $_ }\n    }\n\nOptions\n-------\n\nThe build system currently supports the following options that are so commonly tweaked that they deserved to be specially handled:\n\nWINDOWS_UPDATE\n    Allows installation of predefined set of updates with desired level of determination. To be totally deterministic specify list of KBs, otherwise specify some of the allowed categories.\n\nWINDOWS_TWEAKS\n    Allows for installation of small tweaks from the list of supported tweaks. For the complete list of tweaks see ``scripts\\windows-tweaks.ps1``.\n\nWINDOWS_FEATURES\n    Enables the list of the Windows features that are shipped with the OS and installed using ``OptionalFeatures.exe`` on a workstation Windows (Control Panel -\u003e Turn Windows Features On or Off) or using Server Manager Roles and Features GUI interface on a server. To get the complete list of features, use the following cmdlets: ``Get-WindowsOptionalFeature`` (workstation) and ``Get-WindowsFeature`` (server).\n\nPROVISION\n    Enables the list of provisioning Powershell scriptblocks. Each machine can add its own provisioner in the ``$PROVISION_LIST`` list.\n\nFINALIZE\n    Allows finalization script to run. This script cleans up the system, deletes temporary files, defragments and shreds the disk etc. The procedure is lengthy and can be disabled while testing.\n\nEach of those options can be turned on or off using simple Powershell statement. For instance::\n\n    $WINDOWS_UPDATE = $false\n\nwill turn off integrated Windows update build option which may be useful during testing as updates usually take a long time to finish.\n\nFor detailed description of all options check out comments in the ``machines\\_default.ps1`` script.\n\nBuild\n-----\n\nTo generate the virtual image use ``build.ps1`` script::\n\n    .\\build.ps1 -Machine server-web\n\nThe length of the procedure depends on the machine definition - location of the ISO file, whether Windows updates are enabled and so on. After the build process finishes, the images and log files will be available in the ``output\\\u003cmashine_name\u003e`` directory. Detailed log of the complete operation is saved in the file ``posher.log``. Distribution of the machine should include this file because it provides information about the machine installation and any step of the installation starting from the ISO file can be manually reconstructed using the information within the log file and few other files that are also stored in the output folder.\n\nTo build the machine only for the specific platform use the build parameter ``Only``::\n\n    .\\build.ps1 -Machine server-web -Only virtualbox\n\nWithout this parameter build will produce machines for all supported platforms in parallel.\n\nWhen you try to build above machine with host and guest provisioning ( server-web-extra ), credential pop up will appear on the host and the build continues after the user enters it correctly or fails on any error. To build this machine non-interactively, parameter can be passed to the build script via ``Data`` argument::\n\n    ./build.ps1 -Machine base-server-extra -Data @{ Credential = Get-Credential } -Verbose\n\nIf the provisioning code is big, put it in the separate script file in the ``./machines`` directory and source it from the provisioning scriptblock.\n\nFor detailed description of the build function execute ``man .\\build.ps1 -Full``.\n\nAccessing the machine\n---------------------\n\nAfter the build is completed, you can boot up the VirtualBox image using Vagrant.  ``Vagrantfile`` is designed in such way that you can easily test any local images (those in the ``output`` directory). Quickly switch from using local to remote box storage using ``VAGRANT_LOCAL`` variable. Any machine that is created in ``machines`` directory can be booted this way without modifications of the ``Vagrantfile``::\n\n    vagrant destroy server-web\n    vagrant box remove server-web\n\n    $Env:VAGRANT_LOCAL=1\n    vagrant up server-web\n    vagrant rdp server-web\n\nThe last two commands will fire up the machine and connect to it via remote desktop. If something goes wrong and RDP is not working you can set ``$Env:VAGRANT_GUI=1`` to show VirtualBox GUI, otherwise machine will run in the headless mode.\n\nThe other way to connect to the machine is via Powershell remoting using its IP address::\n\n    etsn 192.168.0.xx -Credential localhost\\vagrant\n\nFor this to work the machine IP (or glob ``*``) must be specified in the  ``TrustedHosts`` parameter in the WinRM client settings::\n\n    Set-Item WSMan:\\localhost\\Client\\TrustedHosts * -Force\n\nOnce you are happy with the machines those should be deployed to the share. For this purpose Vagrant metadata json is crafted that among other things provides option to version remote boxes so that users can see when those boxes they use are later updated during ``vagrant up`` command. Developers can use those boxes but to provide access to them manual intervention of ``Vagrantfile`` is required to specify exact machine names - simply replace dynamic ruby hash ``$machines`` with static version listing machine names.\n\nTo test wmWare images with Vagrant require proprietary Vagrant driver. If those are not available testing can be done with vmWare Workstation command line tools easily, although setting advanced options such as shared folders and customizing memory and disk will require extra work::\n\n    vmrun -T ws start \"output\\server-web\\packer-server-web-vmware.vmx\"\n\nOn production\n-------------\n\nAlthough one of the design goals of the system was to use the same machine code in the production, test and development environments with any specific configuration moved to environment variables, it is not currently tested in production environments and would at minimal require some security related actions such as removal of vagrant administrative user. Some of the future versions will address those issues.\n\nMore info\n---------\n\n**Articles**\n\n- `Immutable Server \u003chttp://martinfowler.com/bliki/ImmutableServer.html\u003e`__\n- `Virtualize Your Windows Development Environments with Vagrant, Packer, and Chocolatey \u003chttp://www.developer.com/net/virtualize-your-windows-development-environments-with-vagrant-packer-and-chocolatey-part-1.html\u003e`__\n- `In search of a light weight windows vagrant box \u003chttp://www.hurryupandwait.io/blog/in-search-of-a-light-weight-windows-vagrant-box\u003e`__\n\n**Related Projects**\n\n- `Packer-Windows \u003chttps://github.com/joefitzgerald/packer-windows\u003e`__\n- `Boxcutter Windows templates \u003chttps://github.com/boxcutter/windows\u003e`__\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmajkinetor%2Fposher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmajkinetor%2Fposher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmajkinetor%2Fposher/lists"}