This repository builds Windows Master Images and publishes it to Azure Shared Image Gallery with a single run command.
It nicely integrates Packer, Terraform and Ansible. Hook this thing into a real CI/CD pipeline and run it.
An additional trick is: Variables from Terrafrom can be reused in Packer, because since Packer 1.5 HCL2 templates are supported and HCL2 is by the way the preferred way to write Packer configurations. This is why there is a shared_vars.hcl file.
Terraform is used to fulfill the pre-requesites in Azure.
Terraform creates:
- a resource group
- a shared image gallery
- a shared image definition
Packer is used to build the windows master image from a Windows Marketplace image. Packer is also responsible for versioning the master image and placing it into the share image gallery.
To customize the master image, Packer runs Ansible and a specific ansible playbook.
The ansible playbook is quite basic, but this is the place to do the real customization of you windows master image.
Make sure you have an azure service principal account. To create one you can use to following command in the azure command line tool.
Replace your-azure-subscription-id with your real Scription ID. Feel free to also adjust the value of the --name
attribute.
Otherwise you can configure a service principal using the Azure Portal. Make sure it has Contributor
role in your Azure subscription.
# Login
az login
# Create service principal with Contributor role
az ad sp create-for-rbac \
--name="packer_sp" \
--role="Contributor" \
--scope="/subscriptions/your-azure-subscription-id" \
--sdk-auth \
> az_client_credentials.json
Rename the file shared_vars.hcl.sample to shared_vars.hcl
and adjust the variables for your Azure environment.
Documentation on each variable is inside the sample file.
# Rename the file
mv shared_vars.hcl.sample shared_vars.hcl
# Edit the file
nano shared_vars.hcl
Especially for each new run you should increment the version number in the variable azure_shared_image_gallery_destination_image_version
.
If you do not increment the version, then an existing image-version gets overridden.
Edit the file ./packer/setup/openssh.ps1 and replace the existing SSH Public Key with your own.
Otherwise I have access to your machines :-)
The relevant part is in Line 24-27.
# Configure SSH public key
$content = @"
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDRYCV99Ge9LI5Y61t95pkcG7trsDyg/eAHLfTGDMHOGxPDdXSk7wW/OCNsKWeJV20wjayoti2JYB+u4FRvsuyccjRZPlRTsul67QOJEzXfORCHD4EYDTR45l0n08zkUPRfs70yo5L5YG9HYgVr6+EK/F8fFReFDvhZwy8LW/hJSeyVCWlbszmuQYcHRmCkWBeAgEiPqx0Mx17txjw9p4RK/LbweYxN4fGu5Nh2Dz9uSBi2IfGds3rPcQvjnJBzt2GEDoZXdXgwXl4T5B0xEDJVMqS/Q+gArcL82cGsY7zpoWpKfOuVy88GD1qaHZgMvQ3LQRyxVysfhxvzSXRX0eF8518/8hGNLxmSak3dZyi5J2ojPdaYzOxtn4JTDFUKNCBQNLHJZCBl1J7HvilTnwQDbgWm0UoFB0dWG3fX4u1wGm11L9sX0kzQ+NZH2Q+9HVX+1vJWZJuNjlhogcsmXG1hecLZPD7WFl82nbmN7zBQwHtUbjUNERMHvXuUvjPnkjJh0avZVtUCRupJhNgyhTR0cWpzffmA2nzdQpIn6z93zVrgefwIpfr+Grk/XQTOXisIfYz/3sofQ9a2hTdPTj0UwKzULB0Pvsf/aYvVEG7zC0sRfPG/pj5cGeFnOB3Ar2/jd58EB7mLzm45MZ+ztSNRW+Eg32Lq1WgOJZ15TiH/pQ== prianto_autodeploy_id_2018
"@
If you don't need SSH Public Key Authentication, you can remove the whole part between Line 24 and 40.
Run the run.sh.
run.sh runs the build.sh in the ./terrform directory.
After that it runs the build.sh in the ./packer directory.
./run.sh
To cleanup everything in azure, make sure to manually delete all image-versions in the share image gallery. terraform destroy
is not able to delete the image-version, because the image-versions have been built by packer.
After you have cleanup any image-version in the share image gallery you can use the following terraform command to destroy everything.
# cd into terraform directory
cd ./terraform
# run terraform destroy
terraform destroy -var-file ../shared_vars.hcl
You can optionally install Windows Updates during the build operation. If you would like this feature then you need to edit the windows.pkr.hcl file before the build operation. Please uncomment the following sections:
Line: 7-10
windows-update = {
version = "0.14.0"
source = "github.com/rgl/windows-update"
}
Line: 217-224
provisioner "windows-update" {
search_criteria = "IsInstalled=0"
filters = [
"exclude:$_.Title -like '*Preview*'",
"include:$true",
]
update_limit = 25
}
If running windows updates during build, then it sometimes makes sense to reboot the machine
before doing addition configuration tasks using ansible. There is a commented pre_tasks
in the ansible playbook to do exactly this.
Simply uncomment the relevant line 18-22.
pre_tasks:
# If we have installed lots of windows update before running this playbook, then it makes sense to reboot first.
- name: Reboot first
win_reboot:
reboot_timeout: 3600
pre_reboot_delay: 10
post_reboot_delay: 30