<powershell>
# Chef Globals and constants
$chefRegion = "us-west-2"
$chefServer = "CPE-Chef"
$chefEndpoint = "cpe-chef-heqtcjndac2ptfp4.us-west-2.opsworks-cm.io"
$chefOrganization = "default"
$chefVersion = "13.2.20"
$chefCAURL = "https://opsworks-cm-${chefRegion}-prod-default-assets.s3.amazonaws.com/misc/opsworks-cm-ca-2016-root.pem"
$opensslExecutable = "c:\opscode\chef\embedded\bin\openssl.exe"

# Chef Paths and constants
$chefConfig = "c:\chef"
$chefConfigFile = Join-Path $chefConfig "client.rb"
$chefCA = "c:\Users\Administrator\.chef\trusted_certs\"
$chefCAPem = Join-Path $chefCA "opsworks-cm-ca-2016-root.pem"
$chefAttributesFile = Join-Path $chefConfig "attributes.json"
$chefBinPath = "C:\opscode\chef\bin"

# Node Configuration
$chefEnvironment = "<%= chef_environment %>"
$chefRunList = "<%= chef_runlist %>"
$chefAttributes = @"
<%= "\n#{JSON.pretty_generate(chef_attributes)}\n" %>
"@

$newhostname = "<%= hostname %>"
$newdomain = "<%= domain %>"

# Helpers
function Get-InstanceId {
    (Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/instance-id).substring(2)
}

function Get-AssumedCredentials {
    $response = (Use-STSRole -Region us-west-2 -RoleArn "arn:aws:iam::994136867826:role/OpsworksRegisterClientIAMRole-USWest2-7adf25b740fb4c9e37248fa01" -RoleSessionName "ChefBootstrap").Credentials
    New-AWSCredentials -AccessKey $response.AccessKeyId -SecretKey $response.SecretAccessKey -SessionToken $response.SessionToken
}

# Calculated values
$currentHostname = hostname
$desiredHostname = $newhostname
$desiredFQDN = "$newhostname.$newdomain"
$assumedCredentials = Get-AssumedCredentials

# On boot script
$chefOnBootScript = @'
    sleep 15
    $logFile = Join-Path $env:temp 'chef-onboot.log'
    $hostname = (hostname).ToLower()
    if ( !($hostname -like "win-*") -And !($hostname -like "ip-*") -And !($hostname -like "ec2amaz-*" )) {
        Write-Host "Hostname looks valid, running chef in 5 seconds."
        sleep 5
        C:\opscode\chef\bin\chef-run.ps1 > $logFile 2>&1
    } else {
        Write-Host "Hostname looks invalid, skipping chef run."
        "On Boot Script executed, but hostname was invalid: ${hostname}" | Out-File -Encoding UTF8 -filepath $logFile
    }
'@

# Steps
function Install-Prerequisites {
    #Chef
    . { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install -version $chefVersion -project chef

    # Guarentee a few paths exist
    if (!(Test-Path -Path $chefCA)) { New-Item $chefCA -type Directory }
    if (!(Test-Path -Path $chefConfig)) { New-Item $chefConfig -type Directory }

    # CA    
    (New-Object System.Net.WebClient).DownloadFile($chefCAURL, $chefCAPem)
}

function Associate-Node {
    $privateKeyPath = Join-Path $chefConfig "client.pem"
    $publicKeyPath = Join-Path $chefConfig "client.pub"

    iex "${opensslExecutable} genrsa -out ${privateKeyPath} 2048"
    sleep 2

    iex "${opensslExecutable} rsa -in ${privateKeyPath} -pubout -out ${publicKeyPath}"
    sleep 2

    $publicKey = [IO.File]::ReadAllText($publicKeyPath)
    
    $associationToken = Add-OWCMNode -Credential $assumedCredentials -Region $chefRegion -ServerName $chefServer -EngineAttribute @{Name = "CHEF_NODE_PUBLIC_KEY"; Value = $publicKey},@{Name = "CHEF_ORGANIZATION"; Value = $chefOrganization} -NodeName $desiredFQDN
    
    Write-Host -NoNewline "Waiting for node association to complete"
    while ($associationStatus -ne "SUCCESS") {
        Write-Host -NoNewline '.'
        $associationStatus = Get-OWCMNodeAssociationStatus -Credential $assumedCredentials -Region $chefRegion -ServerName $chefServer -NodeAssociationStatusToken $associationToken
        sleep 1
    }
    Write-Host $associationStatus
}

function Write-ChefConfiguration {
    # client.rb
    $clientrb = "chef_server_url 'https://${chefEndpoint}/organizations/${chefOrganization}'", "node_name '${desiredFQDN}'", "ssl_ca_file '${chefCAPem}'"
    Set-Content -Encoding UTF8 $chefConfigFile $clientrb 

    # attributes.json
    # This method is required because ruby's JSON parsing chokes on the UTF8 BOM.
    $encoding = New-Object System.Text.UTF8Encoding($false)
    [IO.File]::WriteAllLines($chefAttributesFile, $chefAttributes, $encoding)

    # chef-run.ps1
    $command = "chef-client -j '${chefAttributesFile}' -r '${chefRunList}'"
    if ($chefEnvironment) { $command += " -E '${chefEnvironment}'" }
    Set-Content -Encoding UTF8 $(Join-Path $chefBinPath 'chef-run.ps1') $command

    # chef-onboot.ps1
    $chefOnBootPath = Join-Path $chefBinPath 'chef-onboot.ps1'
    Set-Content -Encoding UTF8 $chefOnBootPath $chefOnBootScript

    $trigger = New-ScheduledTaskTrigger -AtStartup
    $action = New-ScheduledTaskAction -Execute "c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" -Argument "-ExecutionPolicy Bypass -Command $chefOnBootPath" -WorkingDirectory $chefBinPath
    $settings = New-ScheduledTaskSettingsSet -DontStopOnIdleEnd -ExecutionTimeLimit ([TimeSpan]::Zero)
    $principal = New-ScheduledTaskPrincipal -GroupId "NT AUTHORITY\SYSTEM" -RunLevel "Highest"
    
    $existingTask = Get-ScheduledTask -TaskName "ChefOnBoot" -ErrorAction SilentlyContinue
    if ($existingTask) { Unregister-ScheduledTask -TaskName "ChefOnBoot" -Confirm:$false }
    Register-ScheduledTask "ChefOnBoot" -Action $action -Trigger $trigger -Principal $principal -Settings $settings
}

function Change-Hostname {
    if ($desiredHostname.toLower() -ne $currentHostname.toLower()) {
        Write-Host "Renaming computer and restarting..."
        Rename-Computer -NewName $desiredHostname -Restart -Force
    }
}

Install-Prerequisites
Associate-Node
Write-ChefConfiguration
Change-Hostname
</powershell>