3. Git for Admins That Don’t Get Git

My Story

I work in an operations group that doesn’t write much code. Most of my colleagues are the “old dogs” still clicking away in their GUIs. These “old dogs” spend a lot of time working serially in wizards to accomplish the same thing that most people within the PowerShell community know can be done more efficiently with PowerShell. Within this GUI-centric working environment, I have spent the past couple years trying to foster a culture that embraces more Infrastructure-as-Code and less work in the GUI. One of my biggest hurdles was getting the existing organizational code captured in a single place, where everyone in my group could read and learn from each other. Currently, each administrator had unique pieces of code, their unique workflows, and their unique storage locations. This fragmented environment posed a big part of the problem. No one can learn from each other when everyone stays siloed in their unique processes and workflows. I knew getting all the code for these different service stacks into a single place would be a considerable benefit to everyone overall, but the question was how. I believe many other administrators fall into this same category of wanting to centralize code. This solution described in this chapter has worked wonders for our group. It helped those that don’t write much code follow a “Best Practices” model. It has also sparked conversations and training opportunities with other users that do write more code. This sharing and collaboration provide an excellent opportunity to learn git workflows without a heavy burden.

Intro

This chapter will walk you through how to automate moving code into a Git repository. This process will allow users that are not familiar with Git workflows to copy code into a shared folder. This shared folder will automatically sync the new and changed files to a remote Git repository.

The chapter covers the following aspects:

  • How to configure a service account on a local file server.
  • How to enable the service account as a Git account.
  • How to create an SSH key.
  • How to deploy the SSH key to GitHub.
  • How to push code from the shared folder to a remote Git repository.
  • How to create a scheduled task to automate the push.

Step 1: Create Accounts

Local Service Account

You will need a service account to run the scheduled task on the local development server. For this example, the service account will be named svc.gitSync. This account does not need any special permissions on the development server. A basic user account is sufficient. The service account requires the ability to read and write to the shared folder. When selecting the shared folder’s location, avoid a folder within a user profile, and make sure normal users have the appropriate access to this folder.

The following PowerShell code creates this service account.

$Splat = @{
    Name = 'svc.gitSync'
    Password = (ConvertTo-SecureString -String 'P@ssw0rd' -AsPlainText -Force)
    Description = 'Service Acct syncing C:\GitSync to GitHub'
}
New-LocalUser @Splat

Email and GitHub Accounts

Next, create an email account for use by the svc.GitSync account to connect to GitHub. Any email account will work. I used Gmail for this demo and created svc.gitSync@gmail.com. After you set up the email account, use that email address to create a GitHub account. This part will be different depending on the situation.

Step 2: Install Git

Git is a set of binaries that anyone will use to interact with versioning and source control. Download the Git binaries from the Git website. After download, install the software as you would any other. Another option is to use a package manager, such as Chocolatey.

If you have Chocolatey installed, run the following to install Git.

choco install git

Step 3: Configure Git and SSH

Configure Git

At this point, it is now possible to create a folder, initialize it as a Git repository, and begin using Git functions; however, there is no ability to push data to a remote location. To complete post-installation tasks and allow an account to commit to a remote repository, identity information and SSH keys need to be specified and created. The SSH key will be in the user’s local profile directory. If you choose to move the key to another location, you will have to do some extra work to tell Git where the SSH key is.

After the initial Git installation, there will be a small, generic list of data configured. You can view those settings by issuing git config --list. This command shows you the currently configured settings. Notice that user.name and user.email are not listed. To ensure that the service account will submit code as it’s identity, issue the following commands:

git config user.email --global "svc.gitSync@gmail.com"
git config user.name --global "svc.gitSync"

These commands will set the name and email address for this service account globally. All repositories created on this server will default to using the specified username and email. If a single repository needs to use a different identity, issue the above commands without --global.

Configure SSH

To begin creating an SSH key, execute the following command on the development server:

ssh-keygen -t rsa -b 4096 -C "svc.gitSync@gmail.com"

Once executed, there will be a set of prompts that require a response. The first prompt is where to store your public/private key pair. Keep the public key file in a location where only the service account has access. For this example, the default location is sufficient. The next prompt is your password prompt. You need to make sure you DO NOT set a password.

Step 4: Create a GitHub Repo

Once the GitHub account is created and signed in, click “Create Repository” and make a new repository.

For the demo, leave the repository as a Public repository.

On the local development server, log in as the service account. This initial configuration requires an interactive session. There are a few prompts on the first git push that require the service account to respond interactively. First, create a local folder that will sync to the GitHub repository previously created. For this demo, name it GitCronSync. Create the directory with this command:

New-Item -Type Directory -Path 'C:\git\GitCronSync'

This folder will be the destination for all users to save their scripts. Next, retrieve the SSH public key. If you used the default location, then the SSH key will be at:

"C:\Users\<UserName>\.ssh\id_rsa.pub"

Open the id_rsa.pub file with a text editor and copy the data. The SSH key should look something like this:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDpENPa4n90/sXVZ9TkL9r6GhFHLcVvESoctn2COY2
NbSU3D8JT5g4sCvMyTtD1/Z7/noJT0QCv1RLyCmbYa2/kZfPXZoIyeYK1nb0+A5d1ZSW5QmFLheo2HD
p5y/n10/m/A7I3PODQLyNby3XifE6EDKVByKI6W0e3Koc7Q0EQ6Q7WikB4kMhIFI4tBrgQbmqBj569R
ROPFlOR2v2Qfu0BYVg3Od5Fcimf3xPhzeEuGPKTfj7F9WyoD5kSV1cijUoWQlIoRfLi49GAcupMYr98
QvogUPznJxTcyIWmtWVUZOtkaSKCtSawD48BqTJNOXtzPda79ht/LrdAbhx0rthLev/uBlaiNTcAsfq
bmcrSxJOon3XVRR1MG32bylgaAsZFcDCWs0sTcjIWX+kHTVvYrDJRuoTC4USe+G+qUIIoQ/qYsCLjZb
dUii/02i6bSBGjNAByq2iO/OdHrGOCzlcJTgBWv4laK/LQRdPIwWRSqN7gNHx0Aeujpqr3OA0ovvako
taHJa9j9lqKIzTw3biZ0v5kmIIhwhcVcDzOxuaHaqTts+7IJsUNDvp4raJRgzvZAkHDemTdp6IRmYrD
Je1NX0Fu6i8mhuJFORY7e3Yz1SKJThJDUMjs2A22WxKmtalOQD6sj4cwq94ZroP8hWrMF818gfCev/O
8I88K5ynUw== svc.gitSync@gmail.comsvc.gitSync@gmail.com

This key is the public side of the service account’s identity. The service account will authenticate to GitHub using the private key, and this will allow the service account to push to the repository. With the SSH public key in the clipboard, add the copied public key to the GitHub user profile that you set up previously. Navigate to the settings of the GitHub user. At the time of this chapter’s creation, the steps are:

  1. In the top right corner, click your user.
  2. Click Settings.
  3. Select SSH and GPG Keys.
  4. Select “New SSH Key.”
  5. Paste the public key you copied from the text editor, above, into the “key” field and provide a title for this SSH key.
  6. Click “Add SSH Key.”

Once the SSH key is attached to the user, the service account will be able to push data from the local repository on the development server. There should be a code block example in GitHub, located in the empty repository you just created. Copy and paste it into a PowerShell console after traversing into the shared folder on the local development server. These commands will turn the shared folder on your local development server into a Git repository. That code should look something like this:

echo "# GitCronSync" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:svcGitSync/GitCronSync.git
git push -u origin master

Upon refreshing the GitHub webpage, a README file should exist.

Step 5: Create the Git Sync Script

There isn’t much to the script that will automate the push to the remote repository. The code listed below is not complicated. This script can be as verbose or simple as you choose to make it. Overall, the script does the following:

  1. Move into the shared folder that users are copying files in to.
  2. Construct a commit message to apply later.
  3. Execute git add . to select all the current changed files.
  4. Execute git commit -m "$CommitMessage" to add the required commit message.
  5. Execute git push to sync all the changes to the GitHub repository, configured above.

A few points to note:

First, this script’s commit message grabs the current date and time. You can add as much to commit messages as you want. Perhaps use git status to capture the output and use that as the commit message. That would give each commit a list of changed files in the commit message. Let your imagination run wild. Second, capture any file added, removed, or altered in the folder. Any file that has no change will not be selected.

Push-Location 'C:\git\GitCronSync'
$CommitMsg = "Commit files added or updated before {0}" -f (Get-Date -Format g)
git add .
git commit -m "$CommitMsg"
git push

Step 6: Create the Scheduled Task

The last part required is to automate the syncing of the code added to the shared folder. To achieve this, use a scheduled task. Create the scheduled task on the development server where the shared folder exists. When creating a scheduled task via PowerShell, the cmdlets are different from most others. You build a scheduled task using different cmdlets to create specific, required objects for the Register-ScheduledTask cmdlet. Once those objects exist, use them to create the scheduled task. The code should look something like this:

$TaskActionSplat = @{
    Execute = 'PowerShell.exe'
    Argument = '-file C:\scripts\GitCronSync.ps1 -WindowStyle Hidden'
}
$TaskAction = New-ScheduledTaskAction @TaskActionSplat

$TaskTriggerSplat = @{
    At = '1am'
    RepetitionInterval = New-TimeSpan -Minutes 60
    RepetitionDuration = New-TimeSpan -Minutes 60
}
$TaskTrigger =  New-ScheduledTaskTrigger -Once @TaskTriggerSplat

$TaskSettingsSplat = @{
    RestartCount = 3
    RestartInterval = New-TimeSpan -Minutes 5
    ExecutionTimeLimit = New-TimeSpan -Minutes 60
}
$TaskSettings = New-ScheduledTaskSettingsSet @TaskSettingsSplat

$RegisterTaskSplat = @{
    Action = $TaskAction
    Trigger = $TaskTrigger
    Settings = $TaskSettings
    TaskName = 'GitCronSync'
    Description = 'Commits files from C:\GitCronSync to GitHub repo'
    User = 'GitCronSync'
    Password = 'P@ssw0rd'
}
Register-ScheduledTask @RegisterTaskSplat

Looking above, notice you created three different scheduled task objects; a TaskAction, a TaskTrigger, and a TaskSettings object. After you create all the individual parts of the scheduled task, use those objects as parameters to create the scheduled task. This code will configure a scheduled task to run once an hour. The task will stop executing if it runs longer than one hour, and it will attempt to restart three times every 5 minutes if there is a failure to start. The other options in the Splat configure the username and password the task needs to execute as, the name you’ll see in Task Scheduler, and a description.

Conclusion

As with most things, anything worth doing takes some effort. Following the example in this chapter, you have created a service account, a remote repository in GitHub, a folder to sync to the remote GitHub repository, and all the required steps to glue them together. From here, all another user has to do is navigate to the shared folder and drop a script or config file into the appropriate directory. Once per hour, the scheduled task will fire and sync to GitHub. Again, you should not put production code in a public GitHub repository; however, once you have this setup, it shouldn’t be too difficult to adapt this to your production environment.
Enjoy.