env/windows-arm64: add helper program for file writing

Add a tiny helper program that reads an input text file and a windows
path (e.g. "C:\blah\myfile.txt") and writes out a PowerShell script
that will write the specified content to that windows path when run.

The intent here is to provide a mechanism for writing private/secret
files to VM images as opposed to downloading from things from
https://storage.googleapis.com/go-builder-data (where most files are
world-readable).

Change-Id: Ifc043a4a33df14b3f4f853472666c9fc3f03ba2b
Reviewed-on: https://go-review.googlesource.com/c/build/+/549118
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/env/windows-arm64/azure/writefilegenpowerscript.go b/env/windows-arm64/azure/writefilegenpowerscript.go
new file mode 100644
index 0000000..e460915
--- /dev/null
+++ b/env/windows-arm64/azure/writefilegenpowerscript.go
@@ -0,0 +1,88 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Command writefilegenpowerscript reads an ASCII input file and a
+// target windows path, and writes out a Windows PowerShell script
+// that will write the content of the file to the specified location.
+// Notes:
+// - this program is limited to writing text files; trying
+//   to write a binary would most likely not end well.
+// - it is assumed that we want linux-style ("\n") and not
+//   windows-style ("\r\n") line endings
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+)
+
+var (
+	infileflag  = flag.String("input-file", "", "Path to the input file")
+	tgtpathflag = flag.String("windows-target-path", "", "Target full pathname to write on Windows")
+	outfileflag = flag.String("output-file", "", "Name of script to write")
+	ownerflag   = flag.String("set-owner", "", "Username to assign as owner of windows file (optional)")
+	denyflag    = flag.String("deny-user-read", "", "Username to which we'll deny read permission on windows file after creation (optional)")
+)
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintln(os.Stderr, "Usage: writefilegenpowerscript -input-file AAA -output-file BBB -windows-target-path C:\\CCC")
+		flag.PrintDefaults()
+	}
+	flag.Parse()
+	if *infileflag == "" || *outfileflag == "" || *tgtpathflag == "" {
+		flag.Usage()
+		os.Exit(2)
+	}
+	// vet the windows path
+	if !strings.HasPrefix(*tgtpathflag, "C:\\") {
+		fmt.Fprintf(os.Stderr, "warning: suspicious windows target path %q, does not start with C drive prefix\n", *tgtpathflag)
+	}
+
+	// Slurp in the input file.
+	var lines []string
+	if content, err := os.ReadFile(*infileflag); err != nil {
+		log.Fatalf("reading %s: error %v", *infileflag, err)
+	} else {
+		lines = strings.Split(string(content), "\n")
+	}
+
+	// Open output file
+	of, err := os.OpenFile(*outfileflag, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+	if err != nil {
+		log.Fatalf("opening %s for write: error %v", *outfileflag, err)
+	}
+
+	// Write content.
+	fmt.Fprintf(of, "Set-StrictMode -Version Latest\n")
+	fmt.Fprintf(of, "$path = \"%s\"\n", *tgtpathflag)
+	line0 := lines[0]
+	fmt.Fprintf(of, "$line0 = \"%s\"\n", line0)
+	fmt.Fprintf(of, "$line0 | Out-File -Encoding ascii $path\n")
+	for i := 1; i < len(lines)-1; i++ {
+		line := lines[i]
+		fmt.Fprintf(of, "Add-Content -Encoding ascii -Path $path -Value \"%s\"\n", line)
+	}
+
+	// The file we wrote has windows-style line endings; emit a
+	// separate step to convert back to linux-style.
+	fmt.Fprintf(of, "((Get-Content $path) -join \"`n\") + \"`n\" | Set-Content -NoNewline $path\n")
+
+	// Honor the -set-owner and/or -deny-user-read flag if set.
+	if *ownerflag != "" {
+		fmt.Fprintf(of, "icacls $path /setowner %s\n", *ownerflag)
+	}
+	if *denyflag != "" {
+		fmt.Fprintf(of, "icacls $path /deny %s:r\n", *denyflag)
+	}
+
+	// We're done.
+	if err := of.Close(); err != nil {
+		log.Fatalf("closing %s: error %v", *outfileflag, err)
+	}
+}