diff --git a/CHANGELOG.md b/CHANGELOG.md index 74e010582..bcfdf2ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ IMPROVEMENTS: * provider/aws: Added API Gateway integration update [GH-13249] * provider/aws: Add `identifier` | `name_prefix` to RDS resources [GH-13232] * provider/aws: Validate `aws_ecs_task_definition.container_definitions` [GH-12161] + * provider/aws: Update caller_identity data source [GH-13092] * provider/github: Handle the case when issue labels already exist [GH-13182] * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] * provider/triton: Move to joyent/triton-go [GH-13225] diff --git a/builtin/providers/aws/data_source_aws_caller_identity.go b/builtin/providers/aws/data_source_aws_caller_identity.go index 05c03864d..756bc9b52 100644 --- a/builtin/providers/aws/data_source_aws_caller_identity.go +++ b/builtin/providers/aws/data_source_aws_caller_identity.go @@ -5,6 +5,7 @@ import ( "log" "time" + "github.com/aws/aws-sdk-go/service/sts" "github.com/hashicorp/terraform/helper/schema" ) @@ -17,24 +18,40 @@ func dataSourceAwsCallerIdentity() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "user_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } func dataSourceAwsCallerIdentityRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*AWSClient) + client := meta.(*AWSClient).stsconn + + res, err := client.GetCallerIdentity(&sts.GetCallerIdentityInput{}) + if err != nil { + return fmt.Errorf("Error getting Caller Identity: %v", err) + } log.Printf("[DEBUG] Reading Caller Identity.") d.SetId(time.Now().UTC().String()) - if client.accountid == "" { + if *res.Account == "" { log.Println("[DEBUG] No Account ID available, failing") return fmt.Errorf("No AWS Account ID is available to the provider. Please ensure that\n" + "skip_requesting_account_id is not set on the AWS provider.") } - log.Printf("[DEBUG] Setting AWS Account ID to %s.", client.accountid) - d.Set("account_id", meta.(*AWSClient).accountid) - + log.Printf("[DEBUG] Setting AWS Account ID to %s.", *res.Account) + d.Set("account_id", res.Account) + d.Set("arn", res.Arn) + d.Set("user_id", res.UserId) return nil } diff --git a/builtin/providers/aws/data_source_aws_caller_identity_test.go b/builtin/providers/aws/data_source_aws_caller_identity_test.go index f874d7da9..100bb4db8 100644 --- a/builtin/providers/aws/data_source_aws_caller_identity_test.go +++ b/builtin/providers/aws/data_source_aws_caller_identity_test.go @@ -39,6 +39,14 @@ func testAccCheckAwsCallerIdentityAccountId(n string) resource.TestCheckFunc { return fmt.Errorf("Incorrect Account ID: expected %q, got %q", expected, rs.Primary.Attributes["account_id"]) } + if rs.Primary.Attributes["user_id"] == "" { + return fmt.Errorf("UserID expected to not be nil") + } + + if rs.Primary.Attributes["arn"] == "" { + return fmt.Errorf("ARN expected to not be nil") + } + return nil } } diff --git a/command/meta_backend.go b/command/meta_backend.go index c50214116..afcfe533a 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -1002,7 +1002,8 @@ func (m *Meta) backend_C_r_s( } // If the local state is not empty, we need to potentially do a - // state migration to the new backend (with user permission). + // state migration to the new backend (with user permission), unless the + // destination is also "local" if localS := localState.State(); !localS.Empty() { // Perform the migration err = m.backendMigrateState(&backendMigrateOpts{ @@ -1015,12 +1016,27 @@ func (m *Meta) backend_C_r_s( return nil, err } - // We always delete the local state - if err := localState.WriteState(nil); err != nil { - return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + // we usually remove the local state after migration to prevent + // confusion, but adding a default local backend block to the config + // can get us here too. Don't delete our state if the old and new paths + // are the same. + erase := true + if newLocalB, ok := b.(*backendlocal.Local); ok { + if localB, ok := localB.(*backendlocal.Local); ok { + if newLocalB.StatePath == localB.StatePath { + erase = false + } + } } - if err := localState.PersistState(); err != nil { - return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + + if erase { + // We always delete the local state, unless that was our new state too. + if err := localState.WriteState(nil); err != nil { + return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + } + if err := localState.PersistState(); err != nil { + return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) + } } } diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index 143759f98..ea0085dba 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -3275,6 +3275,51 @@ func TestMetaBackend_configureWithExtra(t *testing.T) { } } +// when confniguring a default local state, don't delete local state +func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend-empty"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // create our local state + orig := &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{ + "foo": { + Value: "bar", + Type: "string", + }, + }, + }, + }, + } + + err := (&state.LocalState{Path: DefaultStateFilename}).WriteState(orig) + if err != nil { + t.Fatal(err) + } + + m := testMetaBackend(t, nil) + m.forceInitCopy = true + // init the backend + _, err = m.Backend(&BackendOpts{ + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // check that we can read the state + s := testStateRead(t, DefaultStateFilename) + if s.Empty() { + t.Fatal("our state was deleted") + } +} + // move options from config to -backend-config func TestMetaBackend_configToExtra(t *testing.T) { // Create a temporary working directory that is empty diff --git a/helper/resource/testing.go b/helper/resource/testing.go index 9557207c3..23af81096 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -174,6 +174,13 @@ type TestStep struct { // determined by inspecting the state for ResourceName's ID. ImportStateId string + // ImportStateIdPrefix is the prefix added in front of ImportStateId. + // This can be useful in complex import cases, where more than one + // attribute needs to be passed on as the Import ID. Mainly in cases + // where the ID is not known, and a known prefix needs to be added to + // the unset ImportStateId field. + ImportStateIdPrefix string + // ImportStateCheck checks the results of ImportState. It should be // used to verify that the resulting value of ImportState has the // proper resources, IDs, and attributes. diff --git a/helper/resource/testing_import_state.go b/helper/resource/testing_import_state.go index d5c579629..28ad10526 100644 --- a/helper/resource/testing_import_state.go +++ b/helper/resource/testing_import_state.go @@ -25,6 +25,10 @@ func testStepImportState( importId = resource.Primary.ID } + importPrefix := step.ImportStateIdPrefix + if importPrefix != "" { + importId = fmt.Sprintf("%s%s", importPrefix, importId) + } // Setup the context. We initialize with an empty state. We use the // full config for provider configurations. diff --git a/helper/resource/testing_import_state_test.go b/helper/resource/testing_import_state_test.go index 56e6a4845..96b1edc3d 100644 --- a/helper/resource/testing_import_state_test.go +++ b/helper/resource/testing_import_state_test.go @@ -178,6 +178,80 @@ func TestTest_importStateDetectId(t *testing.T) { } } +func TestTest_importStateIdPrefix(t *testing.T) { + mp := testProvider() + mp.DiffReturn = nil + mp.ApplyFn = func( + info *terraform.InstanceInfo, + state *terraform.InstanceState, + diff *terraform.InstanceDiff) (*terraform.InstanceState, error) { + if !diff.Destroy { + return &terraform.InstanceState{ + ID: "foo", + }, nil + } + + return nil, nil + } + + mp.RefreshFn = func( + i *terraform.InstanceInfo, + s *terraform.InstanceState) (*terraform.InstanceState, error) { + return s, nil + } + + mp.ImportStateFn = func( + info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) { + if id != "bazfoo" { + return nil, fmt.Errorf("bad import ID: %s", id) + } + + return []*terraform.InstanceState{ + { + ID: "bar", + Ephemeral: terraform.EphemeralState{Type: "test_instance"}, + }, + }, nil + } + + checked := false + checkFn := func(s []*terraform.InstanceState) error { + checked = true + + if s[0].ID != "bar" { + return fmt.Errorf("bad: %#v", s) + } + + return nil + } + + mt := new(mockT) + Test(mt, TestCase{ + Providers: map[string]terraform.ResourceProvider{ + "test": mp, + }, + + Steps: []TestStep{ + { + Config: testConfigStr, + }, + { + ResourceName: "test_instance.foo", + ImportState: true, + ImportStateCheck: checkFn, + ImportStateIdPrefix: "baz", + }, + }, + }) + + if mt.failed() { + t.Fatalf("test failed: %s", mt.failMessage()) + } + if !checked { + t.Fatal("didn't call check") + } +} + func TestTest_importStateVerify(t *testing.T) { mp := testProvider() mp.DiffReturn = nil diff --git a/website/source/docs/providers/aws/d/caller_identity.html.markdown b/website/source/docs/providers/aws/d/caller_identity.html.markdown index ea0b1afcc..fdcd41c65 100644 --- a/website/source/docs/providers/aws/d/caller_identity.html.markdown +++ b/website/source/docs/providers/aws/d/caller_identity.html.markdown @@ -9,8 +9,8 @@ description: |- # aws\_caller\_identity -Use this data source to get the access to the effective Account ID in -which Terraform is working. +Use this data source to get the access to the effective Account ID, User ID, and ARN in +which Terraform is authorized. ~> **NOTE on `aws_caller_identity`:** - an Account ID is only available if `skip_requesting_account_id` is not set on the AWS provider. In such @@ -24,6 +24,14 @@ data "aws_caller_identity" "current" {} output "account_id" { value = "${data.aws_caller_identity.current.account_id}" } + +output "caller_arn" { + value = "${data.aws_caller_identity.current.arn}" +} + +output "caller_user" { + value = "${data.aws_caller_identity.current.user_id}" +} ``` ## Argument Reference @@ -32,4 +40,6 @@ There are no arguments available for this data source. ## Attributes Reference -`account_id` is set to the ID of the AWS account. +* `account_id` - The AWS Account ID number of the account that owns or contains the calling entity. +* `arn` - The AWS ARN associated with the calling entity. +* `user_id` - The unique identifier of the calling entity. diff --git a/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown b/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown index 29f4f0dc2..be2333dd7 100644 --- a/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown +++ b/website/source/docs/providers/bitbucket/r/default_reviewers.html.markdown @@ -13,7 +13,7 @@ Provides support for setting up default reviewers for your repository. ## Example Usage ``` -# Manage your respository +# Manage your repository resource "bitbucket_default_reviewers" "infrastructure" { owner = "myteam" repository = "terraform-code" diff --git a/website/source/docs/providers/bitbucket/r/hook.html.markdown b/website/source/docs/providers/bitbucket/r/hook.html.markdown index 49e991c58..404759e18 100644 --- a/website/source/docs/providers/bitbucket/r/hook.html.markdown +++ b/website/source/docs/providers/bitbucket/r/hook.html.markdown @@ -15,7 +15,7 @@ This allows you to manage your webhooks on a repository. ## Example Usage ``` -# Manage your respositories hooks +# Manage your repositories hooks resource "bitbucket_hook" "deploy_on_push" { owner = "myteam" repository = "terraform-code" diff --git a/website/source/docs/providers/bitbucket/r/repository.html.markdown b/website/source/docs/providers/bitbucket/r/repository.html.markdown index 7d1572a1a..cc584d9b1 100644 --- a/website/source/docs/providers/bitbucket/r/repository.html.markdown +++ b/website/source/docs/providers/bitbucket/r/repository.html.markdown @@ -16,7 +16,7 @@ private, how to fork the repository and other options. ## Example Usage ``` -# Manage your respository +# Manage your repository resource "bitbucket_repository" "infrastructure" { owner = "myteam" name = "terraform-code"