diff --git a/.github/actions/build_ami/action.yaml b/.github/actions/build_ami/action.yaml
new file mode 100644
index 000000000..83e16522f
--- /dev/null
+++ b/.github/actions/build_ami/action.yaml
@@ -0,0 +1,191 @@
+name: Build AMI
+description: Builds the AMI for AWS private operators
+
+inputs:
+ identity_scope:
+ description: The identity scope [uid2, euid]
+ required: true
+ github_token:
+ description: The GITHUB token to use to get the EIF
+ required: true
+ eif_repo_owner:
+ description: The owner of the EIF repo
+ required: true
+ eif_repo_name:
+ description: The name of the EIF repo
+ required: true
+ operator_release:
+ description: The operator release that has the EIF to use
+ default: ''
+ operator_branch:
+ description: The Operator Branch to build from
+ default: main
+ operator_run_number:
+ description: The Operator run number for artifacts. Ignored if Operator Release is given
+ save_ami:
+ description: Save the AMIs as a build artifact.
+ default: true
+ uid2_aws_role:
+ description: The AWS role to assume for UID2
+ required: true
+ uid2_aws_region:
+ description: The UID2 AWS region to upload AMI to
+ required: true
+ euid_aws_role:
+ description: The AWS role to assume for EUID
+ required: true
+ euid_aws_region:
+ description: The EUID AWS region to upload AMI to
+ required: true
+
+outputs:
+ version_number:
+ description: The version number set in IABTechLab/uid2-operator pom.xml.
+ value: ${{ steps.versionNumber.outputs.version_number }}
+
+runs:
+ using: "composite"
+
+ steps:
+ - name: Show Context
+ shell: bash
+ run: |
+ printenv
+ echo "$GITHUB_CONTEXT"
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+
+ - name: Checkout full history
+ uses: actions/checkout@v4
+
+ - name: Get EIF for Release ${{ inputs.operator_release }}
+ uses: IABTechLab/uid2-operator/.github/actions/download_release_artifact@main
+ if: ${{ inputs.operator_release != '' }}
+ with:
+ github_token: ${{ inputs.github_token }}
+ repo_owner: ${{ inputs.eif_repo_owner }}
+ repo_name: ${{ inputs.eif_repo_name }}
+ release_name: ${{ inputs.operator_release }}
+ artifact_name: aws-${{ inputs.identity_scope }}-deployment-files
+ folder: ./scripts/aws/uid2-operator-ami/artifacts
+
+ - name: Get EIF for Run ${{ inputs.operator_run_number }}
+ id: get_eif_for_run
+ uses: actions/download-artifact@v4
+ if: ${{ inputs.operator_release == '' }}
+ with:
+ github_token: ${{ inputs.github_token }}
+ repo: IABTechLab/uid2-operator
+ name: 'aws-${{ inputs.identity_scope }}-deployment-files-.*'
+ name_is_regexp: true
+ run_id: ${{ inputs.operator_run_number }}
+ skip_unpack: true
+ path: ./download/artifacts
+
+ - name: Unzip artifacts
+ if: ${{ inputs.operator_release == '' }}
+ shell: bash
+ run: |
+ ARTIFACTS='${{ steps.get_eif_for_run.outputs.artifacts }}'
+ FILE=$(echo $ARTIFACTS | jq -r '.[0].name')
+ unzip -o -d ./scripts/aws/uid2-operator-ami/artifacts $FILE.zip
+ rm $FILE.zip
+
+ - name: Configure UID2 AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ if: ${{ inputs.identity_scope == 'uid2' }}
+ with:
+ aws-region: ${{ inputs.uid2_aws_region }}
+ role-to-assume: ${{ inputs.uid2_aws_role }}
+
+ - name: Configure EUID AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ if: ${{ inputs.identity_scope == 'euid' }}
+ with:
+ aws-region: ${{ inputs.euid_aws_region }}
+ role-to-assume: ${{ inputs.euid_aws_role }}
+
+ - name: Show AWS Identity
+ shell: bash
+ run: |
+ aws sts get-caller-identity
+
+ - name: Get version number
+ id: versionNumber
+ shell: bash
+ working-directory: ./scripts/aws/uid2-operator-ami
+ run: |
+ ls -al
+ VERSION_NUMBER=$(cat ./artifacts/version_number.txt)
+ echo "VERSION_NUMBER=$VERSION_NUMBER" >> $GITHUB_OUTPUT
+ echo $VERSION_NUMBER
+
+ - name: Setup Packer
+ id: setup-packer
+ uses: hashicorp/setup-packer@main
+
+ - name: Create AMI
+ shell: bash
+ working-directory: ./scripts/aws/uid2-operator-ami
+ run: |
+ ls -al
+ TIMESTAMP=$(date +%s)
+ echo "TIMESTAMP=$TIMESTAMP" >> $GITHUB_OUTPUT
+ packer init .
+ packer build \
+ -var "version=${{ steps.versionNumber.outputs.VERSION_NUMBER }}" \
+ -var "timestamp=$TIMESTAMP" \
+ -var-file="${{ inputs.identity_scope }}.pkrvars.hcl" \
+ -debug \
+ .
+
+ - name: Extract AMI ID
+ id: extractAmiId
+ shell: bash
+ working-directory: ./scripts/aws/uid2-operator-ami
+ run: |
+ AMI_ID=$(jq -r '.builds[-1].artifact_id' manifest.json | cut -d ":" -f2)
+ echo "AMI_ID=$AMI_ID" >> $GITHUB_OUTPUT
+ echo $AMI_ID
+
+ - name: Persist UID2 enclave and AMI IDs
+ if: ${{ inputs.identity_scope == 'uid2' }}
+ shell: bash
+ working-directory: ./scripts/aws/uid2-operator-ami
+ run: |
+ touch uid2_AMI_measurement.txt
+ echo "Enclave ID: $(cat ./artifacts/pcr0.txt)" >> uid2_AMI_measurement.txt
+ echo "AMI ID: " ${{ steps.extractAmiId.outputs.AMI_ID }} >> uid2_AMI_measurement.txt
+ echo "uid2_AMI_measurement.txt Contents:"
+ cat uid2_AMI_measurement.txt
+ ls -al
+
+ - name: Copy AMI to us-east-1
+ id: euidCopyAmi
+ if: ${{ inputs.identity_scope == 'euid' }}
+ shell: bash
+ working-directory: ./scripts/aws/uid2-operator-ami
+ run: |
+ US_EAST_AMI_ID=$(aws ec2 copy-image --region us-east-1 --source-region eu-central-1 --source-image-id ${{ steps.extractAmiId.outputs.AMI_ID }} --name euid-operator-${{ steps.versionNumber.outputs.VERSION_NUMBER }}-${{ steps.createAMI.outputs.TIMESTAMP }} --output text)
+ echo "US_EAST_1_AMI_ID=$US_EAST_AMI_ID" >> $GITHUB_OUTPUT
+ echo $US_EAST_AMI_ID
+
+ - name: Persist EUID enclave and AMI IDs
+ if: ${{ inputs.identity_scope == 'euid' }}
+ shell: bash
+ working-directory: ./scripts/aws/uid2-operator-ami
+ run: |
+ touch euid_AMI_measurement.txt
+ echo "Enclave ID: $(cat ./artifacts/pcr0.txt)" >> euid_AMI_measurement.txt
+ echo "eu-central-1 AMI ID:" ${{ steps.extractAmiId.outputs.AMI_ID }} >> euid_AMI_measurement.txt
+ echo "us-east-1 AMI ID:" ${{ steps.euidCopyAmi.outputs.US_EAST_1_AMI_ID }} >> euid_AMI_measurement.txt
+ echo "euid_AMI_measurement.txt contents"
+ cat euid_AMI_measurement.txt
+ ls -al
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ if: ${{ inputs.save_ami == 'true' }}
+ with:
+ name: ${{ inputs.identity_scope }}_AMI_measurement
+ path: ./scripts/aws/uid2-operator-ami/${{ inputs.identity_scope }}_AMI_measurement.txt
diff --git a/.github/actions/download_release_artifact/action.yaml b/.github/actions/download_release_artifact/action.yaml
new file mode 100644
index 000000000..13e38aafd
--- /dev/null
+++ b/.github/actions/download_release_artifact/action.yaml
@@ -0,0 +1,83 @@
+name: Download Release Artifact
+description: Downloads an artifact from a release
+
+inputs:
+ github_token:
+ description: The GITHUB_TOKEN to use for the target repo
+ required: false
+ repo_owner:
+ description: The owner of the target repo
+ required: true
+ repo_name:
+ description: The name of the target repo
+ required: true
+ release_name:
+ description: The release name that contains the aritfact
+ required: true
+ artifact_name:
+ description: The name of the artifact. Can be a partial name
+ required: true
+ folder:
+ description: The folder to download the artifact to and then unpack
+ required: true
+
+runs:
+ using: "composite"
+
+ steps:
+ - name: Get Artifact Ids
+ id: get_asset_id
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ inputs.github_token }}
+ result-encoding: string
+ script: |
+ let allReleases = await github.rest.repos.listReleases({
+ owner: '${{ inputs.repo_owner }}',
+ repo: '${{ inputs.repo_name }}'
+ })
+
+ assetUrl = ""
+ let matchedReleases = allReleases.data.filter((release) => {
+ return release.name == "${{ inputs.release_name }}"
+ })
+ if (matchedReleases.length == 0) {
+ core.setFailed('Unable to find release: ' + '${{ inputs.release_name }}' );
+ } else {
+ let matchedRelease = matchedReleases[0];
+ let releaseId = matchedRelease.id;
+ console.log('Release Id:' + releaseId);
+
+ let assets = matchedRelease.assets.filter((asset) => {
+ return asset.name.includes('${{ inputs.artifact_name }}')
+ });
+ if (assets.length == 0) {
+ core.setFailed('Unable to find asset in release');
+ } else {
+ let asset = assets[0];
+ console.log('Asset:');
+ console.log(asset);
+ assetUrl = asset.url
+ }
+
+ return assetUrl
+ }
+
+ - name: Download Assets
+ shell: bash
+ run: |
+ echo 'Asset URL: ${{ steps.get_asset_id.outputs.result }}'
+
+ mkdir -p ./${{ inputs.folder }}
+ DOWNLOAD_PATH=${{ steps.get_asset_id.outputs.result }}
+ echo $DOWNLOAD_PATH
+ curl -L -H "Accept: application/octet-stream" \
+ -H "Authorization: Bearer ${{ inputs.github_token }}" \
+ -o "./${{ inputs.folder }}/${{ inputs.artifact_name }}.zip" \
+ $DOWNLOAD_PATH
+
+ - name: Unzip artifacts
+ shell: bash
+ run: |
+ unzip -o -d './${{ inputs.folder }}' './${{ inputs.folder }}/${{ inputs.artifact_name }}.zip'
+ rm './${{ inputs.folder }}/${{ inputs.artifact_name }}.zip'
diff --git a/.github/workflows/build-uid2-ami.yaml b/.github/workflows/build-uid2-ami.yaml
new file mode 100644
index 000000000..7a202a5c8
--- /dev/null
+++ b/.github/workflows/build-uid2-ami.yaml
@@ -0,0 +1,126 @@
+name: Build UID2 and EUID AMIs
+run-name: Build UID2 and EUID AMIs for Operator Release ${{ inputs.operator_release }} by @${{ github.actor }}
+on:
+ workflow_dispatch:
+ inputs:
+ operator_release:
+ description: The Operator Release number that built the EIF files to use
+ type: string
+ operator_run_number:
+ description: The Operator run number. Ignored if Release Number specified.
+ type: string
+ operator_branch:
+ description: The branch of Operator to use.
+ type: string
+ default: main
+ save_ami:
+ description: Save the AMIs as a build artifact.
+ type: boolean
+ required: false
+ default: true
+
+env:
+ REPO_OWNER: IABTechLab
+ REPO_NAME: uid2-operator
+ UID2_AWS_ROLE: arn:aws:iam::475720075663:role/github-runner-operator-runner
+ UID2_AWS_REGION: us-east-2
+ EUID_AWS_ROLE: arn:aws:iam::618285103646:role/github-runner-operator-runner
+ EUID_AWS_REGION: eu-central-1
+
+jobs:
+ buildUID2:
+ name: UID2 Operator AMI
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ outputs:
+ version_number: ${{ steps.buildAMI.outputs.version_number }}
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Build UID2 Operator AMI
+ id: buildAMI
+ uses: IABTechLab/uid2-operator/.github/actions/build_ami@main
+ with:
+ identity_scope: uid2
+ eif_repo_owner: ${{ env.REPO_OWNER }}
+ eif_repo_name: ${{ env.REPO_NAME }}
+ github_token: ${{ github.token }}
+ operator_release: ${{ inputs.operator_release }}
+ operator_run_number: ${{ inputs.operator_run_number }}
+ operator_branch: ${{ inputs.operator_branch }}
+ save_ami: ${{ inputs.save_ami }}
+ uid2_aws_role: ${{ env.UID2_AWS_ROLE }}
+ uid2_aws_region: ${{ env.UID2_AWS_REGION }}
+ euid_aws_role: ${{ env.EUID_AWS_ROLE }}
+ euid_aws_region: ${{ env.EUID_AWS_REGION }}
+
+ buildEUID:
+ name: EUID Operator AMI
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ outputs:
+ version_number: ${{ steps.buildAMI.outputs.version_number }}
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Pre-cleanup
+ shell: bash
+ working-directory: ${{ github.workspace }}
+ run: |
+ echo "Cleaning up previous run"
+ sudo rm -rf * || true
+ docker stop $(docker ps -aq) || true
+ docker rm $(docker ps -aq) || true
+ docker rmi $(docker images -q) || true
+
+ - name: Build EUID Operator AMI
+ id: buildAMI
+ uses: IABTechLab/uid2-operator/.github/actions/build_ami@main
+ with:
+ identity_scope: euid
+ eif_repo_owner: ${{ env.REPO_OWNER }}
+ eif_repo_name: ${{ env.REPO_NAME }}
+ github_token: ${{ github.token }}
+ operator_release: ${{ inputs.operator_release }}
+ operator_run_number: ${{ inputs.operator_run_number }}
+ operator_branch: ${{ inputs.operator_branch }}
+ save_ami: ${{ inputs.save_ami }}
+ uid2_aws_role: ${{ env.UID2_AWS_ROLE }}
+ uid2_aws_region: ${{ env.UID2_AWS_REGION }}
+ euid_aws_role: ${{ env.EUID_AWS_ROLE }}
+ euid_aws_region: ${{ env.EUID_AWS_REGION }}
+
+ collectAllArtifacts:
+ name: Collect All Artifacts
+ if: ${{ inputs.save_ami }}
+ runs-on: ubuntu-latest
+ needs: [buildUID2, buildEUID]
+ steps:
+ - name: Download UID2 artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: uid2_AMI_measurement
+ path: ./artifacts
+
+ - name: Download EUID artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: euid_AMI_measurement
+ path: ./artifacts
+
+ - name: Delete staging artifacts
+ uses: geekyeggo/delete-artifact@v5
+ with:
+ name: |
+ uid2_AMI_measurement
+ euid_AMI_measurement
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: uid2-operator-release-${{ needs.buildUID2.outputs.version_number }}-aws
+ path: ./artifacts/
diff --git a/pom.xml b/pom.xml
index 2f621d1b8..0754e7e4b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.uid2
uid2-operator
- 5.37.22
+ 5.37.15
UTF-8
diff --git a/scripts/aws/uid2-operator-ami/ansible/playbook.yml b/scripts/aws/uid2-operator-ami/ansible/playbook.yml
new file mode 100644
index 000000000..f80f5f013
--- /dev/null
+++ b/scripts/aws/uid2-operator-ami/ansible/playbook.yml
@@ -0,0 +1,207 @@
+---
+- name: Test Ansible playbook
+ hosts: all
+ connection: ssh
+ gather_facts: yes
+ become: yes
+ become_user: root
+ become_method: sudo
+
+ tasks:
+ - name: Update all base packages
+ ansible.builtin.yum:
+ update_cache: yes
+ name: '*'
+ state: latest
+
+ - name: Install Netcat
+ ansible.builtin.yum:
+ name: nmap-ncat
+ state: latest
+
+ - name: Create etc/uid2operator directory
+ ansible.builtin.file:
+ path: /etc/uid2operator
+ state: directory
+
+ - name: Create opt/uid2operator directory
+ ansible.builtin.file:
+ path: /opt/uid2operator
+ state: directory
+
+ - name: vsock-proxy config
+ ansible.builtin.copy:
+ src: /tmp/artifacts/proxies.host.yaml
+ dest: /etc/uid2operator/proxy.yaml
+ remote_src: yes
+
+ - name: Install vsock-proxy
+ ansible.builtin.copy:
+ src: /tmp/artifacts/vsockpx
+ dest: /usr/bin/vsockpx
+ remote_src: yes
+
+ - name: Make vsock-proxy executable
+ ansible.builtin.file:
+ path: /usr/bin/vsockpx
+ mode: '0755'
+
+ - name: Install starter script
+ ansible.builtin.copy:
+ src: /tmp/artifacts/start.sh
+ dest: /opt/uid2operator/start.sh
+ remote_src: yes
+
+ - name: Make starter script executable
+ ansible.builtin.file:
+ path: /opt/uid2operator/start.sh
+ mode: '0755'
+
+ - name: Install stopper script
+ ansible.builtin.copy:
+ src: /tmp/artifacts/stop.sh
+ dest: /opt/uid2operator/stop.sh
+ remote_src: yes
+
+ - name: Make starter script executable
+ ansible.builtin.file:
+ path: /opt/uid2operator/stop.sh
+ mode: '0755'
+
+ - name: Install Operator EIF
+ ansible.builtin.copy:
+ src: /tmp/artifacts/uid2operator.eif
+ dest: /opt/uid2operator/uid2operator.eif
+ remote_src: yes
+
+ - name: Install Identity Scope
+ ansible.builtin.copy:
+ src: /tmp/artifacts/identity_scope.txt
+ dest: /opt/uid2operator/identity_scope.txt
+ remote_src: yes
+
+ - name: Dante config
+ ansible.builtin.copy:
+ src: /tmp/artifacts/sockd.conf
+ dest: /etc/sockd.conf
+ remote_src: yes
+
+ - name: Install Dante
+ ansible.builtin.copy:
+ src: /tmp/artifacts/sockd
+ dest: /usr/bin/sockd
+ remote_src: yes
+
+ - name: Copy Syslog package
+ ansible.builtin.copy:
+ src: /tmp/artifacts/syslog-ng-4.6.0-1.el7.x86_64.rpm
+ dest: /opt/uid2operator/syslog-ng-4.6.0-1.el7.x86_64.rpm
+ remote_src: yes
+
+ - name: Copy Syslog public key
+ ansible.builtin.copy:
+ src: /tmp/artifacts/syslog-ng-pubkey.gpg
+ dest: /opt/uid2operator/syslog-ng-pubkey.gpg
+ remote_src: yes
+
+ - name: Make sockd executable
+ ansible.builtin.file:
+ path: /usr/bin/sockd
+ mode: '0755'
+
+ - name: Install Operator service
+ ansible.builtin.copy:
+ src: /tmp/artifacts/uid2operator.service
+ dest: /etc/systemd/system/uid2operator.service
+ remote_src: yes
+
+ - name: Ensure nitro-cli is installed to the latest version
+ ansible.builtin.command: amazon-linux-extras install aws-nitro-enclaves-cli
+
+ - name: Ensure nitro enclave allocator is enabled at boot
+ ansible.builtin.systemd:
+ name: nitro-enclaves-allocator.service
+ enabled: yes
+
+ - name: Update nitro enclave CPU allocation
+ ansible.builtin.command: 'sed -r "s/^(\s*cpu_count\s*:\s*).*/\16/" -i /etc/nitro_enclaves/allocator.yaml'
+
+ - name: Update nitro enclave memory allocation
+ ansible.builtin.command: 'sed -r "s/^(\s*memory_mib\s*:\s*).*/\124576/" -i /etc/nitro_enclaves/allocator.yaml'
+
+ # - name: Apply Nitro Enclave allocator change
+ # ansible.builtin.systemd:
+ # name: nitro-enclaves-allocator.service
+ # state: restarted
+ - name: Install EPEL
+ ansible.builtin.command: amazon-linux-extras install epel
+
+ - name: Install ivykis
+ ansible.builtin.yum:
+ name: ivykis-0.36.2-2.el7
+ state: present
+
+ - name: Install libnet
+ ansible.builtin.yum:
+ name: libnet-1.1.6-7.amzn2.0.2
+ state: present
+
+ - name: Register Public key
+ ansible.builtin.command: rpmkeys --import /opt/uid2operator/syslog-ng-pubkey.gpg
+
+ - name: Install package
+ ansible.builtin.command: rpm -U /opt/uid2operator/syslog-ng-4.6.0-1.el7.x86_64.rpm
+
+ - name: unregister Public key
+ ansible.builtin.command: rpm -e gpg-pubkey-c57846f4-65a8cf14
+
+ - name: syslog-ng config
+ ansible.builtin.copy:
+ src: /tmp/artifacts/syslog-ng-server.conf
+ dest: /etc/syslog-ng/syslog-ng.conf
+ remote_src: yes
+
+ - name: logrotate config
+ ansible.builtin.copy:
+ src: /tmp/artifacts/operator-logrotate.conf
+ dest: /etc/logrotate.d/operator-logrotate.conf
+ remote_src: yes
+
+ - name: Run logrotate under cron.d
+ ansible.builtin.copy:
+ src: /tmp/artifacts/logrotate
+ dest: /etc/cron.d/logrotate
+ mode: 0644
+ remote_src: yes
+
+ - name: Ensure rsyslog is disabled at boot
+ ansible.builtin.systemd:
+ name: rsyslog
+ state: stopped
+ enabled: false
+
+ - name: Ensure syslog-ng is enabled at boot
+ ansible.builtin.systemd:
+ name: syslog-ng
+ state: started
+ enabled: true
+
+ - name: Ensure uid2 operator is enabled at boot
+ ansible.builtin.systemd:
+ name: uid2operator.service
+ enabled: yes
+
+ - name: Clean up tmp files
+ file:
+ path: /tmp/artifacts
+ state: absent
+
+ - name: Clean up ec2_user SSH authorized keys file
+ file:
+ path: /home/ec2-user/.ssh/authorized_keys
+ state: absent
+
+ - name: Clean up root SSH authorized keys file
+ file:
+ path: /root/.ssh/authorized_keys
+ state: absent
diff --git a/scripts/aws/uid2-operator-ami/build.pkr.hcl b/scripts/aws/uid2-operator-ami/build.pkr.hcl
new file mode 100644
index 000000000..36d43390e
--- /dev/null
+++ b/scripts/aws/uid2-operator-ami/build.pkr.hcl
@@ -0,0 +1,19 @@
+build {
+ sources = ["source.amazon-ebs.linux"]
+
+ provisioner "file" {
+ source = "./artifacts"
+ destination = "/tmp"
+ }
+
+ provisioner "ansible" {
+ playbook_file = "./ansible/playbook.yml"
+ extra_arguments = [ "--scp-extra-args", "'-O'", "--version", "9.6.1" ]
+ }
+
+ post-processor "manifest" {
+ output = "manifest.json"
+ strip_path = true
+ }
+}
+
diff --git a/scripts/aws/uid2-operator-ami/euid.pkrvars.hcl b/scripts/aws/uid2-operator-ami/euid.pkrvars.hcl
new file mode 100644
index 000000000..7bf10b8ce
--- /dev/null
+++ b/scripts/aws/uid2-operator-ami/euid.pkrvars.hcl
@@ -0,0 +1,7 @@
+region = "eu-central-1"
+identity_scope = "euid"
+subnet_id = "subnet-0edbf47b073de1c79"
+vpc_id = "vpc-065000fb9082c6a90"
+ami_ou_arns = [
+ "arn:aws:organizations::155852253738:ou/o-v1vmbc3c9h/ou-96c8-2vbyb92d"
+]
diff --git a/scripts/aws/uid2-operator-ami/plugins.pkr.hcl b/scripts/aws/uid2-operator-ami/plugins.pkr.hcl
new file mode 100644
index 000000000..ee414c421
--- /dev/null
+++ b/scripts/aws/uid2-operator-ami/plugins.pkr.hcl
@@ -0,0 +1,12 @@
+packer {
+ required_plugins {
+ amazon = {
+ version = ">= 1.0.0"
+ source = "github.com/hashicorp/amazon"
+ }
+ ansible = {
+ version = "~> 1"
+ source = "github.com/hashicorp/ansible"
+ }
+ }
+}
diff --git a/scripts/aws/uid2-operator-ami/source.pkr.hcl b/scripts/aws/uid2-operator-ami/source.pkr.hcl
new file mode 100644
index 000000000..b12766b0e
--- /dev/null
+++ b/scripts/aws/uid2-operator-ami/source.pkr.hcl
@@ -0,0 +1,39 @@
+source "amazon-ebs" "linux" {
+
+ # source parameters
+ source_ami_filter {
+ filters = {
+ name = "amzn2-ami-hvm-*-x86_64-ebs"
+ root-device-type = "ebs"
+ virtualization-type = "hvm"
+ }
+ most_recent = true
+ owners = ["amazon"]
+ }
+
+ # disable ami creation for testing
+ # skip_create_ami = true
+
+ # instance parameters
+ ami_name = local.ami_name
+ ami_ou_arns = var.ami_ou_arns
+ instance_type = var.instance_type
+ region = var.region
+ subnet_id = var.subnet_id
+ vpc_id = var.vpc_id
+
+ # connection parameters
+ communicator = var.communicator
+ ssh_username = var.ssh_username
+ ssh_interface = var.ssh_interface
+ iam_instance_profile = var.iam_instance_profile
+
+ tags = {
+ Environment = var.env
+ Service = var.service
+ Version = var.version
+ Name = local.ami_name
+ Build = "packer"
+ BuildTime = var.timestamp
+ }
+}
diff --git a/scripts/aws/uid2-operator-ami/uid2.pkrvars.hcl b/scripts/aws/uid2-operator-ami/uid2.pkrvars.hcl
new file mode 100644
index 000000000..968ec4042
--- /dev/null
+++ b/scripts/aws/uid2-operator-ami/uid2.pkrvars.hcl
@@ -0,0 +1,7 @@
+region = "us-east-1"
+identity_scope = "uid2"
+subnet_id = "subnet-03a2ae9b83ee4a1be"
+vpc_id = "vpc-056adf611333ebf06"
+ami_ou_arns = [
+ "arn:aws:organizations::155852253738:ou/o-v1vmbc3c9h/ou-96c8-2vbyb92d"
+]
diff --git a/scripts/aws/uid2-operator-ami/vars.pkr.hcl b/scripts/aws/uid2-operator-ami/vars.pkr.hcl
new file mode 100644
index 000000000..a76f6dcdc
--- /dev/null
+++ b/scripts/aws/uid2-operator-ami/vars.pkr.hcl
@@ -0,0 +1,72 @@
+variable "env" {
+ description = "distinct environment/stage name"
+ default = "production"
+}
+
+variable "identity_scope" {
+ description = "The scope of the operator. uid2 or euid"
+ default = "uid2"
+}
+
+variable "service" {
+ description = "distinct name for the service"
+ default = "operator"
+}
+
+variable "region" {
+ description = "AWS region name"
+ default = "us-east-1"
+}
+
+variable "instance_type" {
+ description = "instance type to build on"
+ default = "m5.2xlarge"
+}
+
+variable "vpc_id" {
+ description = "vpc id for instance creation"
+}
+
+variable "subnet_id" {
+ description = "subnet id for instance creation"
+}
+
+variable "communicator" {
+ description = "communication method used for the instance"
+ default = "ssh"
+}
+
+variable "ssh_username" {
+ description = "ssh username for packer to use for provisioning"
+ default = "ec2-user"
+}
+
+variable "ssh_interface" {
+ description = "ssh interface for packer to use for provisioning"
+ default = "session_manager"
+}
+
+variable "iam_instance_profile" {
+ description = "IAM instance profile to attach to AMI instance for SSM"
+ default = "aws-operator-self-hosted-runner.target"
+}
+
+variable "version" {
+ description = "release version"
+}
+
+variable "ami_ou_arns" {
+ description = "A list of Amazon Resource Names (ARN) of AWS Organizations that have access to launch the resulting AMI(s)."
+ type = list(string)
+}
+
+variable "timestamp" {
+ description = "unique timestamp"
+}
+
+locals {
+ identifier = "${var.identity_scope}-${var.service}"
+ version = "${var.version}"
+
+ ami_name = "${local.identifier}-${local.version}-${var.timestamp}"
+}