Skip to content

Commit 0e403c7

Browse files
committed
Added support for resource principal auth
1 parent 3e8fca2 commit 0e403c7

File tree

7 files changed

+132
-20
lines changed

7 files changed

+132
-20
lines changed

.web-docs/components/builder/oci/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ based on which authentication method is used.
102102
Principals](https://docs.cloud.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm)
103103
instead of User Principals. If this key is set to true, setting any one of the `access_cfg_file`,
104104
`access_cfg_file_account`, `region`, `tenancy_ocid`, `user_ocid`, `key_file`, `fingerprint`,
105-
`pass_phrase` parameters will cause an invalid configuration error.
105+
`pass_phrase`, or `use_resource_principals` parameters will cause an invalid configuration error.
106106
Defaults to `false`.
107107

108+
- `use_resource_principals` (boolean) - (Optional) Use Resource Principals for authentication. Resource Principals is a capability in Oracle Cloud Infrastructure Identity and Access Management (IAM) that allows resources (such as Functions, Data Flow applications, and API Gateways) to make service calls to other OCI services. This attribute is mutually exclusive with `access_cfg_file`, `access_cfg_file_account`, `user_ocid`, `tenancy_ocid`, `region`, `fingerprint`, `key_file`, `pass_phrase`, and `use_instance_principals`. Defaults to `false`.
109+
To use resource principals, the resource running Packer (e.g., a Compute instance, a Function) must be part of a [Dynamic Group](https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/managingdynamicgroups.htm) that has appropriate [Policies](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/policygetstarted.htm) granting it permissions to manage resources (e.g., launch instances, create images).
110+
108111
- `access_cfg_file` (string) - The path to the [OCI config
109112
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm).
110113
This parameter is _optional_ when using token-based authentication.

builder/oci/config.go

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@ type Config struct {
7979
// - PassPhrase
8080
InstancePrincipals bool `mapstructure:"use_instance_principals"`
8181

82+
// Resource Principals (OPTIONAL)
83+
// If set to true the following can't have non empty values
84+
// - AccessCfgFile
85+
// - AccessCfgFileAccount
86+
// - UserID
87+
// - TenancyID
88+
// - Region
89+
// - Fingerprint
90+
// - KeyFile
91+
// - PassPhrase
92+
// - InstancePrincipals
93+
UseResourcePrincipals bool `mapstructure:"use_resource_principals"`
94+
8295
// If true, Packer will not create the image. Useful for setting to `true`
8396
// during a build test stage. Default `false`.
8497
SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"`
@@ -187,12 +200,19 @@ func (c *Config) Prepare(raws ...interface{}) error {
187200

188201
var tenancyOCID string
189202

190-
if c.InstancePrincipals {
191-
// We could go through all keys in one go and report that the below set
192-
// of keys cannot coexist with use_instance_principals but decided to
193-
// split them and report them seperately so that the user sees the specific
194-
// key involved.
195-
var message string = " cannot be present when use_instance_principals is set to true."
203+
if c.InstancePrincipals && c.UseResourcePrincipals {
204+
errs = packersdk.MultiErrorAppend(errs, errors.New("use_instance_principals and use_resource_principals cannot both be true"))
205+
}
206+
207+
if c.InstancePrincipals || c.UseResourcePrincipals {
208+
var authType string
209+
if c.InstancePrincipals {
210+
authType = "use_instance_principals"
211+
} else {
212+
authType = "use_resource_principals"
213+
}
214+
215+
var message string = fmt.Sprintf(" cannot be present when %s is set to true.", authType)
196216
if c.AccessCfgFile != "" {
197217
errs = packersdk.MultiErrorAppend(errs, errors.New("access_cfg_file"+message))
198218
}
@@ -217,22 +237,33 @@ func (c *Config) Prepare(raws ...interface{}) error {
217237
if c.PassPhrase != "" {
218238
errs = packersdk.MultiErrorAppend(errs, errors.New("pass_phrase"+message))
219239
}
220-
// This check is used to facilitate testing. During testing a Mock struct
221-
// is assigned to c.configProvider otherwise testing fails because Instance
222-
// Principals cannot be obtained.
240+
223241
if c.configProvider == nil {
224-
// Even though the previous configuration checks might fail we don't want
225-
// to skip this step. It seems that the logic behind the checks in this
226-
// file is to check everything even getting the configProvider.
227-
c.configProvider, err = ociauth.InstancePrincipalConfigurationProvider()
228-
if err != nil {
229-
return err
242+
if c.InstancePrincipals {
243+
c.configProvider, err = ociauth.InstancePrincipalConfigurationProvider()
244+
if err != nil {
245+
// Surface the error if we can't get a config provider.
246+
// This typically happens when not running on an OCI instance.
247+
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("failed to get instance principal configuration provider: %w", err))
248+
}
249+
} else { // c.UseResourcePrincipals
250+
c.configProvider, err = ociauth.ResourcePrincipalConfigurationProvider()
251+
if err != nil {
252+
// Surface the error if we can't get a config provider.
253+
// This typically happens when the required environment variables are not set.
254+
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("failed to get resource principal configuration provider: %w", err))
255+
}
230256
}
231257
}
232-
tenancyOCID, err = c.configProvider.TenancyOCID()
233-
if err != nil {
234-
return err
258+
// If configProvider is still nil due to an error, we can't proceed with TenancyOCID()
259+
if c.configProvider != nil {
260+
tenancyOCID, err = c.configProvider.TenancyOCID()
261+
if err != nil {
262+
// It's possible that TenancyOCID() fails even if the provider was created (e.g. permissions)
263+
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("failed to get tenancy OCID from provider: %w", err))
264+
}
235265
}
266+
236267
} else {
237268
// Determine where the SDK config is located
238269
if c.AccessCfgFile == "" {

builder/oci/config.hcl2spec.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

builder/oci/config_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,49 @@ func TestConfig(t *testing.T) {
419419
})
420420
}
421421

422+
// Test cases for use_resource_principals
423+
for _, k := range invalidKeys {
424+
t.Run(k+"_mixed_with_use_resource_principals", func(t *testing.T) {
425+
raw := testConfig(cfgFile)
426+
raw["use_resource_principals"] = "true"
427+
raw[k] = "some_random_value"
428+
429+
var c Config
430+
// We need to mock the config provider for resource principals as well,
431+
// similar to how it's done for instance principals.
432+
// Assuming a similar mock exists or can be created: resourcePrincipalConfigurationProviderMock
433+
c.configProvider = instancePrincipalConfigurationProviderMock{} // Or a new mock for resource principals
434+
435+
errs := c.Prepare(raw)
436+
437+
if errs == nil {
438+
t.Fatalf("Expected error when %s is mixed with use_resource_principals, but got none", k)
439+
}
440+
if !strings.Contains(errs.Error(), k) {
441+
t.Errorf("Expected error message for %s to contain '%s', but got: %s", k, k, errs.Error())
442+
}
443+
})
444+
}
445+
446+
t.Run("use_instance_principals_and_use_resource_principals_both_true", func(t *testing.T) {
447+
raw := testConfig(cfgFile)
448+
raw["use_instance_principals"] = "true"
449+
raw["use_resource_principals"] = "true"
450+
451+
var c Config
452+
c.configProvider = instancePrincipalConfigurationProviderMock{} // Mock provider
453+
454+
errs := c.Prepare(raw)
455+
456+
if errs == nil {
457+
t.Fatal("Expected error when both use_instance_principals and use_resource_principals are true, but got none")
458+
}
459+
expectedErrorMsg := "use_instance_principals and use_resource_principals cannot both be true"
460+
if !strings.Contains(errs.Error(), expectedErrorMsg) {
461+
t.Errorf("Expected error message to contain '%s', but got: %s", expectedErrorMsg, errs.Error())
462+
}
463+
})
464+
422465
t.Run("InstanceOptionsAreLegacyImdsEndpointsDisabledTrue", func(t *testing.T) {
423466
raw := testConfig(cfgFile)
424467
raw["instance_options_are_legacy_imds_endpoints_disabled"] = true

docs/builders/oci.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,12 @@ based on which authentication method is used.
109109
Principals](https://docs.cloud.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm)
110110
instead of User Principals. If this key is set to true, setting any one of the `access_cfg_file`,
111111
`access_cfg_file_account`, `region`, `tenancy_ocid`, `user_ocid`, `key_file`, `fingerprint`,
112-
`pass_phrase` parameters will cause an invalid configuration error.
112+
`pass_phrase`, or `use_resource_principals` parameters will cause an invalid configuration error.
113113
Defaults to `false`.
114114

115+
- `use_resource_principals` (boolean) - (Optional) Use Resource Principals for authentication. Resource Principals is a capability in Oracle Cloud Infrastructure Identity and Access Management (IAM) that allows resources (such as Functions, Data Flow applications, and API Gateways) to make service calls to other OCI services. This attribute is mutually exclusive with `access_cfg_file`, `access_cfg_file_account`, `user_ocid`, `tenancy_ocid`, `region`, `fingerprint`, `key_file`, `pass_phrase`, and `use_instance_principals`. Defaults to `false`.
116+
To use resource principals, the resource running Packer (e.g., a Compute instance, a Function) must be part of a [Dynamic Group](https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/managingdynamicgroups.htm) that has appropriate [Policies](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/policygetstarted.htm) granting it permissions to manage resources (e.g., launch instances, create images).
117+
115118
- `access_cfg_file` (string) - The path to the [OCI config
116119
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm).
117120
This parameter is _optional_ when using token-based authentication.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"builders":[{
3+
"use_resource_principals": "true",
4+
"availability_domain": "aaaa:PHX-AD-1",
5+
"base_image_ocid": "ocid1.image.oc1.phx.aaaaaaaa5yu6pw3riqtuhxzov7fdngi4tsteganmao54nq3pyxu3hxcuzmoa",
6+
"compartment_ocid": "ocid1.compartment.oc1..aaa",
7+
"image_name": "ResourcePrincipalExampleImage",
8+
"shape": "VM.Standard2.1",
9+
"ssh_username": "opc",
10+
"subnet_ocid": "ocid1.subnet.oc1..aaa",
11+
"type": "oracle-oci"
12+
}]
13+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) HashiCorp, Inc.
2+
# SPDX-License-Identifier: MPL-2.0
3+
4+
source "oracle-oci" "resource_principal_example" {
5+
availability_domain = "aaaa:PHX-AD-1"
6+
base_image_ocid = "ocid1.image.oc1.phx.aaaaaaaa5yu6pw3riqtuhxzov7fdngi4tsteganmao54nq3pyxu3hxcuzmoa"
7+
compartment_ocid = "ocid1.compartment.oc1..aaa"
8+
image_name = "ResourcePrincipalExampleImage"
9+
shape = "VM.Standard2.1"
10+
subnet_ocid = "ocid1.subnet.oc1..aaa"
11+
use_resource_principals = "true"
12+
ssh_username = "opc"
13+
}
14+
15+
build {
16+
sources = ["source.oracle-oci.resource_principal_example"]
17+
}

0 commit comments

Comments
 (0)