Merge branch 'main' into packer_fmt_multi_args

packer_fmt_multi_args
anshul sharma 1 year ago
commit 294ed3f31c

@ -0,0 +1,40 @@
package function
import (
"encoding/base64"
"fmt"
"os"
"strings"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)
var Filebase64 = function.New(&function.Spec{
Params: []function.Parameter{
function.Parameter{
Name: "path",
Description: "Read a file and encode it as a base64 string",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
path := args[0].AsString()
content, err := os.ReadFile(path)
if err != nil {
return cty.NullVal(cty.String), fmt.Errorf("failed to read file %q: %s", path, err)
}
out := &strings.Builder{}
enc := base64.NewEncoder(base64.StdEncoding, out)
_, err = enc.Write(content)
if err != nil {
return cty.NullVal(cty.String), fmt.Errorf("failed to write file %q as base64: %s", path, err)
}
_ = enc.Close()
return cty.StringVal(out.String()), nil
},
})

@ -0,0 +1,62 @@
package function
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/zclconf/go-cty/cty"
)
func TestFilebase64(t *testing.T) {
tests := []struct {
name string
file string
expectedOutput string
expectError bool
}{
{
"file exists, return base64'd contents, no error",
"./testdata/list.tmpl",
"JXsgZm9yIHggaW4gbGlzdCB+fQotICR7eH0KJXsgZW5kZm9yIH59Cg==",
false,
},
{
"file doesn't exist, return nilval and an error",
"./testdata/no_file",
"",
true,
},
{
"directory passed as arg, should error",
"./testdata",
"",
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := Filebase64.Call([]cty.Value{
cty.StringVal(tt.file),
})
if tt.expectError && err == nil {
t.Fatal("succeeded; want error")
}
if !tt.expectError && err != nil {
t.Fatalf("unexpected error: %s", err)
}
if err != nil {
return
}
retVal := res.AsString()
diff := cmp.Diff(retVal, tt.expectedOutput)
if diff != "" {
t.Errorf("expected output and returned are different: %s", diff)
}
})
}
}

@ -64,6 +64,7 @@ func Functions(basedir string) map[string]function.Function {
"element": stdlib.ElementFunc,
"endswith": pkrfunction.EndsWithFunc,
"file": filesystem.MakeFileFunc(basedir, false),
"filebase64": pkrfunction.Filebase64,
"fileexists": filesystem.MakeFileExistsFunc(basedir),
"fileset": filesystem.MakeFileSetFunc(basedir),
"flatten": stdlib.FlattenFunc,

@ -8,7 +8,6 @@
package powershell
import (
"bufio"
"context"
"errors"
"fmt"
@ -38,6 +37,33 @@ var psEscape = strings.NewReplacer(
"'", "`'",
)
// wraps the content in try catch block and exits with a status.
const wrapPowershellString string = `
if (Test-Path variable:global:ProgressPreference) {
set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'
}
{{if .DebugMode}}
Set-PsDebug -Trace {{.DebugMode}}
{{- end}}
$exitCode = 0
try {
{{.Vars}}
{{.Payload}}
$exitCode = 0
} catch {
Write-Error "An error occurred: $_"
$exitCode = 1
}
if ($LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0) {
$exitCode = $LASTEXITCODE
}
Write-Host $result
exit $exitCode
`
type Config struct {
shell.Provisioner `mapstructure:",squash"`
@ -105,23 +131,15 @@ type Provisioner struct {
}
func (p *Provisioner) defaultExecuteCommand() string {
baseCmd := `& { if (Test-Path variable:global:ProgressPreference)` +
`{set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};`
if p.config.DebugMode != 0 {
baseCmd += fmt.Sprintf(`Set-PsDebug -Trace %d;`, p.config.DebugMode)
}
baseCmd += `. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }`
if p.config.ExecutionPolicy == ExecutionPolicyNone {
return baseCmd
return `-file {{.Path}}`
}
if p.config.UsePwsh {
return fmt.Sprintf(`pwsh -executionpolicy %s -command "%s"`, p.config.ExecutionPolicy, baseCmd)
return fmt.Sprintf(`pwsh -executionpolicy %s -file {{.Path}}`, p.config.ExecutionPolicy)
} else {
return fmt.Sprintf(`powershell -executionpolicy %s "%s"`, p.config.ExecutionPolicy, baseCmd)
return fmt.Sprintf(`powershell -executionpolicy %s -file {{.Path}}`, p.config.ExecutionPolicy)
}
}
@ -247,24 +265,41 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
return nil
}
// Takes the inline scripts, concatenates them into a temporary file and
// Takes the inline scripts, adds a wrapper around the inline scripts, concatenates them into a temporary file and
// returns a string containing the location of said file.
func extractScript(p *Provisioner) (string, error) {
temp, err := tmp.File("powershell-provisioner")
if err != nil {
return "", err
}
defer temp.Close()
writer := bufio.NewWriter(temp)
var commandBuilder strings.Builder
// we concatenate all the inline commands
for _, command := range p.config.Inline {
log.Printf("Found command: %s", command)
if _, err := writer.WriteString(command + "\n"); err != nil {
return "", fmt.Errorf("Error preparing powershell script: %s", err)
if _, err := commandBuilder.WriteString(command); err != nil {
return "", fmt.Errorf("failed to wrap script contents: %w", err)
}
}
if err := writer.Flush(); err != nil {
return "", fmt.Errorf("Error preparing powershell script: %s", err)
// injecting all the variables in the string
ctxData := p.generatedData
ctxData["Vars"] = p.createFlattenedEnvVars(p.config.ElevatedUser != "")
ctxData["Payload"] = commandBuilder.String()
ctxData["DebugMode"] = p.config.DebugMode
p.config.ctx.Data = ctxData
data, err := interpolate.Render(wrapPowershellString, &p.config.ctx)
if err != nil {
return "", fmt.Errorf("Error building powershell wrapper: %w", err)
}
log.Printf("Writing PowerShell script to file: %s", temp.Name())
if _, err := temp.WriteString(data); err != nil {
return "", fmt.Errorf("Error writing PowerShell script: %w", err)
}
return temp.Name(), nil

@ -111,3 +111,26 @@ func TestAccPowershellProvisioner_Script(t *testing.T) {
provisioneracc.TestProvisionersAgainstBuilders(testCase, t)
}
func TestAccPowershellProvisioner_ExitCodes(t *testing.T) {
templateString, err := LoadProvisionerFragment("powershell-exit_codes-provisioner.txt")
if err != nil {
t.Fatalf("Couldn't load test fixture; %s", err.Error())
}
testCase := &provisioneracc.ProvisionerTestCase{
IsCompatible: powershellIsCompatible,
Name: "powershell-provisioner-script",
Template: templateString,
Type: TestProvisionerType,
Check: func(buildcommand *exec.Cmd, logfile string) error {
if buildcommand.ProcessState != nil {
if buildcommand.ProcessState.ExitCode() != 0 {
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
}
}
return nil
},
}
provisioneracc.TestProvisionersAgainstBuilders(testCase, t)
}

@ -23,6 +23,7 @@ func TestProvisionerPrepare_extractScript(t *testing.T) {
config := testConfig()
p := new(Provisioner)
_ = p.Prepare(config)
p.generatedData = generatedData()
file, err := extractScript(p)
defer os.Remove(file)
if err != nil {
@ -35,13 +36,15 @@ func TestProvisionerPrepare_extractScript(t *testing.T) {
// File contents should contain 2 lines concatenated by newlines: foo\nbar
readFile, err := os.ReadFile(file)
expectedContents := "foo\nbar\n"
expectedContents := "if (Test-Path variable:global:ProgressPreference) {\n set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'\n }\n \n $exitCode = 0\n try {\n $env:PACKER_BUILDER_TYPE=\"\"; $env:PACKER_BUILD_NAME=\"\"; \n foobar\n $exitCode = 0\n } catch {\n Write-Error \"An error occurred: $_\"\n $exitCode = 1\n }\n \n if ($LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0) {\n $exitCode = $LASTEXITCODE\n }\n \n Write-Host $result\n exit $exitCode"
normalizedExpectedContent := normalizeWhiteSpace(expectedContents)
if err != nil {
t.Fatalf("Should not be error: %s", err)
}
s := string(readFile[:])
if s != expectedContents {
t.Fatalf("Expected generated inlineScript to equal '%s', got '%s'", expectedContents, s)
normalizedString := normalizeWhiteSpace(s)
if normalizedString != normalizedExpectedContent {
t.Fatalf("Expected generated inlineScript to equal '%s', got '%s'", normalizedExpectedContent, normalizedString)
}
}
@ -74,12 +77,12 @@ func TestProvisionerPrepare_Defaults(t *testing.T) {
t.Error("expected elevated_password to be empty")
}
if p.config.ExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` {
t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"', but got '%s'`, p.config.ExecuteCommand)
if p.config.ExecuteCommand != `powershell -executionpolicy bypass -file {{.Path}}` {
t.Fatalf(`Default command should be 'powershell -executionpolicy bypass -file {{.Path}}', but got '%s'`, p.config.ExecuteCommand)
}
if p.config.ElevatedExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` {
t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"', but got '%s'`, p.config.ElevatedExecuteCommand)
if p.config.ElevatedExecuteCommand != `powershell -executionpolicy bypass -file {{.Path}}` {
t.Fatalf(`Default command should be 'powershell -executionpolicy bypass -file {{.Path}}', but got '%s'`, p.config.ElevatedExecuteCommand)
}
if p.config.ElevatedEnvVarFormat != `$env:%s="%s"; ` {
@ -120,7 +123,7 @@ func TestProvisionerPrepare_DebugMode(t *testing.T) {
t.Fatalf("err: %s", err)
}
command := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};Set-PsDebug -Trace 1;. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"`
command := `powershell -executionpolicy bypass -file {{.Path}}`
if p.config.ExecuteCommand != command {
t.Fatalf(fmt.Sprintf(`Expected command should be '%s' but got '%s'`, command, p.config.ExecuteCommand))
}
@ -483,7 +486,8 @@ func TestProvisionerProvision_Inline(t *testing.T) {
}
cmd := comm.StartCmd.Command
re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1'; exit \$LastExitCode }"`)
re := regexp.MustCompile(`powershell -executionpolicy bypass -file c:/Windows/Temp/inlineScript.ps1`)
matched := re.MatchString(cmd)
if !matched {
t.Fatalf("Got unexpected command: %s", cmd)
@ -503,7 +507,7 @@ func TestProvisionerProvision_Inline(t *testing.T) {
}
cmd = comm.StartCmd.Command
re = regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1'; exit \$LastExitCode }"`)
re = regexp.MustCompile(`powershell -executionpolicy bypass -file c:/Windows/Temp/inlineScript.ps1`)
matched = re.MatchString(cmd)
if !matched {
t.Fatalf("Got unexpected command: %s", cmd)
@ -533,7 +537,7 @@ func TestProvisionerProvision_Scripts(t *testing.T) {
}
cmd := comm.StartCmd.Command
re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1'; exit \$LastExitCode }"`)
re := regexp.MustCompile(`powershell -executionpolicy bypass -file c:/Windows/Temp/script.ps1`)
matched := re.MatchString(cmd)
if !matched {
t.Fatalf("Got unexpected command: %s", cmd)
@ -570,7 +574,7 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) {
}
cmd := comm.StartCmd.Command
re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1'; exit \$LastExitCode }"`)
re := regexp.MustCompile(`powershell -executionpolicy bypass -file c:/Windows/Temp/script.ps1`)
matched := re.MatchString(cmd)
if !matched {
t.Fatalf("Got unexpected command: %s", cmd)
@ -595,11 +599,11 @@ func TestProvisionerProvision_SkipClean(t *testing.T) {
}{
{
SkipClean: true,
LastExecutedCommandRegex: `powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1'; exit \$LastExitCode }"`,
LastExecutedCommandRegex: `powershell -executionpolicy bypass -file c:/Windows/Temp/script.ps1`,
},
{
SkipClean: false,
LastExecutedCommandRegex: `powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/packer-cleanup-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1'; exit \$LastExitCode }"`,
LastExecutedCommandRegex: `powershell -executionpolicy bypass -file c:/Windows/Temp/packer-cleanup-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1`,
},
}
@ -917,7 +921,7 @@ func TestProvision_createCommandText(t *testing.T) {
p.generatedData = make(map[string]interface{})
cmd, _ := p.createCommandText()
re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1'; exit \$LastExitCode }"`)
re := regexp.MustCompile(`powershell -executionpolicy bypass -file c:/Windows/Temp/script.ps1`)
matched := re.MatchString(cmd)
if !matched {
t.Fatalf("Got unexpected command: %s", cmd)
@ -934,6 +938,28 @@ func TestProvision_createCommandText(t *testing.T) {
}
}
func TestProvision_createCommandTextNoneExecutionPolicy(t *testing.T) {
config := testConfig()
config["remote_path"] = "c:/Windows/Temp/script.ps1"
p := new(Provisioner)
comm := new(packersdk.MockCommunicator)
p.communicator = comm
config["execution_policy"] = ExecutionPolicyNone
_ = p.Prepare(config)
// Non-elevated
p.generatedData = make(map[string]interface{})
cmd, _ := p.createCommandText()
re := regexp.MustCompile(`-file c:/Windows/Temp/script.ps1`)
matched := re.MatchString(cmd)
if !matched {
t.Fatalf("Got unexpected command: %s", cmd)
}
}
func TestProvision_uploadEnvVars(t *testing.T) {
p := new(Provisioner)
comm := new(packersdk.MockCommunicator)
@ -976,3 +1002,18 @@ func generatedData() map[string]interface{} {
"PackerHTTPPort": commonsteps.HttpPortNotImplemented,
}
}
func normalizeWhiteSpace(s string) string {
// Replace multiple spaces/tabs with a single space
re := regexp.MustCompile(`[\t ]+`)
s = re.ReplaceAllString(s, " ")
// Trim leading/trailing spaces and newlines
s = strings.TrimSpace(s)
// Normalize line breaks (remove excessive empty lines)
s = strings.ReplaceAll(s, "\r\n", "\n") // Convert Windows line endings to Unix
s = strings.ReplaceAll(s, "\r", "\n") // Convert old Mac line endings to Unix
return s
}

@ -0,0 +1,76 @@
{
"type": "powershell",
"inline": ["invalid-cmdlet"],
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": ["#Requires -Version 10.0"],
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": ["exit 1"],
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": ["}}"],
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": ["$LASTEXITCODE=1"],
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": ["throw 'XXX'"],
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"script": "../../provisioner/powershell/test-fixtures/scripts/set_version_latest.ps1",
"valid_exit_codes": ["0"]
},
{
"type": "powershell",
"elevated_user": "Administrator",
"elevated_password": "{{.WinRMPassword}}",
"inline": "Get-ItemProperty -Path HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
"valid_exit_codes": ["0"]
},
{
"type": "powershell",
"inline": "ping invalidhost",
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": "sc.exe start command",
"valid_exit_codes": ["1060"]
},
{
"type": "powershell",
"inline": "echo 'Hi testing echo'; invalid command!; echo 'Another valid command';",
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": ["$ErrorActionPreference='Stop'", "Get-Item 'C:\\nonexistent.txt'"],
"valid_exit_codes": ["1"]
},
{
"type": "powershell",
"inline": [
"try {",
" invalid command",
"} catch {",
" exit 1",
"}"
],
"valid_exit_codes": ["1"]
}

@ -0,0 +1,40 @@
<powershell>
# Set administrator password
net user Administrator SuperS3cr3t!!!!
wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
# First, make sure WinRM can't be connected to
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
# Delete any existing WinRM listeners
winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
# Disable group policies which block basic authentication and unencrypted login
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowBasic -Value 1
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowUnencryptedTraffic -Value 1
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowBasic -Value 1
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowUnencryptedTraffic -Value 1
# Create a new WinRM listener and configure
winrm create winrm/config/listener?Address=*+Transport=HTTP
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
winrm set winrm/config '@{MaxTimeoutms="7200000"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/client/auth '@{Basic="true"}'
# Configure UAC to allow privilege elevation in remote shells
$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
$Setting = 'LocalAccountTokenFilterPolicy'
Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
# Configure and restart the WinRM Service; Enable the required firewall exception
Stop-Service -Name WinRM
Set-Service -Name WinRM -StartupType Automatic
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
Start-Service -Name WinRM
</powershell>

@ -0,0 +1,13 @@
# Test fixture is a modified version of the example found at
# https://www.powershellmagazine.com/2012/10/23/pstip-set-strictmode-why-should-you-care/
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$myNumbersCollection = 1..5
if($myNumbersCollection -contains 3) {
"collection contains 3"
}
else {
"collection doesn't contain 3"
}

@ -0,0 +1,46 @@
---
page_title: filebase64 - Functions - Configuration Language
description: |-
The filebase64 function reads the contents of the file at the given path and
returns them as a base64-encoded string.
---
# `filebase64` Function
`filebase64` reads the contents of a file at the given path and returns them as
a base64-encoded string.
```hcl
filebase64(path)
```
The result is a Base64 representation of the raw bytes in the given file.
Strings in the Packer language are sequences of Unicode characters, so
Base64 is the standard way to represent raw binary data that cannot be
interpreted as Unicode characters. Resource types that operate on binary
data will accept this data encoded in Base64, thus avoiding the need to
decode the result of this function.
Packer uses the "standard" Base64 alphabet as defined in
[RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4).
This function can be used only with functions that already exist as static
files on disk at the beginning of a Packer run. Language functions do not
participate in the dependency graph, so this function cannot be used with
files that are generated dynamically during a Packer operation.
## Examples
```
> filebase64("${path.module}/hello.txt")
SGVsbG8gV29ybGQ=
```
## Related Functions
* [`file`](/packer/docs/templates/hcl_templates/functions/file/file) also reads the contents of a given file,
but interprets the data as UTF-8 text and returns the result directly
as a string, without any further encoding.
* [`base64decode`](/packer/docs/templates/hcl_templates/functions/encoding/base64decode) can decode a Base64 string representing
bytes in UTF-8, but in practice `base64decode(filebase64(...))` is equivalent
to the shorter expression `file(...)`.

@ -508,6 +508,10 @@
"title": "file",
"path": "templates/hcl_templates/functions/file/file"
},
{
"title": "filebase64",
"path": "templates/hcl_templates/functions/file/filebase64"
},
{
"title": "fileexists",
"path": "templates/hcl_templates/functions/file/fileexists"

Loading…
Cancel
Save